X-Git-Url: http://plrg.eecs.uci.edu/git/?p=folly.git;a=blobdiff_plain;f=folly%2FIPAddress.h;h=943c5cec28a5425517a54d08968433862b912a1d;hp=c5f31c778336802610cbd601adfd4a7d6a4c57ab;hb=9bf260657e140cace3cab8850d6a40fac3a5cd93;hpb=dc93938852aa64604563afbc8d185062f01153f2 diff --git a/folly/IPAddress.h b/folly/IPAddress.h index c5f31c77..943c5cec 100644 --- a/folly/IPAddress.h +++ b/folly/IPAddress.h @@ -1,5 +1,5 @@ /* - * Copyright 2016 Facebook, Inc. + * Copyright 2014-present Facebook, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -17,19 +17,15 @@ #pragma once #include -#include +#include #include #include #include // std::pair -#include - -#include -#include -#include #include #include #include +#include #include namespace folly { @@ -68,10 +64,16 @@ typedef std::pair CIDRNetwork; * CHECK(IPAddress::createIPv6(v4addr) == v6map.asV6()); * @encode */ -class IPAddress : boost::totally_ordered { +class IPAddress { + private: + template + auto pick(F f) const { + return isV4() ? f(asV4()) : f(asV6()); + } + public: // returns true iff the input string can be parsed as an ip-address - static bool validate(StringPiece ip); + static bool validate(StringPiece ip) noexcept; // return the V4 representation of the address, converting it from V6 to V4 if // needed. Note that this will throw an IPAddressFormatException if the V6 @@ -89,11 +91,26 @@ class IPAddress : boost::totally_ordered { * is -1, will use /32 for IPv4 and /128 for IPv6) * @param [in] mask apply mask on the address or not, * e.g. 192.168.13.46/24 => 192.168.13.0/24 + * @return either pair with IPAddress network and uint8_t mask or + * CIDRNetworkError + */ + static Expected tryCreateNetwork( + StringPiece ipSlashCidr, + int defaultCidr = -1, + bool mask = true); + + /** + * Create a network and mask from a CIDR formatted address string. + * Same as tryCreateNetwork() but throws IPAddressFormatException on error. + * The implementation calls tryCreateNetwork(...) underneath + * * @throws IPAddressFormatException if invalid address * @return pair with IPAddress network and uint8_t mask */ static CIDRNetwork createNetwork( - StringPiece ipSlashCidr, int defaultCidr = -1, bool mask = true); + StringPiece ipSlashCidr, + int defaultCidr = -1, + bool mask = true); /** * Return a string representation of a CIDR block created with createNetwork. @@ -101,9 +118,7 @@ class IPAddress : boost::totally_ordered { * * @return string representing the netblock */ - static std::string networkToString(const CIDRNetwork& network) { - return network.first.str() + "/" + folly::to(network.second); - } + static std::string networkToString(const CIDRNetwork& network); /** * Create a new IPAddress instance from the provided binary data @@ -112,6 +127,20 @@ class IPAddress : boost::totally_ordered { */ static IPAddress fromBinary(ByteRange bytes); + /** + * Non-throwing version of fromBinary(). + * On failure returns IPAddressFormatError. + */ + static Expected tryFromBinary( + ByteRange bytes) noexcept; + + /** + * Tries to create a new IPAddress instance from provided string and + * returns it on success. Returns IPAddressFormatError on failure. + */ + static Expected tryFromString( + StringPiece str) noexcept; + /** * Create an IPAddress from a 32bit long (network byte order). * @throws IPAddressFormatException @@ -122,8 +151,9 @@ class IPAddress : boost::totally_ordered { // Given 2 IPAddress,mask pairs extract the longest common IPAddress, // mask pair - static CIDRNetwork longestCommonPrefix(const CIDRNetwork& one, - const CIDRNetwork& two); + static CIDRNetwork longestCommonPrefix( + const CIDRNetwork& one, + const CIDRNetwork& two); /** * Constructs an uninitialized IPAddress. @@ -149,31 +179,29 @@ class IPAddress : boost::totally_ordered { explicit IPAddress(const sockaddr* addr); // Create an IPAddress from a V4 address - /* implicit */ IPAddress(const IPAddressV4 ipV4Addr); - /* implicit */ IPAddress(const in_addr addr); + /* implicit */ IPAddress(const IPAddressV4 ipV4Addr) noexcept; + /* implicit */ IPAddress(const in_addr addr) noexcept; // Create an IPAddress from a V6 address - /* implicit */ IPAddress(const IPAddressV6& ipV6Addr); - /* implicit */ IPAddress(const in6_addr& addr); + /* implicit */ IPAddress(const IPAddressV6& ipV6Addr) noexcept; + /* implicit */ IPAddress(const in6_addr& addr) noexcept; // Assign from V4 address - IPAddress& operator=(const IPAddressV4& ipV4Addr); + IPAddress& operator=(const IPAddressV4& ipV4Addr) noexcept; // Assign from V6 address - IPAddress& operator=(const IPAddressV6& ipV6Addr); + IPAddress& operator=(const IPAddressV6& ipV6Addr) noexcept; /** * Converts an IPAddress to an IPAddressV4 instance. * @note This is not some handy convenience wrapper to convert an IPv4 address * to a mapped IPv6 address. If you want that use * IPAddress::createIPv6(addr) - * @throws IPAddressFormatException is not a V4 instance + * @throws InvalidAddressFamilyException is not a V4 instance */ const IPAddressV4& asV4() const { - if (!isV4()) { - auto familyName = detail::familyNameStr(family()); - throw InvalidAddressFamilyException("Can't convert address with family ", - familyName, " to AF_INET address"); + if (UNLIKELY(!isV4())) { + asV4Throw(); } return addr_.ipV4Addr; } @@ -183,19 +211,19 @@ class IPAddress : boost::totally_ordered { * @throws InvalidAddressFamilyException is not a V6 instance */ const IPAddressV6& asV6() const { - if (!isV6()) { - auto familyName = detail::familyNameStr(family()); - throw InvalidAddressFamilyException("Can't convert address with family ", - familyName, " to AF_INET6 address"); + if (UNLIKELY(!isV6())) { + asV6Throw(); } return addr_.ipV6Addr; } // Return sa_family_t of IPAddress - sa_family_t family() const { return family_; } + sa_family_t family() const { + return family_; + } // Populate sockaddr_storage with an appropriate value - int toSockaddrStorage(sockaddr_storage *dest, uint16_t port = 0) const { + int toSockaddrStorage(sockaddr_storage* dest, uint16_t port = 0) const { if (dest == nullptr) { throw IPAddressFormatException("dest must not be null"); } @@ -203,7 +231,7 @@ class IPAddress : boost::totally_ordered { dest->ss_family = family(); if (isV4()) { - sockaddr_in *sin = reinterpret_cast(dest); + sockaddr_in* sin = reinterpret_cast(dest); sin->sin_addr = asV4().toAddr(); sin->sin_port = port; #if defined(__APPLE__) @@ -211,7 +239,7 @@ class IPAddress : boost::totally_ordered { #endif return sizeof(*sin); } else if (isV6()) { - sockaddr_in6 *sin = reinterpret_cast(dest); + sockaddr_in6* sin = reinterpret_cast(dest); sin->sin6_addr = asV6().toAddr(); sin->sin6_port = port; sin->sin6_scope_id = asV6().getScopeId(); @@ -264,43 +292,49 @@ class IPAddress : boost::totally_ordered { } // @return true if address is uninitialized - bool empty() const { return (family_ == AF_UNSPEC); } + bool empty() const { + return family_ == AF_UNSPEC; + } // @return true if address is initialized - explicit operator bool() const { return !empty(); } + explicit operator bool() const { + return !empty(); + } // @return true if this is an IPAddressV4 instance - bool isV4() const { return (family_ == AF_INET); } + bool isV4() const { + return family_ == AF_INET; + } // @return true if this is an IPAddressV6 instance - bool isV6() const { return (family_ == AF_INET6); } + bool isV6() const { + return family_ == AF_INET6; + } // @return true if this address is all zeros bool isZero() const { - return isV4() ? asV4().isZero() - : asV6().isZero(); + return pick([&](auto& _) { return _.isZero(); }); } // Number of bits in the address representation. size_t bitCount() const { - return isV4() ? IPAddressV4::bitCount() - : IPAddressV6::bitCount(); + return pick([&](auto& _) { return _.bitCount(); }); } // Number of bytes in the address representation. size_t byteCount() const { return bitCount() / 8; } - //get nth most significant bit - 0 indexed + // get nth most significant bit - 0 indexed bool getNthMSBit(size_t bitIndex) const { return detail::getNthMSBitImpl(*this, bitIndex, family()); } - //get nth most significant byte - 0 indexed + // get nth most significant byte - 0 indexed uint8_t getNthMSByte(size_t byteIndex) const; - //get nth bit - 0 indexed + // get nth bit - 0 indexed bool getNthLSBit(size_t bitIndex) const { return getNthMSBit(bitCount() - bitIndex - 1); } - //get nth byte - 0 indexed + // get nth byte - 0 indexed uint8_t getNthLSByte(size_t byteIndex) const { return getNthMSByte(byteCount() - byteIndex - 1); } @@ -312,32 +346,27 @@ class IPAddress : boost::totally_ordered { * {family:'AF_INET|AF_INET6', addr:'address', hash:long}. */ std::string toJson() const { - return isV4() ? asV4().toJson() - : asV6().toJson(); + return pick([&](auto& _) { return _.toJson(); }); } // Hash of address std::size_t hash() const { - return isV4() ? asV4().hash() - : asV6().hash(); + return pick([&](auto& _) { return _.hash(); }); } // Return true if the address qualifies as localhost. bool isLoopback() const { - return isV4() ? asV4().isLoopback() - : asV6().isLoopback(); + return pick([&](auto& _) { return _.isLoopback(); }); } // Return true if the address qualifies as link local bool isLinkLocal() const { - return isV4() ? asV4().isLinkLocal() - : asV6().isLinkLocal(); + return pick([&](auto& _) { return _.isLinkLocal(); }); } // Return true if the address qualifies as broadcast. bool isLinkLocalBroadcast() const { - return isV4() ? asV4().isLinkLocalBroadcast() - : asV6().isLinkLocalBroadcast(); + return pick([&](auto& _) { return _.isLinkLocalBroadcast(); }); } /** @@ -347,8 +376,7 @@ class IPAddress : boost::totally_ordered { * 2000::/3, ffxe::/16. */ bool isNonroutable() const { - return isV4() ? asV4().isNonroutable() - : asV6().isNonroutable(); + return pick([&](auto& _) { return _.isNonroutable(); }); } /** @@ -356,14 +384,12 @@ class IPAddress : boost::totally_ordered { * (for example, 192.168.xxx.xxx or fc00::/7 addresses) */ bool isPrivate() const { - return isV4() ? asV4().isPrivate() - : asV6().isPrivate(); + return pick([&](auto& _) { return _.isPrivate(); }); } // Return true if the address is a multicast address. bool isMulticast() const { - return isV4() ? asV4().isMulticast() - : asV6().isMulticast(); + return pick([&](auto& _) { return _.isMulticast(); }); } /** @@ -373,8 +399,7 @@ class IPAddress : boost::totally_ordered { * @return IPAddress instance with bits set to 0 */ IPAddress mask(uint8_t numBits) const { - return isV4() ? IPAddress(asV4().mask(numBits)) - : IPAddress(asV6().mask(numBits)); + return pick([&](auto& _) { return IPAddress(_.mask(numBits)); }); } /** @@ -383,8 +408,7 @@ class IPAddress : boost::totally_ordered { * @throws IPAddressFormatException on inet_ntop error */ std::string str() const { - return isV4() ? asV4().str() - : asV6().str(); + return pick([&](auto& _) { return _.str(); }); } /** @@ -393,33 +417,39 @@ class IPAddress : boost::totally_ordered { * this is the hex representation with : characters inserted every 4 digits. */ std::string toFullyQualified() const { - return isV4() ? asV4().toFullyQualified() - : asV6().toFullyQualified(); + return pick([&](auto& _) { return _.toFullyQualified(); }); + } + + /// Same as toFullyQualified but append to an output string. + void toFullyQualifiedAppend(std::string& out) const { + return pick([&](auto& _) { return _.toFullyQualifiedAppend(out); }); } // Address version (4 or 6) uint8_t version() const { - return isV4() ? asV4().version() - : asV6().version(); + return pick([&](auto& _) { return _.version(); }); } /** * Access to address bytes, in network byte order. */ const unsigned char* bytes() const { - return isV4() ? asV4().bytes() : asV6().bytes(); + return pick([&](auto& _) { return _.bytes(); }); } private: + [[noreturn]] void asV4Throw() const; + [[noreturn]] void asV6Throw() const; + typedef union IPAddressV46 { IPAddressV4 ipV4Addr; IPAddressV6 ipV6Addr; // default constructor - IPAddressV46() { + IPAddressV46() noexcept { std::memset(this, 0, sizeof(IPAddressV46)); } - explicit IPAddressV46(const IPAddressV4& addr): ipV4Addr(addr) {} - explicit IPAddressV46(const IPAddressV6& addr): ipV6Addr(addr) {} + explicit IPAddressV46(const IPAddressV4& addr) noexcept : ipV4Addr(addr) {} + explicit IPAddressV46(const IPAddressV6& addr) noexcept : ipV6Addr(addr) {} } IPAddressV46; IPAddressV46 addr_; sa_family_t family_; @@ -444,14 +474,27 @@ void toAppend(IPAddress addr, fbstring* result); bool operator==(const IPAddress& addr1, const IPAddress& addr2); // Return true if addr1 < addr2 bool operator<(const IPAddress& addr1, const IPAddress& addr2); - -} // folly +// Derived operators +inline bool operator!=(const IPAddress& a, const IPAddress& b) { + return !(a == b); +} +inline bool operator>(const IPAddress& a, const IPAddress& b) { + return b < a; +} +inline bool operator<=(const IPAddress& a, const IPAddress& b) { + return !(a > b); +} +inline bool operator>=(const IPAddress& a, const IPAddress& b) { + return !(a < b); +} + +} // namespace folly namespace std { -template<> +template <> struct hash { size_t operator()(const folly::IPAddress& addr) const { return addr.hash(); } }; -} // std +} // namespace std