// need to check for V4 address second, since IPv4-mapped IPv6 addresses may
// contain a period
if (ip.find(':') != string::npos) {
- in6_addr ipAddr;
- if (inet_pton(AF_INET6, ip.c_str(), &ipAddr) != 1) {
- throwFormatException("inet_pton failed for V6 address");
+ struct addrinfo* result;
+ struct addrinfo hints;
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_family = AF_INET6;
+ hints.ai_socktype = SOCK_STREAM;
+ hints.ai_flags = AI_NUMERICHOST;
+ if (!getaddrinfo(ip.c_str(), nullptr, &hints, &result)) {
+ struct sockaddr_in6* ipAddr = (struct sockaddr_in6*)result->ai_addr;
+ addr_ = IPAddressV46(IPAddressV6(*ipAddr));
+ family_ = AF_INET6;
+ freeaddrinfo(result);
+ } else {
+ throwFormatException("getsockaddr failed for V6 address");
}
- addr_ = IPAddressV46(IPAddressV6(ipAddr));
- family_ = AF_INET6;
} else if (ip.find('.') != string::npos) {
in_addr ipAddr;
if (inet_pton(AF_INET, ip.c_str(), &ipAddr) != 1) {
}
case AF_INET6: {
const sockaddr_in6 *v6addr = reinterpret_cast<const sockaddr_in6*>(addr);
- addr_.ipV6Addr = IPAddressV6(v6addr->sin6_addr);
+ addr_.ipV6Addr = IPAddressV6(*v6addr);
break;
}
default:
sockaddr_in6 *sin = reinterpret_cast<sockaddr_in6*>(dest);
sin->sin6_addr = asV6().toAddr();
sin->sin6_port = port;
+ sin->sin6_scope_id = asV6().getScopeId();
return sizeof(*sin);
} else {
throw InvalidAddressFamilyException(family());
ip = ip.substr(1, ip.size() - 2);
}
- if (inet_pton(AF_INET6, ip.c_str(), &addr_.in6Addr_) != 1) {
+ struct addrinfo* result;
+ struct addrinfo hints;
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_family = AF_INET6;
+ hints.ai_socktype = SOCK_STREAM;
+ hints.ai_flags = AI_NUMERICHOST;
+ if (!getaddrinfo(ip.c_str(), nullptr, &hints, &result)) {
+ struct sockaddr_in6* ipAddr = (struct sockaddr_in6*)result->ai_addr;
+ addr_.in6Addr_ = ipAddr->sin6_addr;
+ scope_ = ipAddr->sin6_scope_id;
+ freeaddrinfo(result);
+ } else {
throw IPAddressFormatException("Invalid IPv6 address '", ip, "'");
}
}
{
}
+// sockaddr_in6 constructor
+IPAddressV6::IPAddressV6(const sockaddr_in6& src)
+ : addr_(src.sin6_addr)
+ , scope_(src.sin6_scope_id)
+{
+}
+
// ByteArray16 constructor
IPAddressV6::IPAddressV6(const ByteArray16& src)
: addr_(src)
"be 16 bytes, got ", bytes.size());
}
memcpy(&addr_.in6Addr_.s6_addr, bytes.data(), sizeof(in6_addr));
+ scope_ = 0;
}
// public
// public
string IPAddressV6::str() const {
char buffer[INET6_ADDRSTRLEN] = {0};
- if (!inet_ntop(AF_INET6, &addr_.in6Addr_, buffer, INET6_ADDRSTRLEN)) {
+ sockaddr_in6 sock = toSockAddr();
+ if (!getnameinfo(
+ (sockaddr*)&sock, sizeof(sock),
+ buffer, INET6_ADDRSTRLEN,
+ nullptr, 0, NI_NUMERICHOST)) {
+ string ip(buffer);
+ return std::move(ip);
+ } else {
throw IPAddressFormatException("Invalid address with hex ",
"'", detail::Bytes::toHex(bytes(), 16), "'");
}
- string ip(buffer);
- return std::move(ip);
}
// public
* isTeredo, isIPv4Mapped, tryCreateIPv4, type
*
* @see IPAddress
+ *
+ * Notes on scope ID parsing:
+ *
+ * getaddrinfo() uses if_nametoindex() to convert interface names
+ * into a numerical index. For instance,
+ * "fe80::202:c9ff:fec1:ee08%eth0" may return scope ID 2 on some
+ * hosts, but other numbers on other hosts. It will fail entirely on
+ * hosts without an eth0 interface.
+ *
+ * Serializing / Deserializing IPAddressB6's on different hosts
+ * that use link-local scoping probably won't work.
*/
class IPAddressV6 : boost::totally_ordered<IPAddressV6> {
public:
// Create an IPAddressV6 from a string
// @throws IPAddressFormatException
+ //
explicit IPAddressV6(StringPiece ip);
// ByteArray16 constructor
// in6_addr constructor
explicit IPAddressV6(const in6_addr& src);
+ // sockaddr_in6 constructor
+ explicit IPAddressV6(const sockaddr_in6& src);
+
/**
* Create a link-local IPAddressV6 from the specified ethernet MAC address.
*/
// return underlying in6_addr structure
in6_addr toAddr() const { return addr_.in6Addr_; }
+ uint16_t getScopeId() const { return scope_; }
+ void setScopeId(uint16_t scope) {
+ scope_ = scope;
+ }
+
sockaddr_in6 toSockAddr() const {
sockaddr_in6 addr;
memset(&addr, 0, sizeof(sockaddr_in6));
addr.sin6_family = AF_INET6;
+ addr.sin6_scope_id = scope_;
memcpy(&addr.sin6_addr, &addr_.in6Addr_, sizeof(in6_addr));
return addr;
}
explicit AddressStorage(MacAddress mac);
} addr_;
+ // Link-local scope id. This should always be 0 for IPAddresses that
+ // are *not* link-local.
+ uint16_t scope_{0};
+
static const std::array<ByteArray16, 129> masks_;
/**
* Return true if two addresses are equal.
*/
inline bool operator==(const IPAddressV6& addr1, const IPAddressV6& addr2) {
- return (std::memcmp(addr1.toAddr().s6_addr, addr2.toAddr().s6_addr, 16) == 0);
+ return (std::memcmp(addr1.toAddr().s6_addr, addr2.toAddr().s6_addr, 16) == 0)
+ && addr1.getScopeId() == addr2.getScopeId();
}
// Return true if addr1 < addr2
inline bool operator<(const IPAddressV6& addr1, const IPAddressV6& addr2) {
- return (std::memcmp(addr1.toAddr().s6_addr, addr2.toAddr().s6_addr, 16) < 0);
+ auto cmp = std::memcmp(addr1.toAddr().s6_addr,
+ addr2.toAddr().s6_addr, 16) < 0;
+ if (!cmp) {
+ return addr1.getScopeId() < addr2.getScopeId();
+ } else {
+ return cmp;
+ }
}
} // folly
#endif
#include <sys/types.h>
+#include <netdb.h>
}
#include <folly/Conv.h>
// tests code example
TEST(IPAddress, CodeExample) {
EXPECT_EQ(4, sizeof(IPAddressV4));
- EXPECT_EQ(16, sizeof(IPAddressV6));
- EXPECT_EQ(20, sizeof(IPAddress));
+ EXPECT_EQ(20, sizeof(IPAddressV6));
+ EXPECT_EQ(24, sizeof(IPAddress));
IPAddress uninitaddr;
IPAddress v4addr("192.0.2.129");
IPAddress v6map("::ffff:192.0.2.129");
EXPECT_TRUE(IPAddress::createIPv6(v4addr) == v6map.asV6());
}
+TEST(IPAddress, Scope) {
+ // Test that link-local scope is saved
+ auto str = "fe80::62eb:69ff:fe9b:ba60%eth0";
+ IPAddressV6 a2(str);
+ EXPECT_EQ(str, a2.str());
+
+ sockaddr_in6 sock = a2.toSockAddr();
+ EXPECT_NE(0, sock.sin6_scope_id);
+
+ IPAddress a1(str);
+ EXPECT_EQ(str, a1.str());
+
+ a2.setScopeId(0);
+ EXPECT_NE(a1, a2);
+
+ EXPECT_TRUE(a2 < a1);
+}
+
TEST(IPAddress, Ordering) {
IPAddress a1("0.1.1.1");
IPAddress a2("1.1.1.0");
{
// setup
sockaddr_in6 addr;
+ memset(&addr, 0, sizeof(addr));
in6_addr sin_addr;
ByteArray16 sec{{
// 2620:0:1cfe:face:b00c::3