Line data Source code
1 : //
2 : // Copyright (c) 2026 Vinnie Falco (vinnie dot falco at gmail dot com)
3 : //
4 : // Distributed under the Boost Software License, Version 1.0. (See accompanying
5 : // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
6 : //
7 : // Official repository: https://github.com/cppalliance/corosio
8 : //
9 :
10 : #include <boost/corosio/ipv4_address.hpp>
11 :
12 : #include <ostream>
13 : #include <stdexcept>
14 :
15 : namespace boost::corosio {
16 :
17 4847 : ipv4_address::ipv4_address(uint_type u) noexcept
18 4847 : : addr_(u)
19 : {
20 4847 : }
21 :
22 14305 : ipv4_address::ipv4_address(bytes_type const& bytes) noexcept
23 : {
24 14305 : addr_ =
25 14305 : (static_cast<std::uint32_t>(bytes[0]) << 24) |
26 14305 : (static_cast<std::uint32_t>(bytes[1]) << 16) |
27 14305 : (static_cast<std::uint32_t>(bytes[2]) << 8) |
28 14305 : (static_cast<std::uint32_t>(bytes[3]));
29 14305 : }
30 :
31 4 : ipv4_address::ipv4_address(std::string_view s)
32 : {
33 4 : auto ec = parse_ipv4_address(s, *this);
34 4 : if (ec)
35 3 : throw std::invalid_argument("invalid IPv4 address");
36 1 : }
37 :
38 : auto
39 4846 : ipv4_address::to_bytes() const noexcept -> bytes_type
40 : {
41 : bytes_type bytes;
42 4846 : bytes[0] = static_cast<unsigned char>((addr_ >> 24) & 0xff);
43 4846 : bytes[1] = static_cast<unsigned char>((addr_ >> 16) & 0xff);
44 4846 : bytes[2] = static_cast<unsigned char>((addr_ >> 8) & 0xff);
45 4846 : bytes[3] = static_cast<unsigned char>( addr_ & 0xff);
46 4846 : return bytes;
47 : }
48 :
49 : auto
50 10 : ipv4_address::to_uint() const noexcept -> uint_type
51 : {
52 10 : return addr_;
53 : }
54 :
55 : std::string
56 11 : ipv4_address::to_string() const
57 : {
58 : char buf[max_str_len];
59 11 : auto n = print_impl(buf);
60 22 : return std::string(buf, n);
61 : }
62 :
63 : std::string_view
64 4 : ipv4_address::to_buffer(char* dest, std::size_t dest_size) const
65 : {
66 4 : if (dest_size < max_str_len)
67 0 : throw std::length_error("buffer too small for IPv4 address");
68 4 : auto n = print_impl(dest);
69 4 : return std::string_view(dest, n);
70 : }
71 :
72 : bool
73 5 : ipv4_address::is_loopback() const noexcept
74 : {
75 5 : return (addr_ & 0xFF000000) == 0x7F000000;
76 : }
77 :
78 : bool
79 4 : ipv4_address::is_unspecified() const noexcept
80 : {
81 4 : return addr_ == 0;
82 : }
83 :
84 : bool
85 4 : ipv4_address::is_multicast() const noexcept
86 : {
87 4 : return (addr_ & 0xF0000000) == 0xE0000000;
88 : }
89 :
90 : std::ostream&
91 1 : operator<<(std::ostream& os, ipv4_address const& addr)
92 : {
93 : char buf[ipv4_address::max_str_len];
94 1 : os << addr.to_buffer(buf, sizeof(buf));
95 1 : return os;
96 : }
97 :
98 : std::size_t
99 15 : ipv4_address::print_impl(char* dest) const noexcept
100 : {
101 15 : auto const start = dest;
102 60 : auto const write = [](char*& dest, unsigned char v)
103 : {
104 60 : if (v >= 100)
105 : {
106 19 : *dest++ = '0' + v / 100;
107 19 : v %= 100;
108 19 : *dest++ = '0' + v / 10;
109 19 : v %= 10;
110 : }
111 41 : else if (v >= 10)
112 : {
113 5 : *dest++ = '0' + v / 10;
114 5 : v %= 10;
115 : }
116 60 : *dest++ = '0' + v;
117 60 : };
118 15 : write(dest, static_cast<unsigned char>((addr_ >> 24) & 0xff));
119 15 : *dest++ = '.';
120 15 : write(dest, static_cast<unsigned char>((addr_ >> 16) & 0xff));
121 15 : *dest++ = '.';
122 15 : write(dest, static_cast<unsigned char>((addr_ >> 8) & 0xff));
123 15 : *dest++ = '.';
124 15 : write(dest, static_cast<unsigned char>( addr_ & 0xff));
125 15 : return static_cast<std::size_t>(dest - start);
126 : }
127 :
128 : //------------------------------------------------
129 :
130 : namespace {
131 :
132 : // Parse a decimal octet (0-255), no leading zeros except "0"
133 : // Returns true on success, advances `it`
134 : bool
135 193 : parse_dec_octet(
136 : char const*& it,
137 : char const* end,
138 : unsigned char& octet) noexcept
139 : {
140 193 : if (it == end)
141 2 : return false;
142 :
143 191 : char c = *it;
144 191 : if (c < '0' || c > '9')
145 6 : return false;
146 :
147 185 : unsigned v = static_cast<unsigned>(c - '0');
148 185 : ++it;
149 :
150 185 : if (v == 0)
151 : {
152 : // "0" is valid, but "00", "01", etc. are not
153 29 : if (it != end && *it >= '0' && *it <= '9')
154 3 : return false;
155 26 : octet = 0;
156 26 : return true;
157 : }
158 :
159 : // First digit was 1-9, can have more
160 156 : if (it != end && *it >= '0' && *it <= '9')
161 : {
162 42 : v = v * 10 + static_cast<unsigned>(*it - '0');
163 42 : ++it;
164 :
165 42 : if (it != end && *it >= '0' && *it <= '9')
166 : {
167 39 : v = v * 10 + static_cast<unsigned>(*it - '0');
168 39 : ++it;
169 :
170 : // Can't have more than 3 digits
171 39 : if (it != end && *it >= '0' && *it <= '9')
172 1 : return false;
173 : }
174 : }
175 :
176 155 : if (v > 255)
177 7 : return false;
178 :
179 148 : octet = static_cast<unsigned char>(v);
180 148 : return true;
181 : }
182 :
183 : } // namespace
184 :
185 : std::error_code
186 62 : parse_ipv4_address(
187 : std::string_view s,
188 : ipv4_address& addr) noexcept
189 : {
190 62 : auto it = s.data();
191 62 : auto const end = it + s.size();
192 :
193 : unsigned char octets[4];
194 :
195 : // Parse first octet
196 62 : if (!parse_dec_octet(it, end, octets[0]))
197 13 : return std::make_error_code(std::errc::invalid_argument);
198 :
199 : // Parse remaining octets
200 174 : for (int i = 1; i < 4; ++i)
201 : {
202 137 : if (it == end || *it != '.')
203 6 : return std::make_error_code(std::errc::invalid_argument);
204 131 : ++it; // skip '.'
205 :
206 131 : if (!parse_dec_octet(it, end, octets[i]))
207 6 : return std::make_error_code(std::errc::invalid_argument);
208 : }
209 :
210 : // Must have consumed entire string
211 37 : if (it != end)
212 5 : return std::make_error_code(std::errc::invalid_argument);
213 :
214 64 : addr = ipv4_address(ipv4_address::bytes_type{{
215 32 : octets[0], octets[1], octets[2], octets[3]}});
216 :
217 32 : return {};
218 : }
219 :
220 : } // namespace boost::corosio
|