This is an automated email from the ASF dual-hosted git repository. bneradt pushed a commit to branch ipsrv in repository https://gitbox.apache.org/repos/asf/trafficserver-libswoc.git
commit 3b6ed80a2597ad0df6a8bfbd191453fa4cd878f6 Author: Alan M. Carroll <a...@apache.org> AuthorDate: Sat Nov 12 17:55:36 2022 -0600 IP fixes for refreshing ATS usage. --- code/include/swoc/DiscreteRange.h | 53 +++++--- code/include/swoc/IPAddr.h | 175 ++++++++++++++++-------- code/include/swoc/IPRange.h | 280 +++++++++++++++++++++++--------------- code/include/swoc/IPSrv.h | 92 ++++++++++++- code/include/swoc/TextView.h | 2 +- code/include/swoc/bwf_fwd.h | 4 +- code/src/bw_ip_format.cc | 2 +- code/src/swoc_ip.cc | 143 +++++++++++++++---- unit_tests/test_Errata.cc | 2 - unit_tests/test_ip.cc | 130 ++++++++++++++++-- 10 files changed, 645 insertions(+), 238 deletions(-) diff --git a/code/include/swoc/DiscreteRange.h b/code/include/swoc/DiscreteRange.h index fa7cf7c..ee306a9 100644 --- a/code/include/swoc/DiscreteRange.h +++ b/code/include/swoc/DiscreteRange.h @@ -812,34 +812,27 @@ public: /** 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<METRIC, PAYLOAD>::find(METRIC const &metric) -> iterator { return this->end(); } +template <typename METRIC, typename PAYLOAD> +auto +DiscreteSpace<METRIC, PAYLOAD>::find(METRIC const &metric) const -> const_iterator +{ + return const_cast<self_type *>(this)->find(metric); +} + template <typename METRIC, typename PAYLOAD> auto DiscreteSpace<METRIC, PAYLOAD>::lower_bound(METRIC const &target) -> Node * { @@ -1519,4 +1519,17 @@ DiscreteSpace<METRIC, PAYLOAD>::blend(DiscreteSpace::range_type const &range, U return *this; } +template <typename METRIC, typename PAYLOAD> +void +DiscreteSpace<METRIC, PAYLOAD>::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/code/include/swoc/IPAddr.h b/code/include/swoc/IPAddr.h index 3a567d1..cacc455 100644 --- a/code/include/swoc/IPAddr.h +++ b/code/include/swoc/IPAddr.h @@ -53,9 +53,6 @@ public: /// 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); - /// Self assignment. self_type & operator=(self_type const& that) = default; @@ -74,12 +71,11 @@ public: /** Byte access. * * @param idx Byte index. - * @return The byte at @a idx in the address. + * @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 { - return reinterpret_cast<bytes const &>(_addr)[idx]; - } + uint8_t operator[](unsigned idx) const; /// Apply @a mask to address, leaving the network portion. self_type &operator&=(IPMask const &mask); @@ -121,6 +117,12 @@ public: /// @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. @@ -215,11 +217,15 @@ public: explicit IP6Addr(sockaddr_in6 const *addr) { *this = addr; } /// Construct from text representation. - /// If the @a text is invalid the result is an invalid instance. + /// If the @a text is invalid the result is any address. + /// @see load IP6Addr(string_view const &text); - /// Construct from generic @a addr. - explicit IP6Addr(IPAddr const &addr); + /** Construct mapped IPv4 address. + * + * @param addr IPv4 address + */ + explicit IP6Addr(IP4Addr addr); /// Self assignment. self_type & operator=(self_type const& that) = default; @@ -268,6 +274,13 @@ public: /// 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; @@ -301,8 +314,14 @@ public: /// @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_ipv4() const; + bool is_mapped_ip4() const; /** Reset to default constructed state. * @@ -397,6 +416,10 @@ protected: /// This converts from the position in the text format to the quads in the binary format. static constexpr std::array<unsigned, N_QUADS> 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<unsigned, SIZE> 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. @@ -505,7 +528,7 @@ public: /// 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); + bool is_same_family(self_type const &that); /// Get the address family. /// @return The address family. @@ -517,35 +540,29 @@ public: /// 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; - 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 loopback + bool is_loopback() const; + /// Test for multicast bool is_multicast() const; - /// Test for loopback - bool is_loopback() 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; @@ -684,8 +701,6 @@ inline IP4Addr::IP4Addr(string_view const &text) { } } -inline IP4Addr::IP4Addr(IPAddr const &addr) : _addr(addr._family == AF_INET ? addr._addr._ip4._addr : INADDR_ANY) {} - inline constexpr sa_family_t IP4Addr::family() const { return AF_value; @@ -798,7 +813,7 @@ IP4Addr::is_any() const { inline bool IP4Addr::is_loopback() const { - return (*this)[3] == IN_LOOPBACKNET; + return (*this)[0] == IN_LOOPBACKNET; } inline bool @@ -806,6 +821,24 @@ 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<bytes const &>(_addr)[3 - idx]; +} + inline int IP4Addr::cmp(IP4Addr::self_type const &that) const { return _addr < that._addr ? -1 : _addr > that._addr ? 1 : 0; @@ -833,26 +866,41 @@ inline IP6Addr::IP6Addr(string_view const &text) { } } -inline IP6Addr::IP6Addr(IPAddr const &addr) : _addr{addr._addr._ip6._addr} {} +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[0] == 0 && _addr._store[1] == 1; + return _addr._store[MSW] == 0 && _addr._store[LSW] == 1; } inline bool IP6Addr::is_multicast() const { - return _addr._raw[7] == 0xFF; + return _addr._raw[RAW_IDX[0]] == 0xFF; } inline bool IP6Addr::is_any() const { - return _addr._store[0] == 0 && _addr._store[1] == 0; + 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_mapped_ipv4() const { - return 0 == _addr._store[0] && (_addr._quad[7] == 0 && _addr._quad[6] == 0xFFFF); +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 & @@ -869,7 +917,7 @@ IP6Addr::network_order() const { inline auto IP6Addr::clear() -> self_type & { - _addr._store[0] = _addr._store[1] = 0; + _addr._store[MSW] = _addr._store[LSW] = 0; return *this; } @@ -891,16 +939,16 @@ IP6Addr::operator=(sockaddr_in6 const *addr) -> self_type & { inline IP6Addr & IP6Addr::operator++() { - if (++(_addr._store[1]) == 0) { - ++(_addr._store[0]); + if (++(_addr._store[LSW]) == 0) { + ++(_addr._store[MSW]); } return *this; } inline IP6Addr & IP6Addr::operator--() { - if (--(_addr._store[1]) == ~static_cast<uint64_t>(0)) { - --(_addr._store[0]); + if (--(_addr._store[LSW]) == ~static_cast<uint64_t>(0)) { + --(_addr._store[MSW]); } return *this; } @@ -915,20 +963,20 @@ IP6Addr::reorder(unsigned char dst[WORD_SIZE], unsigned char const src[WORD_SIZE /// @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 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[0] != rhs._addr._store[0] || lhs._addr._store[1] != rhs._addr._store[1]; + 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[0] < rhs._addr._store[0] || - (lhs._addr._store[0] == rhs._addr._store[0] && lhs._addr._store[1] < rhs._addr._store[1]); + 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. @@ -940,8 +988,8 @@ operator>(IP6Addr const &lhs, IP6Addr const &rhs) { /// @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 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. @@ -1051,6 +1099,11 @@ 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; @@ -1111,7 +1164,7 @@ IPAddr::is_ip6() const { } inline bool -IPAddr::isCompatibleWith(self_type const &that) { +IPAddr::is_same_family(self_type const &that) { return this->is_valid() && _family == that._family; } @@ -1120,6 +1173,20 @@ 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; @@ -1247,22 +1314,22 @@ operator!=(IPAddr const &lhs, IP4Addr const &rhs) { inline bool operator==(IP4Addr const &lhs, IPAddr const &rhs) { - return rhs.is_ip4() && lhs == static_cast<IP4Addr const &>(rhs); + return rhs.is_ip4() && lhs == rhs.ip4(); } inline bool operator!=(IP4Addr const &lhs, IPAddr const &rhs) { - return !rhs.is_ip4() || lhs != static_cast<IP4Addr const &>(rhs); + return !rhs.is_ip4() || lhs != rhs.ip4(); } inline bool operator==(IPAddr const &lhs, IP6Addr const &rhs) { - return lhs.is_ip6() && static_cast<IP6Addr const &>(lhs) == rhs; + return lhs.is_ip6() && lhs.ip6() == rhs; } inline bool operator!=(IPAddr const &lhs, IP6Addr const &rhs) { - return !lhs.is_ip6() || static_cast<IP6Addr const &>(lhs) != rhs; + return !lhs.is_ip6() || lhs.ip6() != rhs; } inline bool diff --git a/code/include/swoc/IPRange.h b/code/include/swoc/IPRange.h index 6e88705..919cb48 100644 --- a/code/include/swoc/IPRange.h +++ b/code/include/swoc/IPRange.h @@ -797,7 +797,7 @@ public: void clear(); /** Constant iterator. - * THe value type is a tuple of the IP address range and the @a PAYLOAD. Both are constant. + * 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. @@ -871,7 +871,7 @@ public: 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 + // 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. @@ -885,7 +885,7 @@ public: * @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. + * 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); }; @@ -902,62 +902,63 @@ public: friend class IPSpace; - public: + 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<IPRange const, PAYLOAD &>; - using pointer = value_type *; - using reference = value_type &; + using pointer = value_type *; + using reference = value_type &; - /// Default constructor. - iterator() = default; + /// Default constructor. + iterator() = default; - /// Copy constructor. - iterator(self_type const &that); + /// Copy constructor. + iterator(self_type const &that); - /// Assignment. - self_type &operator=(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-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; - } + /// Pre-decrement. + /// Move to the previous element in the list. + /// @return The iterator. + self_type &operator--(); - /// 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; - } + /// 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; + } - /// Dereference. - /// @return A reference to the referent. - value_type operator*() const; + /// 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 pointer to the referent. - value_type const *operator->() const; + /// Dereference. + /// @return A reference to the referent. + value_type operator*() const; - protected: - using super_type::super_type; /// Inherit supertype constructors. + /// Dereference. + /// @return A pointer to the referent. + value_type const *operator->() const; }; /** Find the payload for an @a addr. @@ -965,74 +966,80 @@ public: * @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(); - } + iterator find(IPAddr const &addr); /** 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. + * @return Iterator for the range containing @a addr. */ - iterator - find(IP4Addr const &addr) { - auto spot = _ip4.find(addr); - return spot == _ip4.end() ? this->end() : iterator{_ip4.find(addr), _ip6.begin()}; - } + const_iterator find(IPAddr const &addr) const; /** 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. + * @return An iterator which is valid if @a addr was found, @c end if not. */ - iterator - find(IP6Addr const &addr) { - return {_ip4.end(), _ip6.find(addr)}; - } + iterator find(IP4Addr const &addr); - /// @return A constant iterator to the first element. - const_iterator begin() 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. + */ + const_iterator find(IP4Addr const &addr) const; - /// @return A constent iterator past the last element. - const_iterator end() 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(); - /// Iterator to the first IPv4 address. + /// @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; - /// Iterator past the last IPv4 address. + + /// @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; - 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(); - } + /// @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 { - if (AF_INET == family) { - return this->end_ip4(); - } else if (AF_INET6 == family) { - return this->end_ip6(); - } - return this->end(); - } + end(sa_family_t family) const; protected: IP4Space _ip4; ///< Sub-space containing IPv4 ranges. @@ -1648,55 +1655,102 @@ IPSpace<PAYLOAD>::clear() { } template <typename PAYLOAD> -auto -IPSpace<PAYLOAD>::begin() const -> const_iterator { - auto nc_this = const_cast<self_type *>(this); - return const_iterator(nc_this->_ip4.begin(), nc_this->_ip6.begin()); +auto IPSpace<PAYLOAD>::begin() -> iterator { return iterator{_ip4.begin(), _ip6.begin()}; } + +template <typename PAYLOAD> +auto IPSpace<PAYLOAD>::begin() const -> const_iterator { return const_cast<self_type *>(this)->begin(); } + +template <typename PAYLOAD> +auto IPSpace<PAYLOAD>::end() -> iterator { return iterator{_ip4.end(), _ip6.end()}; } + +template <typename PAYLOAD> +auto IPSpace<PAYLOAD>::end() const -> const_iterator { return const_cast<self_type *>(this)->end(); } + +template <typename PAYLOAD> +auto IPSpace<PAYLOAD>::begin_ip4() -> iterator { return this->begin(); } + +template <typename PAYLOAD> +auto IPSpace<PAYLOAD>::begin_ip4() const -> const_iterator { return this->begin(); } + +template <typename PAYLOAD> +auto IPSpace<PAYLOAD>::end_ip4() -> iterator { return { _ip4.end(), _ip6.begin() }; } + +template <typename PAYLOAD> +auto IPSpace<PAYLOAD>::end_ip4() const -> const_iterator { return const_cast<self_type*>(this)->end_ip4(); } + +template <typename PAYLOAD> +auto IPSpace<PAYLOAD>::begin_ip6() -> iterator { + return { _ip4.end(), _ip6.begin() }; } template <typename PAYLOAD> -auto -IPSpace<PAYLOAD>::end() const -> const_iterator { - auto nc_this = const_cast<self_type *>(this); - return const_iterator(nc_this->_ip4.end(), nc_this->_ip6.end()); +auto IPSpace<PAYLOAD>::begin_ip6() const -> const_iterator { + return const_cast<self_type*>(this)->begin_ip6(); } +template <typename PAYLOAD> +auto IPSpace<PAYLOAD>::end_ip6() -> iterator { return this->end(); } + +template <typename PAYLOAD> +auto IPSpace<PAYLOAD>::end_ip6() const -> const_iterator { return this->end(); } + template <typename PAYLOAD> auto -IPSpace<PAYLOAD>::begin_ip4() const -> const_iterator { - return this->begin(); +IPSpace<PAYLOAD>::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 <typename PAYLOAD> -auto -IPSpace<PAYLOAD>::end_ip4() const -> const_iterator { - auto nc_this = const_cast<self_type *>(this); - return { nc_this->_ip4.end(), nc_this->_ip6.begin() }; +auto IPSpace<PAYLOAD>::find(IPAddr const &addr) const -> const_iterator { + return const_cast<self_type *>(this)->find(addr); } template <typename PAYLOAD> auto -IPSpace<PAYLOAD>::begin_ip6() const -> const_iterator { - auto nc_this = const_cast<self_type *>(this); - return { nc_this->_ip4.end(), nc_this->_ip6.begin() }; +IPSpace<PAYLOAD>::find(IP4Addr const &addr) -> iterator { + if ( auto spot = _ip4.find(addr) ; spot != _ip4.end()) { + return { spot, _ip6.begin() }; + } + return this->end(); } template <typename PAYLOAD> auto -IPSpace<PAYLOAD>::end_ip6() const -> const_iterator { - return this->end(); +IPSpace<PAYLOAD>::find(IP4Addr const &addr) const -> const_iterator { + return const_cast<self_type *>(this)->find(addr); } +template <typename PAYLOAD> +auto IPSpace<PAYLOAD>::find(IP6Addr const &addr) -> iterator { return {_ip4.end(), _ip6.find(addr)}; } + +template <typename PAYLOAD> +auto IPSpace<PAYLOAD>::find(IP6Addr const &addr) const -> const_iterator { return {_ip4.end(), _ip6.find(addr)}; } + template <typename PAYLOAD> auto -IPSpace<PAYLOAD>::begin() -> iterator { - return iterator{_ip4.begin(), _ip6.begin()}; +IPSpace<PAYLOAD>::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 <typename PAYLOAD> auto -IPSpace<PAYLOAD>::end() -> iterator { - return iterator{_ip4.end(), _ip6.end()}; +IPSpace<PAYLOAD>::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 <typename PAYLOAD> diff --git a/code/include/swoc/IPSrv.h b/code/include/swoc/IPSrv.h index 42ce573..e0e4d48 100644 --- a/code/include/swoc/IPSrv.h +++ b/code/include/swoc/IPSrv.h @@ -45,6 +45,14 @@ public: */ 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; @@ -70,6 +78,13 @@ public: 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. @@ -133,6 +148,21 @@ public: */ 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; @@ -205,19 +235,34 @@ public: 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 _srv.addr(_family); } + 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 _family; } + constexpr sa_family_t family() const; /// @return @c true if the data is IPv4, @c false if not. - bool is_ip4() const { return _family == AF_INET; } + bool is_ip4() const; /// @return @c true if hte data is IPv6, @c false if not. - bool is_ip6() const { return _family == AF_INET6; } + bool is_ip6() const; /// @return The IPv4 data. IP4Srv const& ip4() const { return _srv._ip4; } @@ -402,6 +447,11 @@ 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()); @@ -593,4 +643,38 @@ 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/code/include/swoc/TextView.h b/code/include/swoc/TextView.h index 0903a32..e153741 100644 --- a/code/include/swoc/TextView.h +++ b/code/include/swoc/TextView.h @@ -148,7 +148,7 @@ public: /// 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/code/include/swoc/bwf_fwd.h b/code/include/swoc/bwf_fwd.h index f7a9054..59fe601 100644 --- a/code/include/swoc/bwf_fwd.h +++ b/code/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 <size_t N> class LocalBufferWriter; @@ -18,4 +18,4 @@ namespace bwf { struct Spec; class Format; } // namespace bwf -} // namespace SWOC_NAMESPACE +}} // namespace swoc diff --git a/code/src/bw_ip_format.cc b/code/src/bw_ip_format.cc index 1c96b53..ecef729 100644 --- a/code/src/bw_ip_format.cc +++ b/code/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<IP4Addr const &>(addr)); + swoc::bwformat(w, spec, addr.ip4()); } else if (addr.is_ip6()) { swoc::bwformat(w, spec, addr.ip6().network_order()); } else { diff --git a/code/src/swoc_ip.cc b/code/src/swoc_ip.cc index bb4c394..9434221 100644 --- a/code/src/swoc_ip.cc +++ b/code/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<uint8_t *>(&_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<uint8_t>::max()) { - octet[--n] = x; - } else { + in_addr_t max = std::numeric_limits<in_addr_t>::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<uint8_t>::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)) {} @@ -595,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<IP4Addr const &>(addr)); + return self_type::mask_for(addr.ip4()); } else if (addr.is_ip6()) { - return self_type::mask_for(static_cast<IP6Addr const &>(addr)); + return self_type::mask_for(addr.ip6()); } return {}; } @@ -655,6 +671,85 @@ IPMask::as_ip6() const { // --- 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<in_port_t>::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<in_port_t>::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<in_port_t>::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()) { @@ -932,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; } diff --git a/unit_tests/test_Errata.cc b/unit_tests/test_Errata.cc index 3ef3b04..8e04d64 100644 --- a/unit_tests/test_Errata.cc +++ b/unit_tests/test_Errata.cc @@ -40,8 +40,6 @@ void test_Errata_init() { }); } - - Errata Noteworthy(std::string_view text) { diff --git a/unit_tests/test_ip.cc b/unit_tests/test_ip.cc index ae53d67..128c4ba 100644 --- a/unit_tests/test_ip.cc +++ b/unit_tests/test_ip.cc @@ -66,7 +66,7 @@ TEST_CASE("Basic IP", "[libswoc][ip]") { , {{"[ffee::24c3:3349:3cee:0143]"} , {"ffee::24c3:3349:3cee:0143"} , {nullptr}, {nullptr}} , {{"[ffee::24c3:3349:3cee:0143]:80"} , {"ffee::24c3:3349:3cee:0143"} , {"80"} , {nullptr}} , {{"[ffee::24c3:3349:3cee:0143]:8080x"} , {"ffee::24c3:3349:3cee:0143"} , {"8080"} , {"x"}} - ,}; + }; for (auto const&s : names) { std::string_view host, port, rest; @@ -79,26 +79,71 @@ TEST_CASE("Basic IP", "[libswoc][ip]") { IP4Addr alpha{"172.96.12.134"}; CHECK(alpha == IP4Addr{"172.96.12.134"}); - CHECK(alpha == IP4Addr{IPAddr{"172.96.12.134"}}); CHECK(alpha == IPAddr{IPEndpoint{"172.96.12.134:80"}}); CHECK(alpha == IPAddr{IPEndpoint{"172.96.12.134"}}); + REQUIRE(alpha[1] == 96); + REQUIRE(alpha[2] == 12); + REQUIRE(alpha[3] == 134); + + // Alternate forms - inet_aton compabitility. Note in truncated forms, the last value is for + // all remaining octets, those are not zero filled as in IPv6. + CHECK(alpha.load("172.96.12")); + REQUIRE(alpha[0] == 172); + REQUIRE(alpha[2] == 0); + REQUIRE(alpha[3] == 12); + CHECK_FALSE(alpha.load("172.96.71117")); + CHECK(alpha.load("172.96.3136")); + REQUIRE(alpha[0] == 172); + REQUIRE(alpha[2] == 0xC); + REQUIRE(alpha[3] == 0x40); + CHECK(alpha.load("172.12586118")); + REQUIRE(alpha[0] == 172); + REQUIRE(alpha[1] == 192); + REQUIRE(alpha[2] == 12); + REQUIRE(alpha[3] == 134); + CHECK(alpha.load("172.0xD00D56")); + REQUIRE(alpha[0] == 172); + REQUIRE(alpha[1] == 0xD0); + REQUIRE(alpha[2] == 0x0D); + REQUIRE(alpha[3] == 0x56); + CHECK_FALSE(alpha.load("192.172.3.")); + + CHECK(IP6Addr().load("ffee:1f2d:c587:24c3:9128:3349:3cee:143")); IP4Addr lo{"127.0.0.1"}; - REQUIRE(lo.is_loopback() == true); + CHECK(lo.is_loopback()); REQUIRE(lo.is_any() == false); + REQUIRE(lo.is_multicast() == false); + REQUIRE(lo.is_link_local() == false); + REQUIRE(lo[0] == 0x7F); + IP4Addr any{"0.0.0.0"}; REQUIRE(any.is_loopback() == false); REQUIRE(any.is_any() == true); + REQUIRE(lo.is_link_local() == false); + REQUIRE(any == IP4Addr("0")); + IP6Addr lo6{"::1"}; REQUIRE(lo6.is_loopback() == true); REQUIRE(lo6.is_any() == false); REQUIRE(lo6.is_multicast() == false); + REQUIRE(lo.is_link_local() == false); + IP6Addr any6{"::"}; REQUIRE(any6.is_loopback() == false); REQUIRE(any6.is_any() == true); - IP6Addr multi6{"FF02::1"}; + REQUIRE(lo.is_link_local() == false); + + IP6Addr multi6{"FF02::19"}; REQUIRE(multi6.is_loopback() == false); REQUIRE(multi6.is_multicast() == true); + REQUIRE(lo.is_link_local() == false); + REQUIRE(IPAddr(multi6).is_multicast() == true); + + IP6Addr ll{"FE80::56"}; + REQUIRE(ll.is_link_local() == true); + REQUIRE(ll.is_multicast() == false); + REQUIRE(IPAddr(ll).is_link_local() == true); // Do a bit of IPv6 testing. IP6Addr a6_null; @@ -124,6 +169,13 @@ TEST_CASE("Basic IP", "[libswoc][ip]") { REQUIRE(0 == a6_2.cmp(a6_2)); REQUIRE(1 == a6_1.cmp(a6_2)); + REQUIRE(a6_1[0] == 0xFE); + REQUIRE(a6_1[1] == 0x80); + REQUIRE(a6_2[3] == 0xB5); + REQUIRE(a6_3[11] == 0xAE); + REQUIRE(a6_3[14] == 0x1C); + REQUIRE(a6_2[15] == 0x34); + // Little bit of IP4 address arithmetic / comparison testing. IP4Addr a4_null; IP4Addr a4_1{"172.28.56.33"}; @@ -135,6 +187,8 @@ TEST_CASE("Basic IP", "[libswoc][ip]") { REQUIRE(a4_loopback == ip4_loopback); REQUIRE(a4_loopback.is_loopback() == true); REQUIRE(ip4_loopback.is_loopback() == true); + CHECK(a4_2.is_private()); + CHECK_FALSE(a4_3.is_private()); REQUIRE(a4_1 != a4_null); REQUIRE(a4_1 != a4_2); @@ -195,7 +249,10 @@ TEST_CASE("Basic IP", "[libswoc][ip]") { } } + IPRange r; IP4Range r4; + IP6Range r6; + REQUIRE(r4.load("10.242.129.0-10.242.129.127") == true); REQUIRE(r4.min() == IP4Addr("10.242.129.0")); REQUIRE(r4.max() == IP4Addr("10.242.129.127")); @@ -207,6 +264,25 @@ TEST_CASE("Basic IP", "[libswoc][ip]") { REQUIRE(r4.max() == IP4Addr("2.2.2.2")); REQUIRE(r4.load("2.2.2.2.2") == false); REQUIRE(r4.load("2.2.2.2-fe80:20c::29ff:feae:5587::1c33") == false); + CHECK(r4.load("0xC0A83801")); + REQUIRE(r4 == IP4Addr("192.168.56.1")); + + // A few special cases. + static constexpr TextView all_4_txt { "0/0" }; + static constexpr TextView all_6_txt { "::/0" }; + + CHECK(r4.load(all_4_txt)); + CHECK(r.load(all_4_txt)); + REQUIRE(r.ip4() == r4); + REQUIRE(r4.min() == IP4Addr::MIN); + REQUIRE(r4.max() == IP4Addr::MAX); + CHECK(r.load(all_6_txt)); + CHECK(r6.load(all_6_txt)); + REQUIRE(r.ip6() == r6); + REQUIRE(r6.min() == IP6Addr::MIN); + REQUIRE(r6.max() == IP6Addr::MAX); + CHECK_FALSE(r6.load("2.2.2.2-fe80:20c::29ff:feae:5587::1c33")); + CHECK_FALSE(r.load("2.2.2.2-fe80:20c::29ff:feae:5587::1c33")); IPEndpoint ep; ep.set_to_any(AF_INET); @@ -225,11 +301,12 @@ TEST_CASE("Basic IP", "[libswoc][ip]") { REQUIRE(a4.is_loopback() == true); REQUIRE(a4.is_any() == false); - CHECK_FALSE(IP6Addr("1337:0:0:ded:BEEF:0:0:0").is_mapped_ipv4()); - CHECK_FALSE(IP6Addr("1337:0:0:ded:BEEF::").is_mapped_ipv4()); - CHECK(IP6Addr("::FFFF:C0A8:381F").is_mapped_ipv4()); - CHECK_FALSE(IP6Addr("FFFF:C0A8:381F::").is_mapped_ipv4()); - CHECK_FALSE(IP6Addr("::C0A8:381F").is_mapped_ipv4()); + CHECK_FALSE(IP6Addr("1337:0:0:ded:BEEF:0:0:0").is_mapped_ip4()); + CHECK_FALSE(IP6Addr("1337:0:0:ded:BEEF::").is_mapped_ip4()); + CHECK(IP6Addr("::FFFF:C0A8:381F").is_mapped_ip4()); + CHECK_FALSE(IP6Addr("FFFF:C0A8:381F::").is_mapped_ip4()); + CHECK_FALSE(IP6Addr("::C0A8:381F").is_mapped_ip4()); + CHECK(IP6Addr(a4_2).is_mapped_ip4()); }; TEST_CASE("IP Formatting", "[libswoc][ip][bwformat]") { @@ -408,8 +485,8 @@ TEST_CASE("IP ranges and networks", "[libswoc][ip][net][range]") { CHECK(r_5.min() == (r_5.min() | mask)); CHECK(r_5.min() != (r_5.min() & mask)); - swoc::IP6Addr a_1{"2001:1f2d:c587:24c4::"}; - CHECK(a_1 == (a_1 & swoc::IPMask{62})); + swoc::IP6Addr aa_1{"2001:1f2d:c587:24c4::"}; + CHECK(aa_1 == (aa_1 & swoc::IPMask{62})); std::array<swoc::IP4Net, 7> r_4_nets = {{ @@ -627,17 +704,17 @@ TEST_CASE("IP ranges and networks", "[libswoc][ip][net][range]") { }}; auto r5_net = r_5_nets.begin(); - for (auto const&[addr, mask] : r_5.networks()) { + for (auto const&[a, m] : r_5.networks()) { REQUIRE(r5_net != r_5_nets.end()); - CHECK(*r5_net == swoc::IP6Net{addr, mask}); + CHECK(*r5_net == swoc::IP6Net{a, m}); ++r5_net; } // Try it again, using @c IPNet. r5_net = r_5_nets.begin(); - for ( auto const&[addr, mask] : IPRange{r_5}.networks()) { + for ( auto const&[a, m] : IPRange{r_5}.networks()) { REQUIRE(r5_net != r_5_nets.end()); - CHECK(*r5_net == swoc::IPNet{addr, mask}); + CHECK(*r5_net == swoc::IPNet{a, m}); ++r5_net; } } @@ -1765,4 +1842,27 @@ TEST_CASE("IPSrv", "[libswoc][IPSrv]") { IP4Addr tmp3{s4}; REQUIRE(s4 == tmp3); REQUIRE(s4.addr() == tmp3); // double check equality. + + IP4Srv s4_1 { "10.9.8.7:56" }; + REQUIRE(s4_1.host_order_port() == 56); + REQUIRE(s4_1 == a2); + CHECK(s4_1.load("10.2:56")); + CHECK_FALSE(s4_1.load("10.1.2.3.567899")); + CHECK_FALSE(s4_1.load("10.1.2.3.56f")); + CHECK(s4_1.load("10.1.2.3")); + REQUIRE(s4_1.host_order_port() == 0); + + CHECK(s6.load("[ffee:1f2d:c587:24c3:9128:3349:3cee:143]:956")); + REQUIRE(s6 == aa1); + REQUIRE(s6.host_order_port() == 956); + CHECK(s6.load("ffee:1f2d:c587:24c3:9128:3349:3cee:143")); + REQUIRE(s6 == aa1); + REQUIRE(s6.host_order_port() == 0); + + CHECK(s.load("[ffee:1f2d:c587:24c3:9128:3349:3cee:143]:956")); + REQUIRE(s == aa1); + REQUIRE(s.host_order_port() == 956); + CHECK(s.load("ffee:1f2d:c587:24c3:9128:3349:3cee:143")); + REQUIRE(s == aa1); + REQUIRE(s.host_order_port() == 0); }