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 #include <folly/IPAddressV4.h>
18
19 #include <ostream>
20 #include <string>
21
22 #include <fmt/core.h>
23
24 #include <folly/Conv.h>
25 #include <folly/IPAddress.h>
26 #include <folly/IPAddressV6.h>
27 #include <folly/String.h>
28 #include <folly/detail/IPAddressSource.h>
29
30 using std::ostream;
31 using std::string;
32
33 namespace folly {
34
35 // free functions
hash_value(const IPAddressV4 & addr)36 size_t hash_value(const IPAddressV4& addr) {
37 return addr.hash();
38 }
operator <<(ostream & os,const IPAddressV4 & addr)39 ostream& operator<<(ostream& os, const IPAddressV4& addr) {
40 os << addr.str();
41 return os;
42 }
toAppend(IPAddressV4 addr,string * result)43 void toAppend(IPAddressV4 addr, string* result) {
44 result->append(addr.str());
45 }
toAppend(IPAddressV4 addr,fbstring * result)46 void toAppend(IPAddressV4 addr, fbstring* result) {
47 result->append(addr.str());
48 }
49
validate(StringPiece ip)50 bool IPAddressV4::validate(StringPiece ip) noexcept {
51 return tryFromString(ip).hasValue();
52 }
53
54 // public static
fromLong(uint32_t src)55 IPAddressV4 IPAddressV4::fromLong(uint32_t src) {
56 in_addr addr;
57 addr.s_addr = src;
58 return IPAddressV4(addr);
59 }
60
fromLongHBO(uint32_t src)61 IPAddressV4 IPAddressV4::fromLongHBO(uint32_t src) {
62 in_addr addr;
63 addr.s_addr = htonl(src);
64 return IPAddressV4(addr);
65 }
66
67 // static public
toLong(StringPiece ip)68 uint32_t IPAddressV4::toLong(StringPiece ip) {
69 auto str = ip.str();
70 in_addr addr;
71 if (inet_pton(AF_INET, str.c_str(), &addr) != 1) {
72 throw IPAddressFormatException(
73 fmt::format("Can't convert invalid IP '{}' to long", ip));
74 }
75 return addr.s_addr;
76 }
77
78 // static public
toLongHBO(StringPiece ip)79 uint32_t IPAddressV4::toLongHBO(StringPiece ip) {
80 return ntohl(IPAddressV4::toLong(ip));
81 }
82
83 // public default constructor
84 IPAddressV4::IPAddressV4() = default;
85
86 // ByteArray4 constructor
IPAddressV4(const ByteArray4 & src)87 IPAddressV4::IPAddressV4(const ByteArray4& src) noexcept : addr_(src) {}
88
89 // public string constructor
IPAddressV4(StringPiece addr)90 IPAddressV4::IPAddressV4(StringPiece addr) : addr_() {
91 auto maybeIp = tryFromString(addr);
92 if (maybeIp.hasError()) {
93 throw IPAddressFormatException(
94 to<std::string>("Invalid IPv4 address '", addr, "'"));
95 }
96 *this = maybeIp.value();
97 }
98
tryFromString(StringPiece str)99 Expected<IPAddressV4, IPAddressFormatError> IPAddressV4::tryFromString(
100 StringPiece str) noexcept {
101 struct in_addr inAddr;
102 if (inet_pton(AF_INET, str.str().c_str(), &inAddr) != 1) {
103 return makeUnexpected(IPAddressFormatError::INVALID_IP);
104 }
105 return IPAddressV4(inAddr);
106 }
107
108 // in_addr constructor
IPAddressV4(const in_addr src)109 IPAddressV4::IPAddressV4(const in_addr src) noexcept : addr_(src) {}
110
fromBinary(ByteRange bytes)111 IPAddressV4 IPAddressV4::fromBinary(ByteRange bytes) {
112 auto maybeIp = tryFromBinary(bytes);
113 if (maybeIp.hasError()) {
114 throw IPAddressFormatException(to<std::string>(
115 "Invalid IPv4 binary data: length must be 4 bytes, got ",
116 bytes.size()));
117 }
118 return maybeIp.value();
119 }
120
tryFromBinary(ByteRange bytes)121 Expected<IPAddressV4, IPAddressFormatError> IPAddressV4::tryFromBinary(
122 ByteRange bytes) noexcept {
123 IPAddressV4 addr;
124 auto setResult = addr.trySetFromBinary(bytes);
125 if (setResult.hasError()) {
126 return makeUnexpected(setResult.error());
127 }
128 return addr;
129 }
130
trySetFromBinary(ByteRange bytes)131 Expected<Unit, IPAddressFormatError> IPAddressV4::trySetFromBinary(
132 ByteRange bytes) noexcept {
133 if (bytes.size() != 4) {
134 return makeUnexpected(IPAddressFormatError::INVALID_IP);
135 }
136 memcpy(&addr_.inAddr_.s_addr, bytes.data(), sizeof(in_addr));
137 return folly::unit;
138 }
139
140 // static
fromInverseArpaName(const std::string & arpaname)141 IPAddressV4 IPAddressV4::fromInverseArpaName(const std::string& arpaname) {
142 auto piece = StringPiece(arpaname);
143 // input must be something like 1.0.168.192.in-addr.arpa
144 if (!piece.removeSuffix(".in-addr.arpa")) {
145 throw IPAddressFormatException(
146 fmt::format("input does not end with '.in-addr.arpa': '{}'", arpaname));
147 }
148 std::vector<StringPiece> pieces;
149 split(".", piece, pieces);
150 if (pieces.size() != 4) {
151 throw IPAddressFormatException(fmt::format("Invalid input. Got {}", piece));
152 }
153 // reverse 1.0.168.192 -> 192.168.0.1
154 return IPAddressV4(join(".", pieces.rbegin(), pieces.rend()));
155 }
createIPv6() const156 IPAddressV6 IPAddressV4::createIPv6() const {
157 ByteArray16 ba{};
158 ba[10] = 0xff;
159 ba[11] = 0xff;
160 std::memcpy(&ba[12], bytes(), 4);
161 return IPAddressV6(ba);
162 }
163
164 // public
getIPv6For6To4() const165 IPAddressV6 IPAddressV4::getIPv6For6To4() const {
166 ByteArray16 ba{};
167 ba[0] = (uint8_t)((IPAddressV6::PREFIX_6TO4 & 0xFF00) >> 8);
168 ba[1] = (uint8_t)(IPAddressV6::PREFIX_6TO4 & 0x00FF);
169 std::memcpy(&ba[2], bytes(), 4);
170 return IPAddressV6(ba);
171 }
172
173 // public
toJson() const174 string IPAddressV4::toJson() const {
175 return fmt::format("{{family:'AF_INET', addr:'{}', hash:{}}}", str(), hash());
176 }
177
178 // public
inSubnet(StringPiece cidrNetwork) const179 bool IPAddressV4::inSubnet(StringPiece cidrNetwork) const {
180 auto subnetInfo = IPAddress::createNetwork(cidrNetwork);
181 auto addr = subnetInfo.first;
182 if (!addr.isV4()) {
183 throw IPAddressFormatException(
184 fmt::format("Address '{}' is not a V4 address", addr.toJson()));
185 }
186 return inSubnetWithMask(addr.asV4(), fetchMask(subnetInfo.second));
187 }
188
189 // public
inSubnetWithMask(const IPAddressV4 & subnet,const ByteArray4 cidrMask) const190 bool IPAddressV4::inSubnetWithMask(
191 const IPAddressV4& subnet, const ByteArray4 cidrMask) const {
192 const auto mask = detail::Bytes::mask(toByteArray(), cidrMask);
193 const auto subMask = detail::Bytes::mask(subnet.toByteArray(), cidrMask);
194 return (mask == subMask);
195 }
196
197 // public
isLoopback() const198 bool IPAddressV4::isLoopback() const {
199 static IPAddressV4 loopback_addr("127.0.0.0");
200 return inSubnetWithMask(loopback_addr, fetchMask(8));
201 }
202
203 // public
isLinkLocal() const204 bool IPAddressV4::isLinkLocal() const {
205 static IPAddressV4 linklocal_addr("169.254.0.0");
206 return inSubnetWithMask(linklocal_addr, fetchMask(16));
207 }
208
209 // public
isNonroutable() const210 bool IPAddressV4::isNonroutable() const {
211 auto ip = toLongHBO();
212 return isPrivate() ||
213 (/* align */ true && ip <= 0x00FFFFFF) || // 0.0.0.0-0.255.255.255
214 (ip >= 0xC0000000 && ip <= 0xC00000FF) || // 192.0.0.0-192.0.0.255
215 (ip >= 0xC0000200 && ip <= 0xC00002FF) || // 192.0.2.0-192.0.2.255
216 (ip >= 0xC6120000 && ip <= 0xC613FFFF) || // 198.18.0.0-198.19.255.255
217 (ip >= 0xC6336400 && ip <= 0xC63364FF) || // 198.51.100.0-198.51.100.255
218 (ip >= 0xCB007100 && ip <= 0xCB0071FF) || // 203.0.113.0-203.0.113.255
219 (ip >= 0xE0000000 && ip <= 0xFFFFFFFF) || // 224.0.0.0-255.255.255.255
220 false;
221 }
222
223 // public
isPrivate() const224 bool IPAddressV4::isPrivate() const {
225 auto ip = toLongHBO();
226 return // some ranges below
227 (ip >= 0x0A000000 && ip <= 0x0AFFFFFF) || // 10.0.0.0-10.255.255.255
228 (ip >= 0x7F000000 && ip <= 0x7FFFFFFF) || // 127.0.0.0-127.255.255.255
229 (ip >= 0xA9FE0000 && ip <= 0xA9FEFFFF) || // 169.254.0.0-169.254.255.255
230 (ip >= 0xAC100000 && ip <= 0xAC1FFFFF) || // 172.16.0.0-172.31.255.255
231 (ip >= 0xC0A80000 && ip <= 0xC0A8FFFF) || // 192.168.0.0-192.168.255.255
232 false;
233 }
234
235 // public
isMulticast() const236 bool IPAddressV4::isMulticast() const {
237 return (toLongHBO() & 0xf0000000) == 0xe0000000;
238 }
239
240 // public
mask(size_t numBits) const241 IPAddressV4 IPAddressV4::mask(size_t numBits) const {
242 static const auto bits = bitCount();
243 if (numBits > bits) {
244 throw IPAddressFormatException(
245 fmt::format("numBits({}) > bitsCount({})", numBits, bits));
246 }
247
248 ByteArray4 ba = detail::Bytes::mask(fetchMask(numBits), addr_.bytes_);
249 return IPAddressV4(ba);
250 }
251
252 // public
str() const253 string IPAddressV4::str() const {
254 return detail::fastIpv4ToString(addr_.inAddr_);
255 }
256
257 // public
toFullyQualifiedAppend(std::string & out) const258 void IPAddressV4::toFullyQualifiedAppend(std::string& out) const {
259 detail::fastIpv4AppendToString(addr_.inAddr_, out);
260 }
261
262 // public
toInverseArpaName() const263 string IPAddressV4::toInverseArpaName() const {
264 return fmt::format(
265 "{}.{}.{}.{}.in-addr.arpa",
266 addr_.bytes_[3],
267 addr_.bytes_[2],
268 addr_.bytes_[1],
269 addr_.bytes_[0]);
270 }
271
272 // public
getNthMSByte(size_t byteIndex) const273 uint8_t IPAddressV4::getNthMSByte(size_t byteIndex) const {
274 const auto highestIndex = byteCount() - 1;
275 if (byteIndex > highestIndex) {
276 throw std::invalid_argument(fmt::format(
277 "Byte index must be <= {} for addresses of type: {}",
278 highestIndex,
279 detail::familyNameStr(AF_INET)));
280 }
281 return bytes()[byteIndex];
282 }
283 // protected
fetchMask(size_t numBits)284 ByteArray4 IPAddressV4::fetchMask(size_t numBits) {
285 static const size_t bits = bitCount();
286 if (numBits > bits) {
287 throw IPAddressFormatException("IPv4 addresses are 32 bits");
288 }
289 auto const val = Endian::big(uint32_t(~uint64_t(0) << (32 - numBits)));
290 ByteArray4 arr;
291 std::memcpy(arr.data(), &val, sizeof(val));
292 return arr;
293 }
294 // public static
longestCommonPrefix(const CIDRNetworkV4 & one,const CIDRNetworkV4 & two)295 CIDRNetworkV4 IPAddressV4::longestCommonPrefix(
296 const CIDRNetworkV4& one, const CIDRNetworkV4& two) {
297 auto prefix = detail::Bytes::longestCommonPrefix(
298 one.first.addr_.bytes_, one.second, two.first.addr_.bytes_, two.second);
299 return {IPAddressV4(prefix.first), prefix.second};
300 }
301
302 } // namespace folly
303