X-Git-Url: http://plrg.eecs.uci.edu/git/?p=folly.git;a=blobdiff_plain;f=folly%2FIPAddressV6.cpp;h=cdd82acc46a6fd0826c41917ad8e8fa87c11ee77;hp=70f5c883c2b5a238dca7d190822c2d9d7527a7fc;hb=f6ed4a26c0f3e6ba12788206e54add9db18e4dd6;hpb=0b856bd5556ab370e029ff8f2a490db7dd4f3940 diff --git a/folly/IPAddressV6.cpp b/folly/IPAddressV6.cpp index 70f5c883..cdd82acc 100644 --- a/folly/IPAddressV6.cpp +++ b/folly/IPAddressV6.cpp @@ -31,8 +31,8 @@ // Because of the massive pain that is libnl, this can't go into the socket // portability header as you can't include and in // the same translation unit without getting errors -_-... -#include -#include +#include // @manual +#include // @manual // Alias the max size of an interface name to what posix expects. #define IFNAMSIZ IF_NAMESIZE @@ -62,18 +62,8 @@ void toAppend(IPAddressV6 addr, fbstring* result) { result->append(addr.str()); } -bool IPAddressV6::validate(StringPiece ip) { - if (ip.size() > 0 && ip.front() == '[' && ip.back() == ']') { - ip = ip.subpiece(1, ip.size() - 2); - } - - constexpr size_t kStrMaxLen = INET6_ADDRSTRLEN; - std::array ip_cstr; - const size_t len = std::min(ip.size(), kStrMaxLen); - std::memcpy(ip_cstr.data(), ip.data(), len); - ip_cstr[len] = 0; - struct in6_addr addr; - return 1 == inet_pton(AF_INET6, ip_cstr.data(), &addr); +bool IPAddressV6::validate(StringPiece ip) noexcept { + return tryFromString(ip).hasValue(); } // public default constructor @@ -81,12 +71,21 @@ IPAddressV6::IPAddressV6() {} // public string constructor IPAddressV6::IPAddressV6(StringPiece addr) { - auto ip = addr.str(); + auto maybeIp = tryFromString(addr); + if (maybeIp.hasError()) { + throw IPAddressFormatException( + to("Invalid IPv6 address '", addr, "'")); + } + *this = std::move(maybeIp.value()); +} + +Expected IPAddressV6::tryFromString( + StringPiece str) noexcept { + auto ip = str.str(); // Allow addresses surrounded in brackets if (ip.size() < 2) { - throw IPAddressFormatException( - sformat("Invalid IPv6 address '{}': address too short", ip)); + return makeUnexpected(IPAddressFormatError::INVALID_IP); } if (ip.front() == '[' && ip.back() == ']') { ip = ip.substr(1, ip.size() - 2); @@ -98,25 +97,26 @@ IPAddressV6::IPAddressV6(StringPiece addr) { 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_ = uint16_t(ipAddr->sin6_scope_id); - freeaddrinfo(result); - } else { - throw IPAddressFormatException(sformat("Invalid IPv6 address '{}'", ip)); + if (::getaddrinfo(ip.c_str(), nullptr, &hints, &result) == 0) { + SCOPE_EXIT { + ::freeaddrinfo(result); + }; + const struct sockaddr_in6* sa = + reinterpret_cast(result->ai_addr); + return IPAddressV6(*sa); } + return makeUnexpected(IPAddressFormatError::INVALID_IP); } // in6_addr constructor -IPAddressV6::IPAddressV6(const in6_addr& src) : addr_(src) {} +IPAddressV6::IPAddressV6(const in6_addr& src) noexcept : addr_(src) {} // sockaddr_in6 constructor -IPAddressV6::IPAddressV6(const sockaddr_in6& src) +IPAddressV6::IPAddressV6(const sockaddr_in6& src) noexcept : addr_(src.sin6_addr), scope_(uint16_t(src.sin6_scope_id)) {} // ByteArray16 constructor -IPAddressV6::IPAddressV6(const ByteArray16& src) : addr_(src) {} +IPAddressV6::IPAddressV6(const ByteArray16& src) noexcept : addr_(src) {} // link-local constructor IPAddressV6::IPAddressV6(LinkLocalTag, MacAddress mac) : addr_(mac) {} @@ -138,20 +138,23 @@ IPAddressV6::AddressStorage::AddressStorage(MacAddress mac) { Optional IPAddressV6::getMacAddressFromLinkLocal() const { // Returned MacAddress must be constructed from a link-local IPv6 address. - if (!(addr_.bytes_[0] == 0xfe && addr_.bytes_[1] == 0x80 && - addr_.bytes_[2] == 0x00 && addr_.bytes_[3] == 0x00 && - addr_.bytes_[4] == 0x00 && addr_.bytes_[5] == 0x00 && - addr_.bytes_[6] == 0x00 && addr_.bytes_[7] == 0x00 && - addr_.bytes_[11] == 0xff && addr_.bytes_[12] == 0xfe)) { + if (!isLinkLocal()) { return folly::none; } - // The link-local address uses modified EUI-64 format, + return getMacAddressFromEUI64(); +} + +Optional IPAddressV6::getMacAddressFromEUI64() const { + if (!(addr_.bytes_[11] == 0xff && addr_.bytes_[12] == 0xfe)) { + return folly::none; + } + // The auto configured address uses modified EUI-64 format, // See RFC 4291 sections 2.5.1, 2.5.6, and Appendix A std::array bytes; - // Step 1: first 8 bytes are fe:80:00:00:00:00:00:00, and can be stripped + // Step 1: first 8 bytes are network prefix, and can be stripped // Step 2: invert the universal/local (U/L) flag (bit 7) bytes[0] = addr_.bytes_[8] ^ 0x02; - // Step 3: copy thhese bytes are they are + // Step 3: copy these bytes as they are bytes[1] = addr_.bytes_[9]; bytes[2] = addr_.bytes_[10]; // Step 4: strip bytes (0xfffe), which are bytes_[11] and bytes_[12] @@ -162,14 +165,34 @@ Optional IPAddressV6::getMacAddressFromLinkLocal() const { return Optional(MacAddress::fromBinary(range(bytes))); } -void IPAddressV6::setFromBinary(ByteRange bytes) { - if (bytes.size() != 16) { - throw IPAddressFormatException(sformat( - "Invalid IPv6 binary data: length must be 16 bytes, got {}", +IPAddressV6 IPAddressV6::fromBinary(ByteRange bytes) { + auto maybeIp = tryFromBinary(bytes); + if (maybeIp.hasError()) { + throw IPAddressFormatException(to( + "Invalid IPv6 binary data: length must be 16 bytes, got ", bytes.size())); } + return maybeIp.value(); +} + +Expected IPAddressV6::tryFromBinary( + ByteRange bytes) noexcept { + IPAddressV6 addr; + auto setResult = addr.trySetFromBinary(bytes); + if (setResult.hasError()) { + return makeUnexpected(std::move(setResult.error())); + } + return addr; +} + +Expected IPAddressV6::trySetFromBinary( + ByteRange bytes) noexcept { + if (bytes.size() != 16) { + return makeUnexpected(IPAddressFormatError::INVALID_IP); + } memcpy(&addr_.in6Addr_.s6_addr, bytes.data(), sizeof(in6_addr)); scope_ = 0; + return unit; } // static