xref: /aosp_15_r20/external/openscreen/platform/base/ip_address.cc (revision 3f982cf4871df8771c9d4abe6e9a6f8d829b2736)
1*3f982cf4SFabien Sanglard // Copyright 2018 The Chromium Authors. All rights reserved.
2*3f982cf4SFabien Sanglard // Use of this source code is governed by a BSD-style license that can be
3*3f982cf4SFabien Sanglard // found in the LICENSE file.
4*3f982cf4SFabien Sanglard 
5*3f982cf4SFabien Sanglard #include "platform/base/ip_address.h"
6*3f982cf4SFabien Sanglard 
7*3f982cf4SFabien Sanglard #include <algorithm>
8*3f982cf4SFabien Sanglard #include <cassert>
9*3f982cf4SFabien Sanglard #include <cctype>
10*3f982cf4SFabien Sanglard #include <cinttypes>
11*3f982cf4SFabien Sanglard #include <cstdio>
12*3f982cf4SFabien Sanglard #include <cstring>
13*3f982cf4SFabien Sanglard #include <iomanip>
14*3f982cf4SFabien Sanglard #include <iterator>
15*3f982cf4SFabien Sanglard #include <limits>
16*3f982cf4SFabien Sanglard #include <sstream>
17*3f982cf4SFabien Sanglard #include <utility>
18*3f982cf4SFabien Sanglard 
19*3f982cf4SFabien Sanglard namespace openscreen {
20*3f982cf4SFabien Sanglard 
IPAddress(Version version,const uint8_t * b)21*3f982cf4SFabien Sanglard IPAddress::IPAddress(Version version, const uint8_t* b) : version_(version) {
22*3f982cf4SFabien Sanglard   if (version_ == Version::kV4) {
23*3f982cf4SFabien Sanglard     bytes_ = {{b[0], b[1], b[2], b[3]}};
24*3f982cf4SFabien Sanglard   } else {
25*3f982cf4SFabien Sanglard     bytes_ = {{b[0], b[1], b[2], b[3], b[4], b[5], b[6], b[7], b[8], b[9],
26*3f982cf4SFabien Sanglard                b[10], b[11], b[12], b[13], b[14], b[15]}};
27*3f982cf4SFabien Sanglard   }
28*3f982cf4SFabien Sanglard }
29*3f982cf4SFabien Sanglard 
operator ==(const IPAddress & o) const30*3f982cf4SFabien Sanglard bool IPAddress::operator==(const IPAddress& o) const {
31*3f982cf4SFabien Sanglard   if (version_ != o.version_)
32*3f982cf4SFabien Sanglard     return false;
33*3f982cf4SFabien Sanglard 
34*3f982cf4SFabien Sanglard   if (version_ == Version::kV4) {
35*3f982cf4SFabien Sanglard     return bytes_[0] == o.bytes_[0] && bytes_[1] == o.bytes_[1] &&
36*3f982cf4SFabien Sanglard            bytes_[2] == o.bytes_[2] && bytes_[3] == o.bytes_[3];
37*3f982cf4SFabien Sanglard   }
38*3f982cf4SFabien Sanglard   return bytes_ == o.bytes_;
39*3f982cf4SFabien Sanglard }
40*3f982cf4SFabien Sanglard 
operator !=(const IPAddress & o) const41*3f982cf4SFabien Sanglard bool IPAddress::operator!=(const IPAddress& o) const {
42*3f982cf4SFabien Sanglard   return !(*this == o);
43*3f982cf4SFabien Sanglard }
44*3f982cf4SFabien Sanglard 
operator bool() const45*3f982cf4SFabien Sanglard IPAddress::operator bool() const {
46*3f982cf4SFabien Sanglard   if (version_ == Version::kV4)
47*3f982cf4SFabien Sanglard     return bytes_[0] | bytes_[1] | bytes_[2] | bytes_[3];
48*3f982cf4SFabien Sanglard 
49*3f982cf4SFabien Sanglard   for (const auto& byte : bytes_)
50*3f982cf4SFabien Sanglard     if (byte)
51*3f982cf4SFabien Sanglard       return true;
52*3f982cf4SFabien Sanglard 
53*3f982cf4SFabien Sanglard   return false;
54*3f982cf4SFabien Sanglard }
55*3f982cf4SFabien Sanglard 
CopyToV4(uint8_t x[4]) const56*3f982cf4SFabien Sanglard void IPAddress::CopyToV4(uint8_t x[4]) const {
57*3f982cf4SFabien Sanglard   assert(version_ == Version::kV4);
58*3f982cf4SFabien Sanglard   std::memcpy(x, bytes_.data(), 4);
59*3f982cf4SFabien Sanglard }
60*3f982cf4SFabien Sanglard 
CopyToV6(uint8_t x[16]) const61*3f982cf4SFabien Sanglard void IPAddress::CopyToV6(uint8_t x[16]) const {
62*3f982cf4SFabien Sanglard   assert(version_ == Version::kV6);
63*3f982cf4SFabien Sanglard   std::memcpy(x, bytes_.data(), 16);
64*3f982cf4SFabien Sanglard }
65*3f982cf4SFabien Sanglard 
66*3f982cf4SFabien Sanglard namespace {
67*3f982cf4SFabien Sanglard 
ParseV4(const std::string & s)68*3f982cf4SFabien Sanglard ErrorOr<IPAddress> ParseV4(const std::string& s) {
69*3f982cf4SFabien Sanglard   int octets[4];
70*3f982cf4SFabien Sanglard   int chars_scanned;
71*3f982cf4SFabien Sanglard   // Note: sscanf()'s parsing for %d allows leading whitespace; so the invalid
72*3f982cf4SFabien Sanglard   // presence of whitespace must be explicitly checked too.
73*3f982cf4SFabien Sanglard   if (std::any_of(s.begin(), s.end(), [](char c) { return std::isspace(c); }) ||
74*3f982cf4SFabien Sanglard       sscanf(s.c_str(), "%3d.%3d.%3d.%3d%n", &octets[0], &octets[1], &octets[2],
75*3f982cf4SFabien Sanglard              &octets[3], &chars_scanned) != 4 ||
76*3f982cf4SFabien Sanglard       chars_scanned != static_cast<int>(s.size()) ||
77*3f982cf4SFabien Sanglard       std::any_of(std::begin(octets), std::end(octets),
78*3f982cf4SFabien Sanglard                   [](int octet) { return octet < 0 || octet > 255; })) {
79*3f982cf4SFabien Sanglard     return Error::Code::kInvalidIPV4Address;
80*3f982cf4SFabien Sanglard   }
81*3f982cf4SFabien Sanglard   return IPAddress(octets[0], octets[1], octets[2], octets[3]);
82*3f982cf4SFabien Sanglard }
83*3f982cf4SFabien Sanglard 
84*3f982cf4SFabien Sanglard // Returns the zero-expansion of a double-colon in |s| if |s| is a
85*3f982cf4SFabien Sanglard // well-formatted IPv6 address. If |s| is ill-formatted, returns *any* string
86*3f982cf4SFabien Sanglard // that is ill-formatted.
ExpandIPv6DoubleColon(const std::string & s)87*3f982cf4SFabien Sanglard std::string ExpandIPv6DoubleColon(const std::string& s) {
88*3f982cf4SFabien Sanglard   constexpr char kDoubleColon[] = "::";
89*3f982cf4SFabien Sanglard   const size_t double_colon_position = s.find(kDoubleColon);
90*3f982cf4SFabien Sanglard   if (double_colon_position == std::string::npos) {
91*3f982cf4SFabien Sanglard     return s;  // Nothing to expand.
92*3f982cf4SFabien Sanglard   }
93*3f982cf4SFabien Sanglard   if (double_colon_position != s.rfind(kDoubleColon)) {
94*3f982cf4SFabien Sanglard     return {};  // More than one occurrence of double colons is illegal.
95*3f982cf4SFabien Sanglard   }
96*3f982cf4SFabien Sanglard 
97*3f982cf4SFabien Sanglard   std::ostringstream expanded;
98*3f982cf4SFabien Sanglard   const int num_single_colons = std::count(s.begin(), s.end(), ':') - 2;
99*3f982cf4SFabien Sanglard   int num_zero_groups_to_insert = 8 - num_single_colons;
100*3f982cf4SFabien Sanglard   if (double_colon_position != 0) {
101*3f982cf4SFabien Sanglard     // abcd:0123:4567::f000:1
102*3f982cf4SFabien Sanglard     // ^^^^^^^^^^^^^^^
103*3f982cf4SFabien Sanglard     expanded << s.substr(0, double_colon_position + 1);
104*3f982cf4SFabien Sanglard     --num_zero_groups_to_insert;
105*3f982cf4SFabien Sanglard   }
106*3f982cf4SFabien Sanglard   if (double_colon_position != (s.size() - 2)) {
107*3f982cf4SFabien Sanglard     --num_zero_groups_to_insert;
108*3f982cf4SFabien Sanglard   }
109*3f982cf4SFabien Sanglard   while (--num_zero_groups_to_insert > 0) {
110*3f982cf4SFabien Sanglard     expanded << "0:";
111*3f982cf4SFabien Sanglard   }
112*3f982cf4SFabien Sanglard   expanded << '0';
113*3f982cf4SFabien Sanglard   if (double_colon_position != (s.size() - 2)) {
114*3f982cf4SFabien Sanglard     // abcd:0123:4567::f000:1
115*3f982cf4SFabien Sanglard     //                ^^^^^^^
116*3f982cf4SFabien Sanglard     expanded << s.substr(double_colon_position + 1);
117*3f982cf4SFabien Sanglard   }
118*3f982cf4SFabien Sanglard   return expanded.str();
119*3f982cf4SFabien Sanglard }
120*3f982cf4SFabien Sanglard 
ParseV6(const std::string & s)121*3f982cf4SFabien Sanglard ErrorOr<IPAddress> ParseV6(const std::string& s) {
122*3f982cf4SFabien Sanglard   const std::string scan_input = ExpandIPv6DoubleColon(s);
123*3f982cf4SFabien Sanglard   uint16_t hextets[8];
124*3f982cf4SFabien Sanglard   int chars_scanned;
125*3f982cf4SFabien Sanglard   // Note: sscanf()'s parsing for %x allows leading whitespace; so the invalid
126*3f982cf4SFabien Sanglard   // presence of whitespace must be explicitly checked too.
127*3f982cf4SFabien Sanglard   if (std::any_of(s.begin(), s.end(), [](char c) { return std::isspace(c); }) ||
128*3f982cf4SFabien Sanglard       sscanf(scan_input.c_str(),
129*3f982cf4SFabien Sanglard              "%4" SCNx16 ":%4" SCNx16 ":%4" SCNx16 ":%4" SCNx16 ":%4" SCNx16
130*3f982cf4SFabien Sanglard              ":%4" SCNx16 ":%4" SCNx16 ":%4" SCNx16 "%n",
131*3f982cf4SFabien Sanglard              &hextets[0], &hextets[1], &hextets[2], &hextets[3], &hextets[4],
132*3f982cf4SFabien Sanglard              &hextets[5], &hextets[6], &hextets[7], &chars_scanned) != 8 ||
133*3f982cf4SFabien Sanglard       chars_scanned != static_cast<int>(scan_input.size())) {
134*3f982cf4SFabien Sanglard     return Error::Code::kInvalidIPV6Address;
135*3f982cf4SFabien Sanglard   }
136*3f982cf4SFabien Sanglard   return IPAddress(hextets);
137*3f982cf4SFabien Sanglard }
138*3f982cf4SFabien Sanglard 
139*3f982cf4SFabien Sanglard }  // namespace
140*3f982cf4SFabien Sanglard 
141*3f982cf4SFabien Sanglard // static
Parse(const std::string & s)142*3f982cf4SFabien Sanglard ErrorOr<IPAddress> IPAddress::Parse(const std::string& s) {
143*3f982cf4SFabien Sanglard   ErrorOr<IPAddress> v4 = ParseV4(s);
144*3f982cf4SFabien Sanglard 
145*3f982cf4SFabien Sanglard   return v4 ? std::move(v4) : ParseV6(s);
146*3f982cf4SFabien Sanglard }
147*3f982cf4SFabien Sanglard 
148*3f982cf4SFabien Sanglard // static
kAnyV4()149*3f982cf4SFabien Sanglard const IPEndpoint IPEndpoint::kAnyV4() {
150*3f982cf4SFabien Sanglard   return IPEndpoint{};
151*3f982cf4SFabien Sanglard }
152*3f982cf4SFabien Sanglard 
153*3f982cf4SFabien Sanglard // static
kAnyV6()154*3f982cf4SFabien Sanglard const IPEndpoint IPEndpoint::kAnyV6() {
155*3f982cf4SFabien Sanglard   return IPEndpoint{IPAddress::kAnyV6(), 0};
156*3f982cf4SFabien Sanglard }
157*3f982cf4SFabien Sanglard 
operator bool() const158*3f982cf4SFabien Sanglard IPEndpoint::operator bool() const {
159*3f982cf4SFabien Sanglard   return address || port;
160*3f982cf4SFabien Sanglard }
161*3f982cf4SFabien Sanglard 
162*3f982cf4SFabien Sanglard // static
Parse(const std::string & s)163*3f982cf4SFabien Sanglard ErrorOr<IPEndpoint> IPEndpoint::Parse(const std::string& s) {
164*3f982cf4SFabien Sanglard   // Look for the colon that separates the IP address from the port number. Note
165*3f982cf4SFabien Sanglard   // that this check also guards against the case where |s| is the empty string.
166*3f982cf4SFabien Sanglard   const auto colon_pos = s.rfind(':');
167*3f982cf4SFabien Sanglard   if (colon_pos == std::string::npos) {
168*3f982cf4SFabien Sanglard     return Error(Error::Code::kParseError, "missing colon separator");
169*3f982cf4SFabien Sanglard   }
170*3f982cf4SFabien Sanglard   // The colon cannot be the first nor the last character in |s| because that
171*3f982cf4SFabien Sanglard   // would mean there is no address part or port part.
172*3f982cf4SFabien Sanglard   if (colon_pos == 0) {
173*3f982cf4SFabien Sanglard     return Error(Error::Code::kParseError, "missing address before colon");
174*3f982cf4SFabien Sanglard   }
175*3f982cf4SFabien Sanglard   if (colon_pos == (s.size() - 1)) {
176*3f982cf4SFabien Sanglard     return Error(Error::Code::kParseError, "missing port after colon");
177*3f982cf4SFabien Sanglard   }
178*3f982cf4SFabien Sanglard 
179*3f982cf4SFabien Sanglard   ErrorOr<IPAddress> address(Error::Code::kParseError);
180*3f982cf4SFabien Sanglard   if (s[0] == '[' && s[colon_pos - 1] == ']') {
181*3f982cf4SFabien Sanglard     // [abcd:beef:1:1::2600]:8080
182*3f982cf4SFabien Sanglard     // ^^^^^^^^^^^^^^^^^^^^^
183*3f982cf4SFabien Sanglard     address = ParseV6(s.substr(1, colon_pos - 2));
184*3f982cf4SFabien Sanglard   } else {
185*3f982cf4SFabien Sanglard     // 127.0.0.1:22
186*3f982cf4SFabien Sanglard     // ^^^^^^^^^
187*3f982cf4SFabien Sanglard     address = ParseV4(s.substr(0, colon_pos));
188*3f982cf4SFabien Sanglard   }
189*3f982cf4SFabien Sanglard   if (address.is_error()) {
190*3f982cf4SFabien Sanglard     return Error(Error::Code::kParseError, "invalid address part");
191*3f982cf4SFabien Sanglard   }
192*3f982cf4SFabien Sanglard 
193*3f982cf4SFabien Sanglard   const char* const port_part = s.c_str() + colon_pos + 1;
194*3f982cf4SFabien Sanglard   int port, chars_scanned;
195*3f982cf4SFabien Sanglard   // Note: sscanf()'s parsing for %d allows leading whitespace. Thus, if the
196*3f982cf4SFabien Sanglard   // first char is not whitespace, a successful sscanf() parse here can only
197*3f982cf4SFabien Sanglard   // mean numerical chars contributed to the parsed integer.
198*3f982cf4SFabien Sanglard   if (std::isspace(port_part[0]) ||
199*3f982cf4SFabien Sanglard       sscanf(port_part, "%d%n", &port, &chars_scanned) != 1 ||
200*3f982cf4SFabien Sanglard       port_part[chars_scanned] != '\0' || port < 0 ||
201*3f982cf4SFabien Sanglard       port > std::numeric_limits<uint16_t>::max()) {
202*3f982cf4SFabien Sanglard     return Error(Error::Code::kParseError, "invalid port part");
203*3f982cf4SFabien Sanglard   }
204*3f982cf4SFabien Sanglard 
205*3f982cf4SFabien Sanglard   return IPEndpoint{address.value(), static_cast<uint16_t>(port)};
206*3f982cf4SFabien Sanglard }
207*3f982cf4SFabien Sanglard 
operator ==(const IPEndpoint & a,const IPEndpoint & b)208*3f982cf4SFabien Sanglard bool operator==(const IPEndpoint& a, const IPEndpoint& b) {
209*3f982cf4SFabien Sanglard   return (a.address == b.address) && (a.port == b.port);
210*3f982cf4SFabien Sanglard }
211*3f982cf4SFabien Sanglard 
operator !=(const IPEndpoint & a,const IPEndpoint & b)212*3f982cf4SFabien Sanglard bool operator!=(const IPEndpoint& a, const IPEndpoint& b) {
213*3f982cf4SFabien Sanglard   return !(a == b);
214*3f982cf4SFabien Sanglard }
215*3f982cf4SFabien Sanglard 
operator <(const IPAddress & other) const216*3f982cf4SFabien Sanglard bool IPAddress::operator<(const IPAddress& other) const {
217*3f982cf4SFabien Sanglard   if (version() != other.version()) {
218*3f982cf4SFabien Sanglard     return version() < other.version();
219*3f982cf4SFabien Sanglard   }
220*3f982cf4SFabien Sanglard 
221*3f982cf4SFabien Sanglard   if (IsV4()) {
222*3f982cf4SFabien Sanglard     return memcmp(bytes_.data(), other.bytes_.data(), 4) < 0;
223*3f982cf4SFabien Sanglard   } else {
224*3f982cf4SFabien Sanglard     return memcmp(bytes_.data(), other.bytes_.data(), 16) < 0;
225*3f982cf4SFabien Sanglard   }
226*3f982cf4SFabien Sanglard }
227*3f982cf4SFabien Sanglard 
operator <(const IPEndpoint & a,const IPEndpoint & b)228*3f982cf4SFabien Sanglard bool operator<(const IPEndpoint& a, const IPEndpoint& b) {
229*3f982cf4SFabien Sanglard   if (a.address != b.address) {
230*3f982cf4SFabien Sanglard     return a.address < b.address;
231*3f982cf4SFabien Sanglard   }
232*3f982cf4SFabien Sanglard 
233*3f982cf4SFabien Sanglard   return a.port < b.port;
234*3f982cf4SFabien Sanglard }
235*3f982cf4SFabien Sanglard 
operator <<(std::ostream & out,const IPAddress & address)236*3f982cf4SFabien Sanglard std::ostream& operator<<(std::ostream& out, const IPAddress& address) {
237*3f982cf4SFabien Sanglard   uint8_t values[16];
238*3f982cf4SFabien Sanglard   size_t len = 0;
239*3f982cf4SFabien Sanglard   char separator;
240*3f982cf4SFabien Sanglard   size_t values_per_separator;
241*3f982cf4SFabien Sanglard   int value_width;
242*3f982cf4SFabien Sanglard   if (address.IsV4()) {
243*3f982cf4SFabien Sanglard     out << std::dec;
244*3f982cf4SFabien Sanglard     address.CopyToV4(values);
245*3f982cf4SFabien Sanglard     len = 4;
246*3f982cf4SFabien Sanglard     separator = '.';
247*3f982cf4SFabien Sanglard     values_per_separator = 1;
248*3f982cf4SFabien Sanglard     value_width = 0;
249*3f982cf4SFabien Sanglard   } else if (address.IsV6()) {
250*3f982cf4SFabien Sanglard     out << std::hex << std::setfill('0') << std::right;
251*3f982cf4SFabien Sanglard     address.CopyToV6(values);
252*3f982cf4SFabien Sanglard     len = 16;
253*3f982cf4SFabien Sanglard     separator = ':';
254*3f982cf4SFabien Sanglard     values_per_separator = 2;
255*3f982cf4SFabien Sanglard     value_width = 2;
256*3f982cf4SFabien Sanglard   }
257*3f982cf4SFabien Sanglard   for (size_t i = 0; i < len; ++i) {
258*3f982cf4SFabien Sanglard     if (i > 0 && (i % values_per_separator == 0)) {
259*3f982cf4SFabien Sanglard       out << separator;
260*3f982cf4SFabien Sanglard     }
261*3f982cf4SFabien Sanglard     out << std::setw(value_width) << static_cast<int>(values[i]);
262*3f982cf4SFabien Sanglard   }
263*3f982cf4SFabien Sanglard   return out;
264*3f982cf4SFabien Sanglard }
265*3f982cf4SFabien Sanglard 
operator <<(std::ostream & out,const IPEndpoint & endpoint)266*3f982cf4SFabien Sanglard std::ostream& operator<<(std::ostream& out, const IPEndpoint& endpoint) {
267*3f982cf4SFabien Sanglard   if (endpoint.address.IsV6()) {
268*3f982cf4SFabien Sanglard     out << '[';
269*3f982cf4SFabien Sanglard   }
270*3f982cf4SFabien Sanglard   out << endpoint.address;
271*3f982cf4SFabien Sanglard   if (endpoint.address.IsV6()) {
272*3f982cf4SFabien Sanglard     out << ']';
273*3f982cf4SFabien Sanglard   }
274*3f982cf4SFabien Sanglard   return out << ':' << std::dec << static_cast<int>(endpoint.port);
275*3f982cf4SFabien Sanglard }
276*3f982cf4SFabien Sanglard 
ToString() const277*3f982cf4SFabien Sanglard std::string IPEndpoint::ToString() const {
278*3f982cf4SFabien Sanglard   std::ostringstream name;
279*3f982cf4SFabien Sanglard   name << *this;
280*3f982cf4SFabien Sanglard   return name.str();
281*3f982cf4SFabien Sanglard }
282*3f982cf4SFabien Sanglard 
283*3f982cf4SFabien Sanglard }  // namespace openscreen
284