Botan 3.13.0
Crypto and TLS for C&
Botan::IPv6Address Class Referencefinal

#include <ipv6_address.h>

Public Member Functions

std::array< uint8_t, 16 > address () const
std::optional< IPv4Addressas_ipv4 () const
 IPv6Address (std::array< uint8_t, 16 > ip)
 IPv6Address (std::span< const uint8_t, 16 > ip)
IPv6Address operator& (const IPv6Address &other) const
auto operator<=> (const IPv6Address &) const =default
std::optional< size_t > prefix_length () const
std::string to_string () const

Static Public Member Functions

static std::optional< IPv6Addressfrom_string (std::string_view str)
static IPv6Address host_mask ()
static IPv6Address netmask (size_t bits)

Detailed Description

IPv6 Address

Definition at line 25 of file ipv6_address.h.

Constructor & Destructor Documentation

◆ IPv6Address() [1/2]

Botan::IPv6Address::IPv6Address ( std::span< const uint8_t, 16 > ip)
explicit

Create an address from its 16 byte big-endian encoding

Parameters
ipthe bytes of the address

Definition at line 17 of file ipv6_address.cpp.

17 : m_ip{} {
18 for(size_t i = 0; i != 16; ++i) {
19 m_ip[i] = ip[i];
20 }
21}

Referenced by host_mask(), netmask(), operator&(), and operator<=>().

◆ IPv6Address() [2/2]

Botan::IPv6Address::IPv6Address ( std::array< uint8_t, 16 > ip)
inlineexplicit

Create an address from its 16 byte big-endian encoding

Parameters
ipthe bytes of the address

Definition at line 37 of file ipv6_address.h.

37: m_ip(ip) {}

Member Function Documentation

◆ address()

std::array< uint8_t, 16 > Botan::IPv6Address::address ( ) const
inline

Access the raw bytes of the address

Returns
the 16 byte big-endian encoding of the address

Definition at line 79 of file ipv6_address.h.

79{ return m_ip; }

Referenced by Botan::IPv6Subnet::serialize().

◆ as_ipv4()

std::optional< IPv4Address > Botan::IPv6Address::as_ipv4 ( ) const

If this IPv6 address is an IPv4-compatible IPv6 address (RFC 4291 2.5.5.1) or an IPv4-mapped IPv6 address (RFC 4291 2.5.5.2), return the embedded IPv4 address.

Definition at line 283 of file ipv6_address.cpp.

283 {
284 const uint32_t ip0 = load_be<uint32_t>(m_ip.data(), 0);
285 const uint32_t ip1 = load_be<uint32_t>(m_ip.data(), 1);
286 const uint32_t ip2 = load_be<uint32_t>(m_ip.data(), 2);
287 const uint32_t ip3 = load_be<uint32_t>(m_ip.data(), 3);
288
289 if(ip0 == 0x00000000 && ip1 == 0x00000000 && (ip2 == 0x00000000 || ip2 == 0x0000FFFF)) {
290 return IPv4Address(ip3);
291 } else {
292 return {};
293 }
294}
constexpr auto load_be(ParamTs &&... params)
Definition loadstor.h:504

References as_ipv4(), and Botan::load_be().

Referenced by as_ipv4().

◆ from_string()

std::optional< IPv6Address > Botan::IPv6Address::from_string ( std::string_view str)
static

Convert a string representation of an IPv6 address to IPv6Address.

Accepts the full form (eight colon-separated hex groups), the "::"-compressed form (exactly one run of zero groups elided), and combinations such as "2001:db8::1". The final 32 bits may be given in IPv4 dotted-decimal form (e.g. "::ffff:192.0.2.1"). Surrounding brackets and zone identifiers are not accepted.

Definition at line 24 of file ipv6_address.cpp.

24 {
25 if(str.empty()) {
26 return {};
27 }
28
29 // Parsed hex groups, split by whether they appeared before or after a "::".
30 // If no "::" appears, only `pre` is populated and must reach exactly 8 groups.
31 std::array<uint16_t, 8> pre{};
32 std::array<uint16_t, 8> post{};
33 size_t pre_count = 0;
34 size_t post_count = 0;
35 bool seen_double_colon = false;
36
37 auto hex_value = [](char c) -> std::optional<uint8_t> {
38 if(c >= '0' && c <= '9') {
39 return c - '0';
40 } else if(c >= 'a' && c <= 'f') {
41 return 10 + (c - 'a');
42 } else if(c >= 'A' && c <= 'F') {
43 return 10 + (c - 'A');
44 } else {
45 return {};
46 }
47 };
48
49 size_t idx = 0;
50 bool expect_group = true; // set after any separator, cleared after a group
51
52 while(idx < str.size()) {
53 if(str[idx] == ':') {
54 if(idx + 1 < str.size() && str[idx + 1] == ':') {
55 if(seen_double_colon) {
56 return {}; // at most one "::"
57 }
58 seen_double_colon = true;
59 idx += 2;
60 expect_group = (idx < str.size());
61 continue;
62 }
63 // single ':' separator between groups, only valid after a group
64 if(expect_group) {
65 return {};
66 }
67 expect_group = true;
68 idx += 1;
69 continue;
70 }
71
72 // Parse a hex group of 1..4 digits
73 const size_t group_start = idx;
74 uint32_t group = 0;
75 size_t hex_chars = 0;
76 while(idx < str.size() && hex_chars < 4) {
77 const auto digit = hex_value(str[idx]);
78 if(digit.has_value() == false) {
79 break;
80 }
81 group = (group << 4) | static_cast<uint32_t>(digit.value());
82 idx += 1;
83 hex_chars += 1;
84 }
85 if(hex_chars == 0) {
86 return {};
87 }
88 // If a 5th hex digit follows, the group is oversized.
89 if(hex_chars == 4 && idx < str.size() && hex_value(str[idx]).has_value()) {
90 return {};
91 }
92
93 /*
94 RFC 4291 2.2 allows the final 32 bits in dotted decimal, eg
95 "::ffff:1.2.3.4". The dotted quad must consume the remainder of the
96 input, and accounts for two 16-bit groups.
97 */
98 if(idx < str.size() && str[idx] == '.') {
99 const auto ipv4 = IPv4Address::from_string(str.substr(group_start));
100 if(!ipv4.has_value()) {
101 return {};
102 }
103 const uint32_t v4 = ipv4->address();
104 const std::array<uint16_t, 2> v4_groups{static_cast<uint16_t>(v4 >> 16), static_cast<uint16_t>(v4 & 0xFFFF)};
105 for(const auto g : v4_groups) {
106 if(seen_double_colon) {
107 if(post_count >= 8) {
108 return {};
109 }
110 post[post_count++] = g;
111 } else {
112 if(pre_count >= 8) {
113 return {};
114 }
115 pre[pre_count++] = g;
116 }
117 }
118 idx = str.size();
119 expect_group = false;
120 continue;
121 }
122
123 if(seen_double_colon) {
124 if(post_count >= 8) {
125 return {};
126 }
127 post[post_count++] = static_cast<uint16_t>(group);
128 } else {
129 if(pre_count >= 8) {
130 return {};
131 }
132 pre[pre_count++] = static_cast<uint16_t>(group);
133 }
134 expect_group = false;
135 }
136
137 // Trailing single ':' is invalid
138 if(expect_group) {
139 return {};
140 }
141
142 const size_t total_groups = pre_count + post_count;
143 if(seen_double_colon) {
144 // "::" has to cover at least one zero group
145 if(total_groups > 7) {
146 return {};
147 }
148 } else {
149 if(total_groups != 8) {
150 return {};
151 }
152 }
153
154 std::array<uint8_t, 16> out{};
155 for(size_t i = 0; i != pre_count; ++i) {
156 out[2 * i] = get_byte<0>(pre[i]);
157 out[2 * i + 1] = get_byte<1>(pre[i]);
158 }
159 const size_t gap = 8 - total_groups;
160 for(size_t i = 0; i != post_count; ++i) {
161 const size_t target = pre_count + gap + i;
162 out[2 * target] = get_byte<0>(post[i]);
163 out[2 * target + 1] = get_byte<1>(post[i]);
164 }
165 return IPv6Address(out);
166}
IPv6Address(std::span< const uint8_t, 16 > ip)
constexpr uint8_t get_byte(T input)
Definition loadstor.h:79

Referenced by Botan::IPv6Subnet::from_string(), Botan::URI::Authority::from_string(), Botan::TLS::Server_Name_Indicator::hostname_acceptable_for_sni(), and Botan::X509_Certificate::matches_dns_name().

◆ host_mask()

IPv6Address Botan::IPv6Address::host_mask ( )
inlinestatic

Return the netmask matching a single host

Returns
an address with all 128 bits set

Definition at line 60 of file ipv6_address.h.

60{ return netmask(128); }
static IPv6Address netmask(size_t bits)

References IPv6Address(), and netmask().

◆ netmask()

IPv6Address Botan::IPv6Address::netmask ( size_t bits)
static

Return an address with the leading bits set to one and the remainder zero. Throws Invalid_Argument if bits > 128.

Definition at line 169 of file ipv6_address.cpp.

169 {
170 BOTAN_ARG_CHECK(bits <= 128, "IPv6 netmask prefix length must be at most 128");
171
172 const size_t full_bytes = bits / 8;
173 const size_t leftover = bits % 8;
174
175 std::array<uint8_t, 16> m{};
176 for(size_t i = 0; i != full_bytes; ++i) {
177 m[i] = 0xFF;
178 }
179
180 if(leftover > 0) {
181 m[full_bytes] = static_cast<uint8_t>(0xFF << (8 - leftover));
182 }
183
184 return IPv6Address(m);
185}
#define BOTAN_ARG_CHECK(expr, msg)
Definition assert.h:33

References BOTAN_ARG_CHECK, IPv6Address(), and netmask().

Referenced by Botan::IPv6Subnet::contains(), Botan::GeneralName::encode_into(), host_mask(), netmask(), prefix_length(), and Botan::IPv6Subnet::serialize().

◆ operator&()

IPv6Address Botan::IPv6Address::operator& ( const IPv6Address & other) const

Bitwise AND of two addresses, typically used to apply a netmask

Parameters
otherthe address to AND with
Returns
the bitwise AND of the two addresses

Definition at line 257 of file ipv6_address.cpp.

257 {
258 std::array<uint8_t, 16> masked{};
259 for(size_t i = 0; i != 16; ++i) {
260 masked[i] = m_ip[i] & other.m_ip[i];
261 }
262 return IPv6Address(masked);
263}

References IPv6Address(), and operator&().

Referenced by operator&().

◆ operator<=>()

auto Botan::IPv6Address::operator<=> ( const IPv6Address & ) const
default

Order two addresses numerically

Returns
the ordering of this address relative to the other

References IPv6Address().

◆ prefix_length()

std::optional< size_t > Botan::IPv6Address::prefix_length ( ) const

If this value is a netmask consisting of a run of one bits followed by a run of zero bits, return the number of one bits.

Otherwise return nullopt.

Definition at line 265 of file ipv6_address.cpp.

265 {
266 // Count leading one bits, stopping at the first byte that isn't fully set.
267 size_t leading = 0;
268 for(size_t i = 0; i != 16; ++i) {
269 const size_t hw = (m_ip[i] == 0xFF) ? 8 : std::countl_one(m_ip[i]);
270 leading += hw;
271 if(hw != 8) {
272 break;
273 }
274 }
275
276 // Verify this is exactly equal to a netmask of that size
277 if(*this != netmask(leading)) {
278 return std::nullopt;
279 }
280 return leading;
281}

References netmask(), and prefix_length().

Referenced by prefix_length().

◆ to_string()

std::string Botan::IPv6Address::to_string ( ) const

Convert an IPv6 address to the RFC 5952 canonical text form: lowercase hex, leading zeros within a group suppressed, and the longest run of two or more zero groups compressed to "::". The mixed hex/dotted notation is never produced, even for IPv4-mapped addresses.

Definition at line 187 of file ipv6_address.cpp.

187 {
188 static const char* hex = "0123456789abcdef";
189
190 std::array<uint16_t, 8> groups{};
191 for(size_t i = 0; i != 8; ++i) {
192 groups[i] = make_uint16(m_ip[2 * i], m_ip[2 * i + 1]);
193 }
194
195 /*
196 Find the run of zero groups to elide with "::", per RFC 5952 4.2:
197 "The use of the symbol '::' MUST be used to its maximum capability",
198 "The symbol '::' MUST NOT be used to shorten just one 16-bit 0 field",
199 and on ties "the first sequence of zero bits MUST be shortened".
200 */
201 size_t best_start = 0;
202 size_t best_len = 0;
203 size_t run_len = 0;
204 for(size_t i = 0; i != 8; ++i) {
205 if(groups[i] == 0) {
206 run_len += 1;
207 if(run_len > best_len) {
208 best_len = run_len;
209 best_start = i + 1 - run_len;
210 }
211 } else {
212 run_len = 0;
213 }
214 }
215
216 std::string out;
217 out.reserve(39);
218
219 auto append_group = [&](uint16_t group) {
220 bool started = false;
221 // Write each nibble omitting leading 0s
222 for(int s = 12; s >= 0; s -= 4) {
223 const auto nibble = (group >> s) & 0xF;
224 if(nibble != 0 || started || s == 0) {
225 out.push_back(hex[nibble]);
226 started = true;
227 }
228 }
229 };
230
231 if(best_len < 2) {
232 // No run of two or more zero groups; write the full form
233 for(size_t i = 0; i != 8; ++i) {
234 if(i > 0) {
235 out.push_back(':');
236 }
237 append_group(groups[i]);
238 }
239 } else {
240 for(size_t i = 0; i != best_start; ++i) {
241 if(i > 0) {
242 out.push_back(':');
243 }
244 append_group(groups[i]);
245 }
246 out += "::";
247 for(size_t i = best_start + best_len; i != 8; ++i) {
248 if(i > best_start + best_len) {
249 out.push_back(':');
250 }
251 append_group(groups[i]);
252 }
253 }
254 return out;
255}
constexpr uint16_t make_uint16(uint8_t i0, uint8_t i1)
Definition loadstor.h:92

References Botan::make_uint16(), and to_string().

Referenced by to_string().


The documentation for this class was generated from the following files: