LCOV - code coverage report
Current view: top level - corosio - ipv6_address.hpp (source / functions) Coverage Total Hit
Test: coverage_remapped.info Lines: 100.0 % 19 19
Test Date: 2026-09-25 21:36:35 Functions: 100.0 % 7 7

           TLA  Line data    Source code
       1                 : //
       2                 : // Copyright (c) 2026 Vinnie Falco (vinnie.falco@gmail.com)
       3                 : // Copyright (c) 2026 Michael Vandeberg
       4                 : //
       5                 : // Distributed under the Boost Software License, Version 1.0. (See accompanying
       6                 : // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
       7                 : //
       8                 : // Official repository: https://github.com/cppalliance/corosio
       9                 : //
      10                 : 
      11                 : #ifndef BOOST_COROSIO_IPV6_ADDRESS_HPP
      12                 : #define BOOST_COROSIO_IPV6_ADDRESS_HPP
      13                 : 
      14                 : #include <boost/corosio/detail/config.hpp>
      15                 : #include <boost/corosio/ipv4_address.hpp>
      16                 : 
      17                 : #include <boost/capy/io_result.hpp>
      18                 : 
      19                 : #include <array>
      20                 : #include <compare>
      21                 : #include <cstdint>
      22                 : #include <functional>
      23                 : #include <iosfwd>
      24                 : #include <string>
      25                 : #include <string_view>
      26                 : #include <system_error>
      27                 : 
      28                 : namespace boost::corosio {
      29                 : 
      30                 : /** An IP version 6 style address.
      31                 : 
      32                 :     Objects of this type are used to construct,
      33                 :     parse, and manipulate IP version 6 addresses.
      34                 : 
      35                 :     @par BNF
      36                 :     @code
      37                 :     IPv6address =                            6( h16 ":" ) ls32
      38                 :                 /                       "::" 5( h16 ":" ) ls32
      39                 :                 / [               h16 ] "::" 4( h16 ":" ) ls32
      40                 :                 / [ *1( h16 ":" ) h16 ] "::" 3( h16 ":" ) ls32
      41                 :                 / [ *2( h16 ":" ) h16 ] "::" 2( h16 ":" ) ls32
      42                 :                 / [ *3( h16 ":" ) h16 ] "::"    h16 ":"   ls32
      43                 :                 / [ *4( h16 ":" ) h16 ] "::"              ls32
      44                 :                 / [ *5( h16 ":" ) h16 ] "::"              h16
      45                 :                 / [ *6( h16 ":" ) h16 ] "::"
      46                 : 
      47                 :     ls32        = ( h16 ":" h16 ) / IPv4address
      48                 :                 ; least-significant 32 bits of address
      49                 : 
      50                 :     h16         = 1*4HEXDIG
      51                 :                 ; 16 bits of address represented in hexadecimal
      52                 : 
      53                 :     IPv6addrz   = IPv6address "%" ZoneID
      54                 :                 ; rfc6874: an address qualified by its zone
      55                 :     @endcode
      56                 : 
      57                 :     The zone accepts a strict decimal interface index on every
      58                 :     platform; where the platform names interfaces (POSIX), an
      59                 :     interface name maps through `if_nametoindex`. An unknown name
      60                 :     or malformed index is a parse error, never a silent zone 0.
      61                 :     Formatting always emits the numeric form (`%2`).
      62                 : 
      63                 :     @par Specification
      64                 :     @li <a href="https://datatracker.ietf.org/doc/html/rfc4291"
      65                 :         >IP Version 6 Addressing Architecture (rfc4291)</a>
      66                 :     @li <a href="https://datatracker.ietf.org/doc/html/rfc3986#section-3.2.2"
      67                 :         >3.2.2. Host (rfc3986)</a>
      68                 : 
      69                 :     @see
      70                 :         @ref ipv4_address,
      71                 :         @ref make_ipv6_address.
      72                 : */
      73                 : class BOOST_COROSIO_DECL ipv6_address
      74                 : {
      75                 :     std::array<unsigned char, 16> addr_{};
      76                 :     std::uint32_t scope_id_ = 0;
      77                 : 
      78                 : public:
      79                 :     /** The number of characters in the longest possible IPv6 string.
      80                 : 
      81                 :         The longest address body is the IPv4-mapped form
      82                 :         `ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255` (45
      83                 :         characters), and a numeric zone suffix adds up to eleven
      84                 :         more (`%4294967295`), for a worst case of 56; the constant
      85                 :         carries a little slack.
      86                 :     */
      87                 :     static constexpr std::size_t max_str_len = 60;
      88                 : 
      89                 :     /** The type used to represent an address as an array of bytes.
      90                 : 
      91                 :         Octets are stored in network byte order.
      92                 :     */
      93                 :     using bytes_type = std::array<unsigned char, 16>;
      94                 : 
      95                 :     /** Default constructor.
      96                 : 
      97                 :         Constructs the unspecified address (::).
      98                 : 
      99                 :         @li <a href="https://datatracker.ietf.org/doc/html/rfc4291#section-2.5.2"
     100                 :             >2.5.2. The Unspecified Address</a>
     101                 : 
     102                 :         @see
     103                 :             @ref is_unspecified
     104                 :     */
     105 HIT      160374 :     ipv6_address() = default;
     106                 : 
     107                 :     /** Copy constructor.
     108                 :     */
     109                 :     ipv6_address(ipv6_address const&) = default;
     110                 : 
     111                 :     /** Copy assignment.
     112                 : 
     113                 :         @return A reference to this object.
     114                 :     */
     115                 :     ipv6_address& operator=(ipv6_address const&) = default;
     116                 : 
     117                 :     /** Construct from an array of bytes.
     118                 : 
     119                 :         This function constructs an address
     120                 :         from the array in `bytes`, which is
     121                 :         interpreted in big-endian.
     122                 : 
     123                 :         @param bytes The value to construct from.
     124                 :         @param scope_id The zone the address belongs to, as an
     125                 :         interface index; 0 means unscoped.
     126                 :     */
     127                 :     explicit ipv6_address(
     128                 :         bytes_type const& bytes, std::uint32_t scope_id = 0) noexcept;
     129                 : 
     130                 :     /** Return the zone the address belongs to.
     131                 : 
     132                 :         Link-local addresses (`fe80::/10`) are unique only per
     133                 :         network link, so the address bits alone do not identify a
     134                 :         destination; the zone — an interface index, written with a
     135                 :         `%` suffix in text form — disambiguates. For global
     136                 :         addresses the zone is 0 and has no meaning.
     137                 : 
     138                 :         @return The zone as an interface index; 0 if unscoped.
     139                 : 
     140                 :         @par Specification
     141                 :         @li <a href="https://datatracker.ietf.org/doc/html/rfc4007"
     142                 :             >IPv6 Scoped Address Architecture (rfc4007)</a>
     143                 :     */
     144             177 :     std::uint32_t scope_id() const noexcept
     145                 :     {
     146             177 :         return scope_id_;
     147                 :     }
     148                 : 
     149                 :     /** Construct from an IPv4 address.
     150                 : 
     151                 :         This function constructs an IPv6 address
     152                 :         from the IPv4 address `addr`. The resulting
     153                 :         address is an IPv4-Mapped IPv6 Address.
     154                 : 
     155                 :         @param addr The address to construct from.
     156                 : 
     157                 :         @par Specification
     158                 :         @li <a href="https://datatracker.ietf.org/doc/html/rfc4291#section-2.5.5.2"
     159                 :             >2.5.5.2. IPv4-Mapped IPv6 Address (rfc4291)</a>
     160                 :     */
     161                 :     explicit ipv6_address(ipv4_address const& addr) noexcept;
     162                 : 
     163                 :     /** Construct from a string.
     164                 : 
     165                 :         This function constructs an address from
     166                 :         the string `s`, which must contain a valid
     167                 :         IPv6 address string or else an exception
     168                 :         is thrown.
     169                 : 
     170                 :         @par Exception Safety
     171                 :         Strong guarantee.
     172                 : 
     173                 :         @throws std::system_error `errc::invalid_argument` if the input
     174                 :         failed to parse correctly.
     175                 : 
     176                 :         @note For a non-throwing parse function,
     177                 :         use @ref make_ipv6_address.
     178                 : 
     179                 :         @param s The string to parse.
     180                 : 
     181                 :         @par Specification
     182                 :         @li <a href="https://datatracker.ietf.org/doc/html/rfc3986#section-3.2.2"
     183                 :             >3.2.2. Host (rfc3986)</a>
     184                 : 
     185                 :         @see
     186                 :             @ref make_ipv6_address.
     187                 :     */
     188                 :     explicit ipv6_address(std::string_view s);
     189                 : 
     190                 :     /** Return the address as bytes, in network byte order.
     191                 : 
     192                 :         The 16 bytes cannot carry the zone: for a scoped address
     193                 :         the result identifies the value only together with
     194                 :         @ref scope_id.
     195                 : 
     196                 :         @return The address as an array of bytes.
     197                 :     */
     198             261 :     bytes_type to_bytes() const noexcept
     199                 :     {
     200             261 :         return addr_;
     201                 :     }
     202                 : 
     203                 :     /** Return the address as a string.
     204                 : 
     205                 :         The returned string does not
     206                 :         contain surrounding square brackets.
     207                 : 
     208                 :         @par Example
     209                 :         @par !example to_string
     210                 : 
     211                 :         @return The address as a string.
     212                 : 
     213                 :         @par Specification
     214                 :         @li <a href="https://datatracker.ietf.org/doc/html/rfc4291#section-2.2">
     215                 :             2.2. Text Representation of Addresses (rfc4291)</a>
     216                 :     */
     217                 :     std::string to_string() const;
     218                 : 
     219                 :     /** Write a string representing the address to a buffer.
     220                 : 
     221                 :         The resulting buffer is not null-terminated.
     222                 : 
     223                 :         @throws std::length_error `dest_size < ipv6_address::max_str_len`
     224                 : 
     225                 :         @param dest The buffer in which to write,
     226                 :         which must have at least `dest_size` space.
     227                 : 
     228                 :         @param dest_size The size of the output buffer.
     229                 : 
     230                 :         @return The formatted string view.
     231                 :     */
     232                 :     std::string_view to_buffer(char* dest, std::size_t dest_size) const;
     233                 : 
     234                 :     /** Return true if the address is unspecified.
     235                 : 
     236                 :         The address 0:0:0:0:0:0:0:0 is called the
     237                 :         unspecified address. It indicates the
     238                 :         absence of an address.
     239                 : 
     240                 :         @return `true` if the address is unspecified.
     241                 : 
     242                 :         @par Specification
     243                 :         @li <a href="https://datatracker.ietf.org/doc/html/rfc4291#section-2.5.2">
     244                 :             2.5.2. The Unspecified Address (rfc4291)</a>
     245                 :     */
     246                 :     bool is_unspecified() const noexcept;
     247                 : 
     248                 :     /** Return true if the address is a loopback address.
     249                 : 
     250                 :         The unicast address 0:0:0:0:0:0:0:1 is called
     251                 :         the loopback address. It may be used by a node
     252                 :         to send an IPv6 packet to itself.
     253                 : 
     254                 :         @return `true` if the address is a loopback address.
     255                 : 
     256                 :         @par Specification
     257                 :         @li <a href="https://datatracker.ietf.org/doc/html/rfc4291#section-2.5.3">
     258                 :             2.5.3. The Loopback Address (rfc4291)</a>
     259                 :     */
     260                 :     bool is_loopback() const noexcept;
     261                 : 
     262                 :     /** Return true if the address is a mapped IPv4 address.
     263                 : 
     264                 :         This address type is used to represent the
     265                 :         addresses of IPv4 nodes as IPv6 addresses.
     266                 : 
     267                 :         @return `true` if the address is a mapped IPv4 address.
     268                 : 
     269                 :         @par Specification
     270                 :         @li <a href="https://datatracker.ietf.org/doc/html/rfc4291#section-2.5.5.2">
     271                 :             2.5.5.2. IPv4-Mapped IPv6 Address (rfc4291)</a>
     272                 :     */
     273                 :     bool is_v4_mapped() const noexcept;
     274                 : 
     275                 :     /** Convert a v4-mapped address to the IPv4 address it maps.
     276                 : 
     277                 :         This is the inverse of the mapping constructor
     278                 :         `ipv6_address(ipv4_address const&)`: it extracts the low
     279                 :         32 bits of an IPv4-Mapped IPv6 Address (`::ffff:a.b.c.d`)
     280                 :         as an `ipv4_address`.
     281                 : 
     282                 :         @throws std::system_error `errc::address_family_not_supported`
     283                 :         if the address is not v4-mapped.
     284                 : 
     285                 :         @return The mapped IPv4 address.
     286                 : 
     287                 :         @par Specification
     288                 :         @li <a href="https://datatracker.ietf.org/doc/html/rfc4291#section-2.5.5.2">
     289                 :             2.5.5.2. IPv4-Mapped IPv6 Address (rfc4291)</a>
     290                 : 
     291                 :         @see
     292                 :             @ref is_v4_mapped.
     293                 :     */
     294                 :     ipv4_address to_v4() const;
     295                 : 
     296                 :     /** Return true if the address is a multicast address.
     297                 : 
     298                 :         IPv6 multicast addresses have the prefix ff00::/8.
     299                 : 
     300                 :         @return `true` if the address is a multicast address.
     301                 : 
     302                 :         @par Specification
     303                 :         @li <a href="https://datatracker.ietf.org/doc/html/rfc4291#section-2.7">
     304                 :             2.7. Multicast Addresses (rfc4291)</a>
     305                 :     */
     306                 :     bool is_multicast() const noexcept;
     307                 : 
     308                 :     /** Return true if two addresses are equal.
     309                 : 
     310                 :         Addresses are equal if they have the same bytes and the
     311                 :         same zone: the same link-local bits on different links are
     312                 :         different destinations.
     313                 : 
     314                 :         @return `true` if the addresses are equal.
     315                 :     */
     316                 :     friend bool
     317              50 :     operator==(ipv6_address const& a1, ipv6_address const& a2) noexcept
     318                 :     {
     319              50 :         return a1.addr_ == a2.addr_ && a1.scope_id_ == a2.scope_id_;
     320                 :     }
     321                 : 
     322                 :     /** Order two addresses.
     323                 : 
     324                 :         Establishes a strict total ordering consistent with
     325                 :         `operator==`: addresses are ordered lexicographically by
     326                 :         their bytes in network order, then by zone. This makes
     327                 :         `ipv6_address` usable as a key in ordered containers such
     328                 :         as `std::map` and `std::set`.
     329                 : 
     330                 :         @return The relative order of `a1` and `a2`.
     331                 :     */
     332                 :     friend std::strong_ordering
     333              17 :     operator<=>(ipv6_address const& a1, ipv6_address const& a2) noexcept
     334                 :     {
     335              17 :         if (auto c = a1.addr_ <=> a2.addr_; c != 0)
     336               6 :             return c;
     337              11 :         return a1.scope_id_ <=> a2.scope_id_;
     338                 :     }
     339                 : 
     340                 :     /** Return an address object that represents the unspecified address.
     341                 : 
     342                 :         The address 0:0:0:0:0:0:0:0 (::) may be used to bind a socket
     343                 :         to all available interfaces.
     344                 : 
     345                 :         @return The unspecified address (::).
     346                 :     */
     347              19 :     static ipv6_address any() noexcept
     348                 :     {
     349              19 :         return ipv6_address();
     350                 :     }
     351                 : 
     352                 :     /** Return an address object that represents the loopback address.
     353                 : 
     354                 :         The unicast address 0:0:0:0:0:0:0:1 is called
     355                 :         the loopback address. It may be used by a node
     356                 :         to send an IPv6 packet to itself.
     357                 : 
     358                 :         @par Specification
     359                 :         @li <a href="https://datatracker.ietf.org/doc/html/rfc4291#section-2.5.3">
     360                 :             2.5.3. The Loopback Address (rfc4291)</a>
     361                 : 
     362                 :         @return The loopback address (::1).
     363                 :     */
     364                 :     static ipv6_address loopback() noexcept;
     365                 : 
     366                 :     /** Format the address to an output stream.
     367                 : 
     368                 :         This function writes the address to an
     369                 :         output stream using standard notation.
     370                 : 
     371                 :         @return The output stream, for chaining.
     372                 : 
     373                 :         @param os The output stream to write to.
     374                 : 
     375                 :         @param addr The address to write.
     376                 :     */
     377                 :     friend BOOST_COROSIO_DECL std::ostream&
     378                 :     operator<<(std::ostream& os, ipv6_address const& addr);
     379                 : 
     380                 : private:
     381                 :     std::size_t print_impl(char* dest) const noexcept;
     382                 : };
     383                 : 
     384                 : /** Create an IPv6 address from a string.
     385                 : 
     386                 :     This function attempts to parse the string
     387                 :     as an IPv6 address and returns an error code
     388                 :     if the string does not contain a valid IPv6 address.
     389                 : 
     390                 :     @par Exception Safety
     391                 :     Throws nothing.
     392                 : 
     393                 :     @param s The string to parse.
     394                 :     @return The error code, empty on success, and the parsed
     395                 :         address — default-constructed on failure.
     396                 : */
     397                 : [[nodiscard]] BOOST_COROSIO_DECL capy::io_result<ipv6_address>
     398                 : make_ipv6_address(std::string_view s) noexcept;
     399                 : 
     400                 : } // namespace boost::corosio
     401                 : 
     402                 : namespace std {
     403                 : 
     404                 : /// Hash support for `boost::corosio::ipv6_address`.
     405                 : template<>
     406                 : struct hash<boost::corosio::ipv6_address>
     407                 : {
     408                 :     /// Return the hash of `addr`.
     409                 :     std::size_t
     410              21 :     operator()(boost::corosio::ipv6_address const& addr) const noexcept
     411                 :     {
     412              21 :         auto const bytes = addr.to_bytes();
     413              21 :         auto const h     = hash<std::string_view>()(std::string_view(
     414              21 :             reinterpret_cast<char const*>(bytes.data()), bytes.size()));
     415                 :         // The zone participates in equality, so it must feed the
     416                 :         // hash; combine so it cannot cancel the byte entropy
     417              21 :         auto const z = hash<std::uint32_t>()(addr.scope_id());
     418              21 :         return h ^ (z + 0x9e3779b9u + (h << 6) + (h >> 2));
     419                 :     }
     420                 : };
     421                 : 
     422                 : } // namespace std
     423                 : 
     424                 : #endif
        

Generated by: LCOV version 2.3