include/boost/corosio/ipv6_address.hpp

100.0% Lines (19 / 19) 100.0% Functions (7 / 7)
ipv6_address.hpp
f(x) Functions (7)
Line TLA Hits 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 160374x 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 177x std::uint32_t scope_id() const noexcept
145 {
146 177x 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 261x bytes_type to_bytes() const noexcept
199 {
200 261x 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 50x operator==(ipv6_address const& a1, ipv6_address const& a2) noexcept
318 {
319 50x 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 17x operator<=>(ipv6_address const& a1, ipv6_address const& a2) noexcept
334 {
335 17x if (auto c = a1.addr_ <=> a2.addr_; c != 0)
336 6x return c;
337 11x 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 19x static ipv6_address any() noexcept
348 {
349 19x 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 21x operator()(boost::corosio::ipv6_address const& addr) const noexcept
411 {
412 21x auto const bytes = addr.to_bytes();
413 21x auto const h = hash<std::string_view>()(std::string_view(
414 21x 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 21x auto const z = hash<std::uint32_t>()(addr.scope_id());
418 21x return h ^ (z + 0x9e3779b9u + (h << 6) + (h >> 2));
419 }
420 };
421
422 } // namespace std
423
424 #endif
425