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 | #ifndef __STDC_FORMAT_MACROS |
18 | #define __STDC_FORMAT_MACROS |
19 | #endif |
20 | |
21 | #include <folly/SocketAddress.h> |
22 | |
23 | #include <cassert> |
24 | #include <cerrno> |
25 | #include <cstdio> |
26 | #include <cstring> |
27 | #include <sstream> |
28 | #include <string> |
29 | #include <system_error> |
30 | |
31 | #include <boost/functional/hash.hpp> |
32 | |
33 | #include <folly/CppAttributes.h> |
34 | #include <folly/Exception.h> |
35 | #include <folly/Format.h> |
36 | #include <folly/hash/Hash.h> |
37 | #include <folly/net/NetOps.h> |
38 | #include <folly/net/NetworkSocket.h> |
39 | |
40 | namespace { |
41 | |
42 | /** |
43 | * A structure to free a struct addrinfo when it goes out of scope. |
44 | */ |
45 | struct ScopedAddrInfo { |
46 | explicit ScopedAddrInfo(struct addrinfo* addrinfo) : info(addrinfo) {} |
47 | ~ScopedAddrInfo() { |
48 | freeaddrinfo(info); |
49 | } |
50 | |
51 | struct addrinfo* info; |
52 | }; |
53 | |
54 | /** |
55 | * A simple data structure for parsing a host-and-port string. |
56 | * |
57 | * Accepts a string of the form "<host>:<port>" or just "<port>", |
58 | * and contains two string pointers to the host and the port portion of the |
59 | * string. |
60 | * |
61 | * The HostAndPort may contain pointers into the original string. It is |
62 | * responsible for the user to ensure that the input string is valid for the |
63 | * lifetime of the HostAndPort structure. |
64 | */ |
65 | struct HostAndPort { |
66 | HostAndPort(const char* str, bool hostRequired) |
67 | : host(nullptr), port(nullptr), allocated(nullptr) { |
68 | // Look for the last colon |
69 | const char* colon = strrchr(str, ':'); |
70 | if (colon == nullptr) { |
71 | // No colon, just a port number. |
72 | if (hostRequired) { |
73 | throw std::invalid_argument( |
74 | "expected a host and port string of the " |
75 | "form \"<host>:<port>\"" ); |
76 | } |
77 | port = str; |
78 | return; |
79 | } |
80 | |
81 | // We have to make a copy of the string so we can modify it |
82 | // and change the colon to a NUL terminator. |
83 | allocated = strdup(str); |
84 | if (!allocated) { |
85 | throw std::bad_alloc(); |
86 | } |
87 | |
88 | char* allocatedColon = allocated + (colon - str); |
89 | *allocatedColon = '\0'; |
90 | host = allocated; |
91 | port = allocatedColon + 1; |
92 | // bracketed IPv6 address, remove the brackets |
93 | // allocatedColon[-1] is fine, as allocatedColon >= host and |
94 | // *allocatedColon != *host therefore allocatedColon > host |
95 | if (*host == '[' && allocatedColon[-1] == ']') { |
96 | allocatedColon[-1] = '\0'; |
97 | ++host; |
98 | } |
99 | } |
100 | |
101 | ~HostAndPort() { |
102 | free(allocated); |
103 | } |
104 | |
105 | const char* host; |
106 | const char* port; |
107 | char* allocated; |
108 | }; |
109 | |
110 | } // namespace |
111 | |
112 | namespace folly { |
113 | |
114 | bool SocketAddress::isPrivateAddress() const { |
115 | auto family = getFamily(); |
116 | if (family == AF_INET || family == AF_INET6) { |
117 | return storage_.addr.isPrivate() || |
118 | (storage_.addr.isV6() && storage_.addr.asV6().isLinkLocal()); |
119 | } else if (external_) { |
120 | // Unix addresses are always local to a host. Return true, |
121 | // since this conforms to the semantics of returning true for IP loopback |
122 | // addresses. |
123 | return true; |
124 | } |
125 | return false; |
126 | } |
127 | |
128 | bool SocketAddress::isLoopbackAddress() const { |
129 | auto family = getFamily(); |
130 | if (family == AF_INET || family == AF_INET6) { |
131 | return storage_.addr.isLoopback(); |
132 | } else if (external_) { |
133 | // Return true for UNIX addresses, since they are always local to a host. |
134 | return true; |
135 | } |
136 | return false; |
137 | } |
138 | |
139 | void SocketAddress::setFromHostPort(const char* host, uint16_t port) { |
140 | ScopedAddrInfo results(getAddrInfo(host, port, 0)); |
141 | setFromAddrInfo(results.info); |
142 | } |
143 | |
144 | void SocketAddress::setFromIpPort(const char* ip, uint16_t port) { |
145 | ScopedAddrInfo results(getAddrInfo(ip, port, AI_NUMERICHOST)); |
146 | setFromAddrInfo(results.info); |
147 | } |
148 | |
149 | void SocketAddress::setFromIpAddrPort(const IPAddress& ipAddr, uint16_t port) { |
150 | if (external_) { |
151 | storage_.un.free(); |
152 | external_ = false; |
153 | } |
154 | storage_.addr = ipAddr; |
155 | port_ = port; |
156 | } |
157 | |
158 | void SocketAddress::setFromLocalPort(uint16_t port) { |
159 | ScopedAddrInfo results(getAddrInfo(nullptr, port, AI_ADDRCONFIG)); |
160 | setFromLocalAddr(results.info); |
161 | } |
162 | |
163 | void SocketAddress::setFromLocalPort(const char* port) { |
164 | ScopedAddrInfo results(getAddrInfo(nullptr, port, AI_ADDRCONFIG)); |
165 | setFromLocalAddr(results.info); |
166 | } |
167 | |
168 | void SocketAddress::setFromLocalIpPort(const char* addressAndPort) { |
169 | HostAndPort hp(addressAndPort, false); |
170 | ScopedAddrInfo results( |
171 | getAddrInfo(hp.host, hp.port, AI_NUMERICHOST | AI_ADDRCONFIG)); |
172 | setFromLocalAddr(results.info); |
173 | } |
174 | |
175 | void SocketAddress::setFromIpPort(const char* addressAndPort) { |
176 | HostAndPort hp(addressAndPort, true); |
177 | ScopedAddrInfo results(getAddrInfo(hp.host, hp.port, AI_NUMERICHOST)); |
178 | setFromAddrInfo(results.info); |
179 | } |
180 | |
181 | void SocketAddress::setFromHostPort(const char* hostAndPort) { |
182 | HostAndPort hp(hostAndPort, true); |
183 | ScopedAddrInfo results(getAddrInfo(hp.host, hp.port, 0)); |
184 | setFromAddrInfo(results.info); |
185 | } |
186 | |
187 | int SocketAddress::getPortFrom(const struct sockaddr* address) { |
188 | switch (address->sa_family) { |
189 | case AF_INET: |
190 | return ntohs(((sockaddr_in*)address)->sin_port); |
191 | |
192 | case AF_INET6: |
193 | return ntohs(((sockaddr_in6*)address)->sin6_port); |
194 | |
195 | default: |
196 | return -1; |
197 | } |
198 | } |
199 | |
200 | const char* SocketAddress::getFamilyNameFrom( |
201 | const struct sockaddr* address, |
202 | const char* defaultResult) { |
203 | #define GETFAMILYNAMEFROM_IMPL(Family) \ |
204 | case Family: \ |
205 | return #Family |
206 | |
207 | switch (address->sa_family) { |
208 | GETFAMILYNAMEFROM_IMPL(AF_INET); |
209 | GETFAMILYNAMEFROM_IMPL(AF_INET6); |
210 | GETFAMILYNAMEFROM_IMPL(AF_UNIX); |
211 | GETFAMILYNAMEFROM_IMPL(AF_UNSPEC); |
212 | |
213 | default: |
214 | return defaultResult; |
215 | } |
216 | |
217 | #undef GETFAMILYNAMEFROM_IMPL |
218 | } |
219 | |
220 | void SocketAddress::setFromPath(StringPiece path) { |
221 | // Before we touch storage_, check to see if the length is too big. |
222 | // Note that "storage_.un.addr->sun_path" may not be safe to evaluate here, |
223 | // but sizeof() just uses its type, and does't evaluate it. |
224 | if (path.size() > sizeof(storage_.un.addr->sun_path)) { |
225 | throw std::invalid_argument( |
226 | "socket path too large to fit into sockaddr_un" ); |
227 | } |
228 | |
229 | if (!external_) { |
230 | storage_.un.init(); |
231 | external_ = true; |
232 | } |
233 | |
234 | size_t len = path.size(); |
235 | storage_.un.len = socklen_t(offsetof(struct sockaddr_un, sun_path) + len); |
236 | memcpy(storage_.un.addr->sun_path, path.data(), len); |
237 | // If there is room, put a terminating NUL byte in sun_path. In general the |
238 | // path should be NUL terminated, although getsockname() and getpeername() |
239 | // may return Unix socket addresses with paths that fit exactly in sun_path |
240 | // with no terminating NUL. |
241 | if (len < sizeof(storage_.un.addr->sun_path)) { |
242 | storage_.un.addr->sun_path[len] = '\0'; |
243 | } |
244 | } |
245 | |
246 | void SocketAddress::setFromPeerAddress(NetworkSocket socket) { |
247 | setFromSocket(socket, netops::getpeername); |
248 | } |
249 | |
250 | void SocketAddress::setFromLocalAddress(NetworkSocket socket) { |
251 | setFromSocket(socket, netops::getsockname); |
252 | } |
253 | |
254 | void SocketAddress::setFromSockaddr(const struct sockaddr* address) { |
255 | uint16_t port; |
256 | |
257 | if (address->sa_family == AF_INET) { |
258 | port = ntohs(((sockaddr_in*)address)->sin_port); |
259 | } else if (address->sa_family == AF_INET6) { |
260 | port = ntohs(((sockaddr_in6*)address)->sin6_port); |
261 | } else if (address->sa_family == AF_UNIX) { |
262 | // We need an explicitly specified length for AF_UNIX addresses, |
263 | // to be able to distinguish anonymous addresses from addresses |
264 | // in Linux's abstract namespace. |
265 | throw std::invalid_argument( |
266 | "SocketAddress::setFromSockaddr(): the address " |
267 | "length must be explicitly specified when " |
268 | "setting AF_UNIX addresses" ); |
269 | } else { |
270 | throw std::invalid_argument( |
271 | "SocketAddress::setFromSockaddr() called " |
272 | "with unsupported address type" ); |
273 | } |
274 | |
275 | setFromIpAddrPort(folly::IPAddress(address), port); |
276 | } |
277 | |
278 | void SocketAddress::setFromSockaddr( |
279 | const struct sockaddr* address, |
280 | socklen_t addrlen) { |
281 | // Check the length to make sure we can access address->sa_family |
282 | if (addrlen < |
283 | (offsetof(struct sockaddr, sa_family) + sizeof(address->sa_family))) { |
284 | throw std::invalid_argument( |
285 | "SocketAddress::setFromSockaddr() called " |
286 | "with length too short for a sockaddr" ); |
287 | } |
288 | |
289 | if (address->sa_family == AF_INET) { |
290 | if (addrlen < sizeof(struct sockaddr_in)) { |
291 | throw std::invalid_argument( |
292 | "SocketAddress::setFromSockaddr() called " |
293 | "with length too short for a sockaddr_in" ); |
294 | } |
295 | setFromSockaddr(reinterpret_cast<const struct sockaddr_in*>(address)); |
296 | } else if (address->sa_family == AF_INET6) { |
297 | if (addrlen < sizeof(struct sockaddr_in6)) { |
298 | throw std::invalid_argument( |
299 | "SocketAddress::setFromSockaddr() called " |
300 | "with length too short for a sockaddr_in6" ); |
301 | } |
302 | setFromSockaddr(reinterpret_cast<const struct sockaddr_in6*>(address)); |
303 | } else if (address->sa_family == AF_UNIX) { |
304 | setFromSockaddr( |
305 | reinterpret_cast<const struct sockaddr_un*>(address), addrlen); |
306 | } else { |
307 | throw std::invalid_argument( |
308 | "SocketAddress::setFromSockaddr() called " |
309 | "with unsupported address type" ); |
310 | } |
311 | } |
312 | |
313 | void SocketAddress::setFromSockaddr(const struct sockaddr_in* address) { |
314 | assert(address->sin_family == AF_INET); |
315 | setFromSockaddr((sockaddr*)address); |
316 | } |
317 | |
318 | void SocketAddress::setFromSockaddr(const struct sockaddr_in6* address) { |
319 | assert(address->sin6_family == AF_INET6); |
320 | setFromSockaddr((sockaddr*)address); |
321 | } |
322 | |
323 | void SocketAddress::setFromSockaddr( |
324 | const struct sockaddr_un* address, |
325 | socklen_t addrlen) { |
326 | assert(address->sun_family == AF_UNIX); |
327 | if (addrlen > sizeof(struct sockaddr_un)) { |
328 | throw std::invalid_argument( |
329 | "SocketAddress::setFromSockaddr() called " |
330 | "with length too long for a sockaddr_un" ); |
331 | } |
332 | |
333 | if (!external_) { |
334 | storage_.un.init(); |
335 | } |
336 | external_ = true; |
337 | memcpy(storage_.un.addr, address, size_t(addrlen)); |
338 | updateUnixAddressLength(addrlen); |
339 | |
340 | // Fill the rest with 0s, just for safety |
341 | if (addrlen < sizeof(struct sockaddr_un)) { |
342 | auto p = reinterpret_cast<char*>(storage_.un.addr); |
343 | memset(p + addrlen, 0, sizeof(struct sockaddr_un) - addrlen); |
344 | } |
345 | } |
346 | |
347 | const folly::IPAddress& SocketAddress::getIPAddress() const { |
348 | auto family = getFamily(); |
349 | if (family != AF_INET && family != AF_INET6) { |
350 | throw InvalidAddressFamilyException(family); |
351 | } |
352 | return storage_.addr; |
353 | } |
354 | |
355 | socklen_t SocketAddress::getActualSize() const { |
356 | if (external_) { |
357 | return storage_.un.len; |
358 | } |
359 | switch (getFamily()) { |
360 | case AF_UNSPEC: |
361 | case AF_INET: |
362 | return sizeof(struct sockaddr_in); |
363 | case AF_INET6: |
364 | return sizeof(struct sockaddr_in6); |
365 | default: |
366 | throw std::invalid_argument( |
367 | "SocketAddress::getActualSize() called " |
368 | "with unrecognized address family" ); |
369 | } |
370 | } |
371 | |
372 | std::string SocketAddress::getFullyQualified() const { |
373 | if (!isFamilyInet()) { |
374 | throw std::invalid_argument("Can't get address str for non ip address" ); |
375 | } |
376 | return storage_.addr.toFullyQualified(); |
377 | } |
378 | |
379 | std::string SocketAddress::getAddressStr() const { |
380 | if (!isFamilyInet()) { |
381 | throw std::invalid_argument("Can't get address str for non ip address" ); |
382 | } |
383 | return storage_.addr.str(); |
384 | } |
385 | |
386 | bool SocketAddress::isFamilyInet() const { |
387 | auto family = getFamily(); |
388 | return family == AF_INET || family == AF_INET6; |
389 | } |
390 | |
391 | void SocketAddress::getAddressStr(char* buf, size_t buflen) const { |
392 | auto ret = getAddressStr(); |
393 | size_t len = std::min(buflen - 1, ret.size()); |
394 | memcpy(buf, ret.data(), len); |
395 | buf[len] = '\0'; |
396 | } |
397 | |
398 | uint16_t SocketAddress::getPort() const { |
399 | switch (getFamily()) { |
400 | case AF_INET: |
401 | case AF_INET6: |
402 | return port_; |
403 | default: |
404 | throw std::invalid_argument( |
405 | "SocketAddress::getPort() called on non-IP " |
406 | "address" ); |
407 | } |
408 | } |
409 | |
410 | void SocketAddress::setPort(uint16_t port) { |
411 | switch (getFamily()) { |
412 | case AF_INET: |
413 | case AF_INET6: |
414 | port_ = port; |
415 | return; |
416 | default: |
417 | throw std::invalid_argument( |
418 | "SocketAddress::setPort() called on non-IP " |
419 | "address" ); |
420 | } |
421 | } |
422 | |
423 | void SocketAddress::convertToIPv4() { |
424 | if (!tryConvertToIPv4()) { |
425 | throw std::invalid_argument( |
426 | "convertToIPv4() called on an addresse that is " |
427 | "not an IPv4-mapped address" ); |
428 | } |
429 | } |
430 | |
431 | bool SocketAddress::tryConvertToIPv4() { |
432 | if (!isIPv4Mapped()) { |
433 | return false; |
434 | } |
435 | |
436 | storage_.addr = folly::IPAddress::createIPv4(storage_.addr); |
437 | return true; |
438 | } |
439 | |
440 | bool SocketAddress::mapToIPv6() { |
441 | if (getFamily() != AF_INET) { |
442 | return false; |
443 | } |
444 | |
445 | storage_.addr = folly::IPAddress::createIPv6(storage_.addr); |
446 | return true; |
447 | } |
448 | |
449 | std::string SocketAddress::getHostStr() const { |
450 | return getIpString(0); |
451 | } |
452 | |
453 | std::string SocketAddress::getPath() const { |
454 | if (!external_) { |
455 | throw std::invalid_argument( |
456 | "SocketAddress: attempting to get path " |
457 | "for a non-Unix address" ); |
458 | } |
459 | |
460 | if (storage_.un.pathLength() == 0) { |
461 | // anonymous address |
462 | return std::string(); |
463 | } |
464 | if (storage_.un.addr->sun_path[0] == '\0') { |
465 | // abstract namespace |
466 | return std::string( |
467 | storage_.un.addr->sun_path, size_t(storage_.un.pathLength())); |
468 | } |
469 | |
470 | return std::string( |
471 | storage_.un.addr->sun_path, |
472 | strnlen(storage_.un.addr->sun_path, size_t(storage_.un.pathLength()))); |
473 | } |
474 | |
475 | std::string SocketAddress::describe() const { |
476 | if (external_) { |
477 | if (storage_.un.pathLength() == 0) { |
478 | return "<anonymous unix address>" ; |
479 | } |
480 | |
481 | if (storage_.un.addr->sun_path[0] == '\0') { |
482 | // Linux supports an abstract namespace for unix socket addresses |
483 | return "<abstract unix address>" ; |
484 | } |
485 | |
486 | return std::string( |
487 | storage_.un.addr->sun_path, |
488 | strnlen(storage_.un.addr->sun_path, size_t(storage_.un.pathLength()))); |
489 | } |
490 | switch (getFamily()) { |
491 | case AF_UNSPEC: |
492 | return "<uninitialized address>" ; |
493 | case AF_INET: { |
494 | char buf[NI_MAXHOST + 16]; |
495 | getAddressStr(buf, sizeof(buf)); |
496 | size_t iplen = strlen(buf); |
497 | snprintf(buf + iplen, sizeof(buf) - iplen, ":%" PRIu16, getPort()); |
498 | return buf; |
499 | } |
500 | case AF_INET6: { |
501 | char buf[NI_MAXHOST + 18]; |
502 | buf[0] = '['; |
503 | getAddressStr(buf + 1, sizeof(buf) - 1); |
504 | size_t iplen = strlen(buf); |
505 | snprintf(buf + iplen, sizeof(buf) - iplen, "]:%" PRIu16, getPort()); |
506 | return buf; |
507 | } |
508 | default: { |
509 | char buf[64]; |
510 | snprintf(buf, sizeof(buf), "<unknown address family %d>" , getFamily()); |
511 | return buf; |
512 | } |
513 | } |
514 | } |
515 | |
516 | bool SocketAddress::operator==(const SocketAddress& other) const { |
517 | if (external_ != other.external_ || other.getFamily() != getFamily()) { |
518 | return false; |
519 | } |
520 | if (external_) { |
521 | // anonymous addresses are never equal to any other addresses |
522 | if (storage_.un.pathLength() == 0 || other.storage_.un.pathLength() == 0) { |
523 | return false; |
524 | } |
525 | |
526 | if (storage_.un.len != other.storage_.un.len) { |
527 | return false; |
528 | } |
529 | int cmp = memcmp( |
530 | storage_.un.addr->sun_path, |
531 | other.storage_.un.addr->sun_path, |
532 | size_t(storage_.un.pathLength())); |
533 | return cmp == 0; |
534 | } |
535 | |
536 | switch (getFamily()) { |
537 | case AF_INET: |
538 | case AF_INET6: |
539 | return (other.storage_.addr == storage_.addr) && (other.port_ == port_); |
540 | case AF_UNSPEC: |
541 | return other.storage_.addr.empty(); |
542 | default: |
543 | throw_exception<std::invalid_argument>( |
544 | "SocketAddress: unsupported address family for comparison" ); |
545 | } |
546 | } |
547 | |
548 | bool SocketAddress::prefixMatch( |
549 | const SocketAddress& other, |
550 | unsigned prefixLength) const { |
551 | if (other.getFamily() != getFamily()) { |
552 | return false; |
553 | } |
554 | uint8_t mask_length = 128; |
555 | switch (getFamily()) { |
556 | case AF_INET: |
557 | mask_length = 32; |
558 | FOLLY_FALLTHROUGH; |
559 | case AF_INET6: { |
560 | auto prefix = folly::IPAddress::longestCommonPrefix( |
561 | {storage_.addr, mask_length}, {other.storage_.addr, mask_length}); |
562 | return prefix.second >= prefixLength; |
563 | } |
564 | default: |
565 | return false; |
566 | } |
567 | } |
568 | |
569 | size_t SocketAddress::hash() const { |
570 | size_t seed = folly::hash::twang_mix64(getFamily()); |
571 | |
572 | if (external_) { |
573 | enum { kUnixPathMax = sizeof(storage_.un.addr->sun_path) }; |
574 | const char* path = storage_.un.addr->sun_path; |
575 | auto pathLength = storage_.un.pathLength(); |
576 | // TODO: this probably could be made more efficient |
577 | for (off_t n = 0; n < pathLength; ++n) { |
578 | boost::hash_combine(seed, folly::hash::twang_mix64(uint64_t(path[n]))); |
579 | } |
580 | } |
581 | |
582 | switch (getFamily()) { |
583 | case AF_INET: |
584 | case AF_INET6: { |
585 | boost::hash_combine(seed, port_); |
586 | boost::hash_combine(seed, storage_.addr.hash()); |
587 | break; |
588 | } |
589 | case AF_UNIX: |
590 | assert(external_); |
591 | break; |
592 | case AF_UNSPEC: |
593 | assert(storage_.addr.empty()); |
594 | boost::hash_combine(seed, storage_.addr.hash()); |
595 | break; |
596 | default: |
597 | throw_exception<std::invalid_argument>( |
598 | "SocketAddress: unsupported address family for comparison" ); |
599 | } |
600 | |
601 | return seed; |
602 | } |
603 | |
604 | struct addrinfo* |
605 | SocketAddress::getAddrInfo(const char* host, uint16_t port, int flags) { |
606 | // getaddrinfo() requires the port number as a string |
607 | char portString[sizeof("65535" )]; |
608 | snprintf(portString, sizeof(portString), "%" PRIu16, port); |
609 | |
610 | return getAddrInfo(host, portString, flags); |
611 | } |
612 | |
613 | struct addrinfo* |
614 | SocketAddress::getAddrInfo(const char* host, const char* port, int flags) { |
615 | struct addrinfo hints; |
616 | memset(&hints, 0, sizeof(hints)); |
617 | hints.ai_family = AF_UNSPEC; |
618 | hints.ai_socktype = SOCK_STREAM; |
619 | hints.ai_flags = AI_PASSIVE | AI_NUMERICSERV | flags; |
620 | |
621 | struct addrinfo* results; |
622 | int error = getaddrinfo(host, port, &hints, &results); |
623 | if (error != 0) { |
624 | auto os = folly::sformat( |
625 | "Failed to resolve address for '{}': {} (error={})" , |
626 | host, |
627 | gai_strerror(error), |
628 | error); |
629 | throw std::system_error(error, std::generic_category(), os); |
630 | } |
631 | |
632 | return results; |
633 | } |
634 | |
635 | void SocketAddress::setFromAddrInfo(const struct addrinfo* info) { |
636 | setFromSockaddr(info->ai_addr, socklen_t(info->ai_addrlen)); |
637 | } |
638 | |
639 | void SocketAddress::setFromLocalAddr(const struct addrinfo* info) { |
640 | // If an IPv6 address is present, prefer to use it, since IPv4 addresses |
641 | // can be mapped into IPv6 space. |
642 | for (const struct addrinfo* ai = info; ai != nullptr; ai = ai->ai_next) { |
643 | if (ai->ai_family == AF_INET6) { |
644 | setFromSockaddr(ai->ai_addr, socklen_t(ai->ai_addrlen)); |
645 | return; |
646 | } |
647 | } |
648 | |
649 | // Otherwise, just use the first address in the list. |
650 | setFromSockaddr(info->ai_addr, socklen_t(info->ai_addrlen)); |
651 | } |
652 | |
653 | void SocketAddress::setFromSocket( |
654 | NetworkSocket socket, |
655 | int (*fn)(NetworkSocket, struct sockaddr*, socklen_t*)) { |
656 | // Try to put the address into a local storage buffer. |
657 | sockaddr_storage tmp_sock; |
658 | socklen_t addrLen = sizeof(tmp_sock); |
659 | if (fn(socket, (sockaddr*)&tmp_sock, &addrLen) != 0) { |
660 | folly::throwSystemError("setFromSocket() failed" ); |
661 | } |
662 | |
663 | setFromSockaddr((sockaddr*)&tmp_sock, addrLen); |
664 | } |
665 | |
666 | std::string SocketAddress::getIpString(int flags) const { |
667 | char addrString[NI_MAXHOST]; |
668 | getIpString(addrString, sizeof(addrString), flags); |
669 | return std::string(addrString); |
670 | } |
671 | |
672 | void SocketAddress::getIpString(char* buf, size_t buflen, int flags) const { |
673 | auto family = getFamily(); |
674 | if (family != AF_INET && family != AF_INET6) { |
675 | throw std::invalid_argument( |
676 | "SocketAddress: attempting to get IP address " |
677 | "for a non-IP address" ); |
678 | } |
679 | |
680 | sockaddr_storage tmp_sock; |
681 | storage_.addr.toSockaddrStorage(&tmp_sock, port_); |
682 | int rc = getnameinfo( |
683 | (sockaddr*)&tmp_sock, |
684 | sizeof(sockaddr_storage), |
685 | buf, |
686 | buflen, |
687 | nullptr, |
688 | 0, |
689 | flags); |
690 | if (rc != 0) { |
691 | auto os = sformat( |
692 | "getnameinfo() failed in getIpString() error = {}" , gai_strerror(rc)); |
693 | throw std::system_error(rc, std::generic_category(), os); |
694 | } |
695 | } |
696 | |
697 | void SocketAddress::updateUnixAddressLength(socklen_t addrlen) { |
698 | if (addrlen < offsetof(struct sockaddr_un, sun_path)) { |
699 | throw std::invalid_argument( |
700 | "SocketAddress: attempted to set a Unix socket " |
701 | "with a length too short for a sockaddr_un" ); |
702 | } |
703 | |
704 | storage_.un.len = addrlen; |
705 | if (storage_.un.pathLength() == 0) { |
706 | // anonymous address |
707 | return; |
708 | } |
709 | |
710 | if (storage_.un.addr->sun_path[0] == '\0') { |
711 | // abstract namespace. honor the specified length |
712 | } else { |
713 | // Call strnlen(), just in case the length was overspecified. |
714 | size_t maxLength = addrlen - offsetof(struct sockaddr_un, sun_path); |
715 | size_t pathLength = strnlen(storage_.un.addr->sun_path, maxLength); |
716 | storage_.un.len = |
717 | socklen_t(offsetof(struct sockaddr_un, sun_path) + pathLength); |
718 | } |
719 | } |
720 | |
721 | bool SocketAddress::operator<(const SocketAddress& other) const { |
722 | if (getFamily() != other.getFamily()) { |
723 | return getFamily() < other.getFamily(); |
724 | } |
725 | |
726 | if (external_) { |
727 | // Anonymous addresses can't be compared to anything else. |
728 | // Return that they are never less than anything. |
729 | // |
730 | // Note that this still meets the requirements for a strict weak |
731 | // ordering, so we can use this operator<() with standard C++ containers. |
732 | auto thisPathLength = storage_.un.pathLength(); |
733 | if (thisPathLength == 0) { |
734 | return false; |
735 | } |
736 | auto otherPathLength = other.storage_.un.pathLength(); |
737 | if (otherPathLength == 0) { |
738 | return true; |
739 | } |
740 | |
741 | // Compare based on path length first, for efficiency |
742 | if (thisPathLength != otherPathLength) { |
743 | return thisPathLength < otherPathLength; |
744 | } |
745 | int cmp = memcmp( |
746 | storage_.un.addr->sun_path, |
747 | other.storage_.un.addr->sun_path, |
748 | size_t(thisPathLength)); |
749 | return cmp < 0; |
750 | } |
751 | switch (getFamily()) { |
752 | case AF_INET: |
753 | case AF_INET6: { |
754 | if (port_ != other.port_) { |
755 | return port_ < other.port_; |
756 | } |
757 | |
758 | return storage_.addr < other.storage_.addr; |
759 | } |
760 | case AF_UNSPEC: |
761 | default: |
762 | throw std::invalid_argument( |
763 | "SocketAddress: unsupported address family for comparing" ); |
764 | } |
765 | } |
766 | |
767 | size_t hash_value(const SocketAddress& address) { |
768 | return address.hash(); |
769 | } |
770 | |
771 | std::ostream& operator<<(std::ostream& os, const SocketAddress& addr) { |
772 | os << addr.describe(); |
773 | return os; |
774 | } |
775 | |
776 | } // namespace folly |
777 | |