LCOV - code coverage report
Current view: top level - corosio - ip_address.hpp (source / functions) Coverage Total Hit
Test: coverage_remapped.info Lines: 100.0 % 48 48
Test Date: 2026-10-21 21:04:24 Functions: 100.0 % 17 17

           TLA  Line data    Source code
       1                 : //
       2                 : // Copyright (c) 2026 Vinnie Falco (vinnie.falco@gmail.com)
       3                 : // Copyright (c) 2026 Steve Gerbino
       4                 : // Copyright (c) 2026 Michael Vandeberg
       5                 : //
       6                 : // Distributed under the Boost Software License, Version 1.0. (See accompanying
       7                 : // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
       8                 : //
       9                 : // Official repository: https://github.com/cppalliance/corosio
      10                 : //
      11                 : 
      12                 : #ifndef BOOST_COROSIO_IP_ADDRESS_HPP
      13                 : #define BOOST_COROSIO_IP_ADDRESS_HPP
      14                 : 
      15                 : #include <boost/corosio/detail/config.hpp>
      16                 : #include <boost/corosio/detail/except.hpp>
      17                 : #include <boost/corosio/family.hpp>
      18                 : #include <boost/corosio/ipv4_address.hpp>
      19                 : #include <boost/corosio/ipv6_address.hpp>
      20                 : 
      21                 : #include <boost/capy/results.hpp>
      22                 : 
      23                 : #include <compare>
      24                 : #include <iosfwd>
      25                 : #include <string>
      26                 : #include <string_view>
      27                 : #include <system_error>
      28                 : 
      29                 : namespace boost::corosio {
      30                 : 
      31                 : /** A version-independent IP address.
      32                 : 
      33                 :     This class holds either an IPv4 or an IPv6 address. Code that works with
      34                 :     both families carries one value instead of branching between @ref
      35                 :     ipv4_address and @ref ipv6_address. Family-generic queries such as
      36                 :     @ref is_loopback dispatch to the held address, and @ref to_v4 /
      37                 :     @ref to_v6 recover the family-specific form.
      38                 : 
      39                 :     A v4-mapped IPv6 address (`::ffff:a.b.c.d`) is an IPv6-family
      40                 :     value: it does not compare equal to the IPv4 address it maps.
      41                 :     To compare across the mapping, normalize both sides with
      42                 :     @ref to_v4 first.
      43                 : 
      44                 :     @par Thread Safety
      45                 :     Distinct objects: Safe.@n
      46                 :     Shared objects: Safe.
      47                 : 
      48                 :     @par Example
      49                 :     @code
      50                 :     ip_address addr("2001:db8::1");
      51                 :     if (addr.is_loopback())
      52                 :     {
      53                 :         // family-generic query, no branching
      54                 :     }
      55                 :     @endcode
      56                 : 
      57                 :     @see
      58                 :         @ref ipv4_address,
      59                 :         @ref ipv6_address,
      60                 :         @ref make_ip_address
      61                 : */
      62                 : class BOOST_COROSIO_DECL ip_address
      63                 : {
      64                 :     ipv4_address v4_;
      65                 :     ipv6_address v6_;
      66                 :     corosio::family family_ = corosio::family::v4;
      67                 : 
      68                 : public:
      69                 :     /** The number of characters in the longest possible address string.
      70                 :     */
      71                 :     static constexpr std::size_t max_str_len = ipv6_address::max_str_len;
      72                 : 
      73                 :     /** Default constructor.
      74                 : 
      75                 :         Constructs the IPv4 unspecified address (0.0.0.0).
      76                 :     */
      77 HIT      258834 :     ip_address() = default;
      78                 : 
      79                 :     /** Copy constructor.
      80                 :     */
      81                 :     ip_address(ip_address const&) = default;
      82                 : 
      83                 :     /** Copy assignment.
      84                 : 
      85                 :         @return A reference to this object.
      86                 :     */
      87                 :     ip_address& operator=(ip_address const&) = default;
      88                 : 
      89                 :     /** Construct from an IPv4 address.
      90                 : 
      91                 :         @param addr The address to hold.
      92                 :     */
      93           34272 :     ip_address(ipv4_address const& addr) noexcept : v4_(addr) {}
      94                 : 
      95                 :     /** Construct from an IPv6 address.
      96                 : 
      97                 :         @param addr The address to hold.
      98                 :     */
      99             227 :     ip_address(ipv6_address const& addr) noexcept
     100             454 :         : v6_(addr)
     101             227 :         , family_(corosio::family::v6)
     102                 :     {
     103             227 :     }
     104                 : 
     105                 :     /** Construct from a string.
     106                 : 
     107                 :         This function constructs an address from the string `s`,
     108                 :         which must contain a valid IPv4 or IPv6 address string
     109                 :         or else an exception is thrown.
     110                 : 
     111                 :         @par Exception Safety
     112                 :         Strong guarantee.
     113                 : 
     114                 :         @throws std::system_error `errc::invalid_argument` if the input
     115                 :         failed to parse correctly.
     116                 : 
     117                 :         @note For a non-throwing parse function,
     118                 :         use @ref make_ip_address.
     119                 : 
     120                 :         @param s The string to parse.
     121                 : 
     122                 :         @see
     123                 :             @ref make_ip_address
     124                 :     */
     125                 :     explicit ip_address(std::string_view s);
     126                 : 
     127                 :     /** Return the address family.
     128                 : 
     129                 :         The portable spelling of the family; @ref is_v4 and
     130                 :         @ref is_v6 are sugar over it.
     131                 : 
     132                 :         @return The family of the held address.
     133                 :     */
     134             637 :     corosio::family family() const noexcept
     135                 :     {
     136             637 :         return family_;
     137                 :     }
     138                 : 
     139                 :     /** Check if the held address is IPv4.
     140                 : 
     141                 :         @return `true` if the address is IPv4, `false` if IPv6.
     142                 :     */
     143           28936 :     bool is_v4() const noexcept
     144                 :     {
     145           28936 :         return family_ == corosio::family::v4;
     146                 :     }
     147                 : 
     148                 :     /** Check if the held address is IPv6.
     149                 : 
     150                 :         @return `true` if the address is IPv6, `false` if IPv4.
     151                 :     */
     152              69 :     bool is_v6() const noexcept
     153                 :     {
     154              69 :         return family_ == corosio::family::v6;
     155                 :     }
     156                 : 
     157                 :     /** Check if the address is a loopback address.
     158                 : 
     159                 :         @return `true` if the held address is a loopback
     160                 :         address of its family.
     161                 :     */
     162              13 :     bool is_loopback() const noexcept
     163                 :     {
     164              13 :         return is_v4() ? v4_.is_loopback() : v6_.is_loopback();
     165                 :     }
     166                 : 
     167                 :     /** Check if the address is unspecified.
     168                 : 
     169                 :         @return `true` if the held address is the unspecified
     170                 :         address of its family.
     171                 :     */
     172               6 :     bool is_unspecified() const noexcept
     173                 :     {
     174               6 :         return is_v4() ? v4_.is_unspecified() : v6_.is_unspecified();
     175                 :     }
     176                 : 
     177                 :     /** Check if the address is a multicast address.
     178                 : 
     179                 :         @return `true` if the held address is a multicast
     180                 :         address of its family.
     181                 :     */
     182               4 :     bool is_multicast() const noexcept
     183                 :     {
     184               4 :         return is_v4() ? v4_.is_multicast() : v6_.is_multicast();
     185                 :     }
     186                 : 
     187                 :     /** Check if the address is a v4-mapped IPv6 address.
     188                 : 
     189                 :         @return `true` if the address is IPv6 and is an
     190                 :         IPv4-Mapped IPv6 Address (`::ffff:a.b.c.d`).
     191                 : 
     192                 :         @see
     193                 :             @ref to_v4
     194                 :     */
     195               3 :     bool is_v4_mapped() const noexcept
     196                 :     {
     197               3 :         return is_v6() && v6_.is_v4_mapped();
     198                 :     }
     199                 : 
     200                 :     /** Convert to an IPv4 address.
     201                 : 
     202                 :         Returns the held IPv4 address, or the IPv4 address that a
     203                 :         v4-mapped IPv6 address maps. This makes normalize-then-compare
     204                 :         a single call when matching addresses across the mapping.
     205                 : 
     206                 :         @throws std::system_error `errc::address_family_not_supported`
     207                 :         if the address is IPv6 and not v4-mapped.
     208                 : 
     209                 :         @return The IPv4 form of the address.
     210                 : 
     211                 :         @see
     212                 :             @ref is_v4, @ref is_v4_mapped
     213                 :     */
     214           10261 :     ipv4_address to_v4() const
     215                 :     {
     216           10261 :         return is_v4() ? v4_ : v6_.to_v4();
     217                 :     }
     218                 : 
     219                 :     /** Convert to an IPv6 address.
     220                 : 
     221                 :         To map an IPv4 address into IPv6, use the
     222                 :         `ipv6_address(ipv4_address const&)` constructor instead.
     223                 : 
     224                 :         @throws std::system_error `errc::address_family_not_supported`
     225                 :         if the address is IPv4.
     226                 : 
     227                 :         @return The held IPv6 address.
     228                 : 
     229                 :         @see
     230                 :             @ref is_v6
     231                 :     */
     232              89 :     ipv6_address to_v6() const
     233                 :     {
     234              89 :         if (is_v4())
     235               2 :             detail::throw_system_error(
     236               2 :                 std::make_error_code(std::errc::address_family_not_supported),
     237                 :                 "address is not IPv6");
     238              87 :         return v6_;
     239                 :     }
     240                 : 
     241                 :     /** Return the address as a string.
     242                 : 
     243                 :         IPv4 addresses format in dotted decimal, IPv6 addresses
     244                 :         in standard notation without surrounding brackets.
     245                 : 
     246                 :         @return The address as a string.
     247                 :     */
     248              13 :     std::string to_string() const
     249                 :     {
     250              13 :         return is_v4() ? v4_.to_string() : v6_.to_string();
     251                 :     }
     252                 : 
     253                 :     /** Write a string representing the address to a buffer.
     254                 : 
     255                 :         The resulting buffer is not null-terminated.
     256                 : 
     257                 :         @throws std::length_error `dest_size < ip_address::max_str_len`
     258                 : 
     259                 :         @param dest The buffer in which to write,
     260                 :         which must have at least `dest_size` space.
     261                 : 
     262                 :         @param dest_size The size of the output buffer.
     263                 : 
     264                 :         @return The formatted string view.
     265                 :     */
     266                 :     std::string_view to_buffer(char* dest, std::size_t dest_size) const;
     267                 : 
     268                 :     /** Return true if two addresses are equal.
     269                 : 
     270                 :         Addresses are equal if they have the same family and the
     271                 :         same value. A v4-mapped IPv6 address is not equal to the
     272                 :         IPv4 address it maps; normalize with @ref ip_address::to_v4
     273                 :         to compare
     274                 :         across the mapping.
     275                 : 
     276                 :         @return `true` if the addresses are equal.
     277                 :     */
     278             143 :     friend bool operator==(ip_address const& a1, ip_address const& a2) noexcept
     279                 :     {
     280             143 :         if (a1.family_ != a2.family_)
     281               8 :             return false;
     282             135 :         return a1.is_v4() ? a1.v4_ == a2.v4_ : a1.v6_ == a2.v6_;
     283                 :     }
     284                 : 
     285                 :     /** Order two addresses.
     286                 : 
     287                 :         Establishes a strict total ordering consistent with
     288                 :         @ref operator==: addresses are ordered first by family
     289                 :         (IPv4 before IPv6), then by value. This makes `ip_address`
     290                 :         usable as a key in ordered containers such as `std::map`
     291                 :         and `std::set`.
     292                 : 
     293                 :         @return The relative order of `a1` and `a2`.
     294                 :     */
     295                 :     friend std::strong_ordering
     296              33 :     operator<=>(ip_address const& a1, ip_address const& a2) noexcept
     297                 :     {
     298              33 :         if (a1.family_ != a2.family_)
     299              11 :             return a1.is_v4() ? std::strong_ordering::less
     300              11 :                               : std::strong_ordering::greater;
     301              22 :         return a1.is_v4() ? a1.v4_ <=> a2.v4_ : a1.v6_ <=> a2.v6_;
     302                 :     }
     303                 : 
     304                 :     /** Format the address to an output stream.
     305                 : 
     306                 :         @param os The output stream.
     307                 :         @param addr The address to format.
     308                 :         @return The output stream.
     309                 :     */
     310                 :     friend BOOST_COROSIO_DECL std::ostream&
     311                 :     operator<<(std::ostream& os, ip_address const& addr);
     312                 : };
     313                 : 
     314                 : /** Create an IP address from a string.
     315                 : 
     316                 :     This function parses `s` as an IPv4 address in dotted decimal form, or
     317                 :     an IPv6 address in hexadecimal notation. An IPv6 address may carry a
     318                 :     `%zone` suffix: a decimal interface index, or an interface name where
     319                 :     the platform names interfaces. The string must contain the address
     320                 :     alone: port suffixes, surrounding brackets, and host names are not
     321                 :     accepted.
     322                 : 
     323                 :     @par Exception Safety
     324                 :     Throws nothing.
     325                 : 
     326                 :     @param s The string to parse.
     327                 :     @return The error code, empty on success, and the parsed
     328                 :         address — default-constructed on failure.
     329                 : */
     330                 : [[nodiscard]] BOOST_COROSIO_DECL capy::results<ip_address>
     331                 : make_ip_address(std::string_view s) noexcept;
     332                 : 
     333              25 : inline ip_address::ip_address(std::string_view s)
     334                 : {
     335              25 :     auto [ec, addr] = make_ip_address(s);
     336              25 :     if (ec)
     337               2 :         detail::throw_system_error(ec, "invalid IP address");
     338              23 :     *this = addr;
     339              23 : }
     340                 : 
     341                 : } // namespace boost::corosio
     342                 : 
     343                 : namespace std {
     344                 : 
     345                 : /// Hash support for `boost::corosio::ip_address`.
     346                 : template<>
     347                 : struct hash<boost::corosio::ip_address>
     348                 : {
     349                 :     /// Return the hash of `addr`.
     350                 :     std::size_t
     351              25 :     operator()(boost::corosio::ip_address const& addr) const noexcept
     352                 :     {
     353                 :         // Family-guarded dispatch keeps the throwing conversions
     354                 :         // unreachable
     355              25 :         return addr.is_v4()
     356              25 :             ? hash<boost::corosio::ipv4_address>()(addr.to_v4())
     357              25 :             : hash<boost::corosio::ipv6_address>()(addr.to_v6());
     358                 :     }
     359                 : };
     360                 : 
     361                 : } // namespace std
     362                 : 
     363                 : #endif
        

Generated by: LCOV version 2.3