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