Implementation:ClickHouse ClickHouse Poco IPAddressImpl
base/poco/Net/src/IPAddressImpl.cpp:1-718
ClickHouse_ClickHouse
ClickHouse_ClickHouse_Network_Address_Representation
This page documents the implementation details of the low-level `IPv4AddressImpl` and `IPv6AddressImpl` classes. The corresponding header is already documented under the ClickHouse_ClickHouse_Network_Address_Representation principle.
Purpose
Implements the internal `Poco::Net::Impl::IPv4AddressImpl` and `Poco::Net::Impl::IPv6AddressImpl` classes, which are the concrete address storage backends for `IPAddress`. These classes encapsulate the platform-specific `struct in_addr` (IPv4) and `struct in6_addr` (IPv6) structures and provide operations for string conversion, address classification (loopback, multicast, link-local, etc.), bitwise arithmetic, prefix mask construction, and parsing from string representations.
Code Reference
IPv4AddressImpl -- String Conversion
std::string IPv4AddressImpl::toString() const
{
const UInt8* bytes = reinterpret_cast<const UInt8*>(&_addr);
std::string result;
result.reserve(16);
NumberFormatter::append(result, bytes[0]);
result.append(".");
NumberFormatter::append(result, bytes[1]);
result.append(".");
NumberFormatter::append(result, bytes[2]);
result.append(".");
NumberFormatter::append(result, bytes[3]);
return result;
}
IPv4AddressImpl -- Address Classification
Classification methods use network-byte-order masks against standard address ranges:
bool IPv4AddressImpl::isLoopback() const
{
return (ntohl(_addr.s_addr) & 0xFF000000) == 0x7F000000; // 127.0.0.0/8
}
bool IPv4AddressImpl::isMulticast() const
{
return (ntohl(_addr.s_addr) & 0xF0000000) == 0xE0000000; // 224.0.0.0/4
}
bool IPv4AddressImpl::isSiteLocal() const
{
UInt32 addr = ntohl(_addr.s_addr);
return (addr & 0xFF000000) == 0x0A000000 || // 10.0.0.0/8
(addr & 0xFFFF0000) == 0xC0A80000 || // 192.168.0.0/16
(addr >= 0xAC100000 && addr <= 0xAC1FFFFF); // 172.16.0.0/12
}
IPv4AddressImpl -- Prefix Mask Construction
IPv4AddressImpl::IPv4AddressImpl(unsigned prefix)
{
UInt32 addr = (prefix == 32) ? 0xffffffff : ~(0xffffffff >> prefix);
_addr.s_addr = ByteOrder::toNetwork(addr);
}
IPv6AddressImpl -- String Conversion
The IPv6 `toString` method handles IPv4-compatible/mapped addresses and zero compression (`::`):
std::string IPv6AddressImpl::toString() const
{
const UInt16* words = reinterpret_cast<const UInt16*>(&_addr);
if ((isIPv4Compatible() && !isLoopback()) || isIPv4Mapped())
{
// Format as ::ffff:a.b.c.d or ::a.b.c.d
}
else
{
// Standard colon-hex with zero-compression and scope ID
}
}
IPv6AddressImpl -- Parsing with Scope ID
IPv6AddressImpl IPv6AddressImpl::parse(const std::string& addr)
{
if (addr.empty()) return IPv6AddressImpl();
struct in6_addr ia;
std::string::size_type pos = addr.find('%');
if (std::string::npos != pos)
{
// Extract scope name and resolve via if_nametoindex
std::string unscopedAddr(addr, start, pos - start);
std::string scope(addr, pos + 1, ...);
Poco::UInt32 scopeId(0);
if (!(scopeId = if_nametoindex(scope.c_str())))
return IPv6AddressImpl();
if (inet_pton(AF_INET6, unscopedAddr.c_str(), &ia) == 1)
return IPv6AddressImpl(&ia, scopeId);
}
else
{
if (inet_pton(AF_INET6, addr.c_str(), &ia) == 1)
return IPv6AddressImpl(&ia);
}
return IPv6AddressImpl();
}
Helper: maskBits
A template function in an anonymous namespace computes the prefix length from a bitmask value:
template <typename T>
unsigned maskBits(T val, unsigned size)
{
unsigned count = 0;
if (val)
{
val = (val ^ (val - 1)) >> 1;
for (count = 0; val; ++count) val >>= 1;
}
else count = size;
return size - count;
}
I/O Contract
| Input | Output | Side Effects |
|---|---|---|
| `std::string` via `IPv4AddressImpl::parse` | `IPv4AddressImpl` (default if unparseable) | Uses `inet_aton` (or `inet_addr` on older GCC/VxWorks) |
| `std::string` via `IPv6AddressImpl::parse` | `IPv6AddressImpl` (default if unparseable) | Uses `inet_pton`; resolves scope names via `if_nametoindex` |
| `unsigned prefix` (constructor) | Address impl with prefix-length mask bits set | None |
| `IPv4AddressImpl & IPv4AddressImpl` via `operator &` | Bitwise AND result | None |
| `IPv6AddressImpl & IPv6AddressImpl` via `operator &` | Bitwise AND result | Throws if scope IDs mismatch |
| `const void*` (raw address bytes) | Impl wrapping the raw bytes | `std::memcpy` into internal `struct in_addr` / `struct in6_addr` |
Usage Examples
// These classes are internal; usage is through IPAddress
// IPv4 parsing
Poco::Net::Impl::IPv4AddressImpl addr4 = Poco::Net::Impl::IPv4AddressImpl::parse("192.168.1.1");
std::string str = addr4.toString(); // "192.168.1.1"
bool isLoop = addr4.isLoopback(); // false
// IPv6 parsing with scope
Poco::Net::Impl::IPv6AddressImpl addr6 = Poco::Net::Impl::IPv6AddressImpl::parse("fe80::1%eth0");
unsigned prefix = addr6.prefixLength();
// Mask construction
Poco::Net::Impl::IPv4AddressImpl mask24(24); // 255.255.255.0
Internal Details
- `IPv4AddressImpl` stores a `struct in_addr` (4 bytes) and performs all classification by converting to host byte order via `ntohl`.
- `IPv6AddressImpl` stores a `struct in6_addr` (16 bytes) plus a `Poco::UInt32 _scope` for the interface scope ID.
- `IPv6AddressImpl::prefixLength` iterates through the four 32-bit words in reverse to compute the CIDR prefix length using the `maskBits` helper. This is only implemented on Unix (`POCO_OS_FAMILY_UNIX`).
- The `mask` method is implemented only for IPv4; calling it on an IPv6 address throws `Poco::NotImplementedException`.
- Bitwise operators on `IPv6AddressImpl` require matching scope IDs; mismatches throw `Poco::InvalidArgumentException`.
- The `clone` method returns a heap-allocated copy, used by the pImpl reference-counting machinery.