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
34namespace folly {
35
36class IPAddress;
37
38/**
39 * Pair of IPAddress, netmask
40 */
41typedef 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 */
70class 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
522std::size_t hash_value(const IPAddress& addr);
523std::ostream& operator<<(std::ostream& os, const IPAddress& addr);
524// Define toAppend() to allow IPAddress to be used with folly::to<string>
525void toAppend(IPAddress addr, std::string* result);
526void 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 */
536bool operator==(const IPAddress& addr1, const IPAddress& addr2);
537// Return true if addr1 < addr2
538bool operator<(const IPAddress& addr1, const IPAddress& addr2);
539// Derived operators
540inline bool operator!=(const IPAddress& a, const IPAddress& b) {
541 return !(a == b);
542}
543inline bool operator>(const IPAddress& a, const IPAddress& b) {
544 return b < a;
545}
546inline bool operator<=(const IPAddress& a, const IPAddress& b) {
547 return !(a > b);
548}
549inline bool operator>=(const IPAddress& a, const IPAddress& b) {
550 return !(a < b);
551}
552
553} // namespace folly
554
555namespace std {
556template <>
557struct hash<folly::IPAddress> {
558 size_t operator()(const folly::IPAddress& addr) const {
559 return addr.hash();
560 }
561};
562} // namespace std
563