blob: 4ae688c42f39491bcd875eafaa4c4142a93c8145 [file] [log] [blame]
// 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 "base/ip_address.h"
#include <cstring>
#include <iomanip>
#include "platform/api/logging.h"
#include "third_party/abseil/src/absl/types/optional.h"
namespace openscreen {
// static
bool IPAddress::Parse(const std::string& s, IPAddress* address) {
return ParseV4(s, address) || ParseV6(s, address);
}
IPAddress::IPAddress() : version_(Version::kV4), bytes_({}) {}
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(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(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<uint8_t, 16>& bytes)
: version_(Version::kV6), bytes_(bytes) {}
IPAddress::IPAddress(const uint8_t (&b)[16])
: version_(Version::kV6),
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(uint8_t b1,
uint8_t b2,
uint8_t b3,
uint8_t b4,
uint8_t b5,
uint8_t b6,
uint8_t b7,
uint8_t b8,
uint8_t b9,
uint8_t b10,
uint8_t b11,
uint8_t b12,
uint8_t b13,
uint8_t b14,
uint8_t b15,
uint8_t b16)
: version_(Version::kV6),
bytes_{{b1, b2, b3, b4, b5, b6, b7, b8, b9, b10, b11, b12, b13, b14, b15,
b16}} {}
IPAddress::IPAddress(const IPAddress& o) = default;
IPAddress& IPAddress::operator=(const IPAddress& o) = 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 {
// OSP_DCHECK(version_ == Version::kV4);
std::memcpy(x, bytes_.data(), 4);
}
void IPAddress::CopyToV6(uint8_t x[16]) const {
// OSP_DCHECK(version_ == Version::kV6);
std::memcpy(x, bytes_.data(), 16);
}
// static
bool IPAddress::ParseV4(const std::string& s, IPAddress* address) {
if (s.size() > 0 && s[0] == '.')
return false;
uint16_t next_octet = 0;
int i = 0;
bool previous_dot = false;
for (auto c : s) {
if (c == '.') {
if (previous_dot) {
return false;
}
address->bytes_[i++] = static_cast<uint8_t>(next_octet);
next_octet = 0;
previous_dot = true;
if (i > 3)
return false;
continue;
}
previous_dot = false;
if (!std::isdigit(c))
return false;
next_octet = next_octet * 10 + (c - '0');
if (next_octet > 255)
return false;
}
if (previous_dot)
return false;
if (i != 3)
return false;
address->bytes_[i] = static_cast<uint8_t>(next_octet);
address->version_ = Version::kV4;
return true;
}
// static
bool IPAddress::ParseV6(const std::string& s, IPAddress* address) {
if (s.size() > 1 && s[0] == ':' && s[1] != ':')
return false;
uint16_t next_value = 0;
uint8_t values[16];
int i = 0;
int num_previous_colons = 0;
absl::optional<int> double_colon_index = absl::nullopt;
for (auto c : s) {
if (c == ':') {
++num_previous_colons;
if (num_previous_colons == 2) {
if (double_colon_index) {
return false;
}
double_colon_index = i;
} else if (i >= 15 || num_previous_colons > 2) {
return false;
} else {
values[i++] = static_cast<uint8_t>(next_value >> 8);
values[i++] = static_cast<uint8_t>(next_value & 0xff);
next_value = 0;
}
} else {
num_previous_colons = 0;
uint8_t x = 0;
if (c >= '0' && c <= '9') {
x = c - '0';
} else if (c >= 'a' && c <= 'f') {
x = c - 'a' + 10;
} else if (c >= 'A' && c <= 'F') {
x = c - 'A' + 10;
} else {
return false;
}
if (next_value & 0xf000) {
return false;
} else {
next_value = static_cast<uint16_t>(next_value * 16 + x);
}
}
}
if (num_previous_colons == 1)
return false;
if (i >= 15)
return false;
values[i++] = static_cast<uint8_t>(next_value >> 8);
values[i] = static_cast<uint8_t>(next_value & 0xff);
if (!((i == 15 && !double_colon_index) || (i < 14 && double_colon_index))) {
return false;
}
for (int j = 15; j >= 0;) {
if (double_colon_index && (i == double_colon_index)) {
address->bytes_[j--] = values[i--];
while (j > i)
address->bytes_[j--] = 0;
} else {
address->bytes_[j--] = values[i--];
}
}
address->version_ = Version::kV6;
return true;
}
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 IPEndpointComparator::operator()(const IPEndpoint& a,
const IPEndpoint& b) const {
if (a.address.version() != b.address.version())
return a.address.version() < b.address.version();
if (a.address.IsV4()) {
int ret = memcmp(a.address.bytes_.data(), b.address.bytes_.data(), 4);
if (ret != 0)
return ret < 0;
} else {
int ret = memcmp(a.address.bytes_.data(), b.address.bytes_.data(), 16);
if (ret != 0)
return ret < 0;
}
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;
if (address.IsV4()) {
out << std::dec;
address.CopyToV4(values);
len = 4;
separator = '.';
values_per_separator = 1;
} else if (address.IsV6()) {
out << std::hex;
address.CopyToV6(values);
len = 16;
separator = ':';
values_per_separator = 2;
}
out << std::setfill('0') << std::right;
for (size_t i = 0; i < len; ++i) {
if (i > 0 && (i % values_per_separator == 0)) {
out << separator;
}
out << std::setw(2) << 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);
}
} // namespace openscreen