From 415519606e63545a8a00b3dc1d83c06b1a566610 Mon Sep 17 00:00:00 2001 From: "Alan M. Carroll" Date: Tue, 15 Nov 2022 11:35:29 -0600 Subject: [PATCH] libswoc: update to 1.4.0 --- lib/swoc/Makefile.am | 4 + lib/swoc/include/swoc/DiscreteRange.h | 53 +- lib/swoc/include/swoc/IPAddr.h | 1371 ++++++++++ lib/swoc/include/swoc/IPEndpoint.h | 276 ++ lib/swoc/include/swoc/IPRange.h | 1847 ++++++++++++++ lib/swoc/include/swoc/IPSrv.h | 680 +++++ lib/swoc/include/swoc/TextView.h | 2 +- lib/swoc/include/swoc/Vectray.h | 2 +- lib/swoc/include/swoc/bwf_fwd.h | 4 +- lib/swoc/include/swoc/swoc_ip.h | 3375 +------------------------ lib/swoc/include/swoc/swoc_version.h | 6 +- lib/swoc/src/bw_format.cc | 61 - lib/swoc/src/bw_ip_format.cc | 2 +- lib/swoc/src/swoc_ip.cc | 196 +- 14 files changed, 4406 insertions(+), 3473 deletions(-) create mode 100644 lib/swoc/include/swoc/IPAddr.h create mode 100644 lib/swoc/include/swoc/IPEndpoint.h create mode 100644 lib/swoc/include/swoc/IPRange.h create mode 100644 lib/swoc/include/swoc/IPSrv.h diff --git a/lib/swoc/Makefile.am b/lib/swoc/Makefile.am index 3d1700df804..70442b7ece3 100644 --- a/lib/swoc/Makefile.am +++ b/lib/swoc/Makefile.am @@ -47,6 +47,10 @@ library_include_HEADERS = \ include/swoc/RBTree.h \ include/swoc/Scalar.h \ include/swoc/swoc_file.h \ + include/swoc/IPEndpoint.h \ + include/swoc/IPAddr.h \ + include/swoc/IPSrv.h \ + include/swoc/IPRange.h \ include/swoc/swoc_ip.h \ include/swoc/swoc_meta.h \ include/swoc/swoc_version.h\ diff --git a/lib/swoc/include/swoc/DiscreteRange.h b/lib/swoc/include/swoc/DiscreteRange.h index fa7cf7cb735..ee306a9d498 100644 --- a/lib/swoc/include/swoc/DiscreteRange.h +++ b/lib/swoc/include/swoc/DiscreteRange.h @@ -812,34 +812,27 @@ template class DiscreteSpace { /** Find the payload at @a metric. * * @param metric The metric for which to search. - * @return The payload for @a metric if found, @c nullptr if not found. + * @return An iterator for the item or the @c end iterator if not. */ iterator find(METRIC const &metric); + /** Find the payload at @a metric. + * + * @param metric The metric for which to search. + * @return An iterator for the item or the @c end iterator if not. + */ + const_iterator find(METRIC const &metric) const; + /// @return The number of distinct ranges. size_t count() const; - iterator - begin() { - return _list.begin(); - } - - iterator - end() { - return _list.end(); - } + iterator begin() { return _list.begin(); } + iterator end() { return _list.end(); } + const_iterator begin() const { return _list.begin(); } + const_iterator end() const { return _list.end(); } /// Remove all ranges. - void - clear() { - for (auto &node : _list) { - std::destroy_at(&node.payload()); - } - _list.clear(); - _root = nullptr; - _arena.clear(); - _fa.clear(); - } + void clear(); protected: /** Find the lower bound range for @a target. @@ -962,6 +955,13 @@ DiscreteSpace::find(METRIC const &metric) -> iterator { return this->end(); } +template +auto +DiscreteSpace::find(METRIC const &metric) const -> const_iterator +{ + return const_cast(this)->find(metric); +} + template auto DiscreteSpace::lower_bound(METRIC const &target) -> Node * { @@ -1519,4 +1519,17 @@ DiscreteSpace::blend(DiscreteSpace::range_type const &range, U return *this; } +template +void +DiscreteSpace::clear() +{ + for (auto &node : _list) { + std::destroy_at(&node.payload()); + } + _list.clear(); + _root = nullptr; + _arena.clear(); + _fa.clear(); +} + }} // namespace swoc::SWOC_VERSION_NS diff --git a/lib/swoc/include/swoc/IPAddr.h b/lib/swoc/include/swoc/IPAddr.h new file mode 100644 index 00000000000..cacc4554154 --- /dev/null +++ b/lib/swoc/include/swoc/IPAddr.h @@ -0,0 +1,1371 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright Network Geographics 2014 +/** @file + IP address and network related classes. + */ + +#pragma once + +#include +#include + +#include "swoc/swoc_version.h" +#include "swoc/swoc_meta.h" +#include "swoc/MemSpan.h" + +namespace swoc { inline namespace SWOC_VERSION_NS { + +using std::string_view; + +union IPEndpoint; +class IPAddr; +class IPMask; + +/** Storage for an IPv4 address. + Stored in host order. + */ +class IP4Addr { + using self_type = IP4Addr; ///< Self reference type. + friend class IP4Range; + +public: + static constexpr size_t SIZE = sizeof(in_addr_t); ///< Size of IPv4 address in bytes. + static constexpr size_t WIDTH = std::numeric_limits::digits * SIZE; ///< # of bits in an address. + + static const self_type MIN; ///< Minimum value. + static const self_type MAX; ///< Maximum value. + static constexpr sa_family_t AF_value = AF_INET; ///< Address family type. + + constexpr IP4Addr() = default; ///< Default constructor - ANY address. + + /// Copy constructor. + IP4Addr(self_type const& that) = default; + + /// Construct using IPv4 @a addr (in host order). + /// @note Host order seems odd, but all of the standard network macro values such as @c INADDR_LOOPBACK + /// are in host order. + explicit constexpr IP4Addr(in_addr_t addr); + + /// Construct from @c sockaddr_in. + explicit IP4Addr(sockaddr_in const *s); + + /// Construct from text representation. + /// If the @a text is invalid the result is an invalid instance. + IP4Addr(string_view const &text); + + /// Self assignment. + self_type & operator=(self_type const& that) = default; + + /// Assign from IPv4 raw address. + self_type &operator=(in_addr_t ip); + + /// Set to the address in @a addr. + self_type &operator=(sockaddr_in const *sa); + + /// Increment address. + self_type &operator++(); + + /// Decrement address. + self_type &operator--(); + + /** Byte access. + * + * @param idx Byte index. + * @return The byte at @a idx in the address (network order). + * + * For convenience, this returns in "text order" of the octets. + */ + uint8_t operator[](unsigned idx) const; + + /// Apply @a mask to address, leaving the network portion. + self_type &operator&=(IPMask const &mask); + + /// Apply @a mask to address, creating the broadcast address. + self_type &operator|=(IPMask const &mask); + + /// Write this adddress and @a host_order_port to the sockaddr @a sa. + sockaddr_in *copy_to(sockaddr_in *sa, in_port_t port = 0) const; + + /// @return The address in network order. + in_addr_t network_order() const; + + /// @return The address in host order. + in_addr_t host_order() const; + + /** Parse @a text as IPv4 address. + The address resulting from the parse is copied to this object if the conversion is successful, + otherwise this object is invalidated. + + @return @c true on success, @c false otherwise. + */ + bool load(string_view const &text); + + /// Standard ternary compare. + int cmp(self_type const &that) const; + + /// Get the IP address family. + /// @return @c AF_INET + /// @note Useful primarily for template classes. + constexpr sa_family_t family() const; + + /// @return @c true if this is the "any" address, @c false if not. + bool is_any() const; + + /// @return @c true if this is a multicast address, @c false if not. + bool is_multicast() const; + + /// @return @c true if this is a loopback address, @c false if not. + bool is_loopback() const; + + /// @return @c true if the address is in the link local network. + bool is_link_local() const; + + /// @return @c true if the address is private. + bool is_private() const; + + /** Left shift. + * + * @param n Number of bits to shift left. + * @return @a this. + */ + self_type &operator<<=(unsigned n); + + /** Right shift. + * + * @param n Number of bits to shift right. + * @return @a this. + */ + self_type &operator>>=(unsigned n); + + /** Bitwise AND. + * + * @param that Source address. + * @return @a this. + * + * The bits in @a this are set to the bitwise AND of the corresponding bits in @a this and @a that. + */ + self_type &operator&=(self_type const &that); + + /** Bitwise OR. + * + * @param that Source address. + * @return @a this. + * + * The bits in @a this are set to the bitwise OR of the corresponding bits in @a this and @a that. + */ + self_type &operator|=(self_type const &that); + + /** Convert between network and host order. + * + * @param src Input address. + * @return @a src with the byte reversed. + * + * This performs the same computation as @c ntohl and @c htonl but is @c constexpr to be usable + * in situations those two functions are not. + */ + constexpr static in_addr_t reorder(in_addr_t src); + +protected: + /// Access by bytes. + using bytes = std::array; + + friend bool operator==(self_type const &, self_type const &); + friend bool operator!=(self_type const &, self_type const &); + friend bool operator<(self_type const &, self_type const &); + friend bool operator<=(self_type const &, self_type const &); + + in_addr_t _addr = INADDR_ANY; ///< Address in host order. +}; + +/** Storage for an IPv6 address. + Internal storage is not necessarily network ordered. + @see network_order + @see copy_to + */ +class IP6Addr { + using self_type = IP6Addr; ///< Self reference type. + + friend class IP6Range; + friend class IPMask; + +public: + static constexpr size_t WIDTH = 128; ///< Number of bits in the address. + static constexpr size_t SIZE = WIDTH / std::numeric_limits::digits; ///< Size of address in bytes. + static constexpr sa_family_t AF_value = AF_INET6; ///< Address family type. + + using quad_type = uint16_t; ///< Size of one segment of an IPv6 address. + static constexpr size_t N_QUADS = SIZE / sizeof(quad_type); ///< # of quads in an IPv6 address. + /// Number of bits per quad. + static constexpr size_t QUAD_WIDTH = std::numeric_limits::digits * sizeof(quad_type); + + /// Direct access type for the address. + /// Equivalent to the data type for data member @c s6_addr in @c in6_addr. + using raw_type = std::array; + + /// Minimum value of an address. + static const self_type MIN; + /// Maximum value of an address. + static const self_type MAX; + + IP6Addr() = default; ///< Default constructor - ANY address. + IP6Addr(self_type const &that) = default; + + /// Construct using IPv6 @a addr. + explicit IP6Addr(in6_addr const &addr); + + /// Construct from @c sockaddr_in. + explicit IP6Addr(sockaddr_in6 const *addr) { *this = addr; } + + /// Construct from text representation. + /// If the @a text is invalid the result is any address. + /// @see load + IP6Addr(string_view const &text); + + /** Construct mapped IPv4 address. + * + * @param addr IPv4 address + */ + explicit IP6Addr(IP4Addr addr); + + /// Self assignment. + self_type & operator=(self_type const& that) = default; + + /** Left shift. + * + * @param n Number of bits to shift left. + * @return @a this. + */ + self_type &operator<<=(unsigned n); + + /** Right shift. + * + * @param n Number of bits to shift right. + * @return @a this. + */ + self_type &operator>>=(unsigned n); + + /** Bitwise AND. + * + * @param that Source address. + * @return @a this. + * + * The bits in @a this are set to the bitwise AND of the corresponding bits in @a this and @a that. + */ + self_type &operator&=(self_type const &that); + + /** Bitwise OR. + * + * @param that Source address. + * @return @a this. + * + * The bits in @a this are set to the bitwise OR of the corresponding bits in @a this and @a that. + */ + self_type &operator|=(self_type const &that); + + /// Increment address. + self_type &operator++(); + + /// Decrement address. + self_type &operator--(); + + /// Assign from IPv6 raw address. + self_type &operator=(in6_addr const &addr); + + /// Set to the address in @a addr. + self_type &operator=(sockaddr_in6 const *addr); + + /** Access a byte in the address. + * + * @param idx Byte index. + * @return The "text order" byte. + */ + constexpr uint8_t operator [] (int idx) const; + + /// Write to @c sockaddr using network order and @a host_order_port. + sockaddr *copy_to(sockaddr *sa, in_port_t port = 0) const; + + /// Copy address to @a addr in network order. + in6_addr ©_to(in6_addr &addr) const; + + /// Return the address in network order. + in6_addr network_order() const; + + /** Parse a string for an IP address. + + The address resuling from the parse is copied to this object if the conversion is successful, + otherwise this object is invalidated. + + @return @c true on success, @c false otherwise. + */ + bool load(string_view const &str); + + /// Generic three value compare. + int cmp(self_type const &that) const; + + /// @return The address family. + constexpr sa_family_t family() const; + + /// @return @c true if this is the "any" address, @c false if not. + bool is_any() const; + + /// @return @c true if this is a loopback address, @c false if not. + bool is_loopback() const; + + /// @return @c true if this is a multicast address, @c false if not. + bool is_multicast() const; + + /// @return @c true if this is a link local address, @c false if not. + bool is_link_local() const; + + /// @return @c true if the address is private. + bool is_private() const; + + /// @return @c true if this is an IPv4 addressed mapped to IPv6, @c false if not. + bool is_mapped_ip4() const; + + /** Reset to default constructed state. + * + * @return @a this + */ + self_type & clear(); + + /** Bitwise AND. + * + * @param that Source mask. + * @return @a this. + * + * The bits in @a this are set to the bitwise AND of the corresponding bits in @a this and @a that. + */ + self_type &operator&=(IPMask const &that); + + /** Bitwise OR. + * + * @param that Source mask. + * @return @a this. + * + * The bits in @a this are set to the bitwise OR of the corresponding bits in @a this and @a that. + */ + self_type &operator|=(IPMask const &that); + + /** Convert between network and host ordering. + * + * @param dst Destination for re-ordered address. + * @param src Original address. + */ + static void reorder(in6_addr &dst, raw_type const &src); + + /** Convert between network and host ordering. + * + * @param dst Destination for re-ordered address. + * @param src Original address. + */ + static void reorder(raw_type &dst, in6_addr const &src); + + template < typename T > auto as_span() -> std::enable_if_t, swoc::MemSpan> { + return swoc::MemSpan(_addr._store).template rebind(); + } + + template < typename T > auto as_span() const -> std::enable_if_t, std::byte, uint8_t, uint16_t, uint32_t, uint64_t>, swoc::MemSpan> { + return swoc::MemSpan(_addr._store).template rebind(); + } + +protected: + friend bool operator==(self_type const &, self_type const &); + + friend bool operator!=(self_type const &, self_type const &); + + friend bool operator<(self_type const &, self_type const &); + + friend bool operator<=(self_type const &, self_type const &); + + /// Direct access type for the address by quads (16 bits). + /// This corresponds to the elements of the text format of the address. + using quad_store_type = std::array; + + /// A bit mask of all 1 bits the size of a quad. + static constexpr quad_type QUAD_MASK = ~quad_type{0}; + + /// Type used as a "word", the natural working unit of the address. + using word_type = uint64_t; + + static constexpr size_t WORD_SIZE = sizeof(word_type); + + /// Number of bits per word. + static constexpr size_t WORD_WIDTH = std::numeric_limits::digits * WORD_SIZE; + + /// Number of words used for basic address storage. + static constexpr size_t N_STORE = SIZE / WORD_SIZE; + + /// Type used to store the address. + using word_store_type = std::array; + + /// Type for digging around inside the address, with the various forms of access. + /// These are in sort of host order - @a _store elements are host order, but the + /// MSW and LSW are swapped (big-endian). This makes various bits of the implementation + /// easier. Conversion to and from network order is via the @c reorder method. + union { + word_store_type _store = {0}; ///< 0 is MSW, 1 is LSW. + quad_store_type _quad; ///< By quad. + raw_type _raw; ///< By byte. + } _addr; + + static constexpr unsigned LSW = 1; ///< Least significant word index. + static constexpr unsigned MSW = 0; ///< Most significant word index. + + /// Index of quads in @a _addr._quad. + /// This converts from the position in the text format to the quads in the binary format. + static constexpr std::array QUAD_IDX = {3, 2, 1, 0, 7, 6, 5, 4}; + + /// Index of bytes in @a _addr._raw + /// This converts MSB (0) to LSB (15) indicies to the bytes in the binary format. + static constexpr std::array RAW_IDX = { 7, 6, 5, 4, 3, 2, 1, 0, 15, 14, 13, 12, 11, 10, 9, 8 }; + + /// Convert between network and host order. + /// The conversion is symmetric. + /// @param dst Output where reordered value is placed. + /// @param src Input value to resorder. + static void reorder(unsigned char dst[WORD_SIZE], unsigned char const src[WORD_SIZE]); + + /** Construct from two 64 bit values. + * + * @param msw The most significant 64 bits, host order. + * @param lsw The least significant 64 bits, host order. + */ + IP6Addr(word_store_type::value_type msw, word_store_type::value_type lsw) : _addr{{msw, lsw}} {} + + friend IP6Addr operator&(IP6Addr const &addr, IPMask const &mask); + + friend IP6Addr operator|(IP6Addr const &addr, IPMask const &mask); +}; + +/** An IPv4 or IPv6 address. + * + * The family type is stored. For comparisons, invalid < IPv4 < IPv6. All invalid instances are equal. + */ +class IPAddr { + friend class IPRange; + + using self_type = IPAddr; ///< Self reference type. +public: + IPAddr() = default; ///< Default constructor - invalid result. + IPAddr(self_type const &that) = default; ///< Copy constructor. + self_type & operator = (self_type const& that) = default; ///< Copy assignment. + + /// Construct using IPv4 @a addr. + explicit IPAddr(in_addr_t addr); + + /// Construct using an IPv4 @a addr + IPAddr(IP4Addr const &addr) : _addr{addr}, _family(IP4Addr::AF_value) {} + + /// Construct using IPv6 @a addr. + explicit IPAddr(in6_addr const &addr); + + /// construct using an IPv6 @a addr + IPAddr(IP6Addr const &addr) : _addr{addr}, _family(IP6Addr::AF_value) {} + + /// Construct from @c sockaddr. + explicit IPAddr(sockaddr const *addr); + + /// Construct from @c IPEndpoint. + explicit IPAddr(IPEndpoint const &addr); + + /// Construct from text representation. + /// If the @a text is invalid the result is an invalid instance. + explicit IPAddr(string_view const &text); + + /// Set to the address in @a addr. + self_type &assign(sockaddr const *addr); + + /// Set to the address in @a addr. + self_type &assign(sockaddr_in const *addr); + + /// Set to the address in @a addr. + self_type &assign(sockaddr_in6 const *addr); + + /// Set to IPv4 @a addr. + self_type &assign(in_addr_t addr); + + /// Set to IPv6 @a addr + self_type &assign(in6_addr const &addr); + + /// Assign from end point. + self_type &operator=(IPEndpoint const &ip); + + /// Assign from IPv4 raw address. + self_type &operator=(in_addr_t ip); + + /// Assign from IPv6 raw address. + self_type &operator=(in6_addr const &addr); + + bool operator==(self_type const &that) const; + + bool operator!=(self_type const &that) const; + + bool operator<(self_type const &that) const; + + bool operator>(self_type const &that) const; + + bool operator<=(self_type const &that) const; + + bool operator>=(self_type const &that) const; + + /// Assign from @c sockaddr + self_type &operator=(sockaddr const *addr); + + self_type &operator&=(IPMask const &mask); + + self_type &operator|=(IPMask const &mask); + + /** Parse a string and load the result in @a this. + * + * @param text Text to parse. + * @return @c true on success, @c false otherwise. + */ + bool load(string_view const &text); + + /// Generic compare. + int cmp(self_type const &that) const; + + /// Test for same address family. + /// @c return @c true if @a that is the same address family as @a this. + bool is_same_family(self_type const &that); + + /// Get the address family. + /// @return The address family. + sa_family_t family() const; + + /// Test for IPv4. + bool is_ip4() const; + + /// Test for IPv6. + bool is_ip6() const; + + /// @return As IPv4 address - results are undefined if it is not actually IPv4. + IP4Addr const &ip4() const; + + /// @return As IPv6 address - results are undefined if it is not actually IPv6. + IP6Addr const &ip6() const; + + /// Test for validity. + bool is_valid() const; + + /// Make invalid. + self_type &invalidate(); + + /// Test for loopback + bool is_loopback() const; + + /// Test for multicast + bool is_multicast() const; + + /// @return @c true if this is a link local address, @c false if not. + bool is_link_local() const; + + /// @return @c true if this is a private address, @c false if not. + bool is_private() const; + + ///< Pre-constructed invalid instance. + static self_type const INVALID; + +protected: + friend IP4Addr; + friend IP6Addr; + + /// Address data. + union raw_addr_type { + IP4Addr _ip4; ///< IPv4 address (host) + IP6Addr _ip6; ///< IPv6 address (host) + + constexpr raw_addr_type(); + + raw_addr_type(in_addr_t addr) : _ip4(addr) {} + + raw_addr_type(in6_addr const &addr) : _ip6(addr) {} + + raw_addr_type(IP4Addr const &addr) : _ip4(addr) {} + + raw_addr_type(IP6Addr const &addr) : _ip6(addr) {} + } _addr; + + sa_family_t _family{AF_UNSPEC}; ///< Protocol family. +}; + +/** An IP address mask. + * + * This is essentially a width for a bit mask. + */ +class IPMask { + using self_type = IPMask; ///< Self reference type. + + friend class IP4Addr; + friend class IP6Addr; + +public: + using raw_type = uint8_t; ///< Storage for mask width. + + IPMask() = default; ///< Default construct to invalid mask. + + /** Construct a mask of @a width. + * + * @param width Number of bits in the mask. + * + * @note Because this is a network mask, it is always left justified. + */ + explicit IPMask(raw_type width); + + /// @return @c true if the mask is valid, @c false if not. + bool is_valid() const; + + /** Parse mask from @a text. + * + * @param text A number in string format. + * @return @a true if a valid CIDR value, @c false if not. + */ + bool load(string_view const &text); + + /** Copmute a mask for the network at @a addr. + * @param addr Lower bound of network. + * @return A mask with the width of the largest network starting at @a addr. + */ + static self_type mask_for(IPAddr const &addr); + + /** Copmute a mask for the network at @a addr. + * @param addr Lower bound of network. + * @return A mask with the width of the largest network starting at @a addr. + */ + static self_type mask_for(IP4Addr const &addr); + + /** Copmute a mask for the network at @a addr. + * @param addr Lower bound of network. + * @return A mask with the width of the largest network starting at @a addr. + */ + static self_type mask_for(IP6Addr const &addr); + + /// Change to default constructed state (invalid). + self_type & clear(); + + /// The width of the mask. + raw_type width() const; + + /** Extend the mask (cover more addresses). + * + * @param n Number of bits to extend. + * @return @a this + * + * Effectively shifts the mask left, bringing in 0 bits on the right. + */ + self_type & operator<<=(raw_type n); + + /** Narrow the mask (cover fewer addresses). + * + * @param n Number of bits to narrow. + * @return @a this + * + * Effectively shift the mask right, bringing in 1 bits on the left. + */ + self_type & operator>>=(raw_type n); + + /** The mask as an IPv4 address. + * + * @return An IPv4 address that is the mask. + * + * If the mask is wider than an IPv4 address, the maximum mask is returned. + */ + IP4Addr as_ip4() const; + + /** The mask as an IPv6 address. + * + * @return An IPv6 address that is the mask. + * + * If the mask is wider than an IPv6 address, the maximum mask is returned. + */ + IP6Addr as_ip6() const; + +protected: + /// Marker value for an invalid mask. + static constexpr auto INVALID = std::numeric_limits::max(); + + raw_type _cidr = INVALID; ///< Mask width in bits. + + /// Compute a partial IPv6 mask, sized for the basic storage type. + static raw_type mask_for_quad(IP6Addr::quad_type q); +}; + +// --- Implementation + +inline constexpr IP4Addr::IP4Addr(in_addr_t addr) : _addr(addr) {} + +inline IP4Addr::IP4Addr(string_view const &text) { + if (!this->load(text)) { + _addr = INADDR_ANY; + } +} + +inline constexpr sa_family_t +IP4Addr::family() const { + return AF_value; +} + +inline IP4Addr & +IP4Addr::operator<<=(unsigned n) { + _addr <<= n; + return *this; +} + +inline IP4Addr & +IP4Addr::operator>>=(unsigned n) { + _addr >>= n; + return *this; +} + +inline IP4Addr & +IP4Addr::operator&=(self_type const &that) { + _addr &= that._addr; + return *this; +} + +inline IP4Addr & +IP4Addr::operator|=(self_type const &that) { + _addr |= that._addr; + return *this; +} + +inline IP4Addr & +IP4Addr::operator++() { + ++_addr; + return *this; +} + +inline IP4Addr & +IP4Addr::operator--() { + --_addr; + return *this; +} + +inline in_addr_t +IP4Addr::network_order() const { + return htonl(_addr); +} + +inline in_addr_t +IP4Addr::host_order() const { + return _addr; +} + +inline auto +IP4Addr::operator=(in_addr_t ip) -> self_type & { + _addr = ntohl(ip); + return *this; +} + +/// Equality. +inline bool +operator==(IP4Addr const &lhs, IP4Addr const &rhs) { + return lhs._addr == rhs._addr; +} + +/// @return @c true if @a lhs is equal to @a rhs. +inline bool +operator!=(IP4Addr const &lhs, IP4Addr const &rhs) { + return lhs._addr != rhs._addr; +} + +/// @return @c true if @a lhs is less than @a rhs (host order). +inline bool +operator<(IP4Addr const &lhs, IP4Addr const &rhs) { + return lhs._addr < rhs._addr; +} + +/// @return @c true if @a lhs is less than or equal to@a rhs (host order). +inline bool +operator<=(IP4Addr const &lhs, IP4Addr const &rhs) { + return lhs._addr <= rhs._addr; +} + +/// @return @c true if @a lhs is greater than @a rhs (host order). +inline bool +operator>(IP4Addr const &lhs, IP4Addr const &rhs) { + return rhs < lhs; +} + +/// @return @c true if @a lhs is greater than or equal to @a rhs (host order). +inline bool +operator>=(IP4Addr const &lhs, IP4Addr const &rhs) { + return rhs <= lhs; +} + +inline IP4Addr & +IP4Addr::operator&=(IPMask const &mask) { + _addr &= mask.as_ip4()._addr; + return *this; +} + +inline IP4Addr & +IP4Addr::operator|=(IPMask const &mask) { + _addr |= ~(mask.as_ip4()._addr); + return *this; +} + +inline bool +IP4Addr::is_any() const { + return _addr == INADDR_ANY; +} + +inline bool +IP4Addr::is_loopback() const { + return (*this)[0] == IN_LOOPBACKNET; +} + +inline bool +IP4Addr::is_multicast() const { + return IN_MULTICAST(_addr); +} + +inline bool +IP4Addr::is_link_local() const { + return (_addr & 0xFFFF0000) == 0xA9FE0000; // 169.254.0.0/16 +} + +inline bool IP4Addr::is_private() const { + return (((_addr & 0xFF000000) == 0x0A000000) || // 10.0.0.0/8 + ((_addr & 0xFFC00000) == 0x64400000) || // 100.64.0.0/10 + ((_addr & 0xFFF00000) == 0xAC100000) || // 172.16.0.0/12 + ((_addr & 0xFFFF0000) == 0xC0A80000) // 192.168.0.0/16 + ); +} + +inline uint8_t +IP4Addr::operator[](unsigned int idx) const { + return reinterpret_cast(_addr)[3 - idx]; +} + +inline int +IP4Addr::cmp(IP4Addr::self_type const &that) const { + return _addr < that._addr ? -1 : _addr > that._addr ? 1 : 0; +} + +constexpr in_addr_t +IP4Addr::reorder(in_addr_t src) { + return ((src & 0xFF) << 24) | (((src >> 8) & 0xFF) << 16) | (((src >> 16) & 0xFF) << 8) | ((src >> 24) & 0xFF); +} + +// +++ IP6Addr +++ + +inline constexpr sa_family_t +IP6Addr::family() const { + return AF_value; +} + +inline IP6Addr::IP6Addr(in6_addr const &addr) { + *this = addr; +} + +inline IP6Addr::IP6Addr(string_view const &text) { + if (!this->load(text)) { + this->clear(); + } +} + +inline IP6Addr::IP6Addr(IP4Addr addr) { + _addr._store[MSW] = 0; + _addr._quad[QUAD_IDX[4]] = 0; + _addr._quad[QUAD_IDX[5]] = 0xffff; + _addr._quad[QUAD_IDX[6]] = addr.host_order() >> QUAD_WIDTH; + _addr._quad[QUAD_IDX[7]] = addr.host_order(); +} + +inline bool +IP6Addr::is_loopback() const { + return _addr._store[MSW] == 0 && _addr._store[LSW] == 1; +} + +inline bool +IP6Addr::is_multicast() const { + return _addr._raw[RAW_IDX[0]] == 0xFF; +} + +inline bool +IP6Addr::is_any() const { + return _addr._store[MSW] == 0 && _addr._store[LSW] == 0; +} + +inline bool +IP6Addr::is_mapped_ip4() const { + return 0 == _addr._store[MSW] && (_addr._quad[QUAD_IDX[4]] == 0 && _addr._quad[QUAD_IDX[5]] == 0xFFFF); +} + +inline bool +IP6Addr::is_link_local() const { + return _addr._raw[RAW_IDX[0]] == 0xFE && (_addr._raw[RAW_IDX[1]] & 0xC0) == 0x80; // fe80::/10 +} + +inline bool IP6Addr::is_private() const { + return (_addr._raw[RAW_IDX[0]]& 0xFE) == 0xFC; // fc00::/7 +} + +inline in6_addr & +IP6Addr::copy_to(in6_addr &addr) const { + self_type::reorder(addr, _addr._raw); + return addr; +} + +inline in6_addr +IP6Addr::network_order() const { + in6_addr zret; + return this->copy_to(zret); +} + +inline auto +IP6Addr::clear() -> self_type & { + _addr._store[MSW] = _addr._store[LSW] = 0; + return *this; +} + +inline auto +IP6Addr::operator=(in6_addr const &addr) -> self_type & { + self_type::reorder(_addr._raw, addr); + return *this; +} + +inline auto +IP6Addr::operator=(sockaddr_in6 const *addr) -> self_type & { + if (addr) { + *this = addr->sin6_addr; + } else { + this->clear(); + } + return *this; +} + +inline IP6Addr & +IP6Addr::operator++() { + if (++(_addr._store[LSW]) == 0) { + ++(_addr._store[MSW]); + } + return *this; +} + +inline IP6Addr & +IP6Addr::operator--() { + if (--(_addr._store[LSW]) == ~static_cast(0)) { + --(_addr._store[MSW]); + } + return *this; +} + +inline void +IP6Addr::reorder(unsigned char dst[WORD_SIZE], unsigned char const src[WORD_SIZE]) { + for (size_t idx = 0; idx < WORD_SIZE; ++idx) { + dst[idx] = src[WORD_SIZE - (idx + 1)]; + } +} + +/// @return @c true if @a lhs is equal to @a rhs. +inline bool +operator==(IP6Addr const &lhs, IP6Addr const &rhs) { + return lhs._addr._store[IP6Addr::MSW] == rhs._addr._store[IP6Addr::MSW] && lhs._addr._store[IP6Addr::LSW] == rhs._addr._store[IP6Addr::LSW]; +} + +/// @return @c true if @a lhs is not equal to @a rhs. +inline bool +operator!=(IP6Addr const &lhs, IP6Addr const &rhs) { + return lhs._addr._store[IP6Addr::MSW] != rhs._addr._store[IP6Addr::MSW] || lhs._addr._store[IP6Addr::LSW] != rhs._addr._store[IP6Addr::LSW]; +} + +/// @return @c true if @a lhs is less than @a rhs. +inline bool +operator<(IP6Addr const &lhs, IP6Addr const &rhs) { + return lhs._addr._store[IP6Addr::MSW] < rhs._addr._store[IP6Addr::MSW] || + (lhs._addr._store[IP6Addr::MSW] == rhs._addr._store[IP6Addr::MSW] && lhs._addr._store[IP6Addr::LSW] < rhs._addr._store[IP6Addr::LSW]); +} + +/// @return @c true if @a lhs is greater than @a rhs. +inline bool +operator>(IP6Addr const &lhs, IP6Addr const &rhs) { + return rhs < lhs; +} + +/// @return @c true if @a lhs is less than or equal to @a rhs. +inline bool +operator<=(IP6Addr const &lhs, IP6Addr const &rhs) { + return lhs._addr._store[IP6Addr::MSW] < rhs._addr._store[IP6Addr::MSW] || + (lhs._addr._store[IP6Addr::MSW] == rhs._addr._store[IP6Addr::MSW] && lhs._addr._store[IP6Addr::LSW] <= rhs._addr._store[IP6Addr::LSW]); +} + +/// @return @c true if @a lhs is greater than or equal to @a rhs. +inline bool +operator>=(IP6Addr const &lhs, IP6Addr const &rhs) { + return rhs <= lhs; +} + +inline IP6Addr & +IP6Addr::operator&=(IPMask const &mask) { + if (mask._cidr < WORD_WIDTH) { + _addr._store[MSW] &= (~word_type{0} << (WORD_WIDTH - mask._cidr)); + _addr._store[LSW] = 0; + } else if (mask._cidr < WIDTH) { + _addr._store[LSW] &= (~word_type{0} << (2 * WORD_WIDTH - mask._cidr)); + } + return *this; +} + +inline IP6Addr & +IP6Addr::operator|=(IPMask const &mask) { + if (mask._cidr < WORD_WIDTH) { + _addr._store[MSW] |= (~word_type{0} >> mask._cidr); + _addr._store[LSW] = ~word_type{0}; + } else if (mask._cidr < WIDTH) { + _addr._store[LSW] |= (~word_type{0} >> (mask._cidr - WORD_WIDTH)); + } + return *this; +} + +// +++ IPMask +++ + +inline IPMask::IPMask(raw_type width) : _cidr(width) {} + +inline bool +IPMask::is_valid() const { + return _cidr < INVALID; +} + +inline auto +IPMask::width() const -> raw_type { + return _cidr; +} + +inline bool +operator==(IPMask const &lhs, IPMask const &rhs) { + return lhs.width() == rhs.width(); +} + +inline bool +operator!=(IPMask const &lhs, IPMask const &rhs) { + return lhs.width() != rhs.width(); +} + +inline bool +operator<(IPMask const &lhs, IPMask const &rhs) { + return lhs.width() < rhs.width(); +} + +inline IP4Addr +IPMask::as_ip4() const { + static constexpr auto MASK = ~in_addr_t{0}; + in_addr_t addr = MASK; + if (_cidr < IP4Addr::WIDTH) { + addr <<= IP4Addr::WIDTH - _cidr; + } + return IP4Addr{addr}; +} + +inline auto +IPMask::clear() -> self_type & { + _cidr = INVALID; + return *this; +} + +inline auto +IPMask::operator<<=(raw_type n) -> self_type & { + _cidr -= n; + return *this; +} + +inline auto +IPMask::operator>>=(IPMask::raw_type n) -> self_type & { + _cidr += n; + return *this; +} + +// +++ mixed mask operators +++ + +inline IP4Addr +operator&(IP4Addr const &addr, IPMask const &mask) { + return IP4Addr{addr} &= mask; +} + +inline IP4Addr +operator|(IP4Addr const &addr, IPMask const &mask) { + return IP4Addr{addr} |= mask; +} + +inline IP6Addr +operator&(IP6Addr const &addr, IPMask const &mask) { + return IP6Addr{addr} &= mask; +} + +inline IP6Addr +operator|(IP6Addr const &addr, IPMask const &mask) { + return IP6Addr{addr} |= mask; +} + +constexpr uint8_t +IP6Addr::operator[](int idx) const { + return _addr._raw[RAW_IDX[idx]]; +} + +inline IPAddr +operator&(IPAddr const &addr, IPMask const &mask) { + return IPAddr{addr} &= mask; +} + +inline IPAddr +operator|(IPAddr const &addr, IPMask const &mask) { + return IPAddr{addr} |= mask; +} + +// @c constexpr constructor is required to initialize _something_, it can't be completely uninitializing. +inline constexpr IPAddr::raw_addr_type::raw_addr_type() : _ip4(INADDR_ANY) {} + +inline IPAddr::IPAddr(in_addr_t addr) : _addr(addr), _family(IP4Addr::AF_value) {} + +inline IPAddr::IPAddr(in6_addr const &addr) : _addr(addr), _family(IP6Addr::AF_value) {} + +inline IPAddr::IPAddr(sockaddr const *addr) { + this->assign(addr); +} + +inline IPAddr::IPAddr(string_view const &text) { + this->load(text); +} + +inline IPAddr & +IPAddr::operator=(in_addr_t addr) { + _family = AF_INET; + _addr._ip4 = addr; + return *this; +} + +inline IPAddr & +IPAddr::operator=(in6_addr const &addr) { + _family = AF_INET6; + _addr._ip6 = addr; + return *this; +} + +inline IPAddr & +IPAddr::operator=(sockaddr const *addr) { + return this->assign(addr); +} + +inline sa_family_t +IPAddr::family() const { + return _family; +} + +inline bool +IPAddr::is_ip4() const { + return AF_INET == _family; +} + +inline bool +IPAddr::is_ip6() const { + return AF_INET6 == _family; +} + +inline bool +IPAddr::is_same_family(self_type const &that) { + return this->is_valid() && _family == that._family; +} + +inline bool +IPAddr::is_loopback() const { + return (AF_INET == _family && _addr._ip4.is_loopback()) || (AF_INET6 == _family && _addr._ip6.is_loopback()); +} + +inline bool +IPAddr::is_link_local() const { + return this->is_ip4() ? this->ip4().is_link_local() + : this->is_ip6() ? this->ip6().is_link_local() + : false; +} + +inline bool +IPAddr::is_private() const { + return this->is_ip4() ? this->ip4().is_private() + : this->is_ip6() ? this->ip6().is_private() + : false; +} + +inline IPAddr & +IPAddr::assign(in_addr_t addr) { + _family = AF_INET; + _addr._ip4 = addr; + return *this; +} + +inline IPAddr & +IPAddr::assign(in6_addr const &addr) { + _family = AF_INET6; + _addr._ip6 = addr; + return *this; +} + +inline IPAddr & +IPAddr::assign(sockaddr_in const *addr) { + if (addr) { + _family = AF_INET; + _addr._ip4 = addr; + } else { + _family = AF_UNSPEC; + } + return *this; +} + +inline IPAddr & +IPAddr::assign(sockaddr_in6 const *addr) { + if (addr) { + _family = AF_INET6; + _addr._ip6 = addr->sin6_addr; + } else { + _family = AF_UNSPEC; + } + return *this; +} + +inline bool +IPAddr::is_valid() const { + return _family == AF_INET || _family == AF_INET6; +} + +inline IPAddr & +IPAddr::invalidate() { + _family = AF_UNSPEC; + return *this; +} + +// Associated operators. + +/// Equality. +bool operator==(IPAddr const &lhs, sockaddr const *rhs); + +/// Equality. +inline bool +operator==(sockaddr const *lhs, IPAddr const &rhs) { + return rhs == lhs; +} + +/// Inequality. +inline bool +operator!=(IPAddr const &lhs, sockaddr const *rhs) { + return !(lhs == rhs); +} + +/// Inequality. +inline bool +operator!=(sockaddr const *lhs, IPAddr const &rhs) { + return !(rhs == lhs); +} + +/// Equality. +inline IP4Addr const & +IPAddr::ip4() const { + return _addr._ip4; +} + +inline IP6Addr const & +IPAddr::ip6() const { + return _addr._ip6; +} + +inline bool +IPAddr::operator==(self_type const &that) const { + switch (_family) { + case AF_INET: + return that._family == AF_INET && _addr._ip4 == that._addr._ip4; + case AF_INET6: + return that._family == AF_INET6 && _addr._ip6 == that._addr._ip6; + default: + return ! that.is_valid(); + } +} + +inline bool +IPAddr::operator!=(self_type const &that) const { + return !(*this == that); +} + +inline bool +IPAddr::operator>(self_type const &that) const { + return that < *this; +} + +inline bool +IPAddr::operator<=(self_type const &that) const { + return !(that < *this); +} + +inline bool +IPAddr::operator>=(self_type const &that) const { + return !(*this < that); +} + +// Disambiguating between comparisons and implicit conversions. + +inline bool +operator==(IPAddr const &lhs, IP4Addr const &rhs) { + return lhs.is_ip4() && lhs.ip4() == rhs; +} + +inline bool +operator!=(IPAddr const &lhs, IP4Addr const &rhs) { + return !lhs.is_ip4() || lhs.ip4() != rhs; +} + +inline bool +operator==(IP4Addr const &lhs, IPAddr const &rhs) { + return rhs.is_ip4() && lhs == rhs.ip4(); +} + +inline bool +operator!=(IP4Addr const &lhs, IPAddr const &rhs) { + return !rhs.is_ip4() || lhs != rhs.ip4(); +} + +inline bool +operator==(IPAddr const &lhs, IP6Addr const &rhs) { + return lhs.is_ip6() && lhs.ip6() == rhs; +} + +inline bool +operator!=(IPAddr const &lhs, IP6Addr const &rhs) { + return !lhs.is_ip6() || lhs.ip6() != rhs; +} + +inline bool +operator==(IP6Addr const &lhs, IPAddr const &rhs) { + return rhs.is_ip6() && lhs == rhs.ip6(); +} + +inline bool +operator!=(IP6Addr const &lhs, IPAddr const &rhs) { + return !rhs.is_ip6() || lhs != rhs.ip6(); +} +}} // namespace swoc::SWOC_VERSION_NS + +namespace std { + +/// Standard hash support for @a IP4Addr. +template <> struct hash { + size_t operator()(swoc::IP4Addr const &addr) const { + return addr.network_order(); + } +}; + +/// Standard hash support for @a IP6Addr. +template <> struct hash { + size_t operator()(swoc::IP6Addr const &addr) const { + // XOR the 64 chunks then XOR that down to 32 bits. + auto words = addr.as_span(); + return words[0] ^ words[1]; + } +}; + +/// Standard hash support for @a IPAddr. +template <> struct hash { + size_t operator()(swoc::IPAddr const &addr) const { + return addr.is_ip4() ? hash()(addr.ip4()) : addr.is_ip6() ? hash()(addr.ip6()) : 0; + } +}; + +} // namespace std diff --git a/lib/swoc/include/swoc/IPEndpoint.h b/lib/swoc/include/swoc/IPEndpoint.h new file mode 100644 index 00000000000..4ae875cadc9 --- /dev/null +++ b/lib/swoc/include/swoc/IPEndpoint.h @@ -0,0 +1,276 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright Network Geographics 2014 +/** @file + IPEndpoint - wrapper for raw socket address objects. + */ + +#pragma once + +#include +#include + +#include + +#include "swoc/swoc_version.h" +#include "swoc/TextView.h" + +namespace swoc { inline namespace SWOC_VERSION_NS { + +using ::std::string_view; + +class IPAddr; +class IP4Addr; +class IP6Addr; + +namespace detail { +extern void *const pseudo_nullptr; +} + +/** A union to hold @c sockaddr compliant IP address structures. + + This class contains a number of static methods to perform operations on external @c sockaddr + instances. These are all duplicates of methods that operate on the internal @c sockaddr and + are provided primarily for backwards compatibility during the shift to using this class. + + We use the term "endpoint" because these contain more than just the raw address, all of the data + for an IP endpoint is present. + */ +union IPEndpoint { + using self_type = IPEndpoint; ///< Self reference type. + + struct sockaddr sa; ///< Generic address. + struct sockaddr_in sa4; ///< IPv4 + struct sockaddr_in6 sa6; ///< IPv6 + + /// Default construct invalid instance. + IPEndpoint(); + IPEndpoint(self_type const &that); ///< Copy constructor. + ~IPEndpoint() = default; + + /// Construct from the @a text representation of an address. + explicit IPEndpoint(string_view const &text); + + // Construct from @c IPAddr + explicit IPEndpoint(IPAddr const &addr); + + // Construct from @c sockaddr + IPEndpoint(sockaddr const *sa); + + /// Copy assignment. + self_type &operator=(self_type const &that); + + /** Break a string in to IP address relevant tokens. + * + * @param src Source text. [in] + * @param host The host / address. [out] + * @param port The host_order_port. [out] + * @param rest Any text past the end of the IP address. [out] + * @return @c true if an IP address was found, @c false otherwise. + * + * Any of the out parameters can be @c nullptr in which case they are not updated. + * This parses and discards the IPv6 brackets. + * + * @note This is intended for internal use to do address parsing, but it can be useful in other contexts. + */ + static bool tokenize(string_view src, string_view *host = nullptr, string_view *port = nullptr, string_view *rest = nullptr); + + /** Parse a string for an IP address. + + The address resulting from the parse is copied to this object if the conversion is successful, + otherwise this object is invalidated. + + @return @c true on success, @c false otherwise. + */ + bool parse(string_view const &str); + + /// Invalidate a @c sockaddr. + static void invalidate(sockaddr *addr); + + /// Invalidate this endpoint. + self_type &invalidate(); + + /** Copy (assign) the contents of @a src to @a dst. + * + * The caller must ensure @a dst is large enough to hold the contents of @a src, the size of which + * can vary depending on the type of address in @a dst. + * + * @param dst Destination. + * @param src Source. + * @return @c true if @a dst is a valid IP address, @c false otherwise. + */ + static bool assign(sockaddr *dst, sockaddr const *src); + + /** Assign from a socket address. + The entire address (all parts) are copied if the @a ip is valid. + */ + self_type &assign(sockaddr const *addr); + + /// Assign from an @a addr and @a host_order_port. + self_type &assign(IPAddr const &addr, in_port_t port = 0); + + /// Copy to @a sa. + const self_type ©_to(sockaddr *addr) const; + + /// Test for valid IP address. + bool is_valid() const; + + /// Test for IPv4. + bool is_ip4() const; + + /// Test for IPv6. + bool is_ip6() const; + + /** Effectively size of the address. + * + * @return The size of the structure appropriate for the address family of the stored address. + */ + socklen_t size() const; + + /// @return The IP address family. + sa_family_t family() const; + + /// Set to be the ANY address for family @a family. + /// @a family must be @c AF_INET or @c AF_INET6. + /// @return This object. + self_type &set_to_any(int family); + + /// @return @c true if this is the ANY address, @c false if not. + bool is_any() const; + + /// Set to be loopback address for family @a family. + /// @a family must be @c AF_INET or @c AF_INET6. + /// @return This object. + self_type &set_to_loopback(int family); + + /// @return @c true if this is a loopback address, @c false if not. + bool is_loopback() const; + + /// Port in network order. + in_port_t &network_order_port(); + + /// Port in network order. + in_port_t network_order_port() const; + + /// Port in host horder. + in_port_t host_order_port() const; + + /// Port in network order from @a sockaddr. + static in_port_t &port(sockaddr *sa); + + /// Port in network order from @a sockaddr. + static in_port_t port(sockaddr const *sa); + + /// Port in host order directly from a @c sockaddr + static in_port_t host_order_port(sockaddr const *sa); + + /// Automatic conversion to @c sockaddr. + operator sockaddr *() { return &sa; } + + /// Automatic conversion to @c sockaddr. + operator sockaddr const *() const { return &sa; } + + /// The string name of the address family. + static string_view family_name(sa_family_t family); +}; + +inline IPEndpoint::IPEndpoint() { + sa.sa_family = AF_UNSPEC; +} + +inline IPEndpoint::IPEndpoint(IPAddr const &addr) { + this->assign(addr); +} + +inline IPEndpoint::IPEndpoint(sockaddr const *sa) { + this->assign(sa); +} + +inline IPEndpoint::IPEndpoint(IPEndpoint::self_type const &that) : self_type(&that.sa) {} + +inline IPEndpoint & +IPEndpoint::invalidate() { + sa.sa_family = AF_UNSPEC; + return *this; +} + +inline void +IPEndpoint::invalidate(sockaddr *addr) { + addr->sa_family = AF_UNSPEC; +} + +inline bool +IPEndpoint::is_valid() const { + return sa.sa_family == AF_INET || sa.sa_family == AF_INET6; +} + +inline IPEndpoint & +IPEndpoint::operator=(self_type const &that) { + self_type::assign(&sa, &that.sa); + return *this; +} + +inline IPEndpoint & +IPEndpoint::assign(sockaddr const *src) { + self_type::assign(&sa, src); + return *this; +} + +inline IPEndpoint const & +IPEndpoint::copy_to(sockaddr *addr) const { + self_type::assign(addr, &sa); + return *this; +} + +inline bool +IPEndpoint::is_ip4() const { + return AF_INET == sa.sa_family; +} + +inline bool +IPEndpoint::is_ip6() const { + return AF_INET6 == sa.sa_family; +} + +inline sa_family_t +IPEndpoint::family() const { + return sa.sa_family; +} + +inline in_port_t & +IPEndpoint::network_order_port() { + return self_type::port(&sa); +} + +inline in_port_t +IPEndpoint::network_order_port() const { + return self_type::port(&sa); +} + +inline in_port_t +IPEndpoint::host_order_port() const { + return ntohs(this->network_order_port()); +} + +inline in_port_t & +IPEndpoint::port(sockaddr *sa) { + switch (sa->sa_family) { + case AF_INET: + return reinterpret_cast(sa)->sin_port; + case AF_INET6: + return reinterpret_cast(sa)->sin6_port; + } + // Force a failure upstream by returning a null reference. + throw std::domain_error("sockaddr is not a valid IP address"); +} + +inline in_port_t +IPEndpoint::port(sockaddr const *sa) { + return self_type::port(const_cast(sa)); +} + +inline in_port_t +IPEndpoint::host_order_port(sockaddr const *sa) { + return ntohs(self_type::port(sa)); +} + +}} // namespace swoc::SWOC_VERSION_NS diff --git a/lib/swoc/include/swoc/IPRange.h b/lib/swoc/include/swoc/IPRange.h new file mode 100644 index 00000000000..919cb48076e --- /dev/null +++ b/lib/swoc/include/swoc/IPRange.h @@ -0,0 +1,1847 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright Network Geographics 2014 +/** @file + IP address range utilities. + */ + +#pragma once + +#include +#include + +namespace swoc { inline namespace SWOC_VERSION_NS { + +using ::std::string_view; + +class IP4Net; +class IP6Net; +class IPNet; + +namespace detail { +extern void *const pseudo_nullptr; +} + +/** An inclusive range of IPv4 addresses. + */ +class IP4Range : public DiscreteRange { + using self_type = IP4Range; + using super_type = DiscreteRange; + using metric_type = IP4Addr; + +public: + /// Default constructor, invalid range. + IP4Range() = default; + + /// Construct from an network expressed as @a addr and @a mask. + IP4Range(IP4Addr const &addr, IPMask const &mask); + + /// Construct from super type. + /// @internal Why do I have to do this, even though the super type constructors are inherited? + IP4Range(super_type const &r) : super_type(r) {} + + /** Construct range from @a text. + * + * @param text Range text. + * @see IP4Range::load + * + * This results in a zero address if @a text is not a valid string. If this should be checked, + * use @c load. + */ + IP4Range(string_view const &text); + + using super_type::super_type; ///< Import super class constructors. + + /** Set @a this range. + * + * @param addr Minimum address. + * @param mask CIDR mask to compute maximum adddress from @a addr. + * @return @a this + */ + self_type &assign(IP4Addr const &addr, IPMask const &mask); + + using super_type::assign; ///< Import assign methods. + + /** Assign to this range from text. + * + * @param text Range text. + * + * The text must be in one of three formats. + * - A dashed range, "addr1-addr2" + * - A singleton, "addr". This is treated as if it were "addr-addr", a range of size 1. + * - CIDR notation, "addr/cidr" where "cidr" is a number from 0 to the number of bits in the address. + */ + bool load(string_view text); + + /** Compute the mask for @a this as a network. + * + * @return If @a this is a network, the mask for that network. Otherwise an invalid mask. + * + * @see IPMask::is_valid + */ + IPMask network_mask() const; + + class NetSource; + + /** Generate a list of networks covering @a this range. + * + * @return A network generator. + * + * The returned object can be used as an iterator, or as a container to iterating over + * the unique minimal set of networks that cover @a this range. + * + * @code + * void (IP4Range const& range) { + * for ( auto const& net : range ) { + * net.addr(); // network address. + * net.mask(); // network mask; + * } + * } + * @endcode + */ + NetSource networks() const; +}; + +/** Network generator class. + * This generates networks from a range and acts as both a forward iterator and a container. + */ +class IP4Range::NetSource { + using self_type = NetSource; ///< Self reference type. +public: + using range_type = IP4Range; ///< Import base range type. + + /// Construct from @a range. + explicit NetSource(range_type const &range); + + /// Copy constructor. + NetSource(self_type const &that) = default; + + /// This class acts as a container and an iterator. + using iterator = self_type; + /// All iteration is constant so no distinction between iterators. + using const_iterator = iterator; + + iterator begin() const; ///< First network. + static iterator end() ; ///< Past last network. + + /// Return @c true if there are valid networks, @c false if not. + bool empty() const; + + /// @return The current network. + IP4Net operator*() const; + + /// Access @a this as if it were an @c IP4Net. + self_type *operator->(); + + /// Iterator support. + /// @return The current network address. + IP4Addr const &addr() const; + + /// Iterator support. + /// @return The current network mask. + IPMask mask() const; + + /// Move to next network. + self_type &operator++(); + + /// Move to next network. + self_type operator++(int); + + /// Equality. + bool operator==(self_type const &that) const; + + /// Inequality. + bool operator!=(self_type const &that) const; + +protected: + IP4Range _range; ///< Remaining range. + /// Mask for current network. + IP4Addr _mask{~static_cast(0)}; + IPMask::raw_type _cidr = IP4Addr::WIDTH; ///< Current CIDR value. + + void search_wider(); + + void search_narrower(); + + bool is_valid(IP4Addr mask) const; +}; + +/// Inclusive range of IPv6 addresses. +class IP6Range : public DiscreteRange { + using self_type = IP6Range; + using super_type = DiscreteRange; + +public: + /// Construct from super type. + /// @internal Why do I have to do this, even though the super type constructors are inherited? + IP6Range(super_type const &r) : super_type(r) {} + + /** Construct range from @a text. + * + * @param text Range text. + * @see IP4Range::load + * + * This results in a zero address if @a text is not a valid string. If this should be checked, + * use @c load. + */ + IP6Range(string_view const &text); + + using super_type::super_type; ///< Import super class constructors. + + /** Set @a this range. + * + * @param addr Minimum address. + * @param mask CIDR mask to compute maximum adddress from @a addr. + * @return @a this + */ + self_type &assign(IP6Addr const &addr, IPMask const &mask); + + using super_type::assign; ///< Import assign methods. + + /** Assign to this range from text. + * + * @param text Range text. + * + * The text must be in one of three formats. + * - A dashed range, "addr1-addr2" + * - A singleton, "addr". This is treated as if it were "addr-addr", a range of size 1. + * - CIDR notation, "addr/cidr" where "cidr" is a number from 0 to the number of bits in the address. + */ + bool load(string_view text); + + /** Compute the mask for @a this as a network. + * + * @return If @a this is a network, the mask for that network. Otherwise an invalid mask. + * + * @see IPMask::is_valid + */ + IPMask network_mask() const; + + class NetSource; + + /** Generate a list of networks covering @a this range. + * + * @return A network generator. + * + * The returned object can be used as an iterator, or as a container to iterating over + * the unique minimal set of networks that cover @a this range. + * + * @code + * void (IP6Range const& range) { + * for ( auto const& net : range ) { + * net.addr(); // network address. + * net.mask(); // network mask; + * } + * } + * @endcode + */ + NetSource networks() const; +}; + +/** Network generator class. + * This generates networks from a range and acts as both a forward iterator and a container. + */ +class IP6Range::NetSource { + using self_type = NetSource; ///< Self reference type. +public: + using range_type = IP6Range; ///< Import base range type. + + /// Construct from @a range. + explicit NetSource(range_type const &range); + + /// Copy constructor. + NetSource(self_type const &that) = default; + + /// This class acts as a container and an iterator. + using iterator = self_type; + /// All iteration is constant so no distinction between iterators. + using const_iterator = iterator; + + iterator begin() const; ///< First network. + iterator end() const; ///< Past last network. + + /// @return @c true if there are valid networks, @c false if not. + bool empty() const; + + /// @return The current network. + IP6Net operator*() const; + + /// Access @a this as if it were an @c IP6Net. + self_type *operator->(); + + /// @return The current network address. + IP6Addr const & addr() const; + + /// Iterator support. + /// @return The current network mask. + IPMask mask() const; + + /// Move to next network. + self_type &operator++(); + + /// Move to next network. + self_type operator++(int); + + /// Equality. + bool operator==(self_type const &that) const; + + /// Inequality. + bool operator!=(self_type const &that) const; + +protected: + IP6Range _range; ///< Remaining range. + IPMask _mask{IP6Addr::WIDTH}; ///< Current CIDR value. + + void search_wider(); + + void search_narrower(); + + bool is_valid(IPMask const &mask); +}; + +/** Range of IP addresses. + * Although this can hold IPv4 or IPv6, any specific instance is one or the other, this can never contain + * a range of different address families. + */ +class IPRange { + using self_type = IPRange; + +public: + /// Default constructor - construct invalid range. + IPRange() = default; + + IPRange(IPAddr const &min, IPAddr const &max); + + /// Construct from an IPv4 @a range. + IPRange(IP4Range const &range); + + /// Construct from an IPv6 @a range. + IPRange(IP6Range const &range); + + /** Construct from a string format. + * + * @param text Text form of range. + * + * The string can be a single address, two addresses separated by a dash '-' or a CIDR network. + */ + IPRange(string_view const &text); + + /// Equality + bool + operator==(self_type const &that) const; + + /// @return @c true if this is an IPv4 range, @c false if not. + bool + is_ip4() const { + return AF_INET == _family; + } + + /// @return @c true if this is an IPv6 range, @c false if not. + bool + is_ip6() const { + return AF_INET6 == _family; + } + + /** Check if @a this range is the IP address @a family. + * + * @param family IP address family. + * @return @c true if this is @a family, @c false if not. + */ + bool is(sa_family_t family) const; + + /** Load the range from @a text. + * + * @param text Range specifier in text format. + * @return @c true if @a text was successfully parsed, @c false if not. + * + * A successful parse means @a this was loaded with the specified range. If not the range is + * marked as invalid. + */ + bool load(std::string_view const &text); + + /// @return The minimum address in the range. + IPAddr min() const; + + /// @return The maximum address in the range. + IPAddr max() const; + + /// @return @c true if there are no addresses in the range. + bool empty() const; + + /// @return The IPv4 range. + IP4Range const & ip4() const { return _range._ip4; } + + /// @return The IPv6 range. + IP6Range const & ip6() const { return _range._ip6; } + + /** Compute the mask for @a this as a network. + * + * @return If @a this is a network, the mask for that network. Otherwise an invalid mask. + * + * @see IPMask::is_valid + */ + IPMask network_mask() const; + + class NetSource; + + /** Generate a list of networks covering @a this range. + * + * @return A network generator. + * + * The returned object can be used as an iterator, or as a container to iterating over + * the unique minimal set of networks that cover @a this range. + * + * @code + * void (IPRange const& range) { + * for ( auto const& net : range ) { + * net.addr(); // network address. + * net.mask(); // network mask; + * } + * } + * @endcode + */ + NetSource networks() const; + +protected: + /** Range container. + * + * @internal + * + * This was a @c std::variant at one point, but the complexity got in the way because + * - These objects have no state, need no destruction. + * - Construction was problematic because @c variant requires construction, then access, + * whereas this needs access to construct (e.g. via the @c load method). + */ + union { + std::monostate _nil; ///< Make constructor easier to implement. + IP4Range _ip4; ///< IPv4 range. + IP6Range _ip6; ///< IPv6 range. + } _range{std::monostate{}}; + /// Family of @a _range. + sa_family_t _family{AF_UNSPEC}; +}; + +/** Network generator class. + * This generates networks from a range and acts as both a forward iterator and a container. + */ +class IPRange::NetSource { + using self_type = NetSource; ///< Self reference type. +public: + using range_type = IPRange; ///< Import base range type. + + /// Construct from @a range. + explicit NetSource(range_type const &range); + + /// Copy constructor. + NetSource(self_type const &that) = default; + + /// This class acts as a container and an iterator. + using iterator = self_type; + /// All iteration is constant so no distinction between iterators. + using const_iterator = iterator; + + iterator begin() const; ///< First network. + iterator end() const; ///< Past last network. + + /// @return The current network. + IPNet operator*() const; + + /// Access @a this as if it were an @c IP6Net. + self_type *operator->(); + + /// Iterator support. + /// @return The current network address. + IPAddr addr() const; + + /// Iterator support. + /// @return The current network mask. + IPMask mask() const; + + /// Move to next network. + self_type &operator++(); + + /// Move to next network. + self_type operator++(int); + + /// Equality. + bool operator==(self_type const &that) const; + + /// Inequality. + bool operator!=(self_type const &that) const; + +protected: + union { + std::monostate _nil; ///< Default value, no addresses. + IP4Range::NetSource _ip4; ///< IPv4 addresses. + IP6Range::NetSource _ip6; ///< IPv6 addresses. + }; + sa_family_t _family = AF_UNSPEC; ///< Mark for union content. +}; + +/// An IPv4 network. +class IP4Net { + using self_type = IP4Net; ///< Self reference type. +public: + IP4Net() = default; ///< Construct invalid network. + IP4Net(self_type const &that) = default; ///< Copy constructor. + + /** Construct from @a addr and @a mask. + * + * @param addr An address in the network. + * @param mask The mask for the network. + * + * The network is based on the mask, and the resulting network address is chosen such that the + * network will contain @a addr. For a given @a addr and @a mask there is only one network + * that satisifies these criteria. + */ + IP4Net(IP4Addr addr, IPMask mask); + + IP4Net(swoc::TextView text) { this->load(text); } + + /** Parse network as @a text. + * + * @param text String describing the network in CIDR format. + * @return @c true if a valid string, @c false if not. + */ + bool load(swoc::TextView text); + + /// @return @c true if the network is valid, @c false if not. + bool is_valid() const; + + /// @return THh smallest address in the network. + IP4Addr lower_bound() const; + + /// @return The largest address in the network. + IP4Addr upper_bound() const; + + /// @return The mask for the network. + IPMask const &mask() const; + + /// @return A range that exactly covers the network. + IP4Range as_range() const; + + /** Assign an @a addr and @a mask to @a this. + * + * @param addr Network addres. + * @param mask Network mask. + * @return @a this. + */ + self_type &assign(IP4Addr const &addr, IPMask const &mask); + + /// Reset network to invalid state. + self_type & clear() { _mask.clear(); return *this; } + + /// Equality. + bool operator==(self_type const &that) const; + + /// Inequality + bool operator!=(self_type const &that) const; + +protected: + IP4Addr _addr; ///< Network address (also lower_bound). + IPMask _mask; ///< Network mask. +}; + +/// IPv6 network. +class IP6Net { + using self_type = IP6Net; ///< Self reference type. +public: + IP6Net() = default; ///< Construct invalid network. + IP6Net(self_type const &that) = default; ///< Copy constructor. + + /** Construct from @a addr and @a mask. + * + * @param addr An address in the network. + * @param mask The mask for the network. + * + * The network is based on the mask, and the resulting network address is chosen such that the + * network will contain @a addr. For a given @a addr and @a mask there is only one network + * that satisifies these criteria. + */ + IP6Net(IP6Addr addr, IPMask mask); + + /** Parse network as @a text. + * + * @param text String describing the network in CIDR format. + * @return @c true if a valid string, @c false if not. + */ + bool load(swoc::TextView text); + + /// @return @c true if the network is valid, @c false if not. + bool is_valid() const; + + /// @return THh smallest address in the network. + IP6Addr lower_bound() const; + + /// @return The largest address in the network. + IP6Addr upper_bound() const; + + /// @return The mask for the network. + IPMask const &mask() const; + + /// @return A range that exactly covers the network. + IP6Range as_range() const; + + /** Assign an @a addr and @a mask to @a this. + * + * @param addr Network addres. + * @param mask Network mask. + * @return @a this. + */ + self_type &assign(IP6Addr const &addr, IPMask const &mask); + + /// Reset network to invalid state. + self_type & + clear() { + _mask.clear(); + return *this; + } + + /// Equality. + bool operator==(self_type const &that) const; + + /// Inequality + bool operator!=(self_type const &that) const; + +protected: + IP6Addr _addr; ///< Network address (also lower_bound). + IPMask _mask; ///< Network mask. +}; + +/** Representation of an IP address network. + * + */ +class IPNet { + using self_type = IPNet; ///< Self reference type. +public: + IPNet() = default; ///< Construct invalid network. + IPNet(self_type const &that) = default; ///< Copy constructor. + + /** Construct from @a addr and @a mask. + * + * @param addr An address in the network. + * @param mask The mask for the network. + * + * The network is based on the mask, and the resulting network address is chosen such that the + * network will contain @a addr. For a given @a addr and @a mask there is only one network + * that satisifies these criteria. + */ + IPNet(IPAddr const &addr, IPMask const &mask); + + IPNet(TextView text); + + /** Parse network as @a text. + * + * @param text String describing the network in CIDR format. + * @return @c true if a valid string, @c false if not. + */ + bool load(swoc::TextView text); + + /// @return @c true if the network is valid, @c false if not. + bool is_valid() const; + + /// @return THh smallest address in the network. + IPAddr lower_bound() const; + + /// @return The largest address in the network. + IPAddr upper_bound() const; + + IPMask::raw_type width() const; + + /// @return The mask for the network. + IPMask const &mask() const; + + /// @return A range that exactly covers the network. + IPRange as_range() const; + + bool is_ip4() const { return _addr.is_ip4(); } + + bool is_ip6() const { return _addr.is_ip6(); } + + sa_family_t family() const { return _addr.family(); } + + IP4Net + ip4() const { + return IP4Net{_addr.ip4(), _mask}; + } + + IP6Net + ip6() const { + return IP6Net{_addr.ip6(), _mask}; + } + + /** Assign an @a addr and @a mask to @a this. + * + * @param addr Network addres. + * @param mask Network mask. + * @return @a this. + */ + self_type &assign(IPAddr const &addr, IPMask const &mask); + + /// Reset network to invalid state. + self_type & + clear() { + _mask.clear(); + return *this; + } + + /// Equality. + bool operator==(self_type const &that) const; + + /// Inequality + bool operator!=(self_type const &that) const; + +protected: + IPAddr _addr; ///< Address and family. + IPMask _mask; ///< Network mask. +}; + +// -------------------------------------------------------------------------- +/** Coloring of IP address space. + * + * @tparam PAYLOAD The color class. + * + * This is a class to do fast coloring and lookup of the IP address space. It is range oriented and + * performs well for ranges, much less well for singletons. Conceptually every IP address is a key + * in the space and can have a color / payload of type @c PAYLOAD. + * + * @c PAYLOAD must have the properties + * + * - Cheap to copy. + * - Comparable via the equality and inequality operators. + */ +template class IPSpace { + using self_type = IPSpace; + using IP4Space = DiscreteSpace; + using IP6Space = DiscreteSpace; + +public: + using payload_t = PAYLOAD; ///< Export payload type. + using value_type = std::tuple; + + /// Construct an empty space. + IPSpace() = default; + + /** Mark the range @a r with @a payload. + * + * @param range Range to mark. + * @param payload Payload to assign. + * @return @a this + * + * All addresses in @a r are set to have the @a payload. + */ + self_type &mark(IPRange const &range, PAYLOAD const &payload); + + /** Fill the @a range with @a payload. + * + * @param range Destination range. + * @param payload Payload for range. + * @return this + * + * Addresses in @a range are set to have @a payload if the address does not already have a payload. + */ + self_type &fill(IPRange const &range, PAYLOAD const &payload); + + /** Erase addresses in @a range. + * + * @param range Address range. + * @return @a this + */ + self_type &erase(IPRange const &range); + + /** Blend @a color in to the @a range. + * + * @tparam F Blending functor type (deduced). + * @tparam U Data to blend in to payloads. + * @param range Target range. + * @param color Data to blend in to existing payloads in @a range. + * @param blender Blending functor. + * @return @a this + * + * @a blender is required to have the signature void(PAYLOAD& lhs , U CONST&rhs). It must + * act as a compound assignment operator, blending @a rhs into @a lhs. That is, if the result of + * blending @a rhs in to @a lhs is defined as "lhs @ rhs" for the binary operator "@", then @a + * blender computes "lhs @= rhs". + * + * Every address in @a range is assigned a payload. If the address does not already have a color, + * it is assigned the default constructed @c PAYLOAD blended with @a color. If the address has a + * @c PAYLOAD @a p, @a p is updated by invoking blender(p, color), with the expectation + * that @a p will be updated in place. + */ + template self_type &blend(IPRange const &range, U const &color, F &&blender); + + template + self_type & blend(IP4Range const &range, U const &color, F &&blender); + + template + self_type & + blend(IP6Range const &range, U const &color, F &&blender); + + /// @return The number of distinct ranges. + size_t + count() const { + return _ip4.count() + _ip6.count(); + } + + size_t + count_ip4() const { + return _ip4.count(); + } + size_t + count_ip6() const { + return _ip6.count(); + } + + size_t count(sa_family_t f) const; + + /// Remove all ranges. + void clear(); + + /** Constant iterator. + * The value type is a tuple of the IP address range and the @a PAYLOAD. Both are constant. + * + * @internal The non-const iterator is a subclass of this, in order to share implementation. This + * also makes it easy to convert from iterator to const iterator, which is desirable. + * + * @internal The return type is quite tricky because the value type of the nested containers is + * not the same as the value type for this container. It's not even a composite - @c IPRange is + * not an alias for either of the family specific range types. Therefore the iterator itself must + * contain a synthesized instance of the value type, which creates scoping and update problems. + * The approach here is to update the synthetic value when the iterator is modified and returning + * it by value for the dereference operator because a return by reference means code like + * @code + * auto && [ r , p ] = *(space.find(addr)); + * @endcode + * can fail due to the iterator going out of scope after the statement is finished making @a r + * and @a p dangling references. If the return is by value the compiler takes care of it. + */ + class const_iterator { + using self_type = const_iterator; ///< Self reference type. + friend class IPSpace; + + public: + using value_type = std::tuple; /// Import for API compliance. + // STL algorithm compliance. + using iterator_category = std::bidirectional_iterator_tag; + using pointer = value_type *; + using reference = value_type &; + using difference_type = int; + + /// Default constructor. + const_iterator() = default; + + /// Copy constructor. + const_iterator(self_type const &that); + + /// Assignment. + self_type &operator=(self_type const &that); + + /// Pre-increment. + /// Move to the next element in the list. + /// @return The iterator. + self_type &operator++(); + + /// Pre-decrement. + /// Move to the previous element in the list. + /// @return The iterator. + self_type &operator--(); + + /// Post-increment. + /// Move to the next element in the list. + /// @return The iterator value before the increment. + self_type operator++(int); + + /// Post-decrement. + /// Move to the previous element in the list. + /// @return The iterator value before the decrement. + self_type operator--(int); + + /// Dereference. + /// @return A reference to the referent. + value_type operator*() const; + + /// Dereference. + /// @return A pointer to the referent. + value_type const *operator->() const; + + /// Equality + bool operator==(self_type const &that) const; + + /// Inequality + bool operator!=(self_type const &that) const; + + protected: + // These are stored non-const to make implementing @c iterator easier. The containing class provides the + // required @c const protection. Internally a tuple of iterators is stored for forward iteration. If + // the primary (ipv4) iterator is at the end, then use the secondary (ipv6) iterator. The reverse + // is done for reverse iteration. This depends on the extra support @c IntrusiveDList iterators + // provide. + typename IP4Space::iterator _iter_4; ///< IPv4 sub-space iterator. + typename IP6Space::iterator _iter_6; ///< IPv6 sub-space iterator. + /// Current value. + value_type _value{IPRange{}, *static_cast(detail::pseudo_nullptr)}; + + /** Internal constructor. + * + * @param iter4 Starting place for IPv4 subspace. + * @param iter6 Starting place for IPv6 subspace. + * + * In practice, at most one iterator should be "internal", the other should be the beginning or end. + */ + const_iterator(typename IP4Space::iterator const &iter4, typename IP6Space::iterator const &iter6); + }; + + /** Iterator. + * The value type is a tuple of the IP address range and the @a PAYLOAD. The range is constant + * and the @a PAYLOAD is a reference. This can be used to update the @a PAYLOAD for this range. + * + * @note Range merges are not trigged by modifications of the @a PAYLOAD via an iterator. + */ + class iterator : public const_iterator { + using self_type = iterator; + using super_type = const_iterator; + + friend class IPSpace; + + protected: + using super_type::super_type; /// Inherit supertype constructors. + /// Protected constructor to convert const to non-const. + /// @note This makes for much less code duplication in iterator relevant methods. + iterator(const_iterator const& that) : const_iterator(that) {} + public: + /// Value type of iteration. + using value_type = std::tuple; + using pointer = value_type *; + using reference = value_type &; + + /// Default constructor. + iterator() = default; + + /// Copy constructor. + iterator(self_type const &that); + + /// Assignment. + self_type &operator=(self_type const &that); + + /// Pre-increment. + /// Move to the next element in the list. + /// @return The iterator. + self_type &operator++(); + + /// Pre-decrement. + /// Move to the previous element in the list. + /// @return The iterator. + self_type &operator--(); + + /// Post-increment. + /// Move to the next element in the list. + /// @return The iterator value before the increment. + self_type + operator++(int) { + self_type zret{*this}; + ++*this; + return zret; + } + + /// Post-decrement. + /// Move to the previous element in the list. + /// @return The iterator value before the decrement. + self_type + operator--(int) { + self_type zret{*this}; + --*this; + return zret; + } + + /// Dereference. + /// @return A reference to the referent. + value_type operator*() const; + + /// Dereference. + /// @return A pointer to the referent. + value_type const *operator->() const; + }; + + /** Find the payload for an @a addr. + * + * @param addr Address to find. + * @return Iterator for the range containing @a addr. + */ + iterator find(IPAddr const &addr); + + /** Find the payload for an @a addr. + * + * @param addr Address to find. + * @return Iterator for the range containing @a addr. + */ + const_iterator find(IPAddr const &addr) const; + + /** Find the payload for an @a addr. + * + * @param addr Address to find. + * @return An iterator which is valid if @a addr was found, @c end if not. + */ + iterator find(IP4Addr const &addr); + + /** Find the payload for an @a addr. + * + * @param addr Address to find. + * @return An iterator which is valid if @a addr was found, @c end if not. + */ + const_iterator find(IP4Addr const &addr) const; + + /** Find the payload for an @a addr. + * + * @param addr Address to find. + * @return An iterator which is valid if @a addr was found, @c end if not. + */ + iterator find(IP6Addr const &addr); + + /** Find the payload for an @a addr. + * + * @param addr Address to find. + * @return An iterator which is valid if @a addr was found, @c end if not. + */ + const_iterator find(IP6Addr const &addr) const; + + /// @return An iterator to the first element. + iterator begin(); + + /// @return A constant iterator to the first element. + const_iterator begin() const; + + /// @return An iterator past the last element. + iterator end(); + + /// @return A constant iterator past the last element. + const_iterator end() const; + + /// @return Iterator to the first IPv4 address. + iterator begin_ip4(); + /// @return Iterator to the first IPv4 address. + const_iterator begin_ip4() const; + + /// @return Iterator past the last IPv4 address. + iterator end_ip4(); + /// @return Iterator past the last IPv4 address. + const_iterator end_ip4() const; + + /// @return Iterator at the first IPv6 address. + iterator begin_ip6(); + /// @return Iterator at the first IPv6 address. + const_iterator begin_ip6() const; + /// @return Iterator past the last IPv6 address. + iterator end_ip6(); + /// @return Iterator past the last IPv6 address. + const_iterator end_ip6() const; + + /// @return Iterator to the first address of @a family. + const_iterator begin(sa_family_t family) const; + + /// @return Iterator past the last address of @a family. + const_iterator + end(sa_family_t family) const; + +protected: + IP4Space _ip4; ///< Sub-space containing IPv4 ranges. + IP6Space _ip6; ///< sub-space containing IPv6 ranges. +}; + +template +IPSpace::const_iterator::const_iterator(typename IP4Space::iterator const &iter4, typename IP6Space::iterator const &iter6) +: _iter_4(iter4), _iter_6(iter6) { + if (_iter_4.has_next()) { + new (&_value) value_type{_iter_4->range(), _iter_4->payload()}; + } else if (_iter_6.has_next()) { + new (&_value) value_type{_iter_6->range(), _iter_6->payload()}; + } +} + +template IPSpace::const_iterator::const_iterator(self_type const &that) { + *this = that; +} + +template +auto +IPSpace::const_iterator::operator=(self_type const &that) -> self_type & { + _iter_4 = that._iter_4; + _iter_6 = that._iter_6; + new (&_value) value_type{that._value}; + return *this; +} + +template +auto +IPSpace::const_iterator::operator++() -> self_type & { + bool incr_p = false; + if (_iter_4.has_next()) { + ++_iter_4; + incr_p = true; + if (_iter_4.has_next()) { + new (&_value) value_type{_iter_4->range(), _iter_4->payload()}; + return *this; + } + } + + if (_iter_6.has_next()) { + if (incr_p || (++_iter_6).has_next()) { + new (&_value) value_type{_iter_6->range(), _iter_6->payload()}; + return *this; + } + } + new (&_value) value_type{IPRange{}, *static_cast(detail::pseudo_nullptr)}; + return *this; +} + +template +auto +IPSpace::const_iterator::operator++(int) -> self_type { + self_type zret(*this); + ++*this; + return zret; +} + +template +auto +IPSpace::const_iterator::operator--() -> self_type & { + if (_iter_6.has_prev()) { + --_iter_6; + new (&_value) value_type{_iter_6->range(), _iter_6->payload()}; + return *this; + } + if (_iter_4.has_prev()) { + --_iter_4; + new (&_value) value_type{_iter_4->range(), _iter_4->payload()}; + return *this; + } + new (&_value) value_type{IPRange{}, *static_cast(detail::pseudo_nullptr)}; + return *this; +} + +template +auto +IPSpace::const_iterator::operator--(int) -> self_type { + self_type zret(*this); + --*this; + return zret; +} + +template +auto +IPSpace::const_iterator::operator*() const -> value_type { + return _value; +} + +template +auto +IPSpace::const_iterator::operator->() const -> value_type const * { + return &_value; +} + +/* Bit of subtlety with equality - although it seems that if @a _iter_4 is valid, it doesn't matter + * where @a _iter6 is (because it is really the iterator location that's being checked), it's + * neccesary to do the @a _iter_4 validity on both iterators to avoid the case of a false positive + * where different internal iterators are valid. However, in practice the other (non-active) + * iterator won't have an arbitrary value, it will be either @c begin or @c end in step with the + * active iterator therefore it's effective and cheaper to just check both values. + */ + +template +bool +IPSpace::const_iterator::operator==(self_type const &that) const { + return _iter_4 == that._iter_4 && _iter_6 == that._iter_6; +} + +template +bool +IPSpace::const_iterator::operator!=(self_type const &that) const { + return _iter_4 != that._iter_4 || _iter_6 != that._iter_6; +} + +template IPSpace::iterator::iterator(self_type const &that) { + *this = that; +} + +template +auto +IPSpace::iterator::operator=(self_type const &that) -> self_type & { + this->super_type::operator=(that); + return *this; +} + +template +auto +IPSpace::iterator::operator->() const -> value_type const * { + return static_cast(&super_type::_value); +} + +template +auto +IPSpace::iterator::operator*() const -> value_type { + return reinterpret_cast(super_type::_value); +} + +template +auto +IPSpace::iterator::operator++() -> self_type & { + this->super_type::operator++(); + return *this; +} + +template +auto +IPSpace::iterator::operator--() -> self_type & { + this->super_type::operator--(); + return *this; +} + +// -------------------------------------------------------------------------- +/// ------------------------------------------------------------------------------------ + +// +++ IPRange +++ + +inline IP4Range::IP4Range(string_view const &text) { + this->load(text); +} + +inline auto +IP4Range::networks() const -> NetSource { + return {NetSource{*this}}; +} + +inline IP6Range::IP6Range(string_view const &text) { + this->load(text); +} + +inline auto +IP6Range::networks() const -> NetSource { + return {NetSource{*this}}; +} + +inline IPRange::IPRange(IP4Range const &range) : _family(AF_INET) { + _range._ip4 = range; +} + +inline IPRange::IPRange(IP6Range const &range) : _family(AF_INET6) { + _range._ip6 = range; +} + +inline IPRange::IPRange(string_view const &text) { + this->load(text); +} + +inline auto +IPRange::networks() const -> NetSource { + return {NetSource{*this}}; +} + +inline bool +IPRange::is(sa_family_t family) const { + return family == _family; +} + +// +++ IPNet +++ + +inline IP4Net::IP4Net(swoc::IP4Addr addr, swoc::IPMask mask) : _addr(addr & mask), _mask(mask) {} + +inline IPMask const & +IP4Net::mask() const { + return _mask; +} + +inline bool +IP4Net::is_valid() const { + return _mask.is_valid(); +} + +inline IP4Addr +IP4Net::lower_bound() const { + return _addr; +} + +inline IP4Addr +IP4Net::upper_bound() const { + return _addr | _mask; +} + +inline IP4Range +IP4Net::as_range() const { + return {this->lower_bound(), this->upper_bound()}; +} + +inline bool +IP4Net::operator==(self_type const &that) const { + return _mask == that._mask && _addr == that._addr; +} + +inline bool +IP4Net::operator!=(self_type const &that) const { + return _mask != that._mask || _addr != that._addr; +} + +inline IP4Net::self_type & +IP4Net::assign(IP4Addr const &addr, IPMask const &mask) { + _addr = addr & mask; + _mask = mask; + return *this; +} + +inline IP6Net::IP6Net(swoc::IP6Addr addr, swoc::IPMask mask) : _addr(addr & mask), _mask(mask) {} + +inline IPMask const & +IP6Net::mask() const { + return _mask; +} + +inline bool +IP6Net::is_valid() const { + return _mask.is_valid(); +} + +inline IP6Addr +IP6Net::lower_bound() const { + return _addr; +} + +inline IP6Addr +IP6Net::upper_bound() const { + return _addr | _mask; +} + +inline IP6Range +IP6Net::as_range() const { + return {this->lower_bound(), this->upper_bound()}; +} + +inline bool +IP6Net::operator==(self_type const &that) const { + return _mask == that._mask && _addr == that._addr; +} + +inline bool +IP6Net::operator!=(self_type const &that) const { + return _mask != that._mask || _addr != that._addr; +} + +inline IP6Net::self_type & +IP6Net::assign(IP6Addr const &addr, IPMask const &mask) { + _addr = addr & mask; + _mask = mask; + return *this; +} + +inline IPNet::IPNet(IPAddr const &addr, IPMask const &mask) : _addr(addr & mask), _mask(mask) {} + +inline IPNet::IPNet(TextView text) { + this->load(text); +} + +inline bool +IPNet::is_valid() const { + return _mask.is_valid(); +} + +inline IPAddr +IPNet::lower_bound() const { + return _addr; +} + +inline IPAddr +IPNet::upper_bound() const { + return _addr | _mask; +} + +inline IPMask::raw_type +IPNet::width() const { + return _mask.width(); +} + +inline IPMask const & +IPNet::mask() const { + return _mask; +} + +inline IPRange +IPNet::as_range() const { + return {this->lower_bound(), this->upper_bound()}; +} + +inline IPNet::self_type & +IPNet::assign(IPAddr const &addr, IPMask const &mask) { + _addr = addr & mask; + _mask = mask; + return *this; +} + +inline bool +IPNet::operator==(IPNet::self_type const &that) const { + return _mask == that._mask && _addr == that._addr; +} + +inline bool +IPNet::operator!=(IPNet::self_type const &that) const { + return _mask != that._mask || _addr != that._addr; +} + +inline bool +operator==(IPNet const &lhs, IP4Net const &rhs) { + return lhs.is_ip4() && lhs.ip4() == rhs; +} + +inline bool +operator==(IP4Net const &lhs, IPNet const &rhs) { + return rhs.is_ip4() && rhs.ip4() == lhs; +} + +inline bool +operator==(IPNet const &lhs, IP6Net const &rhs) { + return lhs.is_ip6() && lhs.ip6() == rhs; +} + +inline bool +operator==(IP6Net const &lhs, IPNet const &rhs) { + return rhs.is_ip6() && rhs.ip6() == lhs; +} + +// +++ Range -> Network classes +++ + +inline bool +IP4Range::NetSource::is_valid(swoc::IP4Addr mask) const { + return ((mask._addr & _range._min._addr) == _range._min._addr) && ((_range._min._addr | ~mask._addr) <= _range._max._addr); +} + +inline IP4Net +IP4Range::NetSource::operator*() const { + return IP4Net{_range.min(), IPMask{_cidr}}; +} + +inline IP4Range::NetSource::iterator +IP4Range::NetSource::begin() const { + return *this; +} + +inline IP4Range::NetSource::iterator +IP4Range::NetSource::end() { + return self_type{range_type{}}; +} + +inline bool +IP4Range::NetSource::empty() const { + return _range.empty(); +} + +inline IPMask +IP4Range::NetSource::mask() const { + return IPMask{_cidr}; +} + +inline auto +IP4Range::NetSource::operator->() -> self_type * { + return this; +} + +inline IP4Addr const & +IP4Range::NetSource::addr() const { + return _range.min(); +} + +inline bool +IP4Range::NetSource::operator==(IP4Range::NetSource::self_type const &that) const { + return ((_cidr == that._cidr) && (_range == that._range)) || (_range.empty() && that._range.empty()); +} + +inline bool +IP4Range::NetSource::operator!=(IP4Range::NetSource::self_type const &that) const { + return !(*this == that); +} + +inline auto +IP6Range::NetSource::begin() const -> iterator { + return *this; +} + +inline auto +IP6Range::NetSource::end() const -> iterator { + return self_type{range_type{}}; +} + +inline bool +IP6Range::NetSource::empty() const { + return _range.empty(); +} + +inline IP6Net +IP6Range::NetSource::operator*() const { + return IP6Net{_range.min(), _mask}; +} + +inline auto +IP6Range::NetSource::operator->() -> self_type * { + return this; +} + +inline bool +IP6Range::NetSource::is_valid(IPMask const &mask) { + return ((_range.min() & mask) == _range.min()) && ((_range.min() | mask) <= _range.max()); +} + +inline bool +IP6Range::NetSource::operator==(IP6Range::NetSource::self_type const &that) const { + return ((_mask == that._mask) && (_range == that._range)) || (_range.empty() && that._range.empty()); +} + +inline bool +IP6Range::NetSource::operator!=(IP6Range::NetSource::self_type const &that) const { + return !(*this == that); +} + +inline IP6Addr const & +IP6Range::NetSource::addr() const { + return _range.min(); +} + +inline IPMask +IP6Range::NetSource::mask() const { + return _mask; +} + +inline IPRange::NetSource::NetSource(IPRange::NetSource::range_type const &range) { + if (range.is_ip4()) { + new (&_ip4) decltype(_ip4)(range.ip4()); + _family = AF_INET; + } else if (range.is_ip6()) { + new (&_ip6) decltype(_ip6)(range.ip6()); + _family = AF_INET6; + } +} + +inline auto +IPRange::NetSource::begin() const -> iterator { + return *this; +} + +inline auto +IPRange::NetSource::end() const -> iterator { + return AF_INET == _family ? self_type{IP4Range{}} : AF_INET6 == _family ? self_type{IP6Range{}} : self_type{IPRange{}}; +} + +inline IPAddr +IPRange::NetSource::addr() const { + if (AF_INET == _family) { + return _ip4.addr(); + } else if (AF_INET6 == _family) { + return _ip6.addr(); + } + return {}; +} + +inline IPMask +IPRange::NetSource::mask() const { + if (AF_INET == _family) { + return _ip4.mask(); + } else if (AF_INET6 == _family) { + return _ip6.mask(); + } + return {}; +} + +inline IPNet +IPRange::NetSource::operator*() const { + return {this->addr(), this->mask()}; +} + +inline auto +IPRange::NetSource::operator++() -> self_type & { + if (AF_INET == _family) { + ++_ip4; + } else if (AF_INET6 == _family) { + ++_ip6; + } + return *this; +} + +inline auto +IPRange::NetSource::operator->() -> self_type * { + return this; +} + +inline bool +IPRange::NetSource::operator==(self_type const &that) const { + if (_family != that._family) { + return false; + } + if (AF_INET == _family) { + return _ip4 == that._ip4; + } else if (AF_INET6 == _family) { + return _ip6 == that._ip6; + } else if (AF_UNSPEC == _family) { + return true; + } + return false; +} + +inline bool +IPRange::NetSource::operator!=(self_type const &that) const { + return !(*this == that); +} + +// --- IPSpace + +template +auto +IPSpace::mark(IPRange const &range, PAYLOAD const &payload) -> self_type & { + if (range.is(AF_INET)) { + _ip4.mark(range.ip4(), payload); + } else if (range.is(AF_INET6)) { + _ip6.mark(range.ip6(), payload); + } + return *this; +} + +template +auto +IPSpace::fill(IPRange const &range, PAYLOAD const &payload) -> self_type & { + if (range.is(AF_INET6)) { + _ip6.fill(range.ip6(), payload); + } else if (range.is(AF_INET)) { + _ip4.fill(range.ip4(), payload); + } + return *this; +} + +template +auto +IPSpace::erase(IPRange const &range) -> self_type & { + if (range.is(AF_INET)) { + _ip4.erase(range.ip4()); + } else if (range.is(AF_INET6)) { + _ip6.erase(range.ip6()); + } + return *this; +} + +template +template +auto +IPSpace::blend(IPRange const &range, U const &color, F &&blender) -> self_type & { + if (range.is(AF_INET)) { + _ip4.blend(range.ip4(), color, blender); + } else if (range.is(AF_INET6)) { + _ip6.blend(range.ip6(), color, blender); + } + return *this; +} + +template +template +auto +IPSpace::blend(IP4Range const &range, U const &color, F &&blender) -> self_type & { + _ip4.blend(range, color, std::forward(blender)); + return *this; +} + +template +template +auto +IPSpace::blend(IP6Range const &range, U const &color, F &&blender) -> self_type & { + _ip6.blend(range, color, std::forward(blender)); + return *this; +} + +template +void +IPSpace::clear() { + _ip4.clear(); + _ip6.clear(); +} + +template +auto IPSpace::begin() -> iterator { return iterator{_ip4.begin(), _ip6.begin()}; } + +template +auto IPSpace::begin() const -> const_iterator { return const_cast(this)->begin(); } + +template +auto IPSpace::end() -> iterator { return iterator{_ip4.end(), _ip6.end()}; } + +template +auto IPSpace::end() const -> const_iterator { return const_cast(this)->end(); } + +template +auto IPSpace::begin_ip4() -> iterator { return this->begin(); } + +template +auto IPSpace::begin_ip4() const -> const_iterator { return this->begin(); } + +template +auto IPSpace::end_ip4() -> iterator { return { _ip4.end(), _ip6.begin() }; } + +template +auto IPSpace::end_ip4() const -> const_iterator { return const_cast(this)->end_ip4(); } + +template +auto IPSpace::begin_ip6() -> iterator { + return { _ip4.end(), _ip6.begin() }; +} + +template +auto IPSpace::begin_ip6() const -> const_iterator { + return const_cast(this)->begin_ip6(); +} + +template +auto IPSpace::end_ip6() -> iterator { return this->end(); } + +template +auto IPSpace::end_ip6() const -> const_iterator { return this->end(); } + +template +auto +IPSpace::find(IPAddr const &addr) -> iterator { + if (addr.is_ip4()) { + return this->find(addr.ip4()); + } else if (addr.is_ip6()) { + return this->find(addr.ip6()); + } + return this->end(); +} + +template +auto IPSpace::find(IPAddr const &addr) const -> const_iterator { + return const_cast(this)->find(addr); +} + +template +auto +IPSpace::find(IP4Addr const &addr) -> iterator { + if ( auto spot = _ip4.find(addr) ; spot != _ip4.end()) { + return { spot, _ip6.begin() }; + } + return this->end(); +} + +template +auto +IPSpace::find(IP4Addr const &addr) const -> const_iterator { + return const_cast(this)->find(addr); +} + +template +auto IPSpace::find(IP6Addr const &addr) -> iterator { return {_ip4.end(), _ip6.find(addr)}; } + +template +auto IPSpace::find(IP6Addr const &addr) const -> const_iterator { return {_ip4.end(), _ip6.find(addr)}; } + +template +auto +IPSpace::begin(sa_family_t family) const -> const_iterator { + if (AF_INET == family) { + return this->begin_ip4(); + } else if (AF_INET6 == family) { + return this->begin_ip6(); + } + return this->end(); +} + +template +auto +IPSpace::end(sa_family_t family) const -> const_iterator { + if (AF_INET == family) { + return this->end_ip4(); + } else if (AF_INET6 == family) { + return this->end_ip6(); + } + return this->end(); +} + +template +size_t +IPSpace::count(sa_family_t f) const { + return IP4Addr::AF_value == f ? _ip4.count() : IP6Addr::AF_value == f ? _ip6.count() : 0; +} + +}} // namespace swoc::SWOC_VERSION_NS + +/// @cond NOT_DOCUMENTED +namespace std { + +// -- Tuple support for IP4Net -- +template <> class tuple_size : public std::integral_constant {}; + +template class tuple_element { static_assert("swoc::IP4Net tuple index out of range"); }; + +template <> class tuple_element<0, swoc::IP4Net> { +public: + using type = swoc::IP4Addr; +}; + +template <> class tuple_element<1, swoc::IP4Net> { +public: + using type = swoc::IPMask; +}; + +// -- Tuple support for IP6Net -- +template <> class tuple_size : public std::integral_constant {}; + +template class tuple_element { static_assert("swoc::IP6Net tuple index out of range"); }; + +template <> class tuple_element<0, swoc::IP6Net> { +public: + using type = swoc::IP6Addr; +}; + +template <> class tuple_element<1, swoc::IP6Net> { +public: + using type = swoc::IPMask; +}; + +// -- Tuple support for IPNet -- +template <> class tuple_size : public std::integral_constant {}; + +template class tuple_element { static_assert("swoc::IPNet tuple index out of range"); }; + +template <> class tuple_element<0, swoc::IPNet> { +public: + using type = swoc::IPAddr; +}; + +template <> class tuple_element<1, swoc::IPNet> { +public: + using type = swoc::IPMask; +}; + +} // namespace std + +namespace swoc { inline namespace SWOC_VERSION_NS { + +template +typename std::tuple_element::type +get(swoc::IP4Net const &net) { + if constexpr (IDX == 0) { + return net.lower_bound(); + } else if constexpr (IDX == 1) { + return net.mask(); + } +} + +template +typename std::tuple_element::type +get(swoc::IP6Net const &net) { + if constexpr (IDX == 0) { + return net.lower_bound(); + } else if constexpr (IDX == 1) { + return net.mask(); + } +} + +template +typename std::tuple_element::type +get(swoc::IPNet const &net) { + if constexpr (IDX == 0) { + return net.lower_bound(); + } else if constexpr (IDX == 1) { + return net.mask(); + } +} +/// @endcond + +}} // namespace swoc::SWOC_VERSION_NS diff --git a/lib/swoc/include/swoc/IPSrv.h b/lib/swoc/include/swoc/IPSrv.h new file mode 100644 index 00000000000..e0e4d489bb1 --- /dev/null +++ b/lib/swoc/include/swoc/IPSrv.h @@ -0,0 +1,680 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright Network Geographics 2014 +/** @file + IP address and network related classes. + */ + +#pragma once + +#include +#include + +#include "swoc/swoc_version.h" +#include "swoc/IPAddr.h" + +namespace swoc { inline namespace SWOC_VERSION_NS { + +class IPSrv; + +/// An IPv4 address and host_order_port, modeled on an SRV type for DNS. +class IP4Srv { +private: + using self_type = IP4Srv; + +public: + constexpr IP4Srv() = default; ///< Default constructor. + + /** Construct from address and host_order_port. + * + * @param addr The address. + * @param port The port in host order, defaults to 0. + */ + explicit IP4Srv(IP4Addr addr, in_port_t port = 0); + + /** Construct from generic. + * + * @param that The generic SRV. + * + * If @a that is not IPv4 the result is a default constructed instance. + */ + explicit IP4Srv(IPSrv const& that); + + /** Construct from socket address. + * + * @param sa Socket address. + */ + IP4Srv(sockaddr_in const * s) : _addr(s), _port(ntohs(s->sin_port)) {} + + /** Construct from a string. + * + * @param text Input text. + * + * If the port is not present it is set to zero. + */ + explicit IP4Srv(swoc::TextView text); + + /// Implicit conversion to an address. + constexpr operator IP4Addr const& () const; + + /// @return The address. + constexpr IP4Addr const& addr() const; + + /// @return The host_order_port in host order. + in_port_t host_order_port() const; + + /// @return The host_order_port in network order. + in_port_t network_order_port() const; + + /// The protocol family. + /// @return @c AF_INET + /// @note Useful primarily for template classes. + static constexpr sa_family_t family(); + + bool operator == (self_type that) const; + bool operator != (self_type that) const; + + bool operator < (self_type that) const; + bool operator <= (self_type const& that) const; + bool operator > (self_type const& that) const; + bool operator >= (self_type const& that) const; + + /** Load from a string. + * + * @param text Input string. + * @return @c true if @a text in a valid format, @c false if not. + */ + bool load(swoc::TextView text); + + /** Change the address. + * + * @param addr Address to assign. + * @return @a this + */ + self_type & assign(IP4Addr const& addr); + + /** Change the host_order_port. + * + * @param port Port to assign. + * @return @a this. + */ + self_type & assign(in_port_t port); + + /** Change the address and port. + * + * @param addr Address to assign. + * @param port Port to assign. + * @return @a this + */ + self_type & assign(IP4Addr const& addr, in_port_t port); + + /** Change the address and port. + * + * @param s A socket address. + * @return @a this + */ + self_type & assign(sockaddr_in const * s); + +protected: + IP4Addr _addr; ///< Address. + in_port_t _port = 0; ///< Port. +}; + +/// An IPv6 address and host_order_port, modeled on an SRV type for DNS. +class IP6Srv { +private: + using self_type = IP6Srv; + +public: + IP6Srv() = default; ///< Default constructor. + + /** Construct from address and host_order_port. + * + * @param addr The address. + * @param port The port in host order, defaults to 0. + */ + explicit IP6Srv(IP6Addr addr, in_port_t port = 0); + + /** Construct from generic. + * + * @param that The generic SRV. + * + * If @a that is not IPv6 the result is a default constructed instance. + */ + explicit IP6Srv(IPSrv const& that); + + /** Construct from a socket address. + * + * @param s Socket address. + */ + explicit IP6Srv(sockaddr_in6 const * s); + + /** Construct from a string. + * + * @param text Input text. + * + * If the port is not present it is set to zero. + */ + explicit IP6Srv(swoc::TextView text); + + /** Load from a string. + * + * @param text Input string. + * @return @c true if @a text in a valid format, @c false if not. + */ + bool load(swoc::TextView text); + + /// Implicit conversion to address. + constexpr operator IP6Addr const& () const; + + /// @return The address. + constexpr IP6Addr const& addr() const; + /// @return The port in host order. + in_port_t host_order_port() const; + /// @return The port in network order. + in_port_t network_order_port() const; + + /// The protocol family. + /// @return @c AF_INET6 + /// @note Useful primarily for template classes. + static constexpr sa_family_t family(); + + bool operator == (self_type that) const; + bool operator != (self_type that) const; + + bool operator < (self_type that) const; + bool operator <= (self_type const& that) const; + bool operator > (self_type const& that) const; + bool operator >= (self_type const& that) const; + + /** Change the address. + * + * @param addr Address to assign. + * @return @a this + */ + self_type & assign(IP6Addr const& addr); + + /** Change the host_order_port. + * + * @param port Port to assign. + * @return @a this. + */ + self_type & assign(in_port_t port); + + /** Change the address and host_order_port. + * + * @param addr Address to assign. + * @param port Port to assign. + * @return @a this + */ + self_type & assign(IP6Addr const& addr, in_port_t port); + + /** Change the address and port. + * + * @param s A socket address. + * @return @a this + */ + self_type & assign(sockaddr_in6 const * s); + +protected: + IP6Addr _addr; ///< Address. + in_port_t _port = 0; ///< Port. +}; + +/// An IP address and host_order_port, modeled on an SRV type for DNS. +class IPSrv { +private: + using self_type = IPSrv; + +public: + IPSrv() = default; ///< Default constructor. + explicit IPSrv(IP4Addr addr, in_port_t port = 0) : _srv(IP4Srv{addr, port}), _family(addr.family()) {} + explicit IPSrv(IP6Addr addr, in_port_t port = 0) : _srv(IP6Srv{addr, port}), _family(addr.family()) {} + explicit IPSrv(IPAddr addr, in_port_t port = 0); + explicit IPSrv(sockaddr const * sa); + explicit IPSrv(sockaddr_in const * s); + explicit IPSrv(sockaddr_in6 const * s); + explicit IPSrv(IPEndpoint const& ep); + + /** Construct from a string. + * + * @param text Input text. + * + * If the port is not present it is set to zero. + */ + explicit IPSrv(swoc::TextView text); + + /** Load from a string. + * + * @param text Input string. + * @return @c true if @a text in a valid format, @c false if not. + */ + bool load(swoc::TextView text); + + /// @return The address. + IPAddr addr() const; + /// @return The host_order_port in host order.. + constexpr in_port_t host_order_port() const; + /// @return The host_order_port in network order. + in_port_t network_order_port() const; + /// @return The protocol of the current value. + constexpr sa_family_t family() const; + + /// @return @c true if the data is IPv4, @c false if not. + bool is_ip4() const; + /// @return @c true if hte data is IPv6, @c false if not. + bool is_ip6() const; + + /// @return The IPv4 data. + IP4Srv const& ip4() const { return _srv._ip4; } + /// @return The IPv6 data. + IP6Srv const& ip6() const { return _srv._ip6; } + + /** Change the address. + * + * @param addr Address to assign. + * @return @a this + */ + self_type & assign(IP4Addr const& addr); + + /** Change the address. + * + * @param addr Address to assign. + * @return @a this + */ + self_type & assign(IP6Addr const& addr); + + /** Change the address. + * + * @param addr Address to assign. + * @return @a this + * + * If @a addr isn't valid then no assignment is made. + */ + self_type & assign(IPAddr const& addr); + + /** Change the host_order_port. + * + * @param port Port in host order. + * @return @a this. + */ + self_type & assign(in_port_t port); + + /** Change the address and port. + * + * @param addr Address to assign. + * @param port Port to assign. + * @return @a this + */ + self_type & assign(IP4Addr const& addr, in_port_t port); + + /** Change the address and port. + * + * @param addr Address to assign. + * @param port Port to assign. + * @return @a this + */ + self_type & assign(IP6Addr const& addr, in_port_t port); + + /** Change the address and port. + * + * @param sa Socket address. + * @return @a this + */ + self_type & assign(sockaddr const * sa); + + /** Change the address and port. + * + * @param s Socket address. + * @return @a this + */ + self_type & assign(sockaddr_in const * s); + + /** Change the address and port. + * + * @param s Socket address. + * @return @a this + */ + self_type & assign(sockaddr_in6 const * s); + + /** Change the address and host_order_port. + * + * @param addr Address to assign. + * @param port Port to assign. + * @return @a this + * + * If @a addr isn't valid then no assignment is made. + */ + self_type & assign(IPAddr const& addr, in_port_t port); + + self_type & operator = (self_type const& that) = default; + self_type & operator = (IP4Srv const& that); + self_type & operator = (IP6Srv const& that); + self_type & operator = (sockaddr const * sa) { return this->assign(sa); } + self_type & operator = (sockaddr_in const * s) { return this->assign(s); } + self_type & operator = (sockaddr_in6 const * s) { return this->assign(s); } + +protected: + /// Family specialized data. + union data { + IP4Srv _ip4; ///< IPv4 address (host) + IP6Srv _ip6; ///< IPv6 address (host) + + data() {}; // enable default construction. + + /// Construct from IPv4 data. + explicit data(IP4Srv const &srv) : _ip4(srv) {} + explicit data(sockaddr_in const * s) : _ip4(s) {} + + /// Construct from IPv6 data. + explicit data(IP6Srv const &srv) : _ip6(srv) {} + explicit data(sockaddr_in6 const * s) : _ip6(s) {} + + /// @return A generic address. + IPAddr addr(sa_family_t f) const; + + /// @return The port in host order. + constexpr in_port_t port(sa_family_t f) const; + } _srv; + + sa_family_t _family = AF_UNSPEC; ///< Protocol family. + +}; + +// --- Implementation + +inline IP4Srv::IP4Srv(IP4Addr addr, in_port_t port) : _addr(addr), _port(port) {} +inline IP4Srv::IP4Srv(IPSrv const &that) : IP4Srv(that.is_ip4() ? that.ip4() : self_type{}) {} + +inline auto IP4Srv::assign(IP4Addr const &addr) -> self_type & { _addr = addr; return *this; } +inline auto IP4Srv::assign(in_port_t port) -> self_type & { _port = port; return *this; } +inline auto IP4Srv::assign(IP4Addr const &addr, in_port_t port) -> self_type & { _addr = addr; _port = port; return *this;} +inline auto IP4Srv::assign(sockaddr_in const * s) -> self_type & { _addr = s; _port = ntohs(s->sin_port); return *this; } +inline constexpr IP4Srv::operator IP4Addr const &() const { return _addr; } +inline constexpr IP4Addr const & IP4Srv::addr() const { return _addr; } +inline in_port_t IP4Srv::host_order_port() const { return _port; } +inline in_port_t IP4Srv::network_order_port() const { return htons(_port); } +inline constexpr sa_family_t IP4Srv::family() { return AF_INET; } + +inline bool IP4Srv::operator==(IP4Srv::self_type that) const { return _addr == that._addr && _port == that._port; } +inline bool IP4Srv::operator!=(IP4Srv::self_type that) const { return _addr != that._addr || _port != that._port; } +inline bool IP4Srv::operator< (IP4Srv::self_type that) const { return _addr < that._addr || (_addr == that._addr && _port < that._port); } +inline bool IP4Srv::operator<=(IP4Srv::self_type const &that) const { return _addr < that._addr || (_addr == that._addr && _port <= that._port); } +inline bool IP4Srv::operator> (IP4Srv::self_type const &that) const { return that < *this; } +inline bool IP4Srv::operator>=(IP4Srv::self_type const &that) const { return that <= *this; } + +/// --- IPv6 + +inline IP6Srv::IP6Srv(IP6Addr addr, in_port_t port) : _addr(addr), _port(port) {} +inline IP6Srv::IP6Srv(IPSrv const &that) : IP6Srv(that.is_ip6() ? that.ip6() : self_type{}) {} +inline IP6Srv::IP6Srv(const sockaddr_in6 *s) : _addr(s->sin6_addr), _port(ntohs(s->sin6_port)) {} + +inline constexpr IP6Srv::operator IP6Addr const &() const { return _addr; } +inline constexpr IP6Addr const & IP6Srv::addr() const { return _addr; } +inline in_port_t IP6Srv::host_order_port() const { return _port; } +inline in_port_t IP6Srv::network_order_port() const { return htons(_port); } +inline constexpr sa_family_t IP6Srv::family() { return AF_INET6; } + +inline bool IP6Srv::operator==(IP6Srv::self_type that) const { return _addr == that._addr && _port == that._port; } +inline bool IP6Srv::operator!=(IP6Srv::self_type that) const { return _port != that._port || _addr != that._addr; } +inline bool IP6Srv::operator< (IP6Srv::self_type that) const { return _addr < that._addr || (_addr == that._addr && _port < that._port); } +inline bool IP6Srv::operator<=(IP6Srv::self_type const &that) const { return _addr < that._addr || (_addr == that._addr && _port <= that._port); } +inline bool IP6Srv::operator> (IP6Srv::self_type const &that) const { return that < *this; } +inline bool IP6Srv::operator>=(IP6Srv::self_type const &that) const { return that <= *this; } + +inline auto +IP6Srv::assign(in_port_t port) -> self_type & { _port = port; return *this; } + +inline auto +IP6Srv::assign(IP6Addr const &addr) -> self_type & { _addr = addr; return *this; } + +inline auto +IP6Srv::assign(IP6Addr const &addr, in_port_t port) -> self_type & { + _addr = addr; + _port = port; + return *this; +} + +inline auto +IP6Srv::assign(sockaddr_in6 const *s) -> self_type & { + _addr = s; + _port = ntohs(s->sin6_port); + return *this; +} + +// --- Generic SRV + +inline IPSrv::IPSrv(const sockaddr_in *s) : _srv(s), _family(AF_INET) {} +inline IPSrv::IPSrv(const sockaddr_in6 *s) : _srv(s), _family(AF_INET6) {} +inline IPSrv::IPSrv(const sockaddr *sa) { this->assign(sa); } + +inline IPAddr IPSrv::addr() const { return _srv.addr(_family); } +inline constexpr sa_family_t IPSrv::family() const { return _family; } +inline bool IPSrv::is_ip4() const { return _family == AF_INET; } +inline bool IPSrv::is_ip6() const { return _family == AF_INET6; } + +inline auto +IPSrv::assign(IP6Addr const &addr) -> self_type & { + _srv._ip6.assign(addr, this->host_order_port()); + _family = addr.family(); + return *this; +} + +inline auto +IPSrv::assign(IP4Addr const &addr, in_port_t port) -> self_type & { + _srv._ip4.assign(addr, port); + _family = addr.family(); + return *this; +} + +inline auto +IPSrv::assign(IP6Addr const &addr, in_port_t port) -> self_type & { + _srv._ip6.assign(addr, port); + _family = addr.family(); + return *this; +} + +inline auto +IPSrv::assign(IP4Addr const &addr) -> self_type & { + _srv._ip4.assign(addr, this->host_order_port()); + _family = addr.family(); + return *this; +} + +inline auto +IPSrv::assign(in_port_t port) -> self_type & { + if (this->is_ip4()) { _srv._ip4.assign(port); } + else if (this->is_ip6()) { _srv._ip6.assign(port); } + return *this; +} + +inline auto +IPSrv::assign(IPAddr const &addr) -> self_type & { + if (addr.is_ip4()) { this->assign(addr.ip4()); } + else if (addr.is_ip6()) { this->assign(addr.ip6());} + return *this; +} + +inline auto +IPSrv::assign(IPAddr const &addr, in_port_t port) -> self_type & { + if (addr.is_ip4()) { this->assign(addr.ip4(), port); _family = addr.family(); } + else if (addr.is_ip6()) { this->assign(addr.ip6(), port); _family = addr.family(); } + return *this; +} + +inline auto +IPSrv::operator=(IP4Srv const &that) -> self_type & { + _family = that.family(); + _srv._ip4 = that; + return *this; +} + +inline auto +IPSrv::operator=(IP6Srv const &that) -> self_type & { + _family = that.family(); + _srv._ip6 = that; + return *this; +} + +inline auto +IPSrv::assign(sockaddr_in const * s) -> self_type & { + _family = _srv._ip4.family(); + _srv._ip4.assign(s); + return *this; +} + +inline auto +IPSrv::assign(sockaddr_in6 const * s) -> self_type & { + _family = _srv._ip6.family(); + _srv._ip6.assign(s); + return *this; +} + +inline constexpr in_port_t +IPSrv::host_order_port() const { return _srv.port(_family); } + +inline in_port_t +IPSrv::network_order_port() const { return ntohs(_srv.port(_family)); } + +inline IPAddr +IPSrv::data::addr(sa_family_t f) const { + return (f == AF_INET) ? _ip4.addr() : (f == AF_INET6) ? _ip6.addr() : IPAddr::INVALID; +} + +constexpr inline in_port_t +IPSrv::data::port(sa_family_t f) const { + return (f == AF_INET) ? _ip4.host_order_port() : (f == AF_INET6) ? _ip6.host_order_port() : 0; +} +// --- Independent comparisons. + +inline bool operator == (IPSrv const& lhs, IP4Srv const& rhs) { + return lhs.is_ip4() && lhs.ip4() == rhs; +} + +inline bool operator == (IP4Srv const& lhs, IPSrv const& rhs) { + return rhs.is_ip4() && rhs.ip4() == lhs; +} + +inline bool operator != (IPSrv const& lhs, IP4Srv const& rhs) { + return ! lhs.is_ip4() || lhs.ip4() != rhs; +} + +inline bool operator != (IP4Srv const& lhs, IPSrv const& rhs) { + return ! rhs.is_ip4() || rhs.ip4() != lhs; +} + +inline bool operator < (IPSrv const& lhs, IP4Srv const& rhs) { + return lhs.is_ip4() && lhs.ip4() < rhs; +} + +inline bool operator < (IP4Srv const& lhs, IPSrv const& rhs) { + return rhs.is_ip4() && lhs < rhs.ip4(); +} + +inline bool operator <= (IPSrv const& lhs, IP4Srv const& rhs) { + return lhs.is_ip4() && lhs.ip4() <= rhs; +} + +inline bool operator <= (IP4Srv const& lhs, IPSrv const& rhs) { + return rhs.is_ip4() && lhs <= rhs.ip4(); +} + +inline bool operator > (IPSrv const& lhs, IP4Srv const& rhs) { + return lhs.is_ip4() && lhs.ip4() > rhs; +} + +inline bool operator > (IP4Srv const& lhs, IPSrv const& rhs) { + return rhs.is_ip4() && lhs > rhs.ip4(); +} + +inline bool operator >= (IPSrv const& lhs, IP4Srv const& rhs) { + return lhs.is_ip4() && lhs.ip4() >= rhs; +} + +inline bool operator >= (IP4Srv const& lhs, IPSrv const& rhs) { + return rhs.is_ip4() && lhs >= rhs.ip4(); +} + + +inline bool operator == (IPSrv const& lhs, IP6Srv const& rhs) { + return lhs.is_ip6() && lhs.ip6() == rhs; +} + +inline bool operator == (IP6Srv const& lhs, IPSrv const& rhs) { + return rhs.is_ip6() && rhs.ip6() == lhs; +} + +inline bool operator != (IPSrv const& lhs, IP6Srv const& rhs) { + return ! lhs.is_ip6() || lhs.ip6() != rhs; +} + +inline bool operator != (IP6Srv const& lhs, IPSrv const& rhs) { + return ! rhs.is_ip6() || rhs.ip6() != lhs; +} + +inline bool operator < (IPSrv const& lhs, IP6Srv const& rhs) { + return lhs.is_ip6() && lhs.ip6() < rhs; +} + +inline bool operator < (IP6Srv const& lhs, IPSrv const& rhs) { + return rhs.is_ip6() && lhs < rhs.ip6(); +} + +inline bool operator <= (IPSrv const& lhs, IP6Srv const& rhs) { + return lhs.is_ip6() && lhs.ip6() <= rhs; +} + +inline bool operator <= (IP6Srv const& lhs, IPSrv const& rhs) { + return rhs.is_ip6() && lhs <= rhs.ip6(); +} + +inline bool operator > (IPSrv const& lhs, IP6Srv const& rhs) { + return lhs.is_ip6() && lhs.ip6() > rhs; +} + +inline bool operator > (IP6Srv const& lhs, IPSrv const& rhs) { + return rhs.is_ip6() && lhs > rhs.ip6(); +} + +inline bool operator >= (IPSrv const& lhs, IP6Srv const& rhs) { + return lhs.is_ip6() && lhs.ip6() >= rhs; +} + +inline bool operator >= (IP6Srv const& lhs, IPSrv const& rhs) { + return rhs.is_ip6() && lhs >= rhs.ip6(); +} + +// --- Cross address equality + +inline bool operator == (IPSrv const& lhs, IP4Addr const& rhs) { + return lhs.is_ip4() && lhs.ip4() == rhs; +} + +inline bool operator == (IP4Addr const& lhs, IPSrv const& rhs) { + return rhs.is_ip4() && lhs == rhs.ip4(); +} + +inline bool operator != (IPSrv const& lhs, IP4Addr const& rhs) { + return ! lhs.is_ip4() || lhs.ip4() != rhs; +} + +inline bool operator != (IP4Addr const& lhs, IPSrv const& rhs) { + return ! rhs.is_ip4() || lhs != rhs.ip4(); +} + +inline bool operator == (IPSrv const& lhs, IP6Addr const& rhs) { + return lhs.is_ip6() && lhs.ip6() == rhs; +} + +inline bool operator == (IP6Addr const& lhs, IPSrv const& rhs) { + return rhs.is_ip6() && lhs == rhs.ip6(); +} + +inline bool operator != (IPSrv const& lhs, IP6Addr const& rhs) { + return ! lhs.is_ip6() || lhs.ip6() != rhs; +} + +inline bool operator != (IP6Addr const& lhs, IPSrv const& rhs) { + return ! rhs.is_ip6() || lhs != rhs.ip6(); +} + +}} // namespace swoc::SWOC_VERSION_NS diff --git a/lib/swoc/include/swoc/TextView.h b/lib/swoc/include/swoc/TextView.h index 8608b108ea7..4d7dba9814e 100644 --- a/lib/swoc/include/swoc/TextView.h +++ b/lib/swoc/include/swoc/TextView.h @@ -148,7 +148,7 @@ class TextView : public std::string_view { /// Assign from C-string @a s. self_type &operator=(char *&s); /// Assign from C-string @a s. - self_type &operator=(char const *&s); + self_type &operator=(char const * & s); /// Assign from a @c std::string. self_type &operator=(const std::string &s); diff --git a/lib/swoc/include/swoc/Vectray.h b/lib/swoc/include/swoc/Vectray.h index a4c7246a098..75eb3946443 100644 --- a/lib/swoc/include/swoc/Vectray.h +++ b/lib/swoc/include/swoc/Vectray.h @@ -357,7 +357,7 @@ auto Vectray::size() const -> size_type { template bool Vectray::empty() const { return std::visit(swoc::meta::vary{ - [](FixedStore const& fs) { return fs._count > 0; } + [](FixedStore const& fs) { return fs._count == 0; } , [](DynamicStore const& ds) { return ds.empty(); } }, _store); } diff --git a/lib/swoc/include/swoc/bwf_fwd.h b/lib/swoc/include/swoc/bwf_fwd.h index f7a90543b74..59fe601dfca 100644 --- a/lib/swoc/include/swoc/bwf_fwd.h +++ b/lib/swoc/include/swoc/bwf_fwd.h @@ -9,7 +9,7 @@ #include "swoc/swoc_version.h" -namespace SWOC_NAMESPACE { +namespace swoc { inline namespace SWOC_VERSION_NS { class BufferWriter; class FixedBufferWriter; template class LocalBufferWriter; @@ -18,4 +18,4 @@ namespace bwf { struct Spec; class Format; } // namespace bwf -} // namespace SWOC_NAMESPACE +}} // namespace swoc diff --git a/lib/swoc/include/swoc/swoc_ip.h b/lib/swoc/include/swoc/swoc_ip.h index 4f76450dd88..f9f0cfbaccd 100644 --- a/lib/swoc/include/swoc/swoc_ip.h +++ b/lib/swoc/include/swoc/swoc_ip.h @@ -14,3378 +14,37 @@ #include "swoc/swoc_version.h" #include "swoc/TextView.h" -#include "swoc/DiscreteRange.h" -#include "swoc/RBTree.h" -namespace swoc { inline namespace SWOC_VERSION_NS { - -class IP4Addr; - -class IP6Addr; - -class IPAddr; - -class IPMask; - -class IP4Range; - -class IP6Range; - -class IPRange; - -class IP4Net; - -class IP6Net; - -class IPNet; - -using ::std::string_view; - -namespace detail { - extern void *const pseudo_nullptr; -} - -/** A union to hold @c sockaddr compliant IP address structures. - - This class contains a number of static methods to perform operations on external @c sockaddr - instances. These are all duplicates of methods that operate on the internal @c sockaddr and - are provided primarily for backwards compatibility during the shift to using this class. - - We use the term "endpoint" because these contain more than just the raw address, all of the data - for an IP endpoint is present. - */ -union IPEndpoint { - using self_type = IPEndpoint; ///< Self reference type. - - struct sockaddr sa; ///< Generic address. - struct sockaddr_in sa4; ///< IPv4 - struct sockaddr_in6 sa6; ///< IPv6 - - /// Default construct invalid instance. - IPEndpoint(); - IPEndpoint(self_type const &that); ///< Copy constructor. - ~IPEndpoint() = default; - - /// Construct from the @a text representation of an address. - IPEndpoint(string_view const &text); - - // Construct from @c IPAddr - explicit IPEndpoint(IPAddr const &addr); - - // Construct from @c sockaddr - IPEndpoint(sockaddr const *sa); - - /// Copy assignment. - self_type &operator=(self_type const &that); - - /** Break a string in to IP address relevant tokens. - * - * @param src Source text. [in] - * @param host The host / address. [out] - * @param port The port. [out] - * @param rest Any text past the end of the IP address. [out] - * @return @c true if an IP address was found, @c false otherwise. - * - * Any of the out parameters can be @c nullptr in which case they are not updated. - * This parses and discards the IPv6 brackets. - * - * @note This is intended for internal use to do address parsing, but it can be useful in other contexts. - */ - static bool tokenize(string_view src, string_view *host = nullptr, string_view *port = nullptr, string_view *rest = nullptr); - - /** Parse a string for an IP address. - - The address resulting from the parse is copied to this object if the conversion is successful, - otherwise this object is invalidated. - - @return @c true on success, @c false otherwise. - */ - bool parse(string_view const &str); - - /// Invalidate a @c sockaddr. - static void invalidate(sockaddr *addr); - - /// Invalidate this endpoint. - self_type &invalidate(); - - /** Copy (assign) the contents of @a src to @a dst. - * - * The caller must ensure @a dst is large enough to hold the contents of @a src, the size of which - * can vary depending on the type of address in @a dst. - * - * @param dst Destination. - * @param src Source. - * @return @c true if @a dst is a valid IP address, @c false otherwise. - */ - static bool assign(sockaddr *dst, sockaddr const *src); - - /** Assign from a socket address. - The entire address (all parts) are copied if the @a ip is valid. - */ - self_type &assign(sockaddr const *addr); - - /// Assign from an @a addr and @a port. - self_type &assign(IPAddr const &addr, in_port_t port = 0); - - /// Copy to @a sa. - const self_type ©_to(sockaddr *addr) const; - - /// Test for valid IP address. - bool is_valid() const; - - /// Test for IPv4. - bool is_ip4() const; - - /// Test for IPv6. - bool is_ip6() const; - - /** Effectively size of the address. - * - * @return The size of the structure appropriate for the address family of the stored address. - */ - socklen_t size() const; - - /// @return The IP address family. - sa_family_t family() const; - - /// Set to be the ANY address for family @a family. - /// @a family must be @c AF_INET or @c AF_INET6. - /// @return This object. - self_type &set_to_any(int family); - - /// @return @c true if this is the ANY address, @c false if not. - bool is_any() const; - - /// Set to be loopback address for family @a family. - /// @a family must be @c AF_INET or @c AF_INET6. - /// @return This object. - self_type &set_to_loopback(int family); - - /// @return @c true if this is a loopback address, @c false if not. - bool is_loopback() const; - - /// Port in network order. - in_port_t &network_order_port(); - - /// Port in network order. - in_port_t network_order_port() const; - - /// Port in host horder. - in_port_t host_order_port() const; - - /// Port in network order from @a sockaddr. - static in_port_t &port(sockaddr *sa); - - /// Port in network order from @a sockaddr. - static in_port_t port(sockaddr const *sa); - - /// Port in host order directly from a @c sockaddr - static in_port_t host_order_port(sockaddr const *sa); - - /// Automatic conversion to @c sockaddr. - operator sockaddr *() { return &sa; } - - /// Automatic conversion to @c sockaddr. - operator sockaddr const *() const { return &sa; } - - /// The string name of the address family. - static string_view family_name(sa_family_t family); -}; - -/** Storage for an IPv4 address. - Stored in host order. - */ -class IP4Addr { - using self_type = IP4Addr; ///< Self reference type. - friend class IP4Range; - -public: - static constexpr size_t SIZE = sizeof(in_addr_t); ///< Size of IPv4 address in bytes. - static constexpr size_t WIDTH = std::numeric_limits::digits * SIZE; ///< # of bits in an address. - - static const self_type MIN; ///< Minimum value. - static const self_type MAX; ///< Maximum value. - static constexpr sa_family_t AF_value = AF_INET; ///< Address family type. - - constexpr IP4Addr() = default; ///< Default constructor - minimum address. - - /// Construct using IPv4 @a addr (in host order). - /// @note Host order seems odd, but all of the standard network macro values such as @c INADDR_LOOPBACK - /// are in host order. - explicit constexpr IP4Addr(in_addr_t addr); - - /// Construct from @c sockaddr_in. - explicit IP4Addr(sockaddr_in const *sa); - - /// Construct from text representation. - /// If the @a text is invalid the result is an invalid instance. - IP4Addr(string_view const &text); - - /// Construct from generic address @a addr. - explicit IP4Addr(IPAddr const &addr); - - /// Assign from IPv4 raw address. - self_type &operator=(in_addr_t ip); - - /// Set to the address in @a addr. - self_type &operator=(sockaddr_in const *sa); - - /// Increment address. - self_type &operator++(); - - /// Decrement address. - self_type &operator--(); - - /** Byte access. - * - * @param idx Byte index. - * @return The byte at @a idx in the address. - */ - uint8_t - operator[](unsigned idx) const { - return reinterpret_cast(_addr)[idx]; - } - - /// Apply @a mask to address, leaving the network portion. - self_type &operator&=(IPMask const &mask); - - /// Apply @a mask to address, creating the broadcast address. - self_type &operator|=(IPMask const &mask); - - /// Write this adddress and @a port to the sockaddr @a sa. - sockaddr_in *copy_to(sockaddr_in *sa, in_port_t port = 0) const; - - /// @return The address in network order. - in_addr_t network_order() const; - - /// @return The address in host order. - in_addr_t host_order() const; - - /** Parse @a text as IPv4 address. - The address resulting from the parse is copied to this object if the conversion is successful, - otherwise this object is invalidated. - - @return @c true on success, @c false otherwise. - */ - bool load(string_view const &text); - - /// Standard ternary compare. - int cmp(self_type const &that) const; - - /// Get the IP address family. - /// @return @c AF_INET - /// @note Useful primarily for template classes. - constexpr sa_family_t family(); - - /// @return @c true if this is the "any" address, @c false if not. - bool is_any() const; - - /// @return @c true if this is a multicast address, @c false if not. - bool is_multicast() const; - - /// @return @c true if this is a loopback address, @c false if not. - bool is_loopback() const; - - /** Left shift. - * - * @param n Number of bits to shift left. - * @return @a this. - */ - self_type &operator<<=(unsigned n); - - /** Right shift. - * - * @param n Number of bits to shift right. - * @return @a this. - */ - self_type &operator>>=(unsigned n); - - /** Bitwise AND. - * - * @param that Source address. - * @return @a this. - * - * The bits in @a this are set to the bitwise AND of the corresponding bits in @a this and @a that. - */ - self_type &operator&=(self_type const &that); - - /** Bitwise OR. - * - * @param that Source address. - * @return @a this. - * - * The bits in @a this are set to the bitwise OR of the corresponding bits in @a this and @a that. - */ - self_type &operator|=(self_type const &that); - - /** Convert between network and host order. - * - * @param src Input address. - * @return @a src with the byte reversed. - * - * This performs the same computation as @c ntohl and @c htonl but is @c constexpr to be usable - * in situations those two functions are not. - */ - constexpr static in_addr_t reorder(in_addr_t src); - -protected: - /// Access by bytes. - using bytes = std::array; - - friend bool operator==(self_type const &, self_type const &); - friend bool operator!=(self_type const &, self_type const &); - friend bool operator<(self_type const &, self_type const &); - friend bool operator<=(self_type const &, self_type const &); - - in_addr_t _addr = INADDR_ANY; ///< Address in host order. -}; - -/** Storage for an IPv6 address. - Internal storage is not necessarily network ordered. - @see network_order - @see copy_to - */ -class IP6Addr { - using self_type = IP6Addr; ///< Self reference type. - - friend class IP6Range; - friend class IPMask; - -public: - static constexpr size_t WIDTH = 128; ///< Number of bits in the address. - static constexpr size_t SIZE = WIDTH / std::numeric_limits::digits; ///< Size of address in bytes. - static constexpr sa_family_t AF_value = AF_INET6; ///< Address family type. - - using quad_type = uint16_t; ///< Size of one segment of an IPv6 address. - static constexpr size_t N_QUADS = SIZE / sizeof(quad_type); ///< # of quads in an IPv6 address. - /// Number of bits per quad. - static constexpr size_t QUAD_WIDTH = std::numeric_limits::digits * sizeof(quad_type); - - /// Direct access type for the address. - /// Equivalent to the data type for data member @c s6_addr in @c in6_addr. - using raw_type = std::array; - - /// Minimum value of an address. - static const self_type MIN; - /// Maximum value of an address. - static const self_type MAX; - - IP6Addr() = default; ///< Default constructor - 0 address. - IP6Addr(self_type const &that) = default; - - /// Construct using IPv6 @a addr. - explicit IP6Addr(in6_addr const &addr); - - /// Construct from @c sockaddr_in. - explicit IP6Addr(sockaddr_in6 const *addr) { *this = addr; } - - /// Construct from text representation. - /// If the @a text is invalid the result is an invalid instance. - IP6Addr(string_view const &text); - - /// Construct from generic @a addr. - explicit IP6Addr(IPAddr const &addr); - - /** Left shift. - * - * @param n Number of bits to shift left. - * @return @a this. - */ - self_type &operator<<=(unsigned n); - - /** Right shift. - * - * @param n Number of bits to shift right. - * @return @a this. - */ - self_type &operator>>=(unsigned n); - - /** Bitwise AND. - * - * @param that Source address. - * @return @a this. - * - * The bits in @a this are set to the bitwise AND of the corresponding bits in @a this and @a that. - */ - self_type &operator&=(self_type const &that); - - /** Bitwise OR. - * - * @param that Source address. - * @return @a this. - * - * The bits in @a this are set to the bitwise OR of the corresponding bits in @a this and @a that. - */ - self_type &operator|=(self_type const &that); - - /// Increment address. - self_type &operator++(); - - /// Decrement address. - self_type &operator--(); - - self_type & operator = (self_type const& that) = default; - - /// Assign from IPv6 raw address. - self_type &operator=(in6_addr const &addr); - - /// Set to the address in @a addr. - self_type &operator=(sockaddr_in6 const *addr); - - /// Write to @c sockaddr using network order and @a port. - sockaddr *copy_to(sockaddr *sa, in_port_t port = 0) const; - - /// Copy address to @a addr in network order. - in6_addr ©_to(in6_addr &addr) const; - - /// Return the address in network order. - in6_addr network_order() const; - - /** Parse a string for an IP address. - - The address resuling from the parse is copied to this object if the conversion is successful, - otherwise this object is invalidated. - - @return @c true on success, @c false otherwise. - */ - bool load(string_view const &str); - - /// Generic three value compare. - int cmp(self_type const &that) const; - - /// @return The address family. - constexpr sa_family_t family(); - - /// @return @c true if this is the "any" address, @c false if not. - bool is_any() const; - - /// @return @c true if this is a loopback address, @c false if not. - bool is_loopback() const; - - /// @return @c true if this is a multicast address, @c false if not. - bool is_multicast() const; - - /// @return @c true if this is an IPv4 addressed mapped to IPv6, @c false if not. - bool is_mapped_ipv4() const; - - /** Reset to default constructed state. - * - * @return @a this - */ - self_type & clear(); - - /** Bitwise AND. - * - * @param that Source mask. - * @return @a this. - * - * The bits in @a this are set to the bitwise AND of the corresponding bits in @a this and @a that. - */ - self_type &operator&=(IPMask const &that); - - /** Bitwise OR. - * - * @param that Source mask. - * @return @a this. - * - * The bits in @a this are set to the bitwise OR of the corresponding bits in @a this and @a that. - */ - self_type &operator|=(IPMask const &that); - - /** Convert between network and host ordering. - * - * @param dst Destination for re-ordered address. - * @param src Original address. - */ - static void reorder(in6_addr &dst, raw_type const &src); - - /** Convert between network and host ordering. - * - * @param dst Destination for re-ordered address. - * @param src Original address. - */ - static void reorder(raw_type &dst, in6_addr const &src); - - template < typename T > auto as_span() -> std::enable_if_t, swoc::MemSpan> { - return swoc::MemSpan(_addr._store).template rebind(); - } - - template < typename T > auto as_span() const -> std::enable_if_t, std::byte, uint8_t, uint16_t, uint32_t, uint64_t>, swoc::MemSpan> { - return swoc::MemSpan(_addr._store).template rebind(); - } - -protected: - friend bool operator==(self_type const &, self_type const &); - - friend bool operator!=(self_type const &, self_type const &); - - friend bool operator<(self_type const &, self_type const &); - - friend bool operator<=(self_type const &, self_type const &); - - /// Direct access type for the address by quads (16 bits). - /// This corresponds to the elements of the text format of the address. - using quad_store_type = std::array; - - /// A bit mask of all 1 bits the size of a quad. - static constexpr quad_type QUAD_MASK = ~quad_type{0}; - - /// Type used as a "word", the natural working unit of the address. - using word_type = uint64_t; - - static constexpr size_t WORD_SIZE = sizeof(word_type); - - /// Number of bits per word. - static constexpr size_t WORD_WIDTH = std::numeric_limits::digits * WORD_SIZE; - - /// Number of words used for basic address storage. - static constexpr size_t N_STORE = SIZE / WORD_SIZE; - - /// Type used to store the address. - using word_store_type = std::array; - - /// Type for digging around inside the address, with the various forms of access. - /// These are in sort of host order - @a _store elements are host order, but the - /// MSW and LSW are swapped (big-endian). This makes various bits of the implementation - /// easier. Conversion to and from network order is via the @c reorder method. - union { - word_store_type _store = {0}; ///< 0 is MSW, 1 is LSW. - quad_store_type _quad; ///< By quad. - raw_type _raw; ///< By byte. - } _addr; - - static constexpr unsigned LSW = 1; ///< Least significant word index. - static constexpr unsigned MSW = 0; ///< Most significant word index. - - /// Index of quads in @a _addr._quad. - /// This converts from the position in the text format to the quads in the binary format. - static constexpr std::array QUAD_IDX = {3, 2, 1, 0, 7, 6, 5, 4}; - - /// Convert between network and host order. - /// The conversion is symmetric. - /// @param dst Output where reordered value is placed. - /// @param src Input value to resorder. - static void reorder(unsigned char dst[WORD_SIZE], unsigned char const src[WORD_SIZE]); - - /** Construct from two 64 bit values. - * - * @param msw The most significant 64 bits, host order. - * @param lsw The least significant 64 bits, host order. - */ - IP6Addr(word_store_type::value_type msw, word_store_type::value_type lsw) : _addr{{msw, lsw}} {} - - friend IP6Addr operator&(IP6Addr const &addr, IPMask const &mask); - - friend IP6Addr operator|(IP6Addr const &addr, IPMask const &mask); -}; - -/** An IPv4 or IPv6 address. - * - * The family type is stored. For comparisons, invalid < IPv4 < IPv6. All invalid instances are equal. - */ -class IPAddr { - friend class IPRange; - - using self_type = IPAddr; ///< Self reference type. -public: - IPAddr() = default; ///< Default constructor - invalid result. - IPAddr(self_type const &that) = default; ///< Copy constructor. - self_type & operator = (self_type const& that) = default; ///< Copy assignment. - - /// Construct using IPv4 @a addr. - explicit IPAddr(in_addr_t addr); - - /// Construct using an IPv4 @a addr - IPAddr(IP4Addr const &addr) : _addr{addr}, _family(IP4Addr::AF_value) {} - - /// Construct using IPv6 @a addr. - explicit IPAddr(in6_addr const &addr); - - /// construct using an IPv6 @a addr - IPAddr(IP6Addr const &addr) : _addr{addr}, _family(IP6Addr::AF_value) {} - - /// Construct from @c sockaddr. - explicit IPAddr(sockaddr const *addr); - - /// Construct from @c IPEndpoint. - explicit IPAddr(IPEndpoint const &addr); - - /// Construct from text representation. - /// If the @a text is invalid the result is an invalid instance. - explicit IPAddr(string_view const &text); - - /// Set to the address in @a addr. - self_type &assign(sockaddr const *addr); - - /// Set to the address in @a addr. - self_type &assign(sockaddr_in const *addr); - - /// Set to the address in @a addr. - self_type &assign(sockaddr_in6 const *addr); - - /// Set to IPv4 @a addr. - self_type &assign(in_addr_t addr); - - /// Set to IPv6 @a addr - self_type &assign(in6_addr const &addr); - - /// Assign from end point. - self_type &operator=(IPEndpoint const &ip); - - /// Assign from IPv4 raw address. - self_type &operator=(in_addr_t ip); - - /// Assign from IPv6 raw address. - self_type &operator=(in6_addr const &addr); - - bool operator==(self_type const &that) const; - - bool operator!=(self_type const &that) const; - - bool operator<(self_type const &that) const; - - bool operator>(self_type const &that) const; - - bool operator<=(self_type const &that) const; - - bool operator>=(self_type const &that) const; - - /// Assign from @c sockaddr - self_type &operator=(sockaddr const *addr); - - self_type &operator&=(IPMask const &mask); - - self_type &operator|=(IPMask const &mask); - - /** Parse a string and load the result in @a this. - * - * @param text Text to parse. - * @return @c true on success, @c false otherwise. - */ - bool load(string_view const &text); - - /// Generic compare. - int cmp(self_type const &that) const; - - /// Test for same address family. - /// @c return @c true if @a that is the same address family as @a this. - bool isCompatibleWith(self_type const &that); - - /// Get the address family. - /// @return The address family. - sa_family_t family() const; - - /// Test for IPv4. - bool is_ip4() const; - - /// Test for IPv6. - bool is_ip6() const; - - IP4Addr const &ip4() const; - - IP6Addr const &ip6() const; - - explicit operator IP4Addr const &() const { return _addr._ip4; } - - explicit - operator IP4Addr &() { - return _addr._ip4; - } - - explicit operator IP6Addr const &() const { return _addr._ip6; } - - explicit - operator IP6Addr &() { - return _addr._ip6; - } - - /// Test for validity. - bool is_valid() const; - - /// Make invalid. - self_type &invalidate(); - - /// Test for multicast - bool is_multicast() const; - - /// Test for loopback - bool is_loopback() const; - - ///< Pre-constructed invalid instance. - static self_type const INVALID; - -protected: - friend IP4Addr; - friend IP6Addr; - - /// Address data. - union raw_addr_type { - IP4Addr _ip4; ///< IPv4 address (host) - IP6Addr _ip6; ///< IPv6 address (host) - - constexpr raw_addr_type(); - - raw_addr_type(in_addr_t addr) : _ip4(addr) {} - - raw_addr_type(in6_addr const &addr) : _ip6(addr) {} - - raw_addr_type(IP4Addr const &addr) : _ip4(addr) {} - - raw_addr_type(IP6Addr const &addr) : _ip6(addr) {} - } _addr; - - sa_family_t _family{AF_UNSPEC}; ///< Protocol family. -}; - -/** An IP address mask. - * - * This is essentially a width for a bit mask. - */ -class IPMask { - using self_type = IPMask; ///< Self reference type. - friend class IP4Addr; - - friend class IP6Addr; - -public: - using raw_type = uint8_t; ///< Storage for mask width. - - IPMask() = default; ///< Default construct to invalid mask. - - /** Construct a mask of @a width. - * - * @param width Number of bits in the mask. - * - * @note Because this is a network mask, it is always left justified. - */ - explicit IPMask(raw_type width); - - /// @return @c true if the mask is valid, @c false if not. - bool is_valid() const; - - /** Parse mask from @a text. - * - * @param text A number in string format. - * @return @a true if a valid CIDR value, @c false if not. - */ - bool load(string_view const &text); - - /** Copmute a mask for the network at @a addr. - * @param addr Lower bound of network. - * @return A mask with the width of the largest network starting at @a addr. - */ - static self_type mask_for(IPAddr const &addr); - - /** Copmute a mask for the network at @a addr. - * @param addr Lower bound of network. - * @return A mask with the width of the largest network starting at @a addr. - */ - static self_type mask_for(IP4Addr const &addr); - - /** Copmute a mask for the network at @a addr. - * @param addr Lower bound of network. - * @return A mask with the width of the largest network starting at @a addr. - */ - static self_type mask_for(IP6Addr const &addr); - - /// Change to default constructed state (invalid). - self_type & clear(); - - /// The width of the mask. - raw_type width() const; - - /** Extend the mask (cover more addresses). - * - * @param n Number of bits to extend. - * @return @a this - * - * Effectively shifts the mask left, bringing in 0 bits on the right. - */ - self_type & operator<<=(raw_type n); - - /** Narrow the mask (cover fewer addresses). - * - * @param n Number of bits to narrow. - * @return @a this - * - * Effectively shift the mask right, bringing in 1 bits on the left. - */ - self_type & operator>>=(raw_type n); - - /** The mask as an IPv4 address. - * - * @return An IPv4 address that is the mask. - * - * If the mask is wider than an IPv4 address, the maximum mask is returned. - */ - IP4Addr as_ip4() const; - - /** The mask as an IPv6 address. - * - * @return An IPv6 address that is the mask. - * - * If the mask is wider than an IPv6 address, the maximum mask is returned. - */ - IP6Addr as_ip6() const; - -protected: - /// Marker value for an invalid mask. - static constexpr auto INVALID = std::numeric_limits::max(); - - raw_type _cidr = INVALID; ///< Mask width in bits. - - /// Compute a partial IPv6 mask, sized for the basic storage type. - static raw_type mask_for_quad(IP6Addr::quad_type q); -}; - -/** An inclusive range of IPv4 addresses. - */ -class IP4Range : public DiscreteRange { - using self_type = IP4Range; - using super_type = DiscreteRange; - using metric_type = IP4Addr; - -public: - /// Default constructor, invalid range. - IP4Range() = default; - - /// Construct from an network expressed as @a addr and @a mask. - IP4Range(IP4Addr const &addr, IPMask const &mask); - - /// Construct from super type. - /// @internal Why do I have to do this, even though the super type constructors are inherited? - IP4Range(super_type const &r) : super_type(r) {} - - /** Construct range from @a text. - * - * @param text Range text. - * @see IP4Range::load - * - * This results in a zero address if @a text is not a valid string. If this should be checked, - * use @c load. - */ - IP4Range(string_view const &text); - - using super_type::super_type; ///< Import super class constructors. - - /** Set @a this range. - * - * @param addr Minimum address. - * @param mask CIDR mask to compute maximum adddress from @a addr. - * @return @a this - */ - self_type &assign(IP4Addr const &addr, IPMask const &mask); - - using super_type::assign; ///< Import assign methods. - - /** Assign to this range from text. - * - * @param text Range text. - * - * The text must be in one of three formats. - * - A dashed range, "addr1-addr2" - * - A singleton, "addr". This is treated as if it were "addr-addr", a range of size 1. - * - CIDR notation, "addr/cidr" where "cidr" is a number from 0 to the number of bits in the address. - */ - bool load(string_view text); - - /** Compute the mask for @a this as a network. - * - * @return If @a this is a network, the mask for that network. Otherwise an invalid mask. - * - * @see IPMask::is_valid - */ - IPMask network_mask() const; - - class NetSource; - - /** Generate a list of networks covering @a this range. - * - * @return A network generator. - * - * The returned object can be used as an iterator, or as a container to iterating over - * the unique minimal set of networks that cover @a this range. - * - * @code - * void (IP4Range const& range) { - * for ( auto const& net : range ) { - * net.addr(); // network address. - * net.mask(); // network mask; - * } - * } - * @endcode - */ - NetSource networks() const; -}; - -/** Network generator class. - * This generates networks from a range and acts as both a forward iterator and a container. - */ -class IP4Range::NetSource { - using self_type = NetSource; ///< Self reference type. -public: - using range_type = IP4Range; ///< Import base range type. - - /// Construct from @a range. - explicit NetSource(range_type const &range); - - /// Copy constructor. - NetSource(self_type const &that) = default; - - /// This class acts as a container and an iterator. - using iterator = self_type; - /// All iteration is constant so no distinction between iterators. - using const_iterator = iterator; - - iterator begin() const; ///< First network. - static iterator end() ; ///< Past last network. - - /// Return @c true if there are valid networks, @c false if not. - bool empty() const; - - /// @return The current network. - IP4Net operator*() const; - - /// Access @a this as if it were an @c IP4Net. - self_type *operator->(); - - /// Iterator support. - /// @return The current network address. - IP4Addr const &addr() const; - - /// Iterator support. - /// @return The current network mask. - IPMask mask() const; - - /// Move to next network. - self_type &operator++(); - - /// Move to next network. - self_type operator++(int); - - /// Equality. - bool operator==(self_type const &that) const; - - /// Inequality. - bool operator!=(self_type const &that) const; - -protected: - IP4Range _range; ///< Remaining range. - /// Mask for current network. - IP4Addr _mask{~static_cast(0)}; - IPMask::raw_type _cidr = IP4Addr::WIDTH; ///< Current CIDR value. - - void search_wider(); - - void search_narrower(); - - bool is_valid(IP4Addr mask) const; -}; - -/// Inclusive range of IPv6 addresses. -class IP6Range : public DiscreteRange { - using self_type = IP6Range; - using super_type = DiscreteRange; - -public: - /// Construct from super type. - /// @internal Why do I have to do this, even though the super type constructors are inherited? - IP6Range(super_type const &r) : super_type(r) {} - - /** Construct range from @a text. - * - * @param text Range text. - * @see IP4Range::load - * - * This results in a zero address if @a text is not a valid string. If this should be checked, - * use @c load. - */ - IP6Range(string_view const &text); - - using super_type::super_type; ///< Import super class constructors. - - /** Set @a this range. - * - * @param addr Minimum address. - * @param mask CIDR mask to compute maximum adddress from @a addr. - * @return @a this - */ - self_type &assign(IP6Addr const &addr, IPMask const &mask); - - using super_type::assign; ///< Import assign methods. - - /** Assign to this range from text. - * - * @param text Range text. - * - * The text must be in one of three formats. - * - A dashed range, "addr1-addr2" - * - A singleton, "addr". This is treated as if it were "addr-addr", a range of size 1. - * - CIDR notation, "addr/cidr" where "cidr" is a number from 0 to the number of bits in the address. - */ - bool load(string_view text); - - /** Compute the mask for @a this as a network. - * - * @return If @a this is a network, the mask for that network. Otherwise an invalid mask. - * - * @see IPMask::is_valid - */ - IPMask network_mask() const; - - class NetSource; - - /** Generate a list of networks covering @a this range. - * - * @return A network generator. - * - * The returned object can be used as an iterator, or as a container to iterating over - * the unique minimal set of networks that cover @a this range. - * - * @code - * void (IP6Range const& range) { - * for ( auto const& net : range ) { - * net.addr(); // network address. - * net.mask(); // network mask; - * } - * } - * @endcode - */ - NetSource networks() const; -}; - -/** Network generator class. - * This generates networks from a range and acts as both a forward iterator and a container. - */ -class IP6Range::NetSource { - using self_type = NetSource; ///< Self reference type. -public: - using range_type = IP6Range; ///< Import base range type. - - /// Construct from @a range. - explicit NetSource(range_type const &range); - - /// Copy constructor. - NetSource(self_type const &that) = default; - - /// This class acts as a container and an iterator. - using iterator = self_type; - /// All iteration is constant so no distinction between iterators. - using const_iterator = iterator; - - iterator begin() const; ///< First network. - iterator end() const; ///< Past last network. - - /// @return @c true if there are valid networks, @c false if not. - bool empty() const; - - /// @return The current network. - IP6Net operator*() const; - - /// Access @a this as if it were an @c IP6Net. - self_type *operator->(); - - /// @return The current network address. - IP6Addr const & addr() const; - - /// Iterator support. - /// @return The current network mask. - IPMask mask() const; - - /// Move to next network. - self_type &operator++(); - - /// Move to next network. - self_type operator++(int); - - /// Equality. - bool operator==(self_type const &that) const; - - /// Inequality. - bool operator!=(self_type const &that) const; - -protected: - IP6Range _range; ///< Remaining range. - IPMask _mask{IP6Addr::WIDTH}; ///< Current CIDR value. - - void search_wider(); - - void search_narrower(); - - bool is_valid(IPMask const &mask); -}; - -/** Range of IP addresses. - * Although this can hold IPv4 or IPv6, any specific instance is one or the other, this can never contain - * a range of different address families. - */ -class IPRange { - using self_type = IPRange; - -public: - /// Default constructor - construct invalid range. - IPRange() = default; - - IPRange(IPAddr const &min, IPAddr const &max); - - /// Construct from an IPv4 @a range. - IPRange(IP4Range const &range); - - /// Construct from an IPv6 @a range. - IPRange(IP6Range const &range); - - /** Construct from a string format. - * - * @param text Text form of range. - * - * The string can be a single address, two addresses separated by a dash '-' or a CIDR network. - */ - IPRange(string_view const &text); - - /// Equality - bool - operator==(self_type const &that) const; - - /// @return @c true if this is an IPv4 range, @c false if not. - bool - is_ip4() const { - return AF_INET == _family; - } - - /// @return @c true if this is an IPv6 range, @c false if not. - bool - is_ip6() const { - return AF_INET6 == _family; - } - - /** Check if @a this range is the IP address @a family. - * - * @param family IP address family. - * @return @c true if this is @a family, @c false if not. - */ - bool is(sa_family_t family) const; - - /** Load the range from @a text. - * - * @param text Range specifier in text format. - * @return @c true if @a text was successfully parsed, @c false if not. - * - * A successful parse means @a this was loaded with the specified range. If not the range is - * marked as invalid. - */ - bool load(std::string_view const &text); - - /// @return The minimum address in the range. - IPAddr min() const; - - /// @return The maximum address in the range. - IPAddr max() const; - - /// @return @c true if there are no addresses in the range. - bool empty() const; - - /// @return The IPv4 range. - IP4Range const & ip4() const { return _range._ip4; } - - /// @return The IPv6 range. - IP6Range const & ip6() const { return _range._ip6; } - - /** Compute the mask for @a this as a network. - * - * @return If @a this is a network, the mask for that network. Otherwise an invalid mask. - * - * @see IPMask::is_valid - */ - IPMask network_mask() const; - - class NetSource; - - /** Generate a list of networks covering @a this range. - * - * @return A network generator. - * - * The returned object can be used as an iterator, or as a container to iterating over - * the unique minimal set of networks that cover @a this range. - * - * @code - * void (IPRange const& range) { - * for ( auto const& net : range ) { - * net.addr(); // network address. - * net.mask(); // network mask; - * } - * } - * @endcode - */ - NetSource networks() const; - -protected: - /** Range container. - * - * @internal - * - * This was a @c std::variant at one point, but the complexity got in the way because - * - These objects have no state, need no destruction. - * - Construction was problematic because @c variant requires construction, then access, - * whereas this needs access to construct (e.g. via the @c load method). - */ - union { - std::monostate _nil; ///< Make constructor easier to implement. - IP4Range _ip4; ///< IPv4 range. - IP6Range _ip6; ///< IPv6 range. - } _range{std::monostate{}}; - /// Family of @a _range. - sa_family_t _family{AF_UNSPEC}; -}; - -/** Network generator class. - * This generates networks from a range and acts as both a forward iterator and a container. - */ -class IPRange::NetSource { - using self_type = NetSource; ///< Self reference type. -public: - using range_type = IPRange; ///< Import base range type. - - /// Construct from @a range. - explicit NetSource(range_type const &range); - - /// Copy constructor. - NetSource(self_type const &that) = default; - - /// This class acts as a container and an iterator. - using iterator = self_type; - /// All iteration is constant so no distinction between iterators. - using const_iterator = iterator; - - iterator begin() const; ///< First network. - iterator end() const; ///< Past last network. - - /// @return The current network. - IPNet operator*() const; - - /// Access @a this as if it were an @c IP6Net. - self_type *operator->(); - - /// Iterator support. - /// @return The current network address. - IPAddr addr() const; - - /// Iterator support. - /// @return The current network mask. - IPMask mask() const; - - /// Move to next network. - self_type &operator++(); - - /// Move to next network. - self_type operator++(int); - - /// Equality. - bool operator==(self_type const &that) const; - - /// Inequality. - bool operator!=(self_type const &that) const; - -protected: - union { - std::monostate _nil; ///< Default value, no addresses. - IP4Range::NetSource _ip4; ///< IPv4 addresses. - IP6Range::NetSource _ip6; ///< IPv6 addresses. - }; - sa_family_t _family = AF_UNSPEC; ///< Mark for union content. -}; - -/// An IPv4 network. -class IP4Net { - using self_type = IP4Net; ///< Self reference type. -public: - IP4Net() = default; ///< Construct invalid network. - IP4Net(self_type const &that) = default; ///< Copy constructor. - - /** Construct from @a addr and @a mask. - * - * @param addr An address in the network. - * @param mask The mask for the network. - * - * The network is based on the mask, and the resulting network address is chosen such that the - * network will contain @a addr. For a given @a addr and @a mask there is only one network - * that satisifies these criteria. - */ - IP4Net(IP4Addr addr, IPMask mask); - - IP4Net(swoc::TextView text) { this->load(text); } - - /** Parse network as @a text. - * - * @param text String describing the network in CIDR format. - * @return @c true if a valid string, @c false if not. - */ - bool load(swoc::TextView text); - - /// @return @c true if the network is valid, @c false if not. - bool is_valid() const; - - /// @return THh smallest address in the network. - IP4Addr lower_bound() const; - - /// @return The largest address in the network. - IP4Addr upper_bound() const; - - /// @return The mask for the network. - IPMask const &mask() const; - - /// @return A range that exactly covers the network. - IP4Range as_range() const; - - /** Assign an @a addr and @a mask to @a this. - * - * @param addr Network addres. - * @param mask Network mask. - * @return @a this. - */ - self_type &assign(IP4Addr const &addr, IPMask const &mask); - - /// Reset network to invalid state. - self_type & clear() { _mask.clear(); return *this; } - - /// Equality. - bool operator==(self_type const &that) const; - - /// Inequality - bool operator!=(self_type const &that) const; - -protected: - IP4Addr _addr; ///< Network address (also lower_bound). - IPMask _mask; ///< Network mask. -}; - -/// IPv6 network. -class IP6Net { - using self_type = IP6Net; ///< Self reference type. -public: - IP6Net() = default; ///< Construct invalid network. - IP6Net(self_type const &that) = default; ///< Copy constructor. - - /** Construct from @a addr and @a mask. - * - * @param addr An address in the network. - * @param mask The mask for the network. - * - * The network is based on the mask, and the resulting network address is chosen such that the - * network will contain @a addr. For a given @a addr and @a mask there is only one network - * that satisifies these criteria. - */ - IP6Net(IP6Addr addr, IPMask mask); - - /** Parse network as @a text. - * - * @param text String describing the network in CIDR format. - * @return @c true if a valid string, @c false if not. - */ - bool load(swoc::TextView text); - - /// @return @c true if the network is valid, @c false if not. - bool is_valid() const; - - /// @return THh smallest address in the network. - IP6Addr lower_bound() const; - - /// @return The largest address in the network. - IP6Addr upper_bound() const; - - /// @return The mask for the network. - IPMask const &mask() const; - - /// @return A range that exactly covers the network. - IP6Range as_range() const; - - /** Assign an @a addr and @a mask to @a this. - * - * @param addr Network addres. - * @param mask Network mask. - * @return @a this. - */ - self_type &assign(IP6Addr const &addr, IPMask const &mask); - - /// Reset network to invalid state. - self_type & - clear() { - _mask.clear(); - return *this; - } - - /// Equality. - bool operator==(self_type const &that) const; - - /// Inequality - bool operator!=(self_type const &that) const; - -protected: - IP6Addr _addr; ///< Network address (also lower_bound). - IPMask _mask; ///< Network mask. -}; - -/** Representation of an IP address network. - * - */ -class IPNet { - using self_type = IPNet; ///< Self reference type. -public: - IPNet() = default; ///< Construct invalid network. - IPNet(self_type const &that) = default; ///< Copy constructor. - - /** Construct from @a addr and @a mask. - * - * @param addr An address in the network. - * @param mask The mask for the network. - * - * The network is based on the mask, and the resulting network address is chosen such that the - * network will contain @a addr. For a given @a addr and @a mask there is only one network - * that satisifies these criteria. - */ - IPNet(IPAddr const &addr, IPMask const &mask); - - IPNet(TextView text); - - /** Parse network as @a text. - * - * @param text String describing the network in CIDR format. - * @return @c true if a valid string, @c false if not. - */ - bool load(swoc::TextView text); - - /// @return @c true if the network is valid, @c false if not. - bool is_valid() const; - - /// @return THh smallest address in the network. - IPAddr lower_bound() const; - - /// @return The largest address in the network. - IPAddr upper_bound() const; - - IPMask::raw_type width() const; - - /// @return The mask for the network. - IPMask const &mask() const; - - /// @return A range that exactly covers the network. - IPRange as_range() const; - - bool - is_ip4() const { - return _addr.is_ip4(); - } - - bool - is_ip6() const { - return _addr.is_ip6(); - } - - sa_family_t - family() const { - return _addr.family(); - } - - IP4Net - ip4() const { - return IP4Net{_addr.ip4(), _mask}; - } - - IP6Net - ip6() const { - return IP6Net{_addr.ip6(), _mask}; - } - - /** Assign an @a addr and @a mask to @a this. - * - * @param addr Network addres. - * @param mask Network mask. - * @return @a this. - */ - self_type &assign(IPAddr const &addr, IPMask const &mask); - - /// Reset network to invalid state. - self_type & - clear() { - _mask.clear(); - return *this; - } - - /// Equality. - bool operator==(self_type const &that) const; - - /// Inequality - bool operator!=(self_type const &that) const; - -protected: - IPAddr _addr; ///< Address and family. - IPMask _mask; ///< Network mask. -}; - -// -------------------------------------------------------------------------- -/** Coloring of IP address space. - * - * @tparam PAYLOAD The color class. - * - * This is a class to do fast coloring and lookup of the IP address space. It is range oriented and - * performs well for ranges, much less well for singletons. Conceptually every IP address is a key - * in the space and can have a color / payload of type @c PAYLOAD. - * - * @c PAYLOAD must have the properties - * - * - Cheap to copy. - * - Comparable via the equality and inequality operators. - */ -template class IPSpace { - using self_type = IPSpace; - using IP4Space = DiscreteSpace; - using IP6Space = DiscreteSpace; - -public: - using payload_t = PAYLOAD; ///< Export payload type. - using value_type = std::tuple; - - /// Construct an empty space. - IPSpace() = default; - - /** Mark the range @a r with @a payload. - * - * @param range Range to mark. - * @param payload Payload to assign. - * @return @a this - * - * All addresses in @a r are set to have the @a payload. - */ - self_type &mark(IPRange const &range, PAYLOAD const &payload); - - /** Fill the @a range with @a payload. - * - * @param range Destination range. - * @param payload Payload for range. - * @return this - * - * Addresses in @a range are set to have @a payload if the address does not already have a payload. - */ - self_type &fill(IPRange const &range, PAYLOAD const &payload); - - /** Erase addresses in @a range. - * - * @param range Address range. - * @return @a this - */ - self_type &erase(IPRange const &range); - - /** Blend @a color in to the @a range. - * - * @tparam F Blending functor type (deduced). - * @tparam U Data to blend in to payloads. - * @param range Target range. - * @param color Data to blend in to existing payloads in @a range. - * @param blender Blending functor. - * @return @a this - * - * @a blender is required to have the signature void(PAYLOAD& lhs , U CONST&rhs). It must - * act as a compound assignment operator, blending @a rhs into @a lhs. That is, if the result of - * blending @a rhs in to @a lhs is defined as "lhs @ rhs" for the binary operator "@", then @a - * blender computes "lhs @= rhs". - * - * Every address in @a range is assigned a payload. If the address does not already have a color, - * it is assigned the default constructed @c PAYLOAD blended with @a color. If the address has a - * @c PAYLOAD @a p, @a p is updated by invoking blender(p, color), with the expectation - * that @a p will be updated in place. - */ - template self_type &blend(IPRange const &range, U const &color, F &&blender); - - template - self_type & blend(IP4Range const &range, U const &color, F &&blender); - - template - self_type & - blend(IP6Range const &range, U const &color, F &&blender); - - /// @return The number of distinct ranges. - size_t - count() const { - return _ip4.count() + _ip6.count(); - } - - size_t - count_ip4() const { - return _ip4.count(); - } - size_t - count_ip6() const { - return _ip6.count(); - } - - size_t count(sa_family_t f) const; - - /// Remove all ranges. - void clear(); - - /** Constant iterator. - * THe value type is a tuple of the IP address range and the @a PAYLOAD. Both are constant. - * - * @internal The non-const iterator is a subclass of this, in order to share implementation. This - * also makes it easy to convert from iterator to const iterator, which is desirable. - * - * @internal The return type is quite tricky because the value type of the nested containers is - * not the same as the value type for this container. It's not even a composite - @c IPRange is - * not an alias for either of the family specific range types. Therefore the iterator itself must - * contain a synthesized instance of the value type, which creates scoping and update problems. - * The approach here is to update the synthetic value when the iterator is modified and returning - * it by value for the dereference operator because a return by reference means code like - * @code - * auto && [ r , p ] = *(space.find(addr)); - * @endcode - * can fail due to the iterator going out of scope after the statement is finished making @a r - * and @a p dangling references. If the return is by value the compiler takes care of it. - */ - class const_iterator { - using self_type = const_iterator; ///< Self reference type. - friend class IPSpace; - - public: - using value_type = std::tuple; /// Import for API compliance. - // STL algorithm compliance. - using iterator_category = std::bidirectional_iterator_tag; - using pointer = value_type *; - using reference = value_type &; - using difference_type = int; - - /// Default constructor. - const_iterator() = default; - - /// Copy constructor. - const_iterator(self_type const &that); - - /// Assignment. - self_type &operator=(self_type const &that); - - /// Pre-increment. - /// Move to the next element in the list. - /// @return The iterator. - self_type &operator++(); - - /// Pre-decrement. - /// Move to the previous element in the list. - /// @return The iterator. - self_type &operator--(); - - /// Post-increment. - /// Move to the next element in the list. - /// @return The iterator value before the increment. - self_type operator++(int); - - /// Post-decrement. - /// Move to the previous element in the list. - /// @return The iterator value before the decrement. - self_type operator--(int); - - /// Dereference. - /// @return A reference to the referent. - value_type operator*() const; - - /// Dereference. - /// @return A pointer to the referent. - value_type const *operator->() const; - - /// Equality - bool operator==(self_type const &that) const; - - /// Inequality - bool operator!=(self_type const &that) const; - - protected: - // These are stored non-const to make implementing @c iterator easier. The containing class provides the - // required @c const protection. This is basic a tuple of iterators - for forward iteration if - // the primary (ipv4) iterator is at the end, then use the secondary (ipv6) iterator. The reverse - // is done for reverse iteration. This depends on the extra support @c IntrusiveDList iterators - // provide. - typename IP4Space::iterator _iter_4; ///< IPv4 sub-space iterator. - typename IP6Space::iterator _iter_6; ///< IPv6 sub-space iterator. - /// Current value. - value_type _value{IPRange{}, *static_cast(detail::pseudo_nullptr)}; - - /** Internal constructor. - * - * @param iter4 Starting place for IPv4 subspace. - * @param iter6 Starting place for IPv6 subspace. - * - * In practice, both iterators should be either the beginning or ending iterator for the subspace. - */ - const_iterator(typename IP4Space::iterator const &iter4, typename IP6Space::iterator const &iter6); - }; - - /** Iterator. - * The value type is a tuple of the IP address range and the @a PAYLOAD. The range is constant - * and the @a PAYLOAD is a reference. This can be used to update the @a PAYLOAD for this range. - * - * @note Range merges are not trigged by modifications of the @a PAYLOAD via an iterator. - */ - class iterator : public const_iterator { - using self_type = iterator; - using super_type = const_iterator; - - friend class IPSpace; - - public: - public: - /// Value type of iteration. - using value_type = std::tuple; - using pointer = value_type *; - using reference = value_type &; - - /// Default constructor. - iterator() = default; - - /// Copy constructor. - iterator(self_type const &that); - - /// Assignment. - self_type &operator=(self_type const &that); - - /// Pre-increment. - /// Move to the next element in the list. - /// @return The iterator. - self_type &operator++(); - - /// Pre-decrement. - /// Move to the previous element in the list. - /// @return The iterator. - self_type &operator--(); - - /// Post-increment. - /// Move to the next element in the list. - /// @return The iterator value before the increment. - self_type - operator++(int) { - self_type zret{*this}; - ++*this; - return zret; - } - - /// Post-decrement. - /// Move to the previous element in the list. - /// @return The iterator value before the decrement. - self_type - operator--(int) { - self_type zret{*this}; - --*this; - return zret; - } - - /// Dereference. - /// @return A reference to the referent. - value_type operator*() const; - - /// Dereference. - /// @return A pointer to the referent. - value_type const *operator->() const; - - protected: - using super_type::super_type; /// Inherit supertype constructors. - }; - - /** Find the payload for an @a addr. - * - * @param addr Address to find. - * @return Iterator for the range containing @a addr. - */ - iterator - find(IPAddr const &addr) { - if (addr.is_ip4()) { - return this->find(addr.ip4()); - } else if (addr.is_ip6()) { - return this->find(addr.ip6()); - } - return this->end(); - } - - /** Find the payload for an @a addr. - * - * @param addr Address to find. - * @return The payload if any, @c nullptr if the address is not in the space. - */ - iterator - find(IP4Addr const &addr) { - auto spot = _ip4.find(addr); - return spot == _ip4.end() ? this->end() : iterator{_ip4.find(addr), _ip6.begin()}; - } - - /** Find the payload for an @a addr. - * - * @param addr Address to find. - * @return The payload if any, @c nullptr if the address is not in the space. - */ - iterator - find(IP6Addr const &addr) { - return {_ip4.end(), _ip6.find(addr)}; - } - - /// @return A constant iterator to the first element. - const_iterator begin() const; - - /// @return A constent iterator past the last element. - const_iterator end() const; - - iterator begin(); - - iterator end(); - - /// Iterator to the first IPv4 address. - const_iterator begin_ip4() const; - /// Iterator past the last IPv4 address. - const_iterator end_ip4() const; - - const_iterator begin_ip6() const; - const_iterator end_ip6() const; - - const_iterator - begin(sa_family_t family) const { - if (AF_INET == family) { - return this->begin_ip4(); - } else if (AF_INET6 == family) { - return this->begin_ip6(); - } - return this->end(); - } - - const_iterator - end(sa_family_t family) const { - if (AF_INET == family) { - return this->end_ip4(); - } else if (AF_INET6 == family) { - return this->end_ip6(); - } - return this->end(); - } - -protected: - IP4Space _ip4; ///< Sub-space containing IPv4 ranges. - IP6Space _ip6; ///< sub-space containing IPv6 ranges. -}; - -template -IPSpace::const_iterator::const_iterator(typename IP4Space::iterator const &iter4, typename IP6Space::iterator const &iter6) - : _iter_4(iter4), _iter_6(iter6) { - if (_iter_4.has_next()) { - new (&_value) value_type{_iter_4->range(), _iter_4->payload()}; - } else if (_iter_6.has_next()) { - new (&_value) value_type{_iter_6->range(), _iter_6->payload()}; - } -} - -template IPSpace::const_iterator::const_iterator(self_type const &that) { - *this = that; -} - -template -auto -IPSpace::const_iterator::operator=(self_type const &that) -> self_type & { - _iter_4 = that._iter_4; - _iter_6 = that._iter_6; - new (&_value) value_type{that._value}; - return *this; -} - -template -auto -IPSpace::const_iterator::operator++() -> self_type & { - bool incr_p = false; - if (_iter_4.has_next()) { - ++_iter_4; - incr_p = true; - if (_iter_4.has_next()) { - new (&_value) value_type{_iter_4->range(), _iter_4->payload()}; - return *this; - } - } - - if (_iter_6.has_next()) { - if (incr_p || (++_iter_6).has_next()) { - new (&_value) value_type{_iter_6->range(), _iter_6->payload()}; - return *this; - } - } - new (&_value) value_type{IPRange{}, *static_cast(detail::pseudo_nullptr)}; - return *this; -} - -template -auto -IPSpace::const_iterator::operator++(int) -> self_type { - self_type zret(*this); - ++*this; - return zret; -} - -template -auto -IPSpace::const_iterator::operator--() -> self_type & { - if (_iter_6.has_prev()) { - --_iter_6; - new (&_value) value_type{_iter_6->range(), _iter_6->payload()}; - return *this; - } - if (_iter_4.has_prev()) { - --_iter_4; - new (&_value) value_type{_iter_4->range(), _iter_4->payload()}; - return *this; - } - new (&_value) value_type{IPRange{}, *static_cast(detail::pseudo_nullptr)}; - return *this; -} - -template -auto -IPSpace::const_iterator::operator--(int) -> self_type { - self_type zret(*this); - --*this; - return zret; -} - -template -auto -IPSpace::const_iterator::operator*() const -> value_type { - return _value; -} - -template -auto -IPSpace::const_iterator::operator->() const -> value_type const * { - return &_value; -} - -/* Bit of subtlety with equality - although it seems that if @a _iter_4 is valid, it doesn't matter - * where @a _iter6 is (because it is really the iterator location that's being checked), it's - * neccesary to do the @a _iter_4 validity on both iterators to avoid the case of a false positive - * where different internal iterators are valid. However, in practice the other (non-active) - * iterator won't have an arbitrary value, it will be either @c begin or @c end in step with the - * active iterator therefore it's effective and cheaper to just check both values. - */ - -template -bool -IPSpace::const_iterator::operator==(self_type const &that) const { - return _iter_4 == that._iter_4 && _iter_6 == that._iter_6; -} - -template -bool -IPSpace::const_iterator::operator!=(self_type const &that) const { - return _iter_4 != that._iter_4 || _iter_6 != that._iter_6; -} - -template IPSpace::iterator::iterator(self_type const &that) { - *this = that; -} - -template -auto -IPSpace::iterator::operator=(self_type const &that) -> self_type & { - this->super_type::operator=(that); - return *this; -} - -template -auto -IPSpace::iterator::operator->() const -> value_type const * { - return static_cast(&super_type::_value); -} - -template -auto -IPSpace::iterator::operator*() const -> value_type { - return reinterpret_cast(super_type::_value); -} - -template -auto -IPSpace::iterator::operator++() -> self_type & { - this->super_type::operator++(); - return *this; -} - -template -auto -IPSpace::iterator::operator--() -> self_type & { - this->super_type::operator--(); - return *this; -} - -// -------------------------------------------------------------------------- -// -- IP4Addr -- -inline constexpr sa_family_t -IP4Addr::family() { - return AF_value; -} - -// -- IP6Addr -- -inline constexpr sa_family_t -IP6Addr::family() { - return AF_value; -} - -// -- IPAddr -- -// @c constexpr constructor is required to initialize _something_, it can't be completely uninitializing. -inline constexpr IPAddr::raw_addr_type::raw_addr_type() : _ip4(INADDR_ANY) {} - -inline IPAddr::IPAddr(in_addr_t addr) : _addr(addr), _family(IP4Addr::AF_value) {} - -inline IPAddr::IPAddr(in6_addr const &addr) : _addr(addr), _family(IP6Addr::AF_value) {} - -inline IPAddr::IPAddr(sockaddr const *addr) { - this->assign(addr); -} - -inline IPAddr::IPAddr(IPEndpoint const &addr) { - this->assign(&addr.sa); -} - -inline IPAddr::IPAddr(string_view const &text) { - this->load(text); -} - -inline IPAddr & -IPAddr::operator=(in_addr_t addr) { - _family = AF_INET; - _addr._ip4 = addr; - return *this; -} - -inline IPAddr & -IPAddr::operator=(in6_addr const &addr) { - _family = AF_INET6; - _addr._ip6 = addr; - return *this; -} - -inline IPAddr & -IPAddr::operator=(IPEndpoint const &addr) { - return this->assign(&addr.sa); -} - -inline IPAddr & -IPAddr::operator=(sockaddr const *addr) { - return this->assign(addr); -} - -inline sa_family_t -IPAddr::family() const { - return _family; -} - -inline bool -IPAddr::is_ip4() const { - return AF_INET == _family; -} - -inline bool -IPAddr::is_ip6() const { - return AF_INET6 == _family; -} - -inline bool -IPAddr::isCompatibleWith(self_type const &that) { - return this->is_valid() && _family == that._family; -} - -inline bool -IPAddr::is_loopback() const { - return (AF_INET == _family && _addr._ip4.is_loopback()) || (AF_INET6 == _family && _addr._ip6.is_loopback()); -} - -inline IPAddr & -IPAddr::assign(in_addr_t addr) { - _family = AF_INET; - _addr._ip4 = addr; - return *this; -} - -inline IPAddr & -IPAddr::assign(in6_addr const &addr) { - _family = AF_INET6; - _addr._ip6 = addr; - return *this; -} - -inline IPAddr & -IPAddr::assign(sockaddr_in const *addr) { - if (addr) { - _family = AF_INET; - _addr._ip4 = addr; - } else { - _family = AF_UNSPEC; - } - return *this; -} - -inline IPAddr & -IPAddr::assign(sockaddr_in6 const *addr) { - if (addr) { - _family = AF_INET6; - _addr._ip6 = addr->sin6_addr; - } else { - _family = AF_UNSPEC; - } - return *this; -} - -inline bool -IPAddr::is_valid() const { - return _family == AF_INET || _family == AF_INET6; -} - -inline IPAddr & -IPAddr::invalidate() { - _family = AF_UNSPEC; - return *this; -} - -// Associated operators. - -/// Equality. -bool operator==(IPAddr const &lhs, sockaddr const *rhs); - -/// Equality. -inline bool -operator==(sockaddr const *lhs, IPAddr const &rhs) { - return rhs == lhs; -} - -/// Inequality. -inline bool -operator!=(IPAddr const &lhs, sockaddr const *rhs) { - return !(lhs == rhs); -} - -/// Inequality. -inline bool -operator!=(sockaddr const *lhs, IPAddr const &rhs) { - return !(rhs == lhs); -} - -/// Equality. -inline bool -operator==(IPAddr const &lhs, IPEndpoint const &rhs) { - return lhs == &rhs.sa; -} - -/// Equality. -inline bool -operator==(IPEndpoint const &lhs, IPAddr const &rhs) { - return &lhs.sa == rhs; -} - -/// Inequality. -inline bool -operator!=(IPAddr const &lhs, IPEndpoint const &rhs) { - return !(lhs == &rhs.sa); -} - -/// Inequality. -inline bool -operator!=(IPEndpoint const &lhs, IPAddr const &rhs) { - return !(rhs == &lhs.sa); -} - -inline IP4Addr const & -IPAddr::ip4() const { - return _addr._ip4; -} - -inline IP6Addr const & -IPAddr::ip6() const { - return _addr._ip6; -} - -/// ------------------------------------------------------------------------------------ - -inline IPEndpoint::IPEndpoint() { - sa.sa_family = AF_UNSPEC; -} - -inline IPEndpoint::IPEndpoint(IPAddr const &addr) { - this->assign(addr); -} - -inline IPEndpoint::IPEndpoint(sockaddr const *sa) { - this->assign(sa); -} - -inline IPEndpoint::IPEndpoint(IPEndpoint::self_type const &that) : self_type(&that.sa) {} - -inline IPEndpoint & -IPEndpoint::invalidate() { - sa.sa_family = AF_UNSPEC; - return *this; -} - -inline void -IPEndpoint::invalidate(sockaddr *addr) { - addr->sa_family = AF_UNSPEC; -} - -inline bool -IPEndpoint::is_valid() const { - return sa.sa_family == AF_INET || sa.sa_family == AF_INET6; -} - -inline IPEndpoint & -IPEndpoint::operator=(self_type const &that) { - self_type::assign(&sa, &that.sa); - return *this; -} - -inline IPEndpoint & -IPEndpoint::assign(sockaddr const *src) { - self_type::assign(&sa, src); - return *this; -} - -inline IPEndpoint const & -IPEndpoint::copy_to(sockaddr *addr) const { - self_type::assign(addr, &sa); - return *this; -} - -inline bool -IPEndpoint::is_ip4() const { - return AF_INET == sa.sa_family; -} - -inline bool -IPEndpoint::is_ip6() const { - return AF_INET6 == sa.sa_family; -} - -inline sa_family_t -IPEndpoint::family() const { - return sa.sa_family; -} - -inline in_port_t & -IPEndpoint::network_order_port() { - return self_type::port(&sa); -} - -inline in_port_t -IPEndpoint::network_order_port() const { - return self_type::port(&sa); -} - -inline in_port_t -IPEndpoint::host_order_port() const { - return ntohs(this->network_order_port()); -} - -inline in_port_t & -IPEndpoint::port(sockaddr *sa) { - switch (sa->sa_family) { - case AF_INET: - return reinterpret_cast(sa)->sin_port; - case AF_INET6: - return reinterpret_cast(sa)->sin6_port; - } - // Force a failure upstream by returning a null reference. - throw std::domain_error("sockaddr is not a valid IP address"); -} - -inline in_port_t -IPEndpoint::port(sockaddr const *sa) { - return self_type::port(const_cast(sa)); -} - -inline in_port_t -IPEndpoint::host_order_port(sockaddr const *sa) { - return ntohs(self_type::port(sa)); -} - -// --- IPAddr variants --- - -inline constexpr IP4Addr::IP4Addr(in_addr_t addr) : _addr(addr) {} - -inline IP4Addr::IP4Addr(string_view const &text) { - if (!this->load(text)) { - _addr = INADDR_ANY; - } -} - -inline IP4Addr::IP4Addr(IPAddr const &addr) : _addr(addr._family == AF_INET ? addr._addr._ip4._addr : INADDR_ANY) {} - -inline IP4Addr & -IP4Addr::operator<<=(unsigned n) { - _addr <<= n; - return *this; -} - -inline IP4Addr & -IP4Addr::operator>>=(unsigned n) { - _addr >>= n; - return *this; -} - -inline IP4Addr & -IP4Addr::operator&=(self_type const &that) { - _addr &= that._addr; - return *this; -} - -inline IP4Addr & -IP4Addr::operator|=(self_type const &that) { - _addr |= that._addr; - return *this; -} - -inline IP4Addr & -IP4Addr::operator++() { - ++_addr; - return *this; -} - -inline IP4Addr & -IP4Addr::operator--() { - --_addr; - return *this; -} - -inline in_addr_t -IP4Addr::network_order() const { - return htonl(_addr); -} - -inline in_addr_t -IP4Addr::host_order() const { - return _addr; -} - -inline auto -IP4Addr::operator=(in_addr_t ip) -> self_type & { - _addr = ntohl(ip); - return *this; -} - -/// Equality. -inline bool -operator==(IP4Addr const &lhs, IP4Addr const &rhs) { - return lhs._addr == rhs._addr; -} - -/// @return @c true if @a lhs is equal to @a rhs. -inline bool -operator!=(IP4Addr const &lhs, IP4Addr const &rhs) { - return lhs._addr != rhs._addr; -} - -/// @return @c true if @a lhs is less than @a rhs (host order). -inline bool -operator<(IP4Addr const &lhs, IP4Addr const &rhs) { - return lhs._addr < rhs._addr; -} - -/// @return @c true if @a lhs is less than or equal to@a rhs (host order). -inline bool -operator<=(IP4Addr const &lhs, IP4Addr const &rhs) { - return lhs._addr <= rhs._addr; -} - -/// @return @c true if @a lhs is greater than @a rhs (host order). -inline bool -operator>(IP4Addr const &lhs, IP4Addr const &rhs) { - return rhs < lhs; -} - -/// @return @c true if @a lhs is greater than or equal to @a rhs (host order). -inline bool -operator>=(IP4Addr const &lhs, IP4Addr const &rhs) { - return rhs <= lhs; -} - -inline IP4Addr & -IP4Addr::operator&=(IPMask const &mask) { - _addr &= mask.as_ip4()._addr; - return *this; -} - -inline IP4Addr & -IP4Addr::operator|=(IPMask const &mask) { - _addr |= ~(mask.as_ip4()._addr); - return *this; -} - -inline bool -IP4Addr::is_any() const { - return _addr == INADDR_ANY; -} - -inline bool -IP4Addr::is_loopback() const { - return (*this)[3] == IN_LOOPBACKNET; -} - -inline bool -IP4Addr::is_multicast() const { - return IN_MULTICAST(_addr); -} - -inline int -IP4Addr::cmp(IP4Addr::self_type const &that) const { - return _addr < that._addr ? -1 : _addr > that._addr ? 1 : 0; -} - -constexpr in_addr_t -IP4Addr::reorder(in_addr_t src) { - return ((src & 0xFF) << 24) | (((src >> 8) & 0xFF) << 16) | (((src >> 16) & 0xFF) << 8) | ((src >> 24) & 0xFF); -} - -// +++ IP6Addr +++ - -inline IP6Addr::IP6Addr(in6_addr const &addr) { - *this = addr; -} - -inline IP6Addr::IP6Addr(string_view const &text) { - if (!this->load(text)) { - this->clear(); - } -} - -inline IP6Addr::IP6Addr(IPAddr const &addr) : _addr{addr._addr._ip6._addr} {} - -inline bool -IP6Addr::is_loopback() const { - return _addr._store[0] == 0 && _addr._store[1] == 1; -} - -inline bool -IP6Addr::is_multicast() const { - return _addr._raw[7] == 0xFF; -} - -inline bool -IP6Addr::is_any() const { - return _addr._store[0] == 0 && _addr._store[1] == 0; -} - -inline bool -IP6Addr::is_mapped_ipv4() const { - return 0 == _addr._store[0] && (_addr._quad[7] == 0 && _addr._quad[6] == 0xFFFF); -} - -inline in6_addr & -IP6Addr::copy_to(in6_addr &addr) const { - self_type::reorder(addr, _addr._raw); - return addr; -} - -inline sockaddr * -IP6Addr::copy_to(sockaddr *sa, in_port_t port) const { - IPEndpoint addr(sa); - self_type::reorder(addr.sa6.sin6_addr, _addr._raw); - addr.network_order_port() = port; - return sa; -} - -inline in6_addr -IP6Addr::network_order() const { - in6_addr zret; - return this->copy_to(zret); -} - -inline auto -IP6Addr::clear() -> self_type & { - _addr._store[0] = _addr._store[1] = 0; - return *this; -} - -inline auto -IP6Addr::operator=(in6_addr const &addr) -> self_type & { - self_type::reorder(_addr._raw, addr); - return *this; -} - -inline auto -IP6Addr::operator=(sockaddr_in6 const *addr) -> self_type & { - if (addr) { - *this = addr->sin6_addr; - } else { - this->clear(); - } - return *this; -} - -inline IP6Addr & -IP6Addr::operator++() { - if (++(_addr._store[1]) == 0) { - ++(_addr._store[0]); - } - return *this; -} - -inline IP6Addr & -IP6Addr::operator--() { - if (--(_addr._store[1]) == ~static_cast(0)) { - --(_addr._store[0]); - } - return *this; -} - -inline void -IP6Addr::reorder(unsigned char dst[WORD_SIZE], unsigned char const src[WORD_SIZE]) { - for (size_t idx = 0; idx < WORD_SIZE; ++idx) { - dst[idx] = src[WORD_SIZE - (idx + 1)]; - } -} - -/// @return @c true if @a lhs is equal to @a rhs. -inline bool -operator==(IP6Addr const &lhs, IP6Addr const &rhs) { - return lhs._addr._store[0] == rhs._addr._store[0] && lhs._addr._store[1] == rhs._addr._store[1]; -} - -/// @return @c true if @a lhs is not equal to @a rhs. -inline bool -operator!=(IP6Addr const &lhs, IP6Addr const &rhs) { - return lhs._addr._store[0] != rhs._addr._store[0] || lhs._addr._store[1] != rhs._addr._store[1]; -} - -/// @return @c true if @a lhs is less than @a rhs. -inline bool -operator<(IP6Addr const &lhs, IP6Addr const &rhs) { - return lhs._addr._store[0] < rhs._addr._store[0] || - (lhs._addr._store[0] == rhs._addr._store[0] && lhs._addr._store[1] < rhs._addr._store[1]); -} - -/// @return @c true if @a lhs is greater than @a rhs. -inline bool -operator>(IP6Addr const &lhs, IP6Addr const &rhs) { - return rhs < lhs; -} - -/// @return @c true if @a lhs is less than or equal to @a rhs. -inline bool -operator<=(IP6Addr const &lhs, IP6Addr const &rhs) { - return lhs._addr._store[0] < rhs._addr._store[0] || - (lhs._addr._store[0] == rhs._addr._store[0] && lhs._addr._store[1] <= rhs._addr._store[1]); -} - -/// @return @c true if @a lhs is greater than or equal to @a rhs. -inline bool -operator>=(IP6Addr const &lhs, IP6Addr const &rhs) { - return rhs <= lhs; -} - -inline IP6Addr & -IP6Addr::operator&=(IPMask const &mask) { - if (mask._cidr < WORD_WIDTH) { - _addr._store[MSW] &= (~word_type{0} << (WORD_WIDTH - mask._cidr)); - _addr._store[LSW] = 0; - } else if (mask._cidr < WIDTH) { - _addr._store[LSW] &= (~word_type{0} << (2 * WORD_WIDTH - mask._cidr)); - } - return *this; -} - -inline IP6Addr & -IP6Addr::operator|=(IPMask const &mask) { - if (mask._cidr < WORD_WIDTH) { - _addr._store[MSW] |= (~word_type{0} >> mask._cidr); - _addr._store[LSW] = ~word_type{0}; - } else if (mask._cidr < WIDTH) { - _addr._store[LSW] |= (~word_type{0} >> (mask._cidr - WORD_WIDTH)); - } - return *this; -} - -// --- // - -inline bool -IPAddr::operator==(self_type const &that) const { - switch (_family) { - case AF_INET: - return that._family == AF_INET && _addr._ip4 == that._addr._ip4; - case AF_INET6: - return that._family == AF_INET6 && _addr._ip6 == that._addr._ip6; - default: - return ! that.is_valid(); - } -} - -inline bool -IPAddr::operator!=(self_type const &that) const { - return !(*this == that); -} - -inline bool -IPAddr::operator>(self_type const &that) const { - return that < *this; -} - -inline bool -IPAddr::operator<=(self_type const &that) const { - return !(that < *this); -} - -inline bool -IPAddr::operator>=(self_type const &that) const { - return !(*this < that); -} - -// Disambiguating comparisons. - -inline bool -operator==(IPAddr const &lhs, IP4Addr const &rhs) { - return lhs.is_ip4() && static_cast(lhs) == rhs; -} - -inline bool -operator!=(IPAddr const &lhs, IP4Addr const &rhs) { - return !lhs.is_ip4() || static_cast(lhs) != rhs; -} - -inline bool -operator==(IP4Addr const &lhs, IPAddr const &rhs) { - return rhs.is_ip4() && lhs == static_cast(rhs); -} - -inline bool -operator!=(IP4Addr const &lhs, IPAddr const &rhs) { - return !rhs.is_ip4() || lhs != static_cast(rhs); -} - -inline bool -operator==(IPAddr const &lhs, IP6Addr const &rhs) { - return lhs.is_ip6() && static_cast(lhs) == rhs; -} - -inline bool -operator!=(IPAddr const &lhs, IP6Addr const &rhs) { - return !lhs.is_ip6() || static_cast(lhs) != rhs; -} - -inline bool -operator==(IP6Addr const &lhs, IPAddr const &rhs) { - return rhs.is_ip6() && lhs == rhs.ip6(); -} - -inline bool -operator!=(IP6Addr const &lhs, IPAddr const &rhs) { - return !rhs.is_ip6() || lhs != rhs.ip6(); -} - -// +++ IPRange +++ - -inline IP4Range::IP4Range(string_view const &text) { - this->load(text); -} - -inline auto -IP4Range::networks() const -> NetSource { - return {NetSource{*this}}; -} - -inline IP6Range::IP6Range(string_view const &text) { - this->load(text); -} - -inline auto -IP6Range::networks() const -> NetSource { - return {NetSource{*this}}; -} - -inline IPRange::IPRange(IP4Range const &range) : _family(AF_INET) { - _range._ip4 = range; -} - -inline IPRange::IPRange(IP6Range const &range) : _family(AF_INET6) { - _range._ip6 = range; -} - -inline IPRange::IPRange(string_view const &text) { - this->load(text); -} - -inline auto -IPRange::networks() const -> NetSource { - return {NetSource{*this}}; -} - -inline bool -IPRange::is(sa_family_t family) const { - return family == _family; -} - -// +++ IPMask +++ - -inline IPMask::IPMask(raw_type width) : _cidr(width) {} - -inline bool -IPMask::is_valid() const { - return _cidr < INVALID; -} - -inline auto -IPMask::width() const -> raw_type { - return _cidr; -} - -inline bool -operator==(IPMask const &lhs, IPMask const &rhs) { - return lhs.width() == rhs.width(); -} - -inline bool -operator!=(IPMask const &lhs, IPMask const &rhs) { - return lhs.width() != rhs.width(); -} - -inline bool -operator<(IPMask const &lhs, IPMask const &rhs) { - return lhs.width() < rhs.width(); -} - -inline IP4Addr -IPMask::as_ip4() const { - static constexpr auto MASK = ~in_addr_t{0}; - in_addr_t addr = MASK; - if (_cidr < IP4Addr::WIDTH) { - addr <<= IP4Addr::WIDTH - _cidr; - } - return IP4Addr{addr}; -} - -inline auto -IPMask::clear() -> self_type & { - _cidr = INVALID; - return *this; -} - -inline auto -IPMask::operator<<=(raw_type n) -> self_type & { - _cidr -= n; - return *this; -} - -inline auto -IPMask::operator>>=(IPMask::raw_type n) -> self_type & { - _cidr += n; - return *this; -} - -// +++ mixed mask operators +++ - -inline IP4Addr -operator&(IP4Addr const &addr, IPMask const &mask) { - return IP4Addr{addr} &= mask; -} - -inline IP4Addr -operator|(IP4Addr const &addr, IPMask const &mask) { - return IP4Addr{addr} |= mask; -} - -inline IP6Addr -operator&(IP6Addr const &addr, IPMask const &mask) { - return IP6Addr{addr} &= mask; -} - -inline IP6Addr -operator|(IP6Addr const &addr, IPMask const &mask) { - return IP6Addr{addr} |= mask; -} - -inline IPAddr -operator&(IPAddr const &addr, IPMask const &mask) { - return IPAddr{addr} &= mask; -} - -inline IPAddr -operator|(IPAddr const &addr, IPMask const &mask) { - return IPAddr{addr} |= mask; -} - -// +++ IPNet +++ - -inline IP4Net::IP4Net(swoc::IP4Addr addr, swoc::IPMask mask) : _addr(addr & mask), _mask(mask) {} - -inline IPMask const & -IP4Net::mask() const { - return _mask; -} - -inline bool -IP4Net::is_valid() const { - return _mask.is_valid(); -} - -inline IP4Addr -IP4Net::lower_bound() const { - return _addr; -} - -inline IP4Addr -IP4Net::upper_bound() const { - return _addr | _mask; -} - -inline IP4Range -IP4Net::as_range() const { - return {this->lower_bound(), this->upper_bound()}; -} - -inline bool -IP4Net::operator==(self_type const &that) const { - return _mask == that._mask && _addr == that._addr; -} - -inline bool -IP4Net::operator!=(self_type const &that) const { - return _mask != that._mask || _addr != that._addr; -} - -inline IP4Net::self_type & -IP4Net::assign(IP4Addr const &addr, IPMask const &mask) { - _addr = addr & mask; - _mask = mask; - return *this; -} - -inline IP6Net::IP6Net(swoc::IP6Addr addr, swoc::IPMask mask) : _addr(addr & mask), _mask(mask) {} - -inline IPMask const & -IP6Net::mask() const { - return _mask; -} - -inline bool -IP6Net::is_valid() const { - return _mask.is_valid(); -} - -inline IP6Addr -IP6Net::lower_bound() const { - return _addr; -} - -inline IP6Addr -IP6Net::upper_bound() const { - return _addr | _mask; -} - -inline IP6Range -IP6Net::as_range() const { - return {this->lower_bound(), this->upper_bound()}; -} - -inline bool -IP6Net::operator==(self_type const &that) const { - return _mask == that._mask && _addr == that._addr; -} - -inline bool -IP6Net::operator!=(self_type const &that) const { - return _mask != that._mask || _addr != that._addr; -} - -inline IP6Net::self_type & -IP6Net::assign(IP6Addr const &addr, IPMask const &mask) { - _addr = addr & mask; - _mask = mask; - return *this; -} - -inline IPNet::IPNet(IPAddr const &addr, IPMask const &mask) : _addr(addr & mask), _mask(mask) {} - -inline IPNet::IPNet(TextView text) { - this->load(text); -} - -inline bool -IPNet::is_valid() const { - return _mask.is_valid(); -} - -inline IPAddr -IPNet::lower_bound() const { - return _addr; -} - -inline IPAddr -IPNet::upper_bound() const { - return _addr | _mask; -} - -inline IPMask::raw_type -IPNet::width() const { - return _mask.width(); -} - -inline IPMask const & -IPNet::mask() const { - return _mask; -} - -inline IPRange -IPNet::as_range() const { - return {this->lower_bound(), this->upper_bound()}; -} - -inline IPNet::self_type & -IPNet::assign(IPAddr const &addr, IPMask const &mask) { - _addr = addr & mask; - _mask = mask; - return *this; -} - -inline bool -IPNet::operator==(IPNet::self_type const &that) const { - return _mask == that._mask && _addr == that._addr; -} - -inline bool -IPNet::operator!=(IPNet::self_type const &that) const { - return _mask != that._mask || _addr != that._addr; -} - -inline bool -operator==(IPNet const &lhs, IP4Net const &rhs) { - return lhs.is_ip4() && lhs.ip4() == rhs; -} - -inline bool -operator==(IP4Net const &lhs, IPNet const &rhs) { - return rhs.is_ip4() && rhs.ip4() == lhs; -} - -inline bool -operator==(IPNet const &lhs, IP6Net const &rhs) { - return lhs.is_ip6() && lhs.ip6() == rhs; -} - -inline bool -operator==(IP6Net const &lhs, IPNet const &rhs) { - return rhs.is_ip6() && rhs.ip6() == lhs; -} - -// +++ Range -> Network classes +++ - -inline bool -IP4Range::NetSource::is_valid(swoc::IP4Addr mask) const { - return ((mask._addr & _range._min._addr) == _range._min._addr) && ((_range._min._addr | ~mask._addr) <= _range._max._addr); -} - -inline IP4Net -IP4Range::NetSource::operator*() const { - return IP4Net{_range.min(), IPMask{_cidr}}; -} - -inline IP4Range::NetSource::iterator -IP4Range::NetSource::begin() const { - return *this; -} - -inline IP4Range::NetSource::iterator -IP4Range::NetSource::end() { - return self_type{range_type{}}; -} - -inline bool -IP4Range::NetSource::empty() const { - return _range.empty(); -} - -inline IPMask -IP4Range::NetSource::mask() const { - return IPMask{_cidr}; -} - -inline auto -IP4Range::NetSource::operator->() -> self_type * { - return this; -} - -inline IP4Addr const & -IP4Range::NetSource::addr() const { - return _range.min(); -} - -inline bool -IP4Range::NetSource::operator==(IP4Range::NetSource::self_type const &that) const { - return ((_cidr == that._cidr) && (_range == that._range)) || (_range.empty() && that._range.empty()); -} - -inline bool -IP4Range::NetSource::operator!=(IP4Range::NetSource::self_type const &that) const { - return !(*this == that); -} - -inline auto -IP6Range::NetSource::begin() const -> iterator { - return *this; -} - -inline auto -IP6Range::NetSource::end() const -> iterator { - return self_type{range_type{}}; -} - -inline bool -IP6Range::NetSource::empty() const { - return _range.empty(); -} - -inline IP6Net -IP6Range::NetSource::operator*() const { - return IP6Net{_range.min(), _mask}; -} +#include "swoc/IPEndpoint.h" +#include "swoc/IPAddr.h" +#include "swoc/IPSrv.h" +#include "swoc/IPRange.h" -inline auto -IP6Range::NetSource::operator->() -> self_type * { - return this; -} +namespace swoc { inline namespace SWOC_VERSION_NS { -inline bool -IP6Range::NetSource::is_valid(IPMask const &mask) { - return ((_range.min() & mask) == _range.min()) && ((_range.min() | mask) <= _range.max()); -} +// --- Cross type address operators inline bool -IP6Range::NetSource::operator==(IP6Range::NetSource::self_type const &that) const { - return ((_mask == that._mask) && (_range == that._range)) || (_range.empty() && that._range.empty()); +operator==(IPAddr const &lhs, IPEndpoint const &rhs) { + return lhs == &rhs.sa; } +/// Equality. inline bool -IP6Range::NetSource::operator!=(IP6Range::NetSource::self_type const &that) const { - return !(*this == that); -} - -inline IP6Addr const & -IP6Range::NetSource::addr() const { - return _range.min(); -} - -inline IPMask -IP6Range::NetSource::mask() const { - return _mask; -} - -inline IPRange::NetSource::NetSource(IPRange::NetSource::range_type const &range) { - if (range.is_ip4()) { - new (&_ip4) decltype(_ip4)(range.ip4()); - _family = AF_INET; - } else if (range.is_ip6()) { - new (&_ip6) decltype(_ip6)(range.ip6()); - _family = AF_INET6; - } -} - -inline auto -IPRange::NetSource::begin() const -> iterator { - return *this; -} - -inline auto -IPRange::NetSource::end() const -> iterator { - return AF_INET == _family ? self_type{IP4Range{}} : AF_INET6 == _family ? self_type{IP6Range{}} : self_type{IPRange{}}; -} - -inline IPAddr -IPRange::NetSource::addr() const { - if (AF_INET == _family) { - return _ip4.addr(); - } else if (AF_INET6 == _family) { - return _ip6.addr(); - } - return {}; -} - -inline IPMask -IPRange::NetSource::mask() const { - if (AF_INET == _family) { - return _ip4.mask(); - } else if (AF_INET6 == _family) { - return _ip6.mask(); - } - return {}; -} - -inline IPNet -IPRange::NetSource::operator*() const { - return {this->addr(), this->mask()}; -} - -inline auto -IPRange::NetSource::operator++() -> self_type & { - if (AF_INET == _family) { - ++_ip4; - } else if (AF_INET6 == _family) { - ++_ip6; - } - return *this; -} - -inline auto -IPRange::NetSource::operator->() -> self_type * { - return this; +operator==(IPEndpoint const &lhs, IPAddr const &rhs) { + return &lhs.sa == rhs; } +/// Inequality. inline bool -IPRange::NetSource::operator==(self_type const &that) const { - if (_family != that._family) { - return false; - } - if (AF_INET == _family) { - return _ip4 == that._ip4; - } else if (AF_INET6 == _family) { - return _ip6 == that._ip6; - } else if (AF_UNSPEC == _family) { - return true; - } - return false; +operator!=(IPAddr const &lhs, IPEndpoint const &rhs) { + return !(lhs == &rhs.sa); } +/// Inequality. inline bool -IPRange::NetSource::operator!=(self_type const &that) const { - return !(*this == that); -} - -// --- IPSpace - -template -auto -IPSpace::mark(IPRange const &range, PAYLOAD const &payload) -> self_type & { - if (range.is(AF_INET)) { - _ip4.mark(range.ip4(), payload); - } else if (range.is(AF_INET6)) { - _ip6.mark(range.ip6(), payload); - } - return *this; -} - -template -auto -IPSpace::fill(IPRange const &range, PAYLOAD const &payload) -> self_type & { - if (range.is(AF_INET6)) { - _ip6.fill(range.ip6(), payload); - } else if (range.is(AF_INET)) { - _ip4.fill(range.ip4(), payload); - } - return *this; -} - -template -auto -IPSpace::erase(IPRange const &range) -> self_type & { - if (range.is(AF_INET)) { - _ip4.erase(range.ip4()); - } else if (range.is(AF_INET6)) { - _ip6.erase(range.ip6()); - } - return *this; -} - -template -template -auto -IPSpace::blend(IPRange const &range, U const &color, F &&blender) -> self_type & { - if (range.is(AF_INET)) { - _ip4.blend(range.ip4(), color, blender); - } else if (range.is(AF_INET6)) { - _ip6.blend(range.ip6(), color, blender); - } - return *this; -} - -template -template -auto -IPSpace::blend(IP4Range const &range, U const &color, F &&blender) -> self_type & { - _ip4.blend(range, color, std::forward(blender)); - return *this; -} - -template -template -auto -IPSpace::blend(IP6Range const &range, U const &color, F &&blender) -> self_type & { - _ip6.blend(range, color, std::forward(blender)); - return *this; -} - -template -void -IPSpace::clear() { - _ip4.clear(); - _ip6.clear(); -} - -template -auto -IPSpace::begin() const -> const_iterator { - auto nc_this = const_cast(this); - return const_iterator(nc_this->_ip4.begin(), nc_this->_ip6.begin()); -} - -template -auto -IPSpace::end() const -> const_iterator { - auto nc_this = const_cast(this); - return const_iterator(nc_this->_ip4.end(), nc_this->_ip6.end()); -} - -template -auto -IPSpace::begin_ip4() const -> const_iterator { - return this->begin(); -} - -template -auto -IPSpace::end_ip4() const -> const_iterator { - auto nc_this = const_cast(this); - return { nc_this->_ip4.end(), nc_this->_ip6.begin() }; -} - -template -auto -IPSpace::begin_ip6() const -> const_iterator { - auto nc_this = const_cast(this); - return { nc_this->_ip4.end(), nc_this->_ip6.begin() }; -} - -template -auto -IPSpace::end_ip6() const -> const_iterator { - return this->end(); -} - -template -auto -IPSpace::begin() -> iterator { - return iterator{_ip4.begin(), _ip6.begin()}; -} - -template -auto -IPSpace::end() -> iterator { - return iterator{_ip4.end(), _ip6.end()}; -} - -template -size_t -IPSpace::count(sa_family_t f) const { - return IP4Addr::AF_value == f ? _ip4.count() : IP6Addr::AF_value == f ? _ip6.count() : 0; -} - -}} // namespace swoc::SWOC_VERSION_NS - -/// @cond NOT_DOCUMENTED -namespace std { - -// -- Tuple support for IP4Net -- -template <> class tuple_size : public std::integral_constant {}; - -template class tuple_element { static_assert("swoc::IP4Net tuple index out of range"); }; - -template <> class tuple_element<0, swoc::IP4Net> { -public: - using type = swoc::IP4Addr; -}; - -template <> class tuple_element<1, swoc::IP4Net> { -public: - using type = swoc::IPMask; -}; - -// -- Tuple support for IP6Net -- -template <> class tuple_size : public std::integral_constant {}; - -template class tuple_element { static_assert("swoc::IP6Net tuple index out of range"); }; - -template <> class tuple_element<0, swoc::IP6Net> { -public: - using type = swoc::IP6Addr; -}; - -template <> class tuple_element<1, swoc::IP6Net> { -public: - using type = swoc::IPMask; -}; - -// -- Tuple support for IPNet -- -template <> class tuple_size : public std::integral_constant {}; - -template class tuple_element { static_assert("swoc::IPNet tuple index out of range"); }; - -template <> class tuple_element<0, swoc::IPNet> { -public: - using type = swoc::IPAddr; -}; - -template <> class tuple_element<1, swoc::IPNet> { -public: - using type = swoc::IPMask; -}; - -} // namespace std -/// @endcond - -namespace swoc { inline namespace SWOC_VERSION_NS { - -template -typename std::tuple_element::type -get(swoc::IP4Net const &net) { - if constexpr (IDX == 0) { - return net.lower_bound(); - } else if constexpr (IDX == 1) { - return net.mask(); - } -} - -template -typename std::tuple_element::type -get(swoc::IP6Net const &net) { - if constexpr (IDX == 0) { - return net.lower_bound(); - } else if constexpr (IDX == 1) { - return net.mask(); - } -} - -template -typename std::tuple_element::type -get(swoc::IPNet const &net) { - if constexpr (IDX == 0) { - return net.lower_bound(); - } else if constexpr (IDX == 1) { - return net.mask(); - } +operator!=(IPEndpoint const &lhs, IPAddr const &rhs) { + return !(rhs == &lhs.sa); } }} // namespace swoc::SWOC_VERSION_NS - -namespace std { -/// Standard hash support for @a IP4Addr. -template <> struct hash { - size_t operator()(swoc::IP4Addr const &addr) const { - return addr.network_order(); - } -}; - -/// Standard hash support for @a IP6Addr. -template <> struct hash { - size_t operator()(swoc::IP6Addr const &addr) const { - // XOR the 64 chunks then XOR that down to 32 bits. - auto words = addr.as_span(); - return words[0] ^ words[1]; - } -}; - -/// Standard hash support for @a IPAddr. -template <> struct hash { - size_t operator()(swoc::IPAddr const &addr) const { - return addr.is_ip4() ? hash()(addr.ip4()) : addr.is_ip6() ? hash()(addr.ip6()) : 0; - } -}; - -} // namespace std diff --git a/lib/swoc/include/swoc/swoc_version.h b/lib/swoc/include/swoc/swoc_version.h index f6463642724..ee9d56a3ff2 100644 --- a/lib/swoc/include/swoc/swoc_version.h +++ b/lib/swoc/include/swoc/swoc_version.h @@ -23,11 +23,11 @@ #pragma once #if !defined(SWOC_VERSION_NS) -#define SWOC_VERSION_NS _1_3_11 +#define SWOC_VERSION_NS _1_4_0 #endif namespace swoc { inline namespace SWOC_VERSION_NS { static constexpr unsigned MAJOR_VERSION = 1; -static constexpr unsigned MINOR_VERSION = 3; -static constexpr unsigned POINT_VERSION = 11; +static constexpr unsigned MINOR_VERSION = 4; +static constexpr unsigned POINT_VERSION = 0; }} // namespace swoc::SWOC_VERSION_NS diff --git a/lib/swoc/src/bw_format.cc b/lib/swoc/src/bw_format.cc index 20c43ecb9bc..ac9ce9e347a 100644 --- a/lib/swoc/src/bw_format.cc +++ b/lib/swoc/src/bw_format.cc @@ -943,67 +943,6 @@ bwformat(BufferWriter &w, bwf::Spec const &spec, bwf::Pattern const &pattern) { return w; } -// --- IP address support --- - -#if 0 -BufferWriter & -bwformat(BufferWriter &w, bwf::Spec const &spec, IpAddr const &addr) -{ - bwf::Spec local_spec{spec}; // Format for address elements and port. - bool addr_p{true}; - bool family_p{false}; - - if (spec._ext.size()) { - if (spec._ext.front() == '=') { - local_spec._ext.remove_prefix(1); - } else if (spec._ext.size() > 1 && spec._ext[1] == '=') { - local_spec._ext.remove_prefix(2); - } - } - if (local_spec._ext.size()) { - addr_p = false; - for (char c : local_spec._ext) { - switch (c) { - case 'a': - case 'A': - addr_p = true; - break; - case 'f': - case 'F': - family_p = true; - break; - } - } - } - - if (addr_p) { - if (addr.isIp4()) { - bwformat(w, spec, addr._addr._ip4); - } else if (addr.isIp6()) { - bwformat(w, spec, addr._addr._ip6); - } else { - w.print("*Not IP address [{}]*", addr.family()); - } - } - - if (family_p) { - local_spec._min = 0; - if (addr_p) { - w.write(' '); - } - if (spec.has_numeric_type()) { - bwformat(w, local_spec, static_cast(addr.family())); - } else { - bwformat(w, local_spec, addr.family()); - } - } - return w; -} -#endif - -namespace { -} // namespace - }} // namespace swoc::SWOC_VERSION_NS namespace std { diff --git a/lib/swoc/src/bw_ip_format.cc b/lib/swoc/src/bw_ip_format.cc index 1c96b539296..ecef729ab3f 100644 --- a/lib/swoc/src/bw_ip_format.cc +++ b/lib/swoc/src/bw_ip_format.cc @@ -238,7 +238,7 @@ bwformat(BufferWriter &w, Spec const &spec, IPAddr const &addr) { if (addr_p) { if (addr.is_ip4()) { - swoc::bwformat(w, spec, static_cast(addr)); + swoc::bwformat(w, spec, addr.ip4()); } else if (addr.is_ip6()) { swoc::bwformat(w, spec, addr.ip6().network_order()); } else { diff --git a/lib/swoc/src/swoc_ip.cc b/lib/swoc/src/swoc_ip.cc index dc39df5b6fe..9434221cd56 100644 --- a/lib/swoc/src/swoc_ip.cc +++ b/lib/swoc/src/swoc_ip.cc @@ -258,26 +258,42 @@ IP4Addr::load(std::string_view const &text) { TextView src{text}; int n = SIZE; /// # of octets - if (src.empty() || ('[' == *src && ((++src).empty() || src.back() != ']'))) { + _addr = INADDR_ANY; // clear to zero. + + // empty or trailing dot or empty brackets or unmatched brackets. + if (src.empty() || src.back() == '.' || ('[' == *src && ((++src).empty() || src.back() != ']'))) { return false; } - auto octet = reinterpret_cast(&_addr); - while (n > 0 && !src.empty()) { - TextView token{src.take_prefix_at('.')}; - auto x = svto_radix<10>(token); - if (token.empty() && x <= std::numeric_limits::max()) { - octet[--n] = x; - } else { + in_addr_t max = std::numeric_limits::max(); + while (n > 0) { + TextView parsed; + auto token = src.take_prefix_at('.'); + auto v = svtou(token, &parsed); + if (parsed.size() != token.size()) { + break; + } + if (src.empty()) { + if (v <= max) { + _addr += v; + n = 0; // signal complete. + } break; + } else if (v <= std::numeric_limits::max()){ + _addr += v << ( --n * 8); + } else { + break; // invalid. } + max >>= 8; // reduce by one octet. } - if (n == 0 && src.empty()) { - return true; + // If there's text left, or not all the octets were filled, fail. + if (! src.empty() || n != 0) { + _addr = INADDR_ANY; + return false; } - _addr = INADDR_ANY; - return false; + + return true; } IP4Addr::IP4Addr(sockaddr_in const *sa) : _addr(reorder(sa->sin_addr.s_addr)) {} @@ -295,6 +311,16 @@ IP4Addr::copy_to(sockaddr_in *sa, in_port_t port) const { return sa; } +// --- IPv6 + +sockaddr * +IP6Addr::copy_to(sockaddr *sa, in_port_t port) const { + IPEndpoint addr(sa); + self_type::reorder(addr.sa6.sin6_addr, _addr._raw); + addr.network_order_port() = port; + return sa; +} + int IP6Addr::cmp(const self_type &that) const { return *this < that ? -1 : *this > that ? 1 : 0; @@ -439,6 +465,15 @@ IP6Addr::reorder(raw_type &dst, in6_addr const &src) { self_type::reorder(dst.data() + WORD_SIZE, src.s6_addr + WORD_SIZE); } +IPAddr::IPAddr(IPEndpoint const &addr) { + this->assign(&addr.sa); +} + +IPAddr & +IPAddr::operator=(IPEndpoint const &addr) { + return this->assign(&addr.sa); +} + bool IPAddr::load(const std::string_view &text) { TextView src{text}; @@ -576,9 +611,9 @@ IPMask::load(string_view const &text) { IPMask IPMask::mask_for(IPAddr const &addr) { if (addr.is_ip4()) { - return self_type::mask_for(static_cast(addr)); + return self_type::mask_for(addr.ip4()); } else if (addr.is_ip6()) { - return self_type::mask_for(static_cast(addr)); + return self_type::mask_for(addr.ip6()); } return {}; } @@ -634,6 +669,119 @@ IPMask::as_ip6() const { return {MASK, MASK}; } +// --- SRV --- + +IP4Srv::IP4Srv(swoc::TextView text) { + this->load(text); +} + +bool +IP4Srv::load(swoc::TextView text) { + TextView addr_text, port_text, rest; + if (IPEndpoint::tokenize(text, &addr_text, &port_text, &rest)) { + if (rest.empty()) { + uintmax_t n = 0; + if (!port_text.empty()) { + n = swoc::svtou(port_text, &rest); + if (rest.size() != port_text.size() || n > std::numeric_limits::max()) { + return false; // bad port. + } + } + IP4Addr addr; + if (addr.load(addr_text)) { + this->assign(addr, n); + return true; + } + } + } + return false; +} + +IP6Srv::IP6Srv(swoc::TextView text) { + this->load(text); +} + +bool +IP6Srv::load(swoc::TextView text) { + TextView addr_text, port_text, rest; + if (IPEndpoint::tokenize(text, &addr_text, &port_text, &rest)) { + if (rest.empty()) { + uintmax_t n = 0; + if (!port_text.empty()) { + n = swoc::svtou(port_text, &rest); + if (rest.size() != port_text.size() || n > std::numeric_limits::max()) { + return false; // bad port. + } + } + IP6Addr addr; + if (addr.load(addr_text)) { + this->assign(addr, n); + return true; + } + } + } + return false; +} + +IPSrv::IPSrv(swoc::TextView text) { + this->load(text); +} + +bool +IPSrv::load(swoc::TextView text) { + TextView addr_text, port_text, rest; + if (IPEndpoint::tokenize(text, &addr_text, &port_text, &rest)) { + if (rest.empty()) { + uintmax_t n = 0; + if (!port_text.empty()) { + n = swoc::svtou(port_text, &rest); + if (rest.size() != port_text.size() || n > std::numeric_limits::max()) { + return false; // bad port. + } + } + IPAddr addr; + if (addr.load(addr_text)) { + this->assign(addr, n); + return true; + } + } + } + return false; +} + + +IPSrv::IPSrv(IPAddr addr, in_port_t port) { + _family = addr.family(); + if (addr.is_ip4()) { + _srv._ip4.assign(addr.ip4(), port); + } else if (addr.is_ip6()) { + _srv._ip6.assign(addr.ip6(), port); + } else { + _family = AF_UNSPEC; + } +} + +IPSrv::IPSrv(IPEndpoint const& ep) { + if (ep.is_ip4()) { + _family = _srv._ip4.family(); + _srv._ip4.assign(&ep.sa4); + } else if (ep.is_ip6()) { + _family = _srv._ip6.family(); + _srv._ip6.assign(&ep.sa6); + } +} + +auto IPSrv::assign(const sockaddr *sa) -> self_type & { + if (AF_INET == sa->sa_family) { + _family = AF_INET; + _srv._ip4.assign(reinterpret_cast(sa)); + } else if (AF_INET6 == sa->sa_family) { + _family = AF_INET6; + _srv._ip6.assign(reinterpret_cast(sa)); + } + return *this; +} + // ++ IPNet ++ bool @@ -879,20 +1027,16 @@ IPRange::IPRange(IPAddr const &min, IPAddr const &max) { bool IPRange::load(std::string_view const &text) { - static const string_view CHARS{".:"}; - if ( auto idx = text.find_first_of(CHARS) ; idx != text.npos ) { - if (text[idx] == '.') { - if (_range._ip4.load(text)) { - _family = AF_INET; - return true; - } - } else { - if (_range._ip6.load(text)) { - _family = AF_INET6; - return true; - } + if ( auto idx = text.find_first_of(':') ; idx != text.npos ) { + if (_range._ip6.load(text)) { + _family = AF_INET6; + return true; } + } else if (_range._ip4.load(text)) { + _family = AF_INET; + return true; } + return false; }