/*
- * Copyright 2016 Facebook, Inc.
+ * Copyright 2017 Facebook, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
#pragma once
#include <functional>
-#include <iostream>
+#include <iosfwd>
#include <memory>
#include <string>
#include <utility> // std::pair
-#include <boost/operators.hpp>
-
-#include <folly/Conv.h>
-#include <folly/Format.h>
-#include <folly/Range.h>
#include <folly/IPAddressException.h>
#include <folly/IPAddressV4.h>
#include <folly/IPAddressV6.h>
+#include <folly/Range.h>
#include <folly/detail/IPAddress.h>
namespace folly {
* CHECK(IPAddress::createIPv6(v4addr) == v6map.asV6());
* @encode
*/
-class IPAddress : boost::totally_ordered<IPAddress> {
+class IPAddress {
+ private:
+ template <typename F>
+ 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) 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
// address is not IPv4Mapped.
* @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.
*
* @return string representing the netblock
*/
- static std::string networkToString(const CIDRNetwork& network) {
- return network.first.str() + "/" + folly::to<std::string>(network.second);
- }
+ static std::string networkToString(const CIDRNetwork& network);
/**
* Create a new IPAddress instance from the provided binary data
*/
static IPAddress fromBinary(ByteRange bytes);
+ /**
+ * Non-throwing version of fromBinary().
+ * On failure returns IPAddressFormatError.
+ */
+ static Expected<IPAddress, IPAddressFormatError> 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<IPAddress, IPAddressFormatError> tryFromString(
+ StringPiece str) noexcept;
+
/**
* Create an IPAddress from a 32bit long (network byte order).
* @throws IPAddressFormatException
// 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.
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;
}
* @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");
}
memset(dest, 0, sizeof(sockaddr_storage));
dest->ss_family = family();
+
if (isV4()) {
- sockaddr_in *sin = reinterpret_cast<sockaddr_in*>(dest);
+ sockaddr_in* sin = reinterpret_cast<sockaddr_in*>(dest);
sin->sin_addr = asV4().toAddr();
sin->sin_port = port;
+#if defined(__APPLE__)
+ sin->sin_len = sizeof(*sin);
+#endif
return sizeof(*sin);
} else if (isV6()) {
- sockaddr_in6 *sin = reinterpret_cast<sockaddr_in6*>(dest);
+ sockaddr_in6* sin = reinterpret_cast<sockaddr_in6*>(dest);
sin->sin6_addr = asV6().toAddr();
sin->sin6_port = port;
sin->sin6_scope_id = asV6().getScopeId();
+#if defined(__APPLE__)
+ sin->sin6_len = sizeof(*sin);
+#endif
return sizeof(*sin);
} else {
throw InvalidAddressFamilyException(family());
}
// @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);
}
* {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(); });
}
/**
* 2000::/3, ffxe::/16.
*/
bool isNonroutable() const {
- return isV4() ? asV4().isNonroutable()
- : asV6().isNonroutable();
+ return pick([&](auto& _) { return _.isNonroutable(); });
}
/**
* (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(); });
}
/**
* @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)); });
}
/**
* @throws IPAddressFormatException on inet_ntop error
*/
std::string str() const {
- return isV4() ? asV4().str()
- : asV6().str();
+ return pick([&](auto& _) { return _.str(); });
}
/**
* 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_;
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<folly::IPAddress> {
size_t operator()(const folly::IPAddress& addr) const {
return addr.hash();
}
};
-} // std
+} // namespace std