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