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