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

Side by Side Diff: net/base/ip_address.cc

Issue 2881673002: Avoid heap allocations in IPAddress (Closed)
Patch Set: More fixes Created 3 years, 7 months 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 unified diff | Download patch
OLDNEW
1 // Copyright (c) 2015 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2015 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "net/base/ip_address.h" 5 #include "net/base/ip_address.h"
6 6
7 #include <limits.h> 7 #include <limits.h>
8 8
9 #include "base/strings/string_piece.h" 9 #include "base/strings/string_piece.h"
10 #include "base/strings/string_split.h" 10 #include "base/strings/string_split.h"
11 #include "base/strings/stringprintf.h" 11 #include "base/strings/stringprintf.h"
12 #include "net/base/parse_number.h" 12 #include "net/base/parse_number.h"
13 #include "url/gurl.h" 13 #include "url/gurl.h"
14 #include "url/url_canon_ip.h" 14 #include "url/url_canon_ip.h"
15 15
16 namespace { 16 namespace {
17 17
18 // The prefix for IPv6 mapped IPv4 addresses. 18 // The prefix for IPv6 mapped IPv4 addresses.
19 // https://tools.ietf.org/html/rfc4291#section-2.5.5.2 19 // https://tools.ietf.org/html/rfc4291#section-2.5.5.2
20 const uint8_t kIPv4MappedPrefix[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xFF, 0xFF}; 20 const uint8_t kIPv4MappedPrefix[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xFF, 0xFF};
21 21
22 // Note that this function assumes: 22 // Note that this function assumes:
23 // * |ip_address| is at least |prefix_length_in_bits| (bits) long; 23 // * |ip_address| is at least |prefix_length_in_bits| (bits) long;
24 // * |ip_prefix| is at least |prefix_length_in_bits| (bits) long. 24 // * |ip_prefix| is at least |prefix_length_in_bits| (bits) long.
25 bool IPAddressPrefixCheck(const std::vector<uint8_t>& ip_address, 25 bool IPAddressPrefixCheck(const net::IPAddress::IPAddressBytes& ip_address,
26 const uint8_t* ip_prefix, 26 const uint8_t* ip_prefix,
27 size_t prefix_length_in_bits) { 27 size_t prefix_length_in_bits) {
28 // Compare all the bytes that fall entirely within the prefix. 28 // Compare all the bytes that fall entirely within the prefix.
29 size_t num_entire_bytes_in_prefix = prefix_length_in_bits / 8; 29 size_t num_entire_bytes_in_prefix = prefix_length_in_bits / 8;
30 for (size_t i = 0; i < num_entire_bytes_in_prefix; ++i) { 30 for (size_t i = 0; i < num_entire_bytes_in_prefix; ++i) {
31 if (ip_address[i] != ip_prefix[i]) 31 if (ip_address[i] != ip_prefix[i])
32 return false; 32 return false;
33 } 33 }
34 34
35 // In case the prefix was not a multiple of 8, there will be 1 byte 35 // In case the prefix was not a multiple of 8, there will be 1 byte
36 // which is only partially masked. 36 // which is only partially masked.
37 size_t remaining_bits = prefix_length_in_bits % 8; 37 size_t remaining_bits = prefix_length_in_bits % 8;
38 if (remaining_bits != 0) { 38 if (remaining_bits != 0) {
39 uint8_t mask = 0xFF << (8 - remaining_bits); 39 uint8_t mask = 0xFF << (8 - remaining_bits);
40 size_t i = num_entire_bytes_in_prefix; 40 size_t i = num_entire_bytes_in_prefix;
41 if ((ip_address[i] & mask) != (ip_prefix[i] & mask)) 41 if ((ip_address[i] & mask) != (ip_prefix[i] & mask))
42 return false; 42 return false;
43 } 43 }
44 return true; 44 return true;
45 } 45 }
46 46
47 // Returns true if |ip_address| matches any of the reserved IPv4 ranges. This 47 // Returns true if |ip_address| matches any of the reserved IPv4 ranges. This
48 // method operates on a blacklist of reserved IPv4 ranges. Some ranges are 48 // method operates on a blacklist of reserved IPv4 ranges. Some ranges are
49 // consolidated. 49 // consolidated.
50 // Sources for info: 50 // Sources for info:
51 // www.iana.org/assignments/ipv4-address-space/ipv4-address-space.xhtml 51 // www.iana.org/assignments/ipv4-address-space/ipv4-address-space.xhtml
52 // www.iana.org/assignments/iana-ipv4-special-registry/ 52 // www.iana.org/assignments/iana-ipv4-special-registry/
53 // iana-ipv4-special-registry.xhtml 53 // iana-ipv4-special-registry.xhtml
54 bool IsReservedIPv4(const std::vector<uint8_t>& ip_address) { 54 bool IsReservedIPv4(const net::IPAddress::IPAddressBytes& ip_address) {
55 // Different IP versions have different range reservations. 55 // Different IP versions have different range reservations.
56 DCHECK_EQ(net::IPAddress::kIPv4AddressSize, ip_address.size()); 56 DCHECK_EQ(net::IPAddress::kIPv4AddressSize, ip_address.size());
57 struct { 57 struct {
58 const uint8_t address[4]; 58 const uint8_t address[4];
59 size_t prefix_length_in_bits; 59 size_t prefix_length_in_bits;
60 } static const kReservedIPv4Ranges[] = { 60 } static const kReservedIPv4Ranges[] = {
61 {{0, 0, 0, 0}, 8}, {{10, 0, 0, 0}, 8}, {{100, 64, 0, 0}, 10}, 61 {{0, 0, 0, 0}, 8}, {{10, 0, 0, 0}, 8}, {{100, 64, 0, 0}, 10},
62 {{127, 0, 0, 0}, 8}, {{169, 254, 0, 0}, 16}, {{172, 16, 0, 0}, 12}, 62 {{127, 0, 0, 0}, 8}, {{169, 254, 0, 0}, 16}, {{172, 16, 0, 0}, 12},
63 {{192, 0, 2, 0}, 24}, {{192, 88, 99, 0}, 24}, {{192, 168, 0, 0}, 16}, 63 {{192, 0, 2, 0}, 24}, {{192, 88, 99, 0}, 24}, {{192, 168, 0, 0}, 16},
64 {{198, 18, 0, 0}, 15}, {{198, 51, 100, 0}, 24}, {{203, 0, 113, 0}, 24}, 64 {{198, 18, 0, 0}, 15}, {{198, 51, 100, 0}, 24}, {{203, 0, 113, 0}, 24},
65 {{224, 0, 0, 0}, 3}}; 65 {{224, 0, 0, 0}, 3}};
66 66
67 for (const auto& range : kReservedIPv4Ranges) { 67 for (const auto& range : kReservedIPv4Ranges) {
68 if (IPAddressPrefixCheck(ip_address, range.address, 68 if (IPAddressPrefixCheck(ip_address, range.address,
69 range.prefix_length_in_bits)) { 69 range.prefix_length_in_bits)) {
70 return true; 70 return true;
71 } 71 }
72 } 72 }
73 73
74 return false; 74 return false;
75 } 75 }
76 76
77 // Returns true if |ip_address| matches any of the reserved IPv6 ranges. This 77 // Returns true if |ip_address| matches any of the reserved IPv6 ranges. This
78 // method operates on a whitelist of non-reserved IPv6 ranges. All IPv6 78 // method operates on a whitelist of non-reserved IPv6 ranges. All IPv6
79 // addresses outside these ranges are reserved. 79 // addresses outside these ranges are reserved.
80 // Sources for info: 80 // Sources for info:
81 // www.iana.org/assignments/ipv6-address-space/ipv6-address-space.xhtml 81 // www.iana.org/assignments/ipv6-address-space/ipv6-address-space.xhtml
82 bool IsReservedIPv6(const std::vector<uint8_t>& ip_address) { 82 bool IsReservedIPv6(const net::IPAddress::IPAddressBytes& ip_address) {
83 // Different IP versions have different range reservations. 83 // Different IP versions have different range reservations.
84 DCHECK_EQ(net::IPAddress::kIPv6AddressSize, ip_address.size()); 84 DCHECK_EQ(net::IPAddress::kIPv6AddressSize, ip_address.size());
85 struct { 85 struct {
86 const uint8_t address_prefix[2]; 86 const uint8_t address_prefix[2];
87 size_t prefix_length_in_bits; 87 size_t prefix_length_in_bits;
88 } static const kPublicIPv6Ranges[] = { 88 } static const kPublicIPv6Ranges[] = {
89 // 2000::/3 -- Global Unicast 89 // 2000::/3 -- Global Unicast
90 {{0x20, 0}, 3}, 90 {{0x20, 0}, 3},
91 // ff00::/8 -- Multicast 91 // ff00::/8 -- Multicast
92 {{0xff, 0}, 8}, 92 {{0xff, 0}, 8},
93 }; 93 };
94 94
95 for (const auto& range : kPublicIPv6Ranges) { 95 for (const auto& range : kPublicIPv6Ranges) {
96 if (IPAddressPrefixCheck(ip_address, range.address_prefix, 96 if (IPAddressPrefixCheck(ip_address, range.address_prefix,
97 range.prefix_length_in_bits)) { 97 range.prefix_length_in_bits)) {
98 return false; 98 return false;
99 } 99 }
100 } 100 }
101 101
102 return true; 102 return true;
103 } 103 }
104 104
105 bool ParseIPLiteralToBytes(const base::StringPiece& ip_literal, 105 bool ParseIPLiteralToBytes(const base::StringPiece& ip_literal,
106 std::vector<uint8_t>* bytes) { 106 net::IPAddress::IPAddressBytes* bytes) {
107 // |ip_literal| could be either an IPv4 or an IPv6 literal. If it contains 107 // |ip_literal| could be either an IPv4 or an IPv6 literal. If it contains
108 // a colon however, it must be an IPv6 address. 108 // a colon however, it must be an IPv6 address.
109 if (ip_literal.find(':') != base::StringPiece::npos) { 109 if (ip_literal.find(':') != base::StringPiece::npos) {
110 // GURL expects IPv6 hostnames to be surrounded with brackets. 110 // GURL expects IPv6 hostnames to be surrounded with brackets.
111 std::string host_brackets = "["; 111 std::string host_brackets = "[";
112 ip_literal.AppendToString(&host_brackets); 112 ip_literal.AppendToString(&host_brackets);
113 host_brackets.push_back(']'); 113 host_brackets.push_back(']');
114 url::Component host_comp(0, host_brackets.size()); 114 url::Component host_comp(0, host_brackets.size());
115 115
116 // Try parsing the hostname as an IPv6 literal. 116 // Try parsing the hostname as an IPv6 literal.
117 bytes->resize(16); // 128 bits. 117 bytes->resize(16); // 128 bits.
118 return url::IPv6AddressToNumber(host_brackets.data(), host_comp, 118 return url::IPv6AddressToNumber(host_brackets.data(), host_comp,
119 bytes->data()); 119 bytes->data());
120 } 120 }
121 121
122 // Otherwise the string is an IPv4 address. 122 // Otherwise the string is an IPv4 address.
123 bytes->resize(4); // 32 bits. 123 bytes->resize(4); // 32 bits.
124 url::Component host_comp(0, ip_literal.size()); 124 url::Component host_comp(0, ip_literal.size());
125 int num_components; 125 int num_components;
126 url::CanonHostInfo::Family family = url::IPv4AddressToNumber( 126 url::CanonHostInfo::Family family = url::IPv4AddressToNumber(
127 ip_literal.data(), host_comp, bytes->data(), &num_components); 127 ip_literal.data(), host_comp, bytes->data(), &num_components);
128 return family == url::CanonHostInfo::IPV4; 128 return family == url::CanonHostInfo::IPV4;
129 } 129 }
130 130
131 } // namespace 131 } // namespace
132 132
133 namespace net { 133 namespace net {
134 134
135 IPAddress::IPAddressBytes::IPAddressBytes() : size_(0) {}
136
137 net::IPAddress::IPAddressBytes::~IPAddressBytes() {}
138 net::IPAddress::IPAddressBytes::IPAddressBytes(
139 net::IPAddress::IPAddressBytes const& other) = default;
140
141 bool operator<(IPAddress::IPAddressBytes lhs, IPAddress::IPAddressBytes rhs) {
eroman 2017/05/12 23:25:04 std::array<> already defines these operators (as l
Ryan Hamilton 2017/05/13 13:20:46 Ah! So I implemented this initially, and while it
eroman 2017/05/15 22:15:23 Glad that there is test coverage to catch this!
142 if (lhs.size_ < rhs.size_)
143 return true;
144 if (lhs.size_ > rhs.size_)
145 return false;
146 for (size_t i = 0; i < lhs.size_; ++i) {
147 if (lhs.bytes_[i] < rhs.bytes_[i])
148 return true;
149 }
150 return false;
151 }
152
153 bool operator>(IPAddress::IPAddressBytes lhs, IPAddress::IPAddressBytes rhs) {
154 if (lhs.size_ > rhs.size_)
155 return true;
156 if (lhs.size_ < rhs.size_)
157 return false;
158 for (size_t i = 0; i < lhs.size_; ++i) {
159 if (lhs.bytes_[i] > rhs.bytes_[i])
160 return true;
161 }
162 return false;
163 }
164
165 bool operator==(IPAddress::IPAddressBytes lhs, IPAddress::IPAddressBytes rhs) {
166 if (lhs.size_ != rhs.size_)
167 return false;
168 for (size_t i = 0; i < lhs.size_; ++i) {
169 if (lhs.bytes_[i] != rhs.bytes_[i])
170 return false;
171 }
172 return true;
173 }
174
175 bool operator!=(IPAddress::IPAddressBytes lhs, IPAddress::IPAddressBytes rhs) {
176 return !(lhs == rhs);
177 }
178
135 IPAddress::IPAddress() {} 179 IPAddress::IPAddress() {}
136 180
137 IPAddress::IPAddress(const std::vector<uint8_t>& address) 181 IPAddress::IPAddress(const std::vector<uint8_t>& address) {
eroman 2017/05/12 23:25:05 I am a bit worried that we have codepaths that may
Ryan Hamilton 2017/05/13 13:20:47 Fair enough! I added a CHECK() in the IPAddressByt
138 : ip_address_(address) {} 182 for (uint8_t val : address)
183 ip_address_.push_back(val);
eroman 2017/05/12 23:25:05 may want to do a single memcpy(). Or perhaps code
Ryan Hamilton 2017/05/13 13:20:46 Done.
184 }
139 185
140 IPAddress::IPAddress(const IPAddress& other) = default; 186 IPAddress::IPAddress(const IPAddress& other) = default;
141 187
142 IPAddress::IPAddress(const uint8_t* address, size_t address_len) 188 IPAddress::IPAddress(const uint8_t* address, size_t address_len) {
143 : ip_address_(address, address + address_len) {} 189 for (size_t i = 0; i < address_len; ++i)
190 ip_address_.push_back(address[i]);
eroman 2017/05/12 23:25:04 Same comments as above.
Ryan Hamilton 2017/05/13 13:20:47 Done.
191 }
144 192
145 IPAddress::IPAddress(uint8_t b0, uint8_t b1, uint8_t b2, uint8_t b3) { 193 IPAddress::IPAddress(uint8_t b0, uint8_t b1, uint8_t b2, uint8_t b3) {
146 ip_address_.reserve(4);
147 ip_address_.push_back(b0); 194 ip_address_.push_back(b0);
148 ip_address_.push_back(b1); 195 ip_address_.push_back(b1);
149 ip_address_.push_back(b2); 196 ip_address_.push_back(b2);
150 ip_address_.push_back(b3); 197 ip_address_.push_back(b3);
151 } 198 }
152 199
153 IPAddress::IPAddress(uint8_t b0, 200 IPAddress::IPAddress(uint8_t b0,
154 uint8_t b1, 201 uint8_t b1,
155 uint8_t b2, 202 uint8_t b2,
156 uint8_t b3, 203 uint8_t b3,
157 uint8_t b4, 204 uint8_t b4,
158 uint8_t b5, 205 uint8_t b5,
159 uint8_t b6, 206 uint8_t b6,
160 uint8_t b7, 207 uint8_t b7,
161 uint8_t b8, 208 uint8_t b8,
162 uint8_t b9, 209 uint8_t b9,
163 uint8_t b10, 210 uint8_t b10,
164 uint8_t b11, 211 uint8_t b11,
165 uint8_t b12, 212 uint8_t b12,
166 uint8_t b13, 213 uint8_t b13,
167 uint8_t b14, 214 uint8_t b14,
168 uint8_t b15) { 215 uint8_t b15) {
169 const uint8_t address[] = {b0, b1, b2, b3, b4, b5, b6, b7, 216 ip_address_.push_back(b0);
170 b8, b9, b10, b11, b12, b13, b14, b15}; 217 ip_address_.push_back(b1);
171 ip_address_ = std::vector<uint8_t>(std::begin(address), std::end(address)); 218 ip_address_.push_back(b2);
219 ip_address_.push_back(b3);
220 ip_address_.push_back(b4);
221 ip_address_.push_back(b5);
222 ip_address_.push_back(b6);
223 ip_address_.push_back(b7);
224 ip_address_.push_back(b8);
225 ip_address_.push_back(b9);
226 ip_address_.push_back(b10);
227 ip_address_.push_back(b11);
228 ip_address_.push_back(b12);
229 ip_address_.push_back(b13);
230 ip_address_.push_back(b14);
231 ip_address_.push_back(b15);
172 } 232 }
173 233
174 IPAddress::~IPAddress() {} 234 IPAddress::~IPAddress() {}
175 235
176 bool IPAddress::IsIPv4() const { 236 bool IPAddress::IsIPv4() const {
177 return ip_address_.size() == kIPv4AddressSize; 237 return ip_address_.size() == kIPv4AddressSize;
178 } 238 }
179 239
180 bool IPAddress::IsIPv6() const { 240 bool IPAddress::IsIPv6() const {
181 return ip_address_.size() == kIPv6AddressSize; 241 return ip_address_.size() == kIPv6AddressSize;
182 } 242 }
183 243
184 bool IPAddress::IsValid() const { 244 bool IPAddress::IsValid() const {
185 return IsIPv4() || IsIPv6(); 245 return IsIPv4() || IsIPv6();
186 } 246 }
187 247
188 bool IPAddress::IsReserved() const { 248 bool IPAddress::IsReserved() const {
189 if (IsIPv4()) { 249 if (IsIPv4()) {
190 return IsReservedIPv4(ip_address_); 250 return IsReservedIPv4(ip_address_);
191 } else if (IsIPv6()) { 251 } else if (IsIPv6()) {
192 return IsReservedIPv6(ip_address_); 252 return IsReservedIPv6(ip_address_);
193 } 253 }
194 return false; 254 return false;
195 } 255 }
196 256
197 bool IPAddress::IsZero() const { 257 bool IPAddress::IsZero() const {
198 for (auto x : ip_address_) { 258 for (auto x : ip_address_) {
eroman 2017/05/12 23:25:04 Fancy. So this works because of the begin() and en
Ryan Hamilton 2017/05/13 13:20:46 Right?! Every once in a while, C++ doesn't suck. T
199 if (x != 0) 259 if (x != 0)
200 return false; 260 return false;
201 } 261 }
202 262
203 return !empty(); 263 return !empty();
204 } 264 }
205 265
206 bool IPAddress::IsIPv4MappedIPv6() const { 266 bool IPAddress::IsIPv4MappedIPv6() const {
207 return IsIPv6() && IPAddressStartsWith(*this, kIPv4MappedPrefix); 267 return IsIPv6() && IPAddressStartsWith(*this, kIPv4MappedPrefix);
208 } 268 }
209 269
210 bool IPAddress::AssignFromIPLiteral(const base::StringPiece& ip_literal) { 270 bool IPAddress::AssignFromIPLiteral(const base::StringPiece& ip_literal) {
211 std::vector<uint8_t> number; 271 IPAddressBytes number;
212 272
213 if (!ParseIPLiteralToBytes(ip_literal, &number)) 273 if (!ParseIPLiteralToBytes(ip_literal, &number))
214 return false; 274 return false;
215 275
216 std::swap(number, ip_address_); 276 std::swap(number, ip_address_);
eroman 2017/05/12 23:25:05 Should probably just change this to assignment now
Ryan Hamilton 2017/05/13 13:20:46 Done.
217 return true; 277 return true;
218 } 278 }
219 279
280 std::vector<uint8_t> IPAddress::BytesAsVector() const {
281 return std::vector<uint8_t>(ip_address_.begin(), ip_address_.end());
282 }
283
220 // static 284 // static
221 IPAddress IPAddress::IPv4Localhost() { 285 IPAddress IPAddress::IPv4Localhost() {
222 static const uint8_t kLocalhostIPv4[] = {127, 0, 0, 1}; 286 static const uint8_t kLocalhostIPv4[] = {127, 0, 0, 1};
223 return IPAddress(kLocalhostIPv4); 287 return IPAddress(kLocalhostIPv4);
224 } 288 }
225 289
226 // static 290 // static
227 IPAddress IPAddress::IPv6Localhost() { 291 IPAddress IPAddress::IPv6Localhost() {
228 static const uint8_t kLocalhostIPv6[] = {0, 0, 0, 0, 0, 0, 0, 0, 292 static const uint8_t kLocalhostIPv6[] = {0, 0, 0, 0, 0, 0, 0, 0,
229 0, 0, 0, 0, 0, 0, 0, 1}; 293 0, 0, 0, 0, 0, 0, 0, 1};
(...skipping 161 matching lines...) Expand 10 before | Expand all | Expand 10 after
391 if (diff & (1 << (CHAR_BIT - 1))) 455 if (diff & (1 << (CHAR_BIT - 1)))
392 return i * CHAR_BIT + j; 456 return i * CHAR_BIT + j;
393 diff <<= 1; 457 diff <<= 1;
394 } 458 }
395 NOTREACHED(); 459 NOTREACHED();
396 } 460 }
397 return a1.size() * CHAR_BIT; 461 return a1.size() * CHAR_BIT;
398 } 462 }
399 463
400 unsigned MaskPrefixLength(const IPAddress& mask) { 464 unsigned MaskPrefixLength(const IPAddress& mask) {
401 std::vector<uint8_t> all_ones(mask.size(), 0xFF); 465 std::vector<uint8_t> all_ones(mask.size(), 0xFF);
eroman 2017/05/12 23:25:05 If IPAddress were mutable (i.e. merge IPAddressByt
Ryan Hamilton 2017/05/13 13:20:47 How 'bout we do the flattening in a follow up sinc
eroman 2017/05/15 22:15:23 sgtm
402 return CommonPrefixLength(mask, IPAddress(all_ones)); 466 return CommonPrefixLength(mask, IPAddress(all_ones));
403 } 467 }
404 468
405 } // namespace net 469 } // namespace net
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698