/src/pistache/src/common/net.cc
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * SPDX-FileCopyrightText: 2015 Mathieu Stefani |
3 | | * SPDX-FileCopyrightText: 2023 Andrea Pappacoda |
4 | | * SPDX-License-Identifier: Apache-2.0 |
5 | | */ |
6 | | |
7 | | /* |
8 | | * net.cc |
9 | | * Mathieu Stefani, 12 August 2015 |
10 | | */ |
11 | | |
12 | | #include <pistache/winornix.h> |
13 | | |
14 | | #include <pistache/common.h> |
15 | | #include <pistache/config.h> |
16 | | #include <pistache/net.h> |
17 | | |
18 | | #include <limits> |
19 | | #include <stdexcept> |
20 | | #include <string> |
21 | | #include <vector> |
22 | | |
23 | | #include <cassert> |
24 | | #include <cstdlib> |
25 | | #include <cstring> |
26 | | |
27 | | #include <pistache/ps_strl.h> |
28 | | |
29 | | #include PST_ARPA_INET_HDR |
30 | | |
31 | | #include PST_IFADDRS_HDR |
32 | | |
33 | | #include PST_NETDB_HDR |
34 | | #include PST_SOCKET_HDR |
35 | | |
36 | | #include <sys/types.h> |
37 | | |
38 | | namespace Pistache |
39 | | { |
40 | | namespace helpers |
41 | | { |
42 | | Address httpAddr(const std::string_view& view) |
43 | 0 | { |
44 | 0 | return(httpAddr(view, 0/*default port*/, |
45 | 0 | Address::Scheme::Unspecified, |
46 | 0 | nullptr)); // nullptr: page_cptr |
47 | 0 | } |
48 | | } // namespace helpers |
49 | | |
50 | | Port::Port(uint16_t port) |
51 | 19.4k | : port(port) |
52 | 19.4k | { } |
53 | | |
54 | | Port::Port(const std::string& data) |
55 | 2.35k | { |
56 | 2.35k | if (data.empty()) |
57 | 0 | throw std::invalid_argument("Invalid port: empty port"); |
58 | 2.35k | char* end = nullptr; |
59 | 2.35k | long port_num = strtol(data.c_str(), &end, 10); |
60 | 2.35k | if (*end != 0 || port_num < Port::min() || port_num > Port::max()) |
61 | 954 | throw std::invalid_argument("Invalid port: " + data); |
62 | 1.40k | port = static_cast<uint16_t>(port_num); |
63 | 1.40k | } |
64 | | |
65 | 0 | bool Port::isReserved() const { return port < 1024; } |
66 | | |
67 | | bool Port::isUsed() const |
68 | 0 | { |
69 | 0 | throw std::runtime_error("Unimplemented"); |
70 | 0 | } |
71 | | |
72 | 0 | std::string Port::toString() const { return std::to_string(port); } |
73 | | |
74 | | IP::IP() |
75 | 2.27k | { |
76 | 2.27k | addr_.ss_family = AF_UNSPEC; |
77 | 2.27k | } |
78 | | |
79 | | IP::IP(uint8_t a, uint8_t b, uint8_t c, uint8_t d) |
80 | 0 | { |
81 | 0 | addr_.ss_family = AF_INET; |
82 | 0 | const uint8_t buff[] = { a, b, c, d }; |
83 | | // Note that in Windows in_addr is a union - effectively a "u_long" |
84 | | // that can be accessed as 4 u_char, 2 u_short, or 1 u_long. |
85 | | // Meanwhile, s_addr on the right-hand side is type ULONG. So both |
86 | | // sides are effectively "ulong *" and the cast is valid |
87 | 0 | PST_IN_ADDR_T* in_addr = reinterpret_cast<PST_IN_ADDR_T*> |
88 | 0 | (&reinterpret_cast<struct sockaddr_in*>(&addr_)->sin_addr.s_addr); |
89 | |
|
90 | 0 | static_assert(sizeof(buff) == sizeof(*in_addr)); |
91 | 0 | std::memcpy(in_addr, buff, sizeof(*in_addr)); |
92 | 0 | } |
93 | | |
94 | | IP::IP(uint16_t a, uint16_t b, uint16_t c, uint16_t d, uint16_t e, uint16_t f, |
95 | | uint16_t g, uint16_t h) |
96 | 0 | { |
97 | 0 | addr_.ss_family = AF_INET6; |
98 | 0 | const uint16_t buff[8] = { a, b, c, d, e, f, g, h }; |
99 | 0 | uint16_t remap[8] = { 0, 0, 0, 0, 0, 0, 0, 0 }; |
100 | 0 | if (htonl(1) != 1) |
101 | 0 | { |
102 | 0 | for (int i = 0; i < 8; i++) |
103 | 0 | { |
104 | 0 | const uint16_t swapped = htons(buff[i]); |
105 | 0 | remap[i] = swapped; |
106 | 0 | } |
107 | 0 | } |
108 | 0 | else |
109 | 0 | { |
110 | 0 | std::memcpy(remap, buff, sizeof(remap)); |
111 | 0 | } |
112 | 0 | auto& in6_addr = reinterpret_cast<struct sockaddr_in6*>(&addr_)->sin6_addr.s6_addr; |
113 | |
|
114 | 0 | static_assert(sizeof(in6_addr) == sizeof(remap)); |
115 | 0 | std::memcpy(in6_addr, remap, sizeof(in6_addr)); |
116 | 0 | } |
117 | | |
118 | | IP::IP(const struct sockaddr* addr) |
119 | 0 | { |
120 | 0 | if (addr->sa_family == AF_INET) |
121 | 0 | { |
122 | 0 | const struct sockaddr_in* in_addr = reinterpret_cast<const struct sockaddr_in*>(addr); |
123 | 0 | struct sockaddr_in* ss_in_addr = reinterpret_cast<struct sockaddr_in*>(&addr_); |
124 | | |
125 | | /* Should this simply be `*ss_in_addr = *in_addr`? */ |
126 | 0 | ss_in_addr->sin_family = in_addr->sin_family; |
127 | 0 | ss_in_addr->sin_addr.s_addr = in_addr->sin_addr.s_addr; |
128 | 0 | ss_in_addr->sin_port = in_addr->sin_port; |
129 | 0 | } |
130 | 0 | else if (addr->sa_family == AF_INET6) |
131 | 0 | { |
132 | 0 | const struct sockaddr_in6* in_addr = reinterpret_cast<const struct sockaddr_in6*>(addr); |
133 | 0 | struct sockaddr_in6* ss_in_addr = reinterpret_cast<struct sockaddr_in6*>(&addr_); |
134 | | |
135 | | /* Should this simply be `*ss_in_addr = *in_addr`? */ |
136 | 0 | ss_in_addr->sin6_family = in_addr->sin6_family; |
137 | 0 | ss_in_addr->sin6_port = in_addr->sin6_port; |
138 | 0 | ss_in_addr->sin6_flowinfo = in_addr->sin6_flowinfo; /* Should be 0 per RFC 3493 */ |
139 | 0 | std::memcpy(ss_in_addr->sin6_addr.s6_addr, in_addr->sin6_addr.s6_addr, sizeof(ss_in_addr->sin6_addr.s6_addr)); |
140 | 0 | } |
141 | 0 | else if (addr->sa_family == AF_UNIX) |
142 | 0 | { |
143 | 0 | const struct sockaddr_un* un_addr = reinterpret_cast<const struct sockaddr_un*>(addr); |
144 | 0 | struct sockaddr_un* ss_un_addr = reinterpret_cast<struct sockaddr_un*>(&addr_); |
145 | |
|
146 | 0 | ss_un_addr->sun_family = un_addr->sun_family; |
147 | 0 | std::memcpy(ss_un_addr->sun_path, un_addr->sun_path, sizeof(ss_un_addr->sun_path)); |
148 | 0 | } |
149 | 0 | else |
150 | 0 | { |
151 | 0 | PS_LOG_WARNING_ARGS("Invalid socket family %d", addr->sa_family); |
152 | 0 | throw std::invalid_argument("Invalid socket family"); |
153 | 0 | } |
154 | 0 | } |
155 | | |
156 | 0 | IP IP::any() { return IP(0, 0, 0, 0); } |
157 | | |
158 | | IP IP::any(bool is_ipv6) |
159 | 0 | { |
160 | 0 | if (is_ipv6) |
161 | 0 | { |
162 | 0 | return IP(0, 0, 0, 0, 0, 0, 0, 0); |
163 | 0 | } |
164 | 0 | else |
165 | 0 | { |
166 | 0 | return IP(0, 0, 0, 0); |
167 | 0 | } |
168 | 0 | } |
169 | | |
170 | 0 | IP IP::loopback() { return IP(127, 0, 0, 1); } |
171 | | |
172 | | IP IP::loopback(bool is_ipv6) |
173 | 0 | { |
174 | 0 | if (is_ipv6) |
175 | 0 | { |
176 | 0 | return IP(0, 0, 0, 0, 0, 0, 0, 1); |
177 | 0 | } |
178 | 0 | else |
179 | 0 | { |
180 | 0 | return IP(127, 0, 0, 1); |
181 | 0 | } |
182 | 0 | } |
183 | | |
184 | 0 | int IP::getFamily() const { return addr_.ss_family; } |
185 | | |
186 | | uint16_t IP::getPort() const |
187 | 0 | { |
188 | 0 | if (addr_.ss_family == AF_INET) |
189 | 0 | { |
190 | 0 | return ntohs(reinterpret_cast<const struct sockaddr_in*>(&addr_)->sin_port); |
191 | 0 | } |
192 | 0 | else if (addr_.ss_family == AF_INET6) |
193 | 0 | { |
194 | 0 | return ntohs(reinterpret_cast<const struct sockaddr_in6*>(&addr_)->sin6_port); |
195 | 0 | } |
196 | 0 | else if (addr_.ss_family == AF_UNIX) |
197 | 0 | { |
198 | | // Ports are a meaningless concept for unix domain sockets. Return |
199 | | // an arbitrary value. |
200 | 0 | return 0; |
201 | 0 | } |
202 | 0 | else |
203 | 0 | { |
204 | 0 | Pistache::details::unreachable(); |
205 | 0 | } |
206 | 0 | } |
207 | | |
208 | | std::string IP::toString() const |
209 | 0 | { |
210 | 0 | if (addr_.ss_family == AF_UNIX) |
211 | 0 | { |
212 | 0 | auto& unAddr = reinterpret_cast<const struct sockaddr_un&>(addr_); |
213 | 0 | if (unAddr.sun_path[0] == '\0') |
214 | 0 | { |
215 | | // The socket is abstract (not present in the file system name |
216 | | // space). Its name starts with the byte following the initial |
217 | | // NUL. As the name may contain embedded NUL bytes and its |
218 | | // length is not available here, simply note that it's an |
219 | | // abstract address. |
220 | 0 | return std::string("[Abstract]"); |
221 | 0 | } |
222 | 0 | else |
223 | 0 | { |
224 | 0 | return std::string(unAddr.sun_path); |
225 | 0 | } |
226 | 0 | } |
227 | | |
228 | 0 | char buff[INET6_ADDRSTRLEN]; |
229 | 0 | const auto* addr_sa = reinterpret_cast<const struct sockaddr*>(&addr_); |
230 | 0 | int err = getnameinfo( |
231 | 0 | addr_sa, sizeof(addr_), buff, sizeof(buff), nullptr, 0, NI_NUMERICHOST); |
232 | 0 | if (err) /* [[unlikely]] */ |
233 | 0 | { |
234 | 0 | throw std::runtime_error(gai_strerror(err)); |
235 | 0 | } |
236 | 0 | return std::string(buff); |
237 | 0 | } |
238 | | |
239 | | void IP::toNetwork(PST_IN_ADDR_T* out) const |
240 | 0 | { |
241 | 0 | if (addr_.ss_family != AF_INET) |
242 | 0 | { |
243 | 0 | throw std::invalid_argument("Inapplicable or invalid address family"); |
244 | 0 | } |
245 | | // Note that in Windows in_addr is a union - effectively a "u_long" |
246 | | // that can be accessed as 4 u_char, 2 u_short, or 1 u_long. |
247 | | // Meanwhile, s_addr on the right-hand side is type ULONG. So both |
248 | | // sides are effectively "ulong *" and the cast is valid |
249 | 0 | const PST_IN_ADDR_T* out_ptr = reinterpret_cast<const PST_IN_ADDR_T*> |
250 | 0 | (&reinterpret_cast<const struct sockaddr_in*>(&addr_)->sin_addr.s_addr); |
251 | 0 | *out = *out_ptr; |
252 | 0 | } |
253 | | |
254 | | void IP::toNetwork(struct in6_addr* out) const |
255 | 0 | { |
256 | 0 | if (addr_.ss_family != AF_INET) |
257 | 0 | { |
258 | 0 | throw std::invalid_argument("Inapplicable or invalid address family"); |
259 | 0 | } |
260 | 0 | *out = reinterpret_cast<const struct sockaddr_in6*>(&addr_)->sin6_addr; |
261 | 0 | } |
262 | | |
263 | | bool IP::supported() |
264 | 0 | { |
265 | 0 | struct PST_IFADDRS* ifaddr = nullptr; |
266 | 0 | struct PST_IFADDRS* ifa = nullptr; |
267 | 0 | int addr_family, n; |
268 | 0 | bool supportsIpv6 = false; |
269 | |
|
270 | 0 | if (PST_GETIFADDRS(&ifaddr) == -1) |
271 | 0 | { |
272 | 0 | throw std::runtime_error("Call to getifaddrs() failed"); |
273 | 0 | } |
274 | | |
275 | 0 | for (ifa = ifaddr, n = 0; ifa != nullptr; ifa = ifa->ifa_next, n++) |
276 | 0 | { |
277 | 0 | if (ifa->ifa_addr == nullptr) |
278 | 0 | { |
279 | 0 | continue; |
280 | 0 | } |
281 | | |
282 | 0 | addr_family = ifa->ifa_addr->sa_family; |
283 | 0 | if (addr_family == AF_INET6) |
284 | 0 | { |
285 | 0 | supportsIpv6 = true; |
286 | 0 | continue; |
287 | 0 | } |
288 | 0 | } |
289 | |
|
290 | 0 | PST_FREEIFADDRS(ifaddr); |
291 | 0 | return supportsIpv6; |
292 | 0 | } |
293 | | |
294 | | AddressParser::AddressParser(const std::string& data) |
295 | 9.80k | { |
296 | | /* If the passed value is a simple IPv6 address as defined by RFC 2373 |
297 | | * (i.e without port nor '[' and ']'), no custom parsing is required. */ |
298 | 9.80k | struct in6_addr tmp; |
299 | 9.80k | if (inet_pton(AF_INET6, data.c_str(), &tmp) == 1) |
300 | 87 | { |
301 | 87 | char normalized_addr[INET6_ADDRSTRLEN]; |
302 | 87 | inet_ntop(AF_INET6, &tmp, normalized_addr, sizeof(normalized_addr)); |
303 | 87 | host_ = normalized_addr; |
304 | 87 | family_ = AF_INET6; |
305 | 87 | return; |
306 | 87 | } |
307 | | |
308 | 9.71k | std::size_t end_pos = data.find(']'); |
309 | 9.71k | std::size_t start_pos = data.find('['); |
310 | 9.71k | if (start_pos != std::string::npos && end_pos != std::string::npos && start_pos < end_pos) |
311 | 1.77k | { |
312 | 1.77k | std::size_t colon_pos = data.find_first_of(':', end_pos); |
313 | 1.77k | if (colon_pos != std::string::npos) |
314 | 443 | { |
315 | 443 | hasColon_ = true; |
316 | 443 | } |
317 | | // Strip '[' and ']' in IPv6 addresses, as it is not part of the |
318 | | // address itself according to RFC 4291 and RFC 5952, but just a way |
319 | | // to represent address + port in an unambiguous way. |
320 | 1.77k | host_ = data.substr(start_pos + 1, end_pos - 1); |
321 | 1.77k | family_ = AF_INET6; |
322 | 1.77k | ++end_pos; |
323 | 1.77k | } |
324 | 7.94k | else |
325 | 7.94k | { |
326 | 7.94k | std::size_t colon_pos = data.find(':'); |
327 | 7.94k | if (colon_pos != std::string::npos) |
328 | 2.03k | { |
329 | 2.03k | hasColon_ = true; |
330 | 2.03k | } |
331 | 7.94k | end_pos = colon_pos; |
332 | 7.94k | host_ = data.substr(0, end_pos); |
333 | 7.94k | family_ = AF_INET; |
334 | 7.94k | } |
335 | | |
336 | 9.71k | if (end_pos != std::string::npos && hasColon_) |
337 | 2.47k | { |
338 | 2.47k | port_ = data.substr(end_pos + 1); |
339 | 2.47k | if (port_.empty()) |
340 | 45 | { |
341 | 45 | PS_LOG_DEBUG_ARGS("port_ empty, data (addr string) %s, " |
342 | 45 | "throwing \"Invalid port\"", |
343 | 45 | data.c_str()); |
344 | | |
345 | 45 | throw std::invalid_argument("Invalid port"); |
346 | 45 | } |
347 | | |
348 | | // Check if port_ is a valid number |
349 | 2.42k | char* tmp2 = nullptr; |
350 | 2.42k | long strtol_res = std::strtol(port_.c_str(), &tmp2, 10); |
351 | 2.42k | if ((strtol_res < 0) || (!tmp2)) |
352 | 74 | { |
353 | 74 | PS_LOG_DEBUG_ARGS("strtol failed for port_ %s, " |
354 | 74 | "throwing \"Invalid port\"", |
355 | 74 | port_.c_str()); |
356 | 74 | throw std::invalid_argument("Invalid port"); |
357 | 74 | } |
358 | 2.35k | hasNumericPort_ = (*tmp2 == '\0'); |
359 | 2.35k | } |
360 | 9.71k | } |
361 | | |
362 | 9.68k | const std::string& AddressParser::rawHost() const { return host_; } |
363 | | |
364 | 9.68k | const std::string& AddressParser::rawPort() const { return port_; } |
365 | | |
366 | 0 | bool AddressParser::hasColon() const { return hasColon_; } |
367 | | |
368 | 0 | bool AddressParser::hasNumericPort() const { return hasNumericPort_; } |
369 | | |
370 | 9.68k | int AddressParser::family() const { return family_; } |
371 | | |
372 | | Address::Address() |
373 | 2.27k | : ip_ {} |
374 | 2.27k | , port_ { 0 } |
375 | 2.27k | , addrLen_(sizeof(struct sockaddr_in6)) |
376 | 2.27k | , scheme_(Scheme::Unspecified) |
377 | 2.27k | { } |
378 | | |
379 | | Address::Address(std::string host, Port port) |
380 | 0 | { |
381 | 0 | std::string addr = std::move(host); |
382 | 0 | addr.append(":"); |
383 | 0 | addr.append(port.toString()); |
384 | 0 | init(std::move(addr), 0 /* no default port, set explicitly */); |
385 | 0 | } |
386 | | |
387 | | Address::Address(std::string addr) |
388 | 0 | { |
389 | 0 | init(std::move(addr), 0 /* default port*/); |
390 | 0 | } |
391 | | |
392 | | Address::Address(const char* addr) |
393 | 0 | { |
394 | 0 | init(std::string(addr), 0 /* default port*/); |
395 | 0 | } |
396 | | |
397 | | Address Address::makeWithDefaultPort(std::string addr, |
398 | | Port default_port, // defaults to zero |
399 | | Scheme scheme, |
400 | | const std::string * page_cptr) |
401 | 0 | { // static |
402 | 0 | Address res; |
403 | 0 | res.init(std::move(addr), default_port, scheme, page_cptr); |
404 | |
|
405 | 0 | return (res); |
406 | 0 | } |
407 | | |
408 | | Address::Address(IP ip, Port port) |
409 | 0 | : ip_(ip) |
410 | 0 | , port_(port) |
411 | 0 | { |
412 | 0 | addrLen_ = ip.getFamily() == AF_INET ? sizeof(struct sockaddr_in) : sizeof(struct sockaddr_in6); |
413 | 0 | } |
414 | | |
415 | | Address Address::fromUnix(struct sockaddr* addr) |
416 | 0 | { |
417 | 0 | const auto family = addr->sa_family; |
418 | 0 | if (family == AF_INET || family == AF_INET6 || family == AF_UNIX) |
419 | 0 | { |
420 | 0 | IP ip = IP(addr); |
421 | 0 | Port port = Port(ip.getPort()); |
422 | 0 | return Address(ip, port); |
423 | 0 | } |
424 | 0 | throw Error("Not an IP or unix domain socket"); |
425 | 0 | } |
426 | | |
427 | 0 | std::string Address::host() const { return ip_.toString(); } |
428 | | |
429 | 0 | Port Address::port() const { return port_; } |
430 | | |
431 | 0 | int Address::family() const { return ip_.getFamily(); } |
432 | | |
433 | | void Address::init(const std::string& addr) |
434 | 0 | { |
435 | 0 | init(addr, 0 /*default port*/); |
436 | 0 | } |
437 | | |
438 | | void Address::init(const std::string& addr, Port default_port) |
439 | 0 | { |
440 | 0 | scheme_ = Scheme::Unspecified; |
441 | | |
442 | | // Handle unix domain addresses separately. |
443 | 0 | if (isUnixDomain(addr)) |
444 | 0 | { |
445 | 0 | struct sockaddr_un unAddr = {}; |
446 | 0 | unAddr.sun_family = AF_UNIX; |
447 | | |
448 | | // See unix(7) manual page; distinguish among unnamed, abstract, |
449 | | // and pathname socket addresses. |
450 | 0 | const auto size = std::min(addr.size(), sizeof unAddr.sun_path); |
451 | 0 | if (size == 0) |
452 | 0 | { |
453 | 0 | addrLen_ = sizeof unAddr.sun_family; |
454 | 0 | } |
455 | 0 | else if (addr[0] == '\0') |
456 | 0 | { |
457 | 0 | addrLen_ = static_cast<socklen_t>( |
458 | 0 | sizeof unAddr.sun_family + size); |
459 | 0 | std::memcpy(unAddr.sun_path, addr.data(), size); |
460 | 0 | } |
461 | 0 | else |
462 | 0 | { |
463 | 0 | addrLen_ = static_cast<socklen_t>( |
464 | 0 | offsetof(struct sockaddr_un, sun_path) + size); |
465 | 0 | PS_STRLCPY(unAddr.sun_path, addr.c_str(), size); |
466 | 0 | if (size == sizeof unAddr.sun_path) |
467 | 0 | { |
468 | 0 | unAddr.sun_path[size - 1] = '\0'; |
469 | 0 | } |
470 | 0 | } |
471 | |
|
472 | 0 | ip_ = IP(reinterpret_cast<struct sockaddr*>(&unAddr)); |
473 | 0 | port_ = Port(ip_.getPort()); |
474 | 0 | return; |
475 | 0 | } |
476 | | |
477 | 0 | if (!default_port) |
478 | 0 | default_port = 80; |
479 | 0 | std::string default_port_str(std::to_string(default_port)); |
480 | |
|
481 | 0 | addrLen_ = family() == AF_INET ? sizeof(struct sockaddr_in) : sizeof(struct sockaddr_in6); |
482 | |
|
483 | 0 | const AddressParser parser(addr); |
484 | 0 | const std::string& host = parser.rawHost(); |
485 | 0 | const std::string& port = parser.rawPort(); |
486 | |
|
487 | 0 | const bool wildcard = host == "*"; |
488 | |
|
489 | 0 | struct addrinfo hints = {}; |
490 | 0 | hints.ai_family = AF_UNSPEC; |
491 | 0 | hints.ai_socktype = SOCK_STREAM; |
492 | 0 | hints.ai_protocol = IPPROTO_TCP; |
493 | |
|
494 | 0 | if (wildcard) |
495 | 0 | { |
496 | 0 | hints.ai_flags = AI_PASSIVE; |
497 | 0 | } |
498 | | |
499 | | // The host is set to nullptr if empty because getaddrinfo() requires |
500 | | // it, and also when it is set to "*" because, when combined with the |
501 | | // AI_PASSIVE flag, it yields the proper wildcard address. The port, if |
502 | | // empty, is set to 80 (http) by default. |
503 | 0 | const char* const addrinfo_host = host.empty() || wildcard ? nullptr : host.c_str(); |
504 | 0 | const char* const addrinfo_port = port.empty() ? default_port_str.c_str() : port.c_str(); |
505 | |
|
506 | 0 | AddrInfo addrinfo; |
507 | 0 | const int err = addrinfo.invoke(addrinfo_host, addrinfo_port, &hints); |
508 | 0 | if (err) |
509 | 0 | { |
510 | 0 | throw std::invalid_argument(gai_strerror(err)); |
511 | 0 | } |
512 | | |
513 | 0 | const struct addrinfo* result = addrinfo.get_info_ptr(); |
514 | |
|
515 | 0 | ip_ = IP(result->ai_addr); |
516 | 0 | port_ = Port(ip_.getPort()); |
517 | | |
518 | | // Check that the port has not overflowed while calling getaddrinfo() |
519 | 0 | if (parser.hasNumericPort() && port_ != std::strtol(addrinfo_port, nullptr, 10)) |
520 | 0 | { |
521 | 0 | throw std::invalid_argument("Invalid numeric port"); |
522 | 0 | } |
523 | 0 | } |
524 | | |
525 | | void Address::init(const std::string& addr, Port default_port, |
526 | | Scheme scheme, const std::string * page_cptr) |
527 | 0 | { |
528 | 0 | init(addr, default_port); |
529 | 0 | scheme_ = scheme; |
530 | |
|
531 | 0 | if (page_cptr) |
532 | 0 | page_ = (*page_cptr); |
533 | 0 | } |
534 | | |
535 | | // Applies heuristics to deterimine whether or not addr names a unix |
536 | | // domain address. If it is zero-length, begins with a NUL byte, or |
537 | | // contains a '/' character (none of which are possible for legitimate |
538 | | // IP-based addresses), it's deemed to be a unix domain address. |
539 | | // |
540 | | // This heuristic rejects pathname unix domain addresses that contain no |
541 | | // '/' characters; such addresses tend not to occur in practice. See the |
542 | | // unix(7) manual page for more infomation. |
543 | | bool Address::isUnixDomain(const std::string& addr) |
544 | 0 | { |
545 | 0 | return addr.size() == 0 || addr[0] == '\0' || addr.find('/') != std::string::npos; |
546 | 0 | } |
547 | | |
548 | | std::ostream& operator<<(std::ostream& os, const Address& address) |
549 | 0 | { |
550 | | /* As recommended by section 6 of RFC 5952, |
551 | | * Notes on Combining IPv6 Addresses with Port Numbers */ |
552 | 0 | if (address.family() == AF_INET6) |
553 | 0 | { |
554 | 0 | os << '['; |
555 | 0 | } |
556 | 0 | os << address.host(); |
557 | 0 | if (address.family() == AF_INET6) |
558 | 0 | { |
559 | 0 | os << ']'; |
560 | 0 | } |
561 | 0 | os << ":" << address.port(); |
562 | 0 | return os; |
563 | 0 | } |
564 | | |
565 | | Error::Error(const char* message) |
566 | 0 | : std::runtime_error(message) |
567 | 0 | { } |
568 | | |
569 | | Error::Error(std::string message) |
570 | 0 | : std::runtime_error(std::move(message)) |
571 | 0 | { } |
572 | | |
573 | | Error Error::system(const char* message) |
574 | 0 | { |
575 | 0 | PST_DECL_SE_ERR_P_EXTRA; |
576 | |
|
577 | 0 | std::string str(message); |
578 | 0 | str += ": "; |
579 | 0 | str += PST_STRERROR_R_ERRNO; |
580 | |
|
581 | 0 | return Error(std::move(str)); |
582 | 0 | } |
583 | | |
584 | | } // namespace Pistache |