1 | /* |
2 | * Copyright (c) Facebook, Inc. and its affiliates. |
3 | * |
4 | * Licensed under the Apache License, Version 2.0 (the "License"); |
5 | * you may not use this file except in compliance with the License. |
6 | * You may obtain a copy of the License at |
7 | * |
8 | * http://www.apache.org/licenses/LICENSE-2.0 |
9 | * |
10 | * Unless required by applicable law or agreed to in writing, software |
11 | * distributed under the License is distributed on an "AS IS" BASIS, |
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
13 | * See the License for the specific language governing permissions and |
14 | * limitations under the License. |
15 | */ |
16 | |
17 | #pragma once |
18 | |
19 | #include <functional> |
20 | #include <iosfwd> |
21 | #include <memory> |
22 | #include <string> |
23 | #include <type_traits> |
24 | #include <utility> // std::pair |
25 | |
26 | #include <folly/ConstexprMath.h> |
27 | #include <folly/IPAddressException.h> |
28 | #include <folly/IPAddressV4.h> |
29 | #include <folly/IPAddressV6.h> |
30 | #include <folly/Range.h> |
31 | #include <folly/detail/IPAddress.h> |
32 | #include <folly/lang/Exception.h> |
33 | |
34 | namespace folly { |
35 | |
36 | class IPAddress; |
37 | |
38 | /** |
39 | * Pair of IPAddress, netmask |
40 | */ |
41 | typedef std::pair<IPAddress, uint8_t> CIDRNetwork; |
42 | |
43 | /** |
44 | * Provides a unified interface for IP addresses. |
45 | * |
46 | * @note If you compare 2 IPAddress instances, v4-to-v6-mapped addresses are |
47 | * compared as V4 addresses. |
48 | * |
49 | * @note toLong/fromLong deal in network byte order, use toLongHBO/fromLongHBO |
50 | * if working in host byte order. |
51 | * |
52 | * Example usage: |
53 | * @code |
54 | * IPAddress v4addr("192.0.2.129"); |
55 | * IPAddress v6map("::ffff:192.0.2.129"); |
56 | * CHECK(v4addr.inSubnet("192.0.2.0/24") == |
57 | * v4addr.inSubnet(IPAddress("192.0.2.0"), 24)); |
58 | * CHECK(v4addr.inSubnet("192.0.2.128/30")); |
59 | * CHECK(!v4addr.inSubnet("192.0.2.128/32")); |
60 | * CHECK(v4addr.asV4().toLong() == 2164392128); |
61 | * CHECK(v4addr.asV4().toLongHBO() == 3221226113); |
62 | * CHECK(v4addr.isV4()); |
63 | * CHECK(v6addr.isV6()); |
64 | * CHECK(v4addr == v6map); |
65 | * CHECK(v6map.isIPv4Mapped()); |
66 | * CHECK(v4addr.asV4() == IPAddress::createIPv4(v6map)); |
67 | * CHECK(IPAddress::createIPv6(v4addr) == v6map.asV6()); |
68 | * @encode |
69 | */ |
70 | class IPAddress { |
71 | private: |
72 | template <typename F> |
73 | auto pick(F f) const { |
74 | return isV4() ? f(asV4()) : isV6() ? f(asV6()) : f(asNone()); |
75 | } |
76 | |
77 | class IPAddressNone { |
78 | public: |
79 | bool isZero() const { |
80 | return true; |
81 | } |
82 | size_t bitCount() const { |
83 | return 0; |
84 | } |
85 | std::string toJson() const { |
86 | return "{family:'AF_UNSPEC', addr:'', hash:0}" ; |
87 | } |
88 | std::size_t hash() const { |
89 | return std::hash<uint64_t>{}(0); |
90 | } |
91 | bool isLoopback() const { |
92 | throw_exception<InvalidAddressFamilyException>("empty address" ); |
93 | } |
94 | bool isLinkLocal() const { |
95 | throw_exception<InvalidAddressFamilyException>("empty address" ); |
96 | } |
97 | bool isLinkLocalBroadcast() const { |
98 | throw_exception<InvalidAddressFamilyException>("empty address" ); |
99 | } |
100 | bool isNonroutable() const { |
101 | throw_exception<InvalidAddressFamilyException>("empty address" ); |
102 | } |
103 | bool isPrivate() const { |
104 | throw_exception<InvalidAddressFamilyException>("empty address" ); |
105 | } |
106 | bool isMulticast() const { |
107 | throw_exception<InvalidAddressFamilyException>("empty address" ); |
108 | } |
109 | IPAddress mask(uint8_t numBits) const { |
110 | (void)numBits; |
111 | return IPAddress(); |
112 | } |
113 | std::string str() const { |
114 | return "" ; |
115 | } |
116 | std::string toFullyQualified() const { |
117 | return "" ; |
118 | } |
119 | void toFullyQualifiedAppend(std::string& out) const { |
120 | (void)out; |
121 | return; |
122 | } |
123 | uint8_t version() const { |
124 | return 0; |
125 | } |
126 | const unsigned char* bytes() const { |
127 | return nullptr; |
128 | } |
129 | }; |
130 | |
131 | IPAddressNone const& asNone() const { |
132 | if (!empty()) { |
133 | throw_exception<InvalidAddressFamilyException>("not empty" ); |
134 | } |
135 | return addr_.ipNoneAddr; |
136 | } |
137 | |
138 | public: |
139 | // returns true iff the input string can be parsed as an ip-address |
140 | static bool validate(StringPiece ip) noexcept; |
141 | |
142 | // return the V4 representation of the address, converting it from V6 to V4 if |
143 | // needed. Note that this will throw an IPAddressFormatException if the V6 |
144 | // address is not IPv4Mapped. |
145 | static IPAddressV4 createIPv4(const IPAddress& addr); |
146 | |
147 | // return the V6 representation of the address, converting it from V4 to V6 if |
148 | // needed. |
149 | static IPAddressV6 createIPv6(const IPAddress& addr); |
150 | |
151 | /** |
152 | * Create a network and mask from a CIDR formatted address string. |
153 | * @param [in] ipSlashCidr IP/CIDR formatted string to split |
154 | * @param [in] defaultCidr default value if no /N specified (if defaultCidr |
155 | * is -1, will use /32 for IPv4 and /128 for IPv6) |
156 | * @param [in] mask apply mask on the address or not, |
157 | * e.g. 192.168.13.46/24 => 192.168.13.0/24 |
158 | * @return either pair with IPAddress network and uint8_t mask or |
159 | * CIDRNetworkError |
160 | */ |
161 | static Expected<CIDRNetwork, CIDRNetworkError> tryCreateNetwork( |
162 | StringPiece ipSlashCidr, |
163 | int defaultCidr = -1, |
164 | bool mask = true); |
165 | |
166 | /** |
167 | * Create a network and mask from a CIDR formatted address string. |
168 | * Same as tryCreateNetwork() but throws IPAddressFormatException on error. |
169 | * The implementation calls tryCreateNetwork(...) underneath |
170 | * |
171 | * @throws IPAddressFormatException if invalid address |
172 | * @return pair with IPAddress network and uint8_t mask |
173 | */ |
174 | static CIDRNetwork createNetwork( |
175 | StringPiece ipSlashCidr, |
176 | int defaultCidr = -1, |
177 | bool mask = true); |
178 | |
179 | /** |
180 | * Return a string representation of a CIDR block created with createNetwork. |
181 | * @param [in] network, pair of address and cidr |
182 | * |
183 | * @return string representing the netblock |
184 | */ |
185 | static std::string networkToString(const CIDRNetwork& network); |
186 | |
187 | /** |
188 | * Create a new IPAddress instance from the provided binary data |
189 | * in network byte order. |
190 | * @throws IPAddressFormatException if len is not 4 or 16 |
191 | */ |
192 | static IPAddress fromBinary(ByteRange bytes); |
193 | |
194 | /** |
195 | * Non-throwing version of fromBinary(). |
196 | * On failure returns IPAddressFormatError. |
197 | */ |
198 | static Expected<IPAddress, IPAddressFormatError> tryFromBinary( |
199 | ByteRange bytes) noexcept; |
200 | |
201 | /** |
202 | * Tries to create a new IPAddress instance from provided string and |
203 | * returns it on success. Returns IPAddressFormatError on failure. |
204 | */ |
205 | static Expected<IPAddress, IPAddressFormatError> tryFromString( |
206 | StringPiece str) noexcept; |
207 | |
208 | /** |
209 | * Create an IPAddress from a 32bit long (network byte order). |
210 | * @throws IPAddressFormatException |
211 | */ |
212 | static IPAddress fromLong(uint32_t src); |
213 | // Same as above, but host byte order |
214 | static IPAddress fromLongHBO(uint32_t src); |
215 | |
216 | // Given 2 IPAddress,mask pairs extract the longest common IPAddress, |
217 | // mask pair |
218 | static CIDRNetwork longestCommonPrefix( |
219 | const CIDRNetwork& one, |
220 | const CIDRNetwork& two); |
221 | |
222 | /** |
223 | * Constructs an uninitialized IPAddress. |
224 | */ |
225 | IPAddress(); |
226 | |
227 | /** |
228 | * Parse an IPAddress from a string representation. |
229 | * |
230 | * Formats accepted are exactly the same as the ones accepted by inet_pton(), |
231 | * using AF_INET6 if the string contains colons, and AF_INET otherwise; |
232 | * with the exception that the whole address can optionally be enclosed |
233 | * in square brackets. |
234 | * |
235 | * @throws IPAddressFormatException |
236 | */ |
237 | explicit IPAddress(StringPiece str); |
238 | |
239 | /** |
240 | * Create an IPAddress from a sockaddr. |
241 | * @throws IPAddressFormatException if nullptr or not AF_INET or AF_INET6 |
242 | */ |
243 | explicit IPAddress(const sockaddr* addr); |
244 | |
245 | // Create an IPAddress from a V4 address |
246 | /* implicit */ IPAddress(const IPAddressV4 ipV4Addr) noexcept; |
247 | /* implicit */ IPAddress(const in_addr addr) noexcept; |
248 | |
249 | // Create an IPAddress from a V6 address |
250 | /* implicit */ IPAddress(const IPAddressV6& ipV6Addr) noexcept; |
251 | /* implicit */ IPAddress(const in6_addr& addr) noexcept; |
252 | |
253 | // Assign from V4 address |
254 | IPAddress& operator=(const IPAddressV4& ipv4_addr) noexcept; |
255 | |
256 | // Assign from V6 address |
257 | IPAddress& operator=(const IPAddressV6& ipv6_addr) noexcept; |
258 | |
259 | /** |
260 | * Converts an IPAddress to an IPAddressV4 instance. |
261 | * @note This is not some handy convenience wrapper to convert an IPv4 address |
262 | * to a mapped IPv6 address. If you want that use |
263 | * IPAddress::createIPv6(addr) |
264 | * @throws InvalidAddressFamilyException is not a V4 instance |
265 | */ |
266 | const IPAddressV4& asV4() const { |
267 | if (UNLIKELY(!isV4())) { |
268 | asV4Throw(); |
269 | } |
270 | return addr_.ipV4Addr; |
271 | } |
272 | |
273 | /** |
274 | * Converts an IPAddress to an IPAddressV6 instance. |
275 | * @throws InvalidAddressFamilyException is not a V6 instance |
276 | */ |
277 | const IPAddressV6& asV6() const { |
278 | if (UNLIKELY(!isV6())) { |
279 | asV6Throw(); |
280 | } |
281 | return addr_.ipV6Addr; |
282 | } |
283 | |
284 | // Return sa_family_t of IPAddress |
285 | sa_family_t family() const { |
286 | return family_; |
287 | } |
288 | |
289 | // Populate sockaddr_storage with an appropriate value |
290 | int toSockaddrStorage(sockaddr_storage* dest, uint16_t port = 0) const { |
291 | if (dest == nullptr) { |
292 | throw IPAddressFormatException("dest must not be null" ); |
293 | } |
294 | memset(dest, 0, sizeof(sockaddr_storage)); |
295 | dest->ss_family = family(); |
296 | |
297 | if (isV4()) { |
298 | sockaddr_in* sin = reinterpret_cast<sockaddr_in*>(dest); |
299 | sin->sin_addr = asV4().toAddr(); |
300 | sin->sin_port = port; |
301 | #if defined(__APPLE__) |
302 | sin->sin_len = sizeof(*sin); |
303 | #endif |
304 | return sizeof(*sin); |
305 | } else if (isV6()) { |
306 | sockaddr_in6* sin = reinterpret_cast<sockaddr_in6*>(dest); |
307 | sin->sin6_addr = asV6().toAddr(); |
308 | sin->sin6_port = port; |
309 | sin->sin6_scope_id = asV6().getScopeId(); |
310 | #if defined(__APPLE__) |
311 | sin->sin6_len = sizeof(*sin); |
312 | #endif |
313 | return sizeof(*sin); |
314 | } else { |
315 | throw InvalidAddressFamilyException(family()); |
316 | } |
317 | } |
318 | |
319 | /** |
320 | * Check if the address is found in the specified CIDR netblock. |
321 | * |
322 | * This will return false if the specified cidrNet is V4, but the address is |
323 | * V6. It will also return false if the specified cidrNet is V6 but the |
324 | * address is V4. This method will do the right thing in the case of a v6 |
325 | * mapped v4 address. |
326 | * |
327 | * @note This is slower than the below counterparts. If perf is important use |
328 | * one of the two argument variations below. |
329 | * @param [in] ipSlashCidr address in "192.168.1.0/24" format |
330 | * @throws IPAddressFormatException if no /mask |
331 | * @return true if address is part of specified subnet with cidr |
332 | */ |
333 | bool inSubnet(StringPiece cidrNetwork) const; |
334 | |
335 | /** |
336 | * Check if an IPAddress belongs to a subnet. |
337 | * @param [in] subnet Subnet to check against (e.g. 192.168.1.0) |
338 | * @param [in] cidr CIDR for subnet (e.g. 24 for /24) |
339 | * @return true if address is part of specified subnet with cidr |
340 | */ |
341 | bool inSubnet(const IPAddress& subnet, uint8_t cidr) const; |
342 | |
343 | /** |
344 | * Check if an IPAddress belongs to the subnet with the given mask. |
345 | * This is the same as inSubnet but the mask is provided instead of looked up |
346 | * from the cidr. |
347 | * @param [in] subnet Subnet to check against |
348 | * @param [in] mask The netmask for the subnet |
349 | * @return true if address is part of the specified subnet with mask |
350 | */ |
351 | bool inSubnetWithMask(const IPAddress& subnet, ByteRange mask) const; |
352 | |
353 | // @return true if address is a v4 mapped address |
354 | bool isIPv4Mapped() const { |
355 | return isV6() && asV6().isIPv4Mapped(); |
356 | } |
357 | |
358 | // @return true if address is uninitialized |
359 | bool empty() const { |
360 | return family_ == AF_UNSPEC; |
361 | } |
362 | |
363 | // @return true if address is initialized |
364 | explicit operator bool() const { |
365 | return !empty(); |
366 | } |
367 | |
368 | // @return true if this is an IPAddressV4 instance |
369 | bool isV4() const { |
370 | return family_ == AF_INET; |
371 | } |
372 | |
373 | // @return true if this is an IPAddressV6 instance |
374 | bool isV6() const { |
375 | return family_ == AF_INET6; |
376 | } |
377 | |
378 | // @return true if this address is all zeros |
379 | bool isZero() const { |
380 | return pick([&](auto& _) { return _.isZero(); }); |
381 | } |
382 | |
383 | // Number of bits in the address representation. |
384 | size_t bitCount() const { |
385 | return pick([&](auto& _) { return _.bitCount(); }); |
386 | } |
387 | // Number of bytes in the address representation. |
388 | size_t byteCount() const { |
389 | return bitCount() / 8; |
390 | } |
391 | // get nth most significant bit - 0 indexed |
392 | bool getNthMSBit(size_t bitIndex) const { |
393 | return detail::getNthMSBitImpl(*this, bitIndex, family()); |
394 | } |
395 | // get nth most significant byte - 0 indexed |
396 | uint8_t getNthMSByte(size_t byteIndex) const; |
397 | // get nth bit - 0 indexed |
398 | bool getNthLSBit(size_t bitIndex) const { |
399 | return getNthMSBit(bitCount() - bitIndex - 1); |
400 | } |
401 | // get nth byte - 0 indexed |
402 | uint8_t getNthLSByte(size_t byteIndex) const { |
403 | return getNthMSByte(byteCount() - byteIndex - 1); |
404 | } |
405 | /** |
406 | * Get human-readable string representation of the address. |
407 | * |
408 | * This prints a string representation of the address, for human consumption |
409 | * or logging. The string will take the form of a JSON object that looks like: |
410 | * {family:'AF_INET|AF_INET6', addr:'address', hash:long}. |
411 | */ |
412 | std::string toJson() const { |
413 | return pick([&](auto& _) { return _.toJson(); }); |
414 | } |
415 | |
416 | // Hash of address |
417 | std::size_t hash() const { |
418 | return pick([&](auto& _) { return _.hash(); }); |
419 | } |
420 | |
421 | // Return true if the address qualifies as localhost. |
422 | bool isLoopback() const { |
423 | return pick([&](auto& _) { return _.isLoopback(); }); |
424 | } |
425 | |
426 | // Return true if the address qualifies as link local |
427 | bool isLinkLocal() const { |
428 | return pick([&](auto& _) { return _.isLinkLocal(); }); |
429 | } |
430 | |
431 | // Return true if the address qualifies as broadcast. |
432 | bool isLinkLocalBroadcast() const { |
433 | return pick([&](auto& _) { return _.isLinkLocalBroadcast(); }); |
434 | } |
435 | |
436 | /** |
437 | * Return true if the address is a special purpose address, as per rfc6890 |
438 | * (i.e. 0.0.0.0). |
439 | * For V6, true if the address is not in one of global scope blocks: |
440 | * 2000::/3, ffxe::/16. |
441 | */ |
442 | bool isNonroutable() const { |
443 | return pick([&](auto& _) { return _.isNonroutable(); }); |
444 | } |
445 | |
446 | /** |
447 | * Return true if the address is private, as per rfc1918 and rfc4193 |
448 | * (for example, 192.168.xxx.xxx or fc00::/7 addresses) |
449 | */ |
450 | bool isPrivate() const { |
451 | return pick([&](auto& _) { return _.isPrivate(); }); |
452 | } |
453 | |
454 | // Return true if the address is a multicast address. |
455 | bool isMulticast() const { |
456 | return pick([&](auto& _) { return _.isMulticast(); }); |
457 | } |
458 | |
459 | /** |
460 | * Creates IPAddress instance with all but most significant numBits set to 0. |
461 | * @param [in] numBits number of bits to mask |
462 | * @throws abort if numBits > bitCount() |
463 | * @return IPAddress instance with bits set to 0 |
464 | */ |
465 | IPAddress mask(uint8_t numBits) const { |
466 | return pick([&](auto& _) { return IPAddress(_.mask(numBits)); }); |
467 | } |
468 | |
469 | /** |
470 | * Provides a string representation of address. |
471 | * @note The string representation is calculated on demand. |
472 | * @throws IPAddressFormatException on inet_ntop error |
473 | */ |
474 | std::string str() const { |
475 | return pick([&](auto& _) { return _.str(); }); |
476 | } |
477 | |
478 | /** |
479 | * Return the fully qualified string representation of the address. |
480 | * For V4 addresses this is the same as calling str(). For V6 addresses |
481 | * this is the hex representation with : characters inserted every 4 digits. |
482 | */ |
483 | std::string toFullyQualified() const { |
484 | return pick([&](auto& _) { return _.toFullyQualified(); }); |
485 | } |
486 | |
487 | /// Same as toFullyQualified but append to an output string. |
488 | void toFullyQualifiedAppend(std::string& out) const { |
489 | return pick([&](auto& _) { return _.toFullyQualifiedAppend(out); }); |
490 | } |
491 | |
492 | // Address version (0 if empty, or 4 or 6 if nonempty) |
493 | uint8_t version() const { |
494 | return pick([&](auto& _) { return _.version(); }); |
495 | } |
496 | |
497 | /** |
498 | * Access to address bytes, in network byte order. |
499 | */ |
500 | const unsigned char* bytes() const { |
501 | return pick([&](auto& _) { return _.bytes(); }); |
502 | } |
503 | |
504 | private: |
505 | [[noreturn]] void asV4Throw() const; |
506 | [[noreturn]] void asV6Throw() const; |
507 | |
508 | typedef union IPAddressV46 { |
509 | IPAddressNone ipNoneAddr; |
510 | IPAddressV4 ipV4Addr; |
511 | IPAddressV6 ipV6Addr; |
512 | IPAddressV46() noexcept : ipNoneAddr() {} |
513 | explicit IPAddressV46(const IPAddressV4& addr) noexcept : ipV4Addr(addr) {} |
514 | explicit IPAddressV46(const IPAddressV6& addr) noexcept : ipV6Addr(addr) {} |
515 | } IPAddressV46; |
516 | IPAddressV46 addr_; |
517 | sa_family_t family_; |
518 | }; |
519 | |
520 | // boost::hash uses hash_value() so this allows boost::hash to work |
521 | // automatically for IPAddress |
522 | std::size_t hash_value(const IPAddress& addr); |
523 | std::ostream& operator<<(std::ostream& os, const IPAddress& addr); |
524 | // Define toAppend() to allow IPAddress to be used with folly::to<string> |
525 | void toAppend(IPAddress addr, std::string* result); |
526 | void toAppend(IPAddress addr, fbstring* result); |
527 | |
528 | /** |
529 | * Return true if two addresses are equal. |
530 | * |
531 | * @note This takes into consideration V4 mapped addresses as well. If one |
532 | * address is v4 mapped we compare the v4 addresses. |
533 | * |
534 | * @return true if the two addresses are equal. |
535 | */ |
536 | bool operator==(const IPAddress& addr1, const IPAddress& addr2); |
537 | // Return true if addr1 < addr2 |
538 | bool operator<(const IPAddress& addr1, const IPAddress& addr2); |
539 | // Derived operators |
540 | inline bool operator!=(const IPAddress& a, const IPAddress& b) { |
541 | return !(a == b); |
542 | } |
543 | inline bool operator>(const IPAddress& a, const IPAddress& b) { |
544 | return b < a; |
545 | } |
546 | inline bool operator<=(const IPAddress& a, const IPAddress& b) { |
547 | return !(a > b); |
548 | } |
549 | inline bool operator>=(const IPAddress& a, const IPAddress& b) { |
550 | return !(a < b); |
551 | } |
552 | |
553 | } // namespace folly |
554 | |
555 | namespace std { |
556 | template <> |
557 | struct hash<folly::IPAddress> { |
558 | size_t operator()(const folly::IPAddress& addr) const { |
559 | return addr.hash(); |
560 | } |
561 | }; |
562 | } // namespace std |
563 | |