| // Copyright 2018 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "platform/base/ip_address.h" |
| |
| #include <algorithm> |
| #include <cassert> |
| #include <cctype> |
| #include <cinttypes> |
| #include <cstdio> |
| #include <cstring> |
| #include <iomanip> |
| #include <iterator> |
| #include <limits> |
| #include <sstream> |
| #include <utility> |
| |
| namespace openscreen { |
| |
| // static |
| const IPAddress IPAddress::kAnyV4() { |
| return IPAddress{0, 0, 0, 0}; |
| } |
| |
| // static |
| const IPAddress IPAddress::kAnyV6() { |
| return IPAddress{0, 0, 0, 0, 0, 0, 0, 0}; |
| } |
| |
| // static |
| const IPAddress IPAddress::kV4LoopbackAddress() { |
| return IPAddress{127, 0, 0, 1}; |
| } |
| |
| // static |
| const IPAddress IPAddress::kV6LoopbackAddress() { |
| return IPAddress{0, 0, 0, 0, 0, 0, 0, 1}; |
| } |
| |
| IPAddress::IPAddress(Version version, const uint8_t* b) : version_(version) { |
| if (version_ == Version::kV4) { |
| bytes_ = {{b[0], b[1], b[2], b[3]}}; |
| } else { |
| bytes_ = {{b[0], b[1], b[2], b[3], b[4], b[5], b[6], b[7], b[8], b[9], |
| b[10], b[11], b[12], b[13], b[14], b[15]}}; |
| } |
| } |
| |
| IPAddress::IPAddress(const std::array<uint8_t, 4>& bytes) |
| : version_(Version::kV4), |
| bytes_{{bytes[0], bytes[1], bytes[2], bytes[3]}} {} |
| |
| IPAddress::IPAddress(const uint8_t (&b)[4]) |
| : version_(Version::kV4), bytes_{{b[0], b[1], b[2], b[3]}} {} |
| |
| IPAddress::IPAddress(uint8_t b1, uint8_t b2, uint8_t b3, uint8_t b4) |
| : version_(Version::kV4), bytes_{{b1, b2, b3, b4}} {} |
| |
| IPAddress::IPAddress(const std::array<uint16_t, 8>& hextets) |
| : IPAddress(hextets[0], |
| hextets[1], |
| hextets[2], |
| hextets[3], |
| hextets[4], |
| hextets[5], |
| hextets[6], |
| hextets[7]) {} |
| |
| IPAddress::IPAddress(const uint16_t (&hextets)[8]) |
| : IPAddress(hextets[0], |
| hextets[1], |
| hextets[2], |
| hextets[3], |
| hextets[4], |
| hextets[5], |
| hextets[6], |
| hextets[7]) {} |
| |
| IPAddress::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) |
| : version_(Version::kV6), |
| bytes_{{ |
| static_cast<uint8_t>(h0 >> 8), |
| static_cast<uint8_t>(h0), |
| static_cast<uint8_t>(h1 >> 8), |
| static_cast<uint8_t>(h1), |
| static_cast<uint8_t>(h2 >> 8), |
| static_cast<uint8_t>(h2), |
| static_cast<uint8_t>(h3 >> 8), |
| static_cast<uint8_t>(h3), |
| static_cast<uint8_t>(h4 >> 8), |
| static_cast<uint8_t>(h4), |
| static_cast<uint8_t>(h5 >> 8), |
| static_cast<uint8_t>(h5), |
| static_cast<uint8_t>(h6 >> 8), |
| static_cast<uint8_t>(h6), |
| static_cast<uint8_t>(h7 >> 8), |
| static_cast<uint8_t>(h7), |
| }} {} |
| |
| IPAddress::IPAddress(const IPAddress& o) noexcept = default; |
| IPAddress::IPAddress(IPAddress&& o) noexcept = default; |
| IPAddress& IPAddress::operator=(const IPAddress& o) noexcept = default; |
| IPAddress& IPAddress::operator=(IPAddress&& o) noexcept = default; |
| |
| bool IPAddress::operator==(const IPAddress& o) const { |
| if (version_ != o.version_) |
| return false; |
| |
| if (version_ == Version::kV4) { |
| return bytes_[0] == o.bytes_[0] && bytes_[1] == o.bytes_[1] && |
| bytes_[2] == o.bytes_[2] && bytes_[3] == o.bytes_[3]; |
| } |
| return bytes_ == o.bytes_; |
| } |
| |
| bool IPAddress::operator!=(const IPAddress& o) const { |
| return !(*this == o); |
| } |
| |
| IPAddress::operator bool() const { |
| if (version_ == Version::kV4) |
| return bytes_[0] | bytes_[1] | bytes_[2] | bytes_[3]; |
| |
| for (const auto& byte : bytes_) |
| if (byte) |
| return true; |
| |
| return false; |
| } |
| |
| void IPAddress::CopyToV4(uint8_t x[4]) const { |
| assert(version_ == Version::kV4); |
| std::memcpy(x, bytes_.data(), 4); |
| } |
| |
| void IPAddress::CopyToV6(uint8_t x[16]) const { |
| assert(version_ == Version::kV6); |
| std::memcpy(x, bytes_.data(), 16); |
| } |
| |
| namespace { |
| |
| ErrorOr<IPAddress> ParseV4(const std::string& s) { |
| int octets[4]; |
| int chars_scanned; |
| // Note: sscanf()'s parsing for %d allows leading whitespace; so the invalid |
| // presence of whitespace must be explicitly checked too. |
| if (std::any_of(s.begin(), s.end(), [](char c) { return std::isspace(c); }) || |
| sscanf(s.c_str(), "%3d.%3d.%3d.%3d%n", &octets[0], &octets[1], &octets[2], |
| &octets[3], &chars_scanned) != 4 || |
| chars_scanned != static_cast<int>(s.size()) || |
| std::any_of(std::begin(octets), std::end(octets), |
| [](int octet) { return octet < 0 || octet > 255; })) { |
| return Error::Code::kInvalidIPV4Address; |
| } |
| return IPAddress(octets[0], octets[1], octets[2], octets[3]); |
| } |
| |
| // Returns the zero-expansion of a double-colon in |s| if |s| is a |
| // well-formatted IPv6 address. If |s| is ill-formatted, returns *any* string |
| // that is ill-formatted. |
| std::string ExpandIPv6DoubleColon(const std::string& s) { |
| constexpr char kDoubleColon[] = "::"; |
| const size_t double_colon_position = s.find(kDoubleColon); |
| if (double_colon_position == std::string::npos) { |
| return s; // Nothing to expand. |
| } |
| if (double_colon_position != s.rfind(kDoubleColon)) { |
| return {}; // More than one occurrence of double colons is illegal. |
| } |
| |
| std::ostringstream expanded; |
| const int num_single_colons = std::count(s.begin(), s.end(), ':') - 2; |
| int num_zero_groups_to_insert = 8 - num_single_colons; |
| if (double_colon_position != 0) { |
| // abcd:0123:4567::f000:1 |
| // ^^^^^^^^^^^^^^^ |
| expanded << s.substr(0, double_colon_position + 1); |
| --num_zero_groups_to_insert; |
| } |
| if (double_colon_position != (s.size() - 2)) { |
| --num_zero_groups_to_insert; |
| } |
| while (--num_zero_groups_to_insert > 0) { |
| expanded << "0:"; |
| } |
| expanded << '0'; |
| if (double_colon_position != (s.size() - 2)) { |
| // abcd:0123:4567::f000:1 |
| // ^^^^^^^ |
| expanded << s.substr(double_colon_position + 1); |
| } |
| return expanded.str(); |
| } |
| |
| ErrorOr<IPAddress> ParseV6(const std::string& s) { |
| const std::string scan_input = ExpandIPv6DoubleColon(s); |
| uint16_t hextets[8]; |
| int chars_scanned; |
| // Note: sscanf()'s parsing for %x allows leading whitespace; so the invalid |
| // presence of whitespace must be explicitly checked too. |
| if (std::any_of(s.begin(), s.end(), [](char c) { return std::isspace(c); }) || |
| sscanf(scan_input.c_str(), |
| "%4" SCNx16 ":%4" SCNx16 ":%4" SCNx16 ":%4" SCNx16 ":%4" SCNx16 |
| ":%4" SCNx16 ":%4" SCNx16 ":%4" SCNx16 "%n", |
| &hextets[0], &hextets[1], &hextets[2], &hextets[3], &hextets[4], |
| &hextets[5], &hextets[6], &hextets[7], &chars_scanned) != 8 || |
| chars_scanned != static_cast<int>(scan_input.size())) { |
| return Error::Code::kInvalidIPV6Address; |
| } |
| return IPAddress(hextets); |
| } |
| |
| } // namespace |
| |
| // static |
| ErrorOr<IPAddress> IPAddress::Parse(const std::string& s) { |
| ErrorOr<IPAddress> v4 = ParseV4(s); |
| |
| return v4 ? std::move(v4) : ParseV6(s); |
| } |
| |
| // static |
| const IPEndpoint IPEndpoint::kAnyV4() { |
| return IPEndpoint{}; |
| } |
| |
| // static |
| const IPEndpoint IPEndpoint::kAnyV6() { |
| return IPEndpoint{IPAddress::kAnyV6(), 0}; |
| } |
| |
| IPEndpoint::operator bool() const { |
| return address || port; |
| } |
| |
| // static |
| ErrorOr<IPEndpoint> IPEndpoint::Parse(const std::string& s) { |
| // Look for the colon that separates the IP address from the port number. Note |
| // that this check also guards against the case where |s| is the empty string. |
| const auto colon_pos = s.rfind(':'); |
| if (colon_pos == std::string::npos) { |
| return Error(Error::Code::kParseError, "missing colon separator"); |
| } |
| // The colon cannot be the first nor the last character in |s| because that |
| // would mean there is no address part or port part. |
| if (colon_pos == 0) { |
| return Error(Error::Code::kParseError, "missing address before colon"); |
| } |
| if (colon_pos == (s.size() - 1)) { |
| return Error(Error::Code::kParseError, "missing port after colon"); |
| } |
| |
| ErrorOr<IPAddress> address(Error::Code::kParseError); |
| if (s[0] == '[' && s[colon_pos - 1] == ']') { |
| // [abcd:beef:1:1::2600]:8080 |
| // ^^^^^^^^^^^^^^^^^^^^^ |
| address = ParseV6(s.substr(1, colon_pos - 2)); |
| } else { |
| // 127.0.0.1:22 |
| // ^^^^^^^^^ |
| address = ParseV4(s.substr(0, colon_pos)); |
| } |
| if (address.is_error()) { |
| return Error(Error::Code::kParseError, "invalid address part"); |
| } |
| |
| const char* const port_part = s.c_str() + colon_pos + 1; |
| int port, chars_scanned; |
| // Note: sscanf()'s parsing for %d allows leading whitespace. Thus, if the |
| // first char is not whitespace, a successful sscanf() parse here can only |
| // mean numerical chars contributed to the parsed integer. |
| if (std::isspace(port_part[0]) || |
| sscanf(port_part, "%d%n", &port, &chars_scanned) != 1 || |
| port_part[chars_scanned] != '\0' || port < 0 || |
| port > std::numeric_limits<uint16_t>::max()) { |
| return Error(Error::Code::kParseError, "invalid port part"); |
| } |
| |
| return IPEndpoint{address.value(), static_cast<uint16_t>(port)}; |
| } |
| |
| bool operator==(const IPEndpoint& a, const IPEndpoint& b) { |
| return (a.address == b.address) && (a.port == b.port); |
| } |
| |
| bool operator!=(const IPEndpoint& a, const IPEndpoint& b) { |
| return !(a == b); |
| } |
| |
| bool IPAddress::operator<(const IPAddress& other) const { |
| if (version() != other.version()) { |
| return version() < other.version(); |
| } |
| |
| if (IsV4()) { |
| return memcmp(bytes_.data(), other.bytes_.data(), 4) < 0; |
| } else { |
| return memcmp(bytes_.data(), other.bytes_.data(), 16) < 0; |
| } |
| } |
| |
| bool operator<(const IPEndpoint& a, const IPEndpoint& b) { |
| if (a.address != b.address) { |
| return a.address < b.address; |
| } |
| |
| return a.port < b.port; |
| } |
| |
| std::ostream& operator<<(std::ostream& out, const IPAddress& address) { |
| uint8_t values[16]; |
| size_t len = 0; |
| char separator; |
| size_t values_per_separator; |
| int value_width; |
| if (address.IsV4()) { |
| out << std::dec; |
| address.CopyToV4(values); |
| len = 4; |
| separator = '.'; |
| values_per_separator = 1; |
| value_width = 0; |
| } else if (address.IsV6()) { |
| out << std::hex << std::setfill('0') << std::right; |
| address.CopyToV6(values); |
| len = 16; |
| separator = ':'; |
| values_per_separator = 2; |
| value_width = 2; |
| } |
| for (size_t i = 0; i < len; ++i) { |
| if (i > 0 && (i % values_per_separator == 0)) { |
| out << separator; |
| } |
| out << std::setw(value_width) << static_cast<int>(values[i]); |
| } |
| return out; |
| } |
| |
| std::ostream& operator<<(std::ostream& out, const IPEndpoint& endpoint) { |
| if (endpoint.address.IsV6()) { |
| out << '['; |
| } |
| out << endpoint.address; |
| if (endpoint.address.IsV6()) { |
| out << ']'; |
| } |
| return out << ':' << std::dec << static_cast<int>(endpoint.port); |
| } |
| |
| std::string IPEndpoint::ToString() const { |
| std::ostringstream name; |
| name << *this; |
| return name.str(); |
| } |
| |
| } // namespace openscreen |