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