Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(416)

Unified Diff: net/base/ip_address.cc

Issue 1408803010: Add IPAddress class as a replacement for the IPAddressNumber typedef. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Created 5 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: net/base/ip_address.cc
diff --git a/net/base/ip_address.cc b/net/base/ip_address.cc
new file mode 100644
index 0000000000000000000000000000000000000000..9b76f2e34319f48fd0e4f80a9b843fc5e57eb59b
--- /dev/null
+++ b/net/base/ip_address.cc
@@ -0,0 +1,390 @@
+// Copyright (c) 2015 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 "net/base/ip_address.h"
+
+#include "base/logging.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/string_split.h"
+#include "base/strings/stringprintf.h"
+#include "base/sys_byteorder.h"
+#include "url/gurl.h"
+#include "url/url_canon_ip.h"
+
+namespace net {
+
+const unsigned char IPAddress::kIPv4MappedPrefix[] = {0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0xFF, 0xFF};
+
+IPAddress::IPAddress() {}
+
+IPAddress::~IPAddress() {}
+
+IPAddress::IPAddress(std::vector<unsigned char>& ip_address)
+ : ip_address_(ip_address) {}
+
+// Don't compare IPv4 and IPv6 addresses (they have different range
+// reservations). Keep separate reservation arrays for each IP type, and
+// consolidate adjacent reserved ranges within a reservation array when
+// possible.
+// Sources for info:
+// www.iana.org/assignments/ipv4-address-space/ipv4-address-space.xhtml
+// www.iana.org/assignments/ipv6-address-space/ipv6-address-space.xhtml
+// They're formatted here with the prefix as the last element. For example:
+// 10.0.0.0/8 becomes 10,0,0,0,8 and fec0::/10 becomes 0xfe,0xc0,0,0,0...,10.
+bool IPAddress::IsIPAddressReserved() const {
+ static const unsigned char kReservedIPv4[][5] = {
+ {0, 0, 0, 0, 8}, {10, 0, 0, 0, 8}, {100, 64, 0, 0, 10},
+ {127, 0, 0, 0, 8}, {169, 254, 0, 0, 16}, {172, 16, 0, 0, 12},
+ {192, 0, 2, 0, 24}, {192, 88, 99, 0, 24}, {192, 168, 0, 0, 16},
+ {198, 18, 0, 0, 15}, {198, 51, 100, 0, 24}, {203, 0, 113, 0, 24},
+ {224, 0, 0, 0, 3}};
+ static const unsigned char kReservedIPv6[][17] = {
+ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8},
+ {0x40, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2},
+ {0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2},
+ {0xc0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3},
+ {0xe0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4},
+ {0xf0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5},
+ {0xf8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6},
+ {0xfc, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7},
+ {0xfe, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9},
+ {0xfe, 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10},
+ {0xfe, 0xc0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10},
+ };
+ size_t array_size = 0;
+ const unsigned char* array = NULL;
+ switch (ip_address_.size()) {
+ case kIPv4AddressSize:
+ array_size = arraysize(kReservedIPv4);
+ array = kReservedIPv4[0];
+ break;
+ case kIPv6AddressSize:
+ array_size = arraysize(kReservedIPv6);
+ array = kReservedIPv6[0];
+ break;
+ }
+ if (!array)
+ return false;
+ size_t width = ip_address_.size() + 1;
+ for (size_t i = 0; i < array_size; ++i, array += width) {
+ if (IPAddressPrefixCheck(array, array[width - 1]))
+ return true;
+ }
+ return false;
+}
+
+std::string IPAddress::ToString() const {
+ std::string str;
+ url::StdStringCanonOutput output(&str);
+
+ if (IsIPv4()) {
+ url::AppendIPv4Address(&ip_address_.front(), &output);
+ } else if (IsIPv6()) {
+ url::AppendIPv6Address(&ip_address_.front(), &output);
+ } else {
+ size_t address_len = ip_address_.size();
+ CHECK(false) << "Invalid IP address with length: " << address_len;
+ }
+
+ output.Complete();
+ return str;
+}
+
+std::string IPAddress::ToStringWithPort(uint16_t port) const {
+ std::string address_str = ToString();
+
+ if (IsIPv6()) {
+ // Need to bracket IPv6 addresses since they contain colons.
+ return base::StringPrintf("[%s]:%d", address_str.c_str(), port);
+ }
+ return base::StringPrintf("%s:%d", address_str.c_str(), port);
+}
+
+std::string IPAddress::ToPackedString() const {
+ return std::string(reinterpret_cast<const char*>(&ip_address_.front()),
+ ip_address_.size());
+}
+
+// Returns number of matching initial bits between the addresses and |other|.
+unsigned IPAddress::CommonPrefixLength(const IPAddress& other) const {
+ DCHECK_EQ(ip_address_.size(), other.ip_address_.size());
+ for (size_t i = 0; i < ip_address_.size(); ++i) {
+ unsigned diff = ip_address_[i] ^ other.ip_address_[i];
+ if (!diff)
+ continue;
+ for (unsigned j = 0; j < CHAR_BIT; ++j) {
+ if (diff & (1 << (CHAR_BIT - 1)))
+ return i * CHAR_BIT + j;
+ diff <<= 1;
+ }
+ NOTREACHED();
+ }
+ return ip_address_.size() * CHAR_BIT;
+}
+
+// Computes the number of leading 1-bits in |ip_address_|.
+unsigned IPAddress::MaskPrefixLength() const {
+ std::vector<unsigned char> all_ones_mask(ip_address_.size(), 0xFF);
+ IPAddress all_ones(all_ones_mask);
+ return all_ones.CommonPrefixLength(all_ones);
+}
+
+bool IPAddress::IsIPv4Mapped() const {
+ if (!IsIPv6())
+ return false;
+ return std::equal(ip_address_.begin(),
+ ip_address_.begin() + arraysize(kIPv4MappedPrefix),
+ kIPv4MappedPrefix);
+}
+
+AddressFamily IPAddress::GetAddressFamily() const {
+ switch (ip_address_.size()) {
+ case kIPv4AddressSize:
+ return ADDRESS_FAMILY_IPV4;
+ case kIPv6AddressSize:
+ return ADDRESS_FAMILY_IPV6;
+ default:
+ return ADDRESS_FAMILY_UNSPECIFIED;
+ }
+}
+
+bool IPAddress::MatchesPrefix(const IPAddress& ip_prefix,
+ size_t prefix_length_in_bits) {
+ // Both the input IP address and the prefix IP address should be
+ // either IPv4 or IPv6.
+ DCHECK(IsIPv4() || IsIPv6());
+ DCHECK(ip_prefix.IsIPv4() || ip_prefix.IsIPv6());
+
+ DCHECK_LE(prefix_length_in_bits, ip_prefix.ip_address_.size() * 8);
+
+ // In case we have an IPv6 / IPv4 mismatch, convert the IPv4 addresses to
+ // IPv6 addresses in order to do the comparison.
+ if (ip_address_.size() != ip_prefix.ip_address_.size()) {
+ if (IsIPv4()) {
+ IPAddress temp = IPAddress::ConvertIPv4ToIPv6(*this);
+ return temp.MatchesPrefix(ip_prefix, prefix_length_in_bits);
+ }
+ IPAddress temp = IPAddress::ConvertIPv4ToIPv6(ip_prefix);
+ return MatchesPrefix(temp, 96 + prefix_length_in_bits);
+ }
+
+ return IPAddressPrefixCheck(&ip_prefix, prefix_length_in_bits);
+}
+
+bool IPAddress::ToSockAddrWithPort(struct sockaddr* address,
+ socklen_t* address_length,
+ int port) const {
+ DCHECK(address);
+ DCHECK(address_length);
+ switch (ip_address_.size()) {
+ case kIPv4AddressSize: {
+ socklen_t kSockaddrInSize = sizeof(struct sockaddr_in);
+ if (*address_length < kSockaddrInSize)
+ return false;
+ *address_length = kSockaddrInSize;
+ struct sockaddr_in* addr = reinterpret_cast<struct sockaddr_in*>(address);
+ memset(addr, 0, sizeof(struct sockaddr_in));
+ addr->sin_family = AF_INET;
+ addr->sin_port = base::HostToNet16(port);
+ memcpy(&addr->sin_addr, &ip_address_[0], kIPv4AddressSize);
+ break;
+ }
+ case kIPv6AddressSize: {
+ socklen_t kSockaddrInSize = sizeof(struct sockaddr_in6);
+ if (*address_length < kSockaddrInSize)
+ return false;
+ *address_length = kSockaddrInSize;
+ struct sockaddr_in6* addr6 =
+ reinterpret_cast<struct sockaddr_in6*>(address);
+ memset(addr6, 0, sizeof(struct sockaddr_in6));
+ addr6->sin6_family = AF_INET6;
+ addr6->sin6_port = base::HostToNet16(port);
+ memcpy(&addr6->sin6_addr, &ip_address_[0], kIPv6AddressSize);
+ break;
+ }
+ default:
+ return false;
+ }
+ return true;
+}
+
+// static
+bool IPAddress::FromURLHostname(const std::string& hostname,
+ IPAddress* ip_address) {
+ // |hostname| is an already canoncalized hostname, conforming to RFC 3986.
+ // For an IP address, this is defined in Section 3.2.2 of RFC 3986, with
+ // the canonical form for IPv6 addresses defined in Section 4 of RFC 5952.
+ url::Component host_comp(0, hostname.size());
+
+ std::vector<unsigned char> ip;
+
+ // If it has a bracket, try parsing it as an IPv6 address.
+ if (hostname[0] == '[') {
+ ip.resize(16); // 128 bits.
+ bool result =
+ url::IPv6AddressToNumber(hostname.data(), host_comp, &(ip)[0]);
+ ip_address->ip_address_ = ip;
+ return result;
+ }
+
+ // Otherwise, try IPv4.
+ ip.resize(4); // 32 bits.
+ int num_components;
+ url::CanonHostInfo::Family family = url::IPv4AddressToNumber(
+ hostname.data(), host_comp, &(ip)[0], &num_components);
+ ip_address->ip_address_ = ip;
+ return family == url::CanonHostInfo::IPV4;
+}
+
+// static
+bool IPAddress::FromIPLiteral(const base::StringPiece& ip_literal,
+ IPAddress* ip_address) {
+ std::vector<unsigned char> ip;
+
+ // |ip_literal| could be either a IPv4 or an IPv6 literal. If it contains
+ // a colon however, it must be an IPv6 address.
+ if (ip_literal.find(':') != base::StringPiece::npos) {
+ // GURL expects IPv6 hostnames to be surrounded with brackets.
+ std::string host_brackets = "[";
+ ip_literal.AppendToString(&host_brackets);
+ host_brackets.push_back(']');
+ url::Component host_comp(0, host_brackets.size());
+
+ // Try parsing the hostname as an IPv6 literal.
+ ip.resize(16); // 128 bits.
+ bool result =
+ url::IPv6AddressToNumber(host_brackets.data(), host_comp, &(ip)[0]);
+ ip_address->ip_address_ = ip;
+ return result;
+ }
+
+ // Otherwise the string is an IPv4 address.
+ ip.resize(4); // 32 bits.
+ url::Component host_comp(0, ip_literal.size());
+ int num_components;
+ url::CanonHostInfo::Family family = url::IPv4AddressToNumber(
+ ip_literal.data(), host_comp, &(ip)[0], &num_components);
+
+ ip_address->ip_address_ = ip;
+ return family == url::CanonHostInfo::IPV4;
+}
+
+// static
+bool IPAddress::FromCIDRBlock(const std::string& cidr_literal,
+ IPAddress* ip_address,
+ size_t* prefix_length_in_bits) {
+ // We expect CIDR notation to match one of these two templates:
+ // <IPv4-literal> "/" <number of bits>
+ // <IPv6-literal> "/" <number of bits>
+
+ std::vector<base::StringPiece> parts = base::SplitStringPiece(
+ cidr_literal, "/", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL);
+ if (parts.size() != 2)
+ return false;
+
+ // Parse the IP address.
+ if (!IPAddress::FromIPLiteral(parts[0], ip_address))
+ return false;
+
+ // Parse the prefix length.
+ int number_of_bits = -1;
+ if (!base::StringToInt(parts[1], &number_of_bits))
+ return false;
+
+ // Make sure the prefix length is in a valid range.
+ if (number_of_bits < 0 ||
+ number_of_bits > static_cast<int>(ip_address->ip_address_.size() * 8))
+ return false;
+
+ *prefix_length_in_bits = static_cast<size_t>(number_of_bits);
+ return true;
+}
+
+// static
+IPAddress IPAddress::FromLegacy(const IPAddressNumber& ip_address) {
+ std::vector<unsigned char> address_copy(ip_address);
+ IPAddress new_address(address_copy);
+ return new_address;
+}
+
+// static
+IPAddress IPAddress::ConvertIPv4ToIPv6(const IPAddress& ipv4_address) {
+ DCHECK(ipv4_address.IsIPv4());
+
+ // IPv4-mapped addresses are formed by:
+ // <80 bits of zeros> + <16 bits of ones> + <32-bit IPv4 address>.
+ std::vector<unsigned char> ipv6_address;
+ ipv6_address.reserve(16);
+ ipv6_address.insert(ipv6_address.end(), kIPv4MappedPrefix,
+ kIPv4MappedPrefix + arraysize(kIPv4MappedPrefix));
+ ipv6_address.insert(ipv6_address.end(), ipv4_address.ip_address_.begin(),
+ ipv4_address.ip_address_.end());
+ return IPAddress(ipv6_address);
+}
+
+// static
+IPAddress IPAddress::ConvertIPv4MappedToIPv4(const IPAddress& address) {
+ DCHECK(address.IsIPv4Mapped());
+ std::vector<unsigned char> without_prefix(
+ address.ip_address_.begin() + arraysize(kIPv4MappedPrefix),
+ address.ip_address_.end());
+ IPAddress ip_address(without_prefix);
+ return ip_address;
+}
+
+bool IPAddress::operator==(const IPAddress& that) const {
+ return ip_address_ == that.ip_address_;
+}
+
+bool IPAddress::operator!=(const IPAddress& that) const {
+ return ip_address_ != that.ip_address_;
+}
+
+bool IPAddress::operator<(const IPAddress& that) const {
+ // Sort IPv4 before IPv6.
+ if (ip_address_.size() != that.ip_address_.size()) {
+ return ip_address_.size() < that.ip_address_.size();
+ }
+
+ return ip_address_ < that.ip_address_;
+}
+
+bool IPAddress::operator>(const IPAddress& that) const {
+ // Sort IPv4 after IPv6.
+ if (ip_address_.size() != that.ip_address_.size()) {
+ return ip_address_.size() > that.ip_address_.size();
+ }
+
+ return ip_address_ > that.ip_address_;
+}
+
+bool IPAddress::IPAddressPrefixCheck(const IPAddress* ip_prefix,
+ size_t prefix_length_in_bits) const {
+ return IPAddressPrefixCheck(&(ip_prefix->ip_address_)[0],
+ prefix_length_in_bits);
+}
+
+bool IPAddress::IPAddressPrefixCheck(const unsigned char* ip_prefix,
+ size_t prefix_length_in_bits) const {
+ // Compare all the bytes that fall entirely within the prefix.
+ int num_entire_bytes_in_prefix = prefix_length_in_bits / 8;
+ for (int i = 0; i < num_entire_bytes_in_prefix; ++i) {
+ if (ip_address_[i] != ip_prefix[i])
+ return false;
+ }
+
+ // In case the prefix was not a multiple of 8, there will be 1 byte
+ // which is only partially masked.
+ int remaining_bits = prefix_length_in_bits % 8;
+ if (remaining_bits != 0) {
+ unsigned char mask = 0xFF << (8 - remaining_bits);
+ int i = num_entire_bytes_in_prefix;
+ if ((ip_address_[i] & mask) != (ip_prefix[i] & mask))
+ return false;
+ }
+ return true;
+}
+
+} // namespace net

Powered by Google App Engine
This is Rietveld 408576698