1 /** @file
2
3 A brief file description
4
5 @section license License
6
7 Licensed to the Apache Software Foundation (ASF) under one
8 or more contributor license agreements. See the NOTICE file
9 distributed with this work for additional information
10 regarding copyright ownership. The ASF licenses this file
11 to you under the Apache License, Version 2.0 (the
12 "License"); you may not use this file except in compliance
13 with the License. You may obtain a copy of the License at
14
15 http://www.apache.org/licenses/LICENSE-2.0
16
17 Unless required by applicable law or agreed to in writing, software
18 distributed under the License is distributed on an "AS IS" BASIS,
19 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
20 See the License for the specific language governing permissions and
21 limitations under the License.
22 */
23
24 #include <fstream>
25
26 #include "tscore/ink_platform.h"
27 #include "tscore/ink_defs.h"
28 #include "tscore/ink_inet.h"
29 #include "tscore/ParseRules.h"
30 #include "tscore/CryptoHash.h"
31 #include "tscore/ink_assert.h"
32 #include "ts/apidefs.h"
33 #include "tscpp/util/TextView.h"
34 #include "tscore/ink_inet.h"
35
36 IpAddr const IpAddr::INVALID;
37
38 using namespace std::literals;
39
40 const std::string_view IP_PROTO_TAG_IPV4("ipv4"sv);
41 const std::string_view IP_PROTO_TAG_IPV6("ipv6"sv);
42 const std::string_view IP_PROTO_TAG_UDP("udp"sv);
43 const std::string_view IP_PROTO_TAG_TCP("tcp"sv);
44 const std::string_view IP_PROTO_TAG_QUIC("quic"sv);
45 const std::string_view IP_PROTO_TAG_TLS_1_0("tls/1.0"sv);
46 const std::string_view IP_PROTO_TAG_TLS_1_1("tls/1.1"sv);
47 const std::string_view IP_PROTO_TAG_TLS_1_2("tls/1.2"sv);
48 const std::string_view IP_PROTO_TAG_TLS_1_3("tls/1.3"sv);
49 const std::string_view IP_PROTO_TAG_HTTP_0_9("http/0.9"sv);
50 const std::string_view IP_PROTO_TAG_HTTP_1_0("http/1.0"sv);
51 const std::string_view IP_PROTO_TAG_HTTP_1_1("http/1.1"sv);
52 const std::string_view IP_PROTO_TAG_HTTP_2_0("h2"sv); // HTTP/2 over TLS
53 const std::string_view IP_PROTO_TAG_HTTP_QUIC("hq-29"sv); // HTTP/0.9 over QUIC
54 const std::string_view IP_PROTO_TAG_HTTP_3("h3-29"sv); // HTTP/3 over QUIC
55 const std::string_view IP_PROTO_TAG_HTTP_QUIC_D27("hq-27"sv); // HTTP/0.9 over QUIC (draft-27)
56 const std::string_view IP_PROTO_TAG_HTTP_3_D27("h3-27"sv); // HTTP/3 over QUIC (draft-27)
57
58 const std::string_view UNIX_PROTO_TAG{"unix"sv};
59
60 uint32_t
ink_inet_addr(const char * s)61 ink_inet_addr(const char *s)
62 {
63 uint32_t u[4];
64 uint8_t *pc = (uint8_t *)s;
65 int n = 0;
66 uint32_t base = 10;
67
68 if (nullptr == s) {
69 return htonl(static_cast<uint32_t>(-1));
70 }
71
72 while (n < 4) {
73 u[n] = 0;
74 base = 10;
75
76 // handle hex, octal
77
78 if (*pc == '0') {
79 if (*++pc == 'x' || *pc == 'X') {
80 base = 16, pc++;
81 } else {
82 base = 8;
83 }
84 }
85 // handle hex, octal, decimal
86
87 while (*pc) {
88 if (ParseRules::is_digit(*pc)) {
89 u[n] = u[n] * base + (*pc++ - '0');
90 continue;
91 }
92 if (base == 16 && ParseRules::is_hex(*pc)) {
93 u[n] = u[n] * 16 + ParseRules::ink_tolower(*pc++) - 'a' + 10;
94 continue;
95 }
96 break;
97 }
98
99 n++;
100 if (*pc == '.') {
101 pc++;
102 } else {
103 break;
104 }
105 }
106
107 if (*pc && !ParseRules::is_wslfcr(*pc)) {
108 return htonl(static_cast<uint32_t>(-1));
109 }
110
111 switch (n) {
112 case 1:
113 return htonl(u[0]);
114 case 2:
115 if (u[0] > 0xff || u[1] > 0xffffff) {
116 return htonl(static_cast<uint32_t>(-1));
117 }
118 return htonl((u[0] << 24) | u[1]);
119 case 3:
120 if (u[0] > 0xff || u[1] > 0xff || u[2] > 0xffff) {
121 return htonl(static_cast<uint32_t>(-1));
122 }
123 return htonl((u[0] << 24) | (u[1] << 16) | u[2]);
124 case 4:
125 if (u[0] > 0xff || u[1] > 0xff || u[2] > 0xff || u[3] > 0xff) {
126 return htonl(static_cast<uint32_t>(-1));
127 }
128 return htonl((u[0] << 24) | (u[1] << 16) | (u[2] << 8) | u[3]);
129 }
130 return htonl(static_cast<uint32_t>(-1));
131 }
132
133 const char *
ats_ip_ntop(const struct sockaddr * addr,char * dst,size_t size)134 ats_ip_ntop(const struct sockaddr *addr, char *dst, size_t size)
135 {
136 const char *zret = nullptr;
137
138 switch (addr->sa_family) {
139 case AF_INET:
140 zret = inet_ntop(AF_INET, &ats_ip4_addr_cast(addr), dst, size);
141 break;
142 case AF_INET6:
143 zret = inet_ntop(AF_INET6, &ats_ip6_addr_cast(addr), dst, size);
144 break;
145 default:
146 zret = dst;
147 snprintf(dst, size, "*Not IP address [%u]*", addr->sa_family);
148 break;
149 }
150 return zret;
151 }
152
153 std::string_view
ats_ip_family_name(int family)154 ats_ip_family_name(int family)
155 {
156 switch (family) {
157 case AF_INET:
158 return IP_PROTO_TAG_IPV4;
159 case AF_INET6:
160 return IP_PROTO_TAG_IPV6;
161 case AF_UNIX:
162 return UNIX_PROTO_TAG;
163 case AF_UNSPEC:
164 return "unspec"sv;
165 default:
166 return "unknown"sv;
167 }
168 }
169
170 const char *
ats_ip_nptop(sockaddr const * addr,char * dst,size_t size)171 ats_ip_nptop(sockaddr const *addr, char *dst, size_t size)
172 {
173 char buff[INET6_ADDRPORTSTRLEN];
174 snprintf(dst, size, "%s:%u", ats_ip_ntop(addr, buff, sizeof(buff)), ats_ip_port_host_order(addr));
175 return dst;
176 }
177
178 int
ats_ip_parse(std::string_view str,std::string_view * addr,std::string_view * port,std::string_view * rest)179 ats_ip_parse(std::string_view str, std::string_view *addr, std::string_view *port, std::string_view *rest)
180 {
181 ts::TextView src(str); /// Easier to work with for parsing.
182 // In case the incoming arguments are null, set them here and only check for null once.
183 // it doesn't matter if it's all the same, the results will be thrown away.
184 std::string_view local;
185 if (!addr) {
186 addr = &local;
187 }
188 if (!port) {
189 port = &local;
190 }
191 if (!rest) {
192 rest = &local;
193 }
194
195 ink_zero(*addr);
196 ink_zero(*port);
197 ink_zero(*rest);
198
199 // Let's see if we can find out what's in the address string.
200 if (src) {
201 bool colon_p = false;
202 src.ltrim_if(&ParseRules::is_ws);
203 // Check for brackets.
204 if ('[' == *src) {
205 /* Ugly. In a number of places we must use bracket notation
206 to support port numbers. Rather than mucking with that
207 everywhere, we'll tweak it here. Experimentally we can't
208 depend on getaddrinfo to handle it. Note that the text
209 buffer size includes space for the nul, so a bracketed
210 address is at most that size - 1 + 2 -> size+1.
211
212 It just gets better. In order to bind link local addresses
213 the scope_id must be set to the interface index. That's
214 most easily done by appending a %intf (where "intf" is the
215 name of the interface) to the address. Which makes
216 the address potentially larger than the standard maximum.
217 So we can't depend on that sizing.
218 */
219 ++src; // skip bracket.
220 *addr = src.take_prefix_at(']');
221 if (':' == *src) {
222 colon_p = true;
223 ++src;
224 }
225 } else {
226 ts::TextView::size_type last = src.rfind(':');
227 if (last != ts::TextView::npos && last == src.find(':')) {
228 // Exactly one colon - leave post colon stuff in @a src.
229 *addr = src.take_prefix_at(last);
230 colon_p = true;
231 } else { // presume no port, use everything.
232 *addr = src;
233 src.clear();
234 }
235 }
236 if (colon_p) {
237 ts::TextView tmp{src};
238 src.ltrim_if(&ParseRules::is_digit);
239
240 if (tmp.data() == src.data()) { // no digits at all
241 src.assign(tmp.data() - 1, tmp.size() + 1); // back up to include colon
242 } else {
243 *port = std::string_view(tmp.data(), src.data() - tmp.data());
244 }
245 }
246 *rest = src;
247 }
248 return addr->empty() ? -1 : 0; // true if we found an address.
249 }
250
251 int
ats_ip_pton(const std::string_view & src,sockaddr * ip)252 ats_ip_pton(const std::string_view &src, sockaddr *ip)
253 {
254 int zret = -1;
255 std::string_view addr, port;
256
257 ats_ip_invalidate(ip);
258 if (0 == ats_ip_parse(src, &addr, &port)) {
259 // Copy if not terminated.
260 if (0 != addr[addr.size() - 1]) {
261 char *tmp = static_cast<char *>(alloca(addr.size() + 1));
262 memcpy(tmp, addr.data(), addr.size());
263 tmp[addr.size()] = 0;
264 addr = std::string_view(tmp, addr.size());
265 }
266 if (addr.find(':') != std::string_view::npos) { // colon -> IPv6
267 in6_addr addr6;
268 if (inet_pton(AF_INET6, addr.data(), &addr6)) {
269 zret = 0;
270 ats_ip6_set(ip, addr6);
271 }
272 } else { // no colon -> must be IPv4
273 in_addr addr4;
274 if (inet_aton(addr.data(), &addr4)) {
275 zret = 0;
276 ats_ip4_set(ip, addr4.s_addr);
277 }
278 }
279 // If we had a successful conversion, set the port.
280 if (ats_is_ip(ip)) {
281 ats_ip_port_cast(ip) = port.empty() ? 0 : htons(atoi(port.data()));
282 }
283 }
284
285 return zret;
286 }
287
288 int
ats_ip_range_parse(std::string_view src,IpAddr & lower,IpAddr & upper)289 ats_ip_range_parse(std::string_view src, IpAddr &lower, IpAddr &upper)
290 {
291 int zret = TS_ERROR;
292 IpAddr addr, addr2;
293 static const IpAddr ZERO_ADDR4{INADDR_ANY};
294 static const IpAddr MAX_ADDR4{INADDR_BROADCAST};
295 static const IpAddr ZERO_ADDR6{in6addr_any};
296 // I can't find a clean way to static const initialize an IPv6 address to all ones.
297 // This is the best I can find that's portable.
298 static const uint64_t ones[]{UINT64_MAX, UINT64_MAX};
299 static const IpAddr MAX_ADDR6{reinterpret_cast<in6_addr const &>(ones)};
300
301 auto idx = src.find_first_of("/-"sv);
302 if (idx != src.npos) {
303 if (idx + 1 >= src.size()) { // must have something past the separator or it's bogus.
304 zret = TS_ERROR;
305 } else if ('/' == src[idx]) {
306 if (TS_SUCCESS == addr.load(src.substr(0, idx))) { // load the address
307 ts::TextView parsed;
308 src.remove_prefix(idx + 1); // drop address and separator.
309 int cidr = ts::svtoi(src, &parsed);
310 if (parsed.size() && 0 <= cidr) { // a cidr that's a positive integer.
311 // Special case the cidr sizes for 0, maximum, and for IPv6 64 bit boundaries.
312 if (addr.isIp4()) {
313 zret = TS_SUCCESS;
314 if (0 == cidr) {
315 lower = ZERO_ADDR4;
316 upper = MAX_ADDR4;
317 } else if (cidr <= 32) {
318 lower = upper = addr;
319 if (cidr < 32) {
320 in_addr_t mask = htonl(INADDR_BROADCAST << (32 - cidr));
321 lower._addr._ip4 &= mask;
322 upper._addr._ip4 |= ~mask;
323 }
324 } else {
325 zret = TS_ERROR;
326 }
327 } else if (addr.isIp6()) {
328 uint64_t mask;
329 zret = TS_SUCCESS;
330 if (cidr == 0) {
331 lower = ZERO_ADDR6;
332 upper = MAX_ADDR6;
333 } else if (cidr < 64) { // only upper bytes affected, lower bytes are forced.
334 mask = htobe64(~static_cast<uint64_t>(0) << (64 - cidr));
335 lower._family = upper._family = addr._family;
336 lower._addr._u64[0] = addr._addr._u64[0] & mask;
337 lower._addr._u64[1] = 0;
338 upper._addr._u64[0] = addr._addr._u64[0] | ~mask;
339 upper._addr._u64[1] = ~static_cast<uint64_t>(0);
340 } else if (cidr == 64) {
341 lower._family = upper._family = addr._family;
342 lower._addr._u64[0] = upper._addr._u64[0] = addr._addr._u64[0];
343 lower._addr._u64[1] = 0;
344 upper._addr._u64[1] = ~static_cast<uint64_t>(0);
345 } else if (cidr <= 128) { // lower bytes changed, upper bytes unaffected.
346 lower = upper = addr;
347 if (cidr < 128) {
348 mask = htobe64(~static_cast<uint64_t>(0) << (128 - cidr));
349 lower._addr._u64[1] &= mask;
350 upper._addr._u64[1] |= ~mask;
351 }
352 } else {
353 zret = TS_ERROR;
354 }
355 }
356 }
357 }
358 } else if (TS_SUCCESS == addr.load(src.substr(0, idx)) && TS_SUCCESS == addr2.load(src.substr(idx + 1)) &&
359 addr.family() == addr2.family()) {
360 zret = TS_SUCCESS;
361 // not '/' so must be '-'
362 lower = addr;
363 upper = addr2;
364 }
365 } else if (TS_SUCCESS == addr.load(src)) {
366 zret = TS_SUCCESS;
367 lower = upper = addr;
368 }
369 return zret;
370 }
371
372 uint32_t
ats_ip_hash(sockaddr const * addr)373 ats_ip_hash(sockaddr const *addr)
374 {
375 if (ats_is_ip4(addr)) {
376 return ats_ip4_addr_cast(addr);
377 } else if (ats_is_ip6(addr)) {
378 CryptoHash hash;
379 CryptoContext().hash_immediate(hash, const_cast<uint8_t *>(ats_ip_addr8_cast(addr)), TS_IP6_SIZE);
380 return hash.u32[0];
381 } else {
382 // Bad address type.
383 return 0;
384 }
385 }
386
387 uint64_t
ats_ip_port_hash(sockaddr const * addr)388 ats_ip_port_hash(sockaddr const *addr)
389 {
390 if (ats_is_ip4(addr)) {
391 return (static_cast<uint64_t>(ats_ip4_addr_cast(addr)) << 16) | (ats_ip_port_cast(addr));
392 } else if (ats_is_ip6(addr)) {
393 CryptoHash hash;
394 CryptoContext hash_context;
395 hash_context.update(const_cast<uint8_t *>(ats_ip_addr8_cast(addr)), TS_IP6_SIZE);
396 in_port_t port = ats_ip_port_cast(addr);
397 hash_context.update(reinterpret_cast<uint8_t *>(&port), sizeof(port));
398 hash_context.finalize(hash);
399 return hash.u64[0];
400 } else {
401 // Bad address type.
402 return 0;
403 }
404 }
405
406 int
ats_ip_to_hex(sockaddr const * src,char * dst,size_t len)407 ats_ip_to_hex(sockaddr const *src, char *dst, size_t len)
408 {
409 int zret = 0;
410 ink_assert(len);
411 const char *dst_limit = dst + len - 1; // reserve null space.
412 if (ats_is_ip(src)) {
413 uint8_t const *data = ats_ip_addr8_cast(src);
414 for (uint8_t const *src_limit = data + ats_ip_addr_size(src); data < src_limit && dst + 1 < dst_limit; ++data, zret += 2) {
415 uint8_t n1 = (*data >> 4) & 0xF; // high nybble.
416 uint8_t n0 = *data & 0xF; // low nybble.
417
418 *dst++ = n1 > 9 ? n1 + 'A' - 10 : n1 + '0';
419 *dst++ = n0 > 9 ? n0 + 'A' - 10 : n0 + '0';
420 }
421 }
422 *dst = 0; // terminate but don't include that in the length.
423 return zret;
424 }
425
426 sockaddr *
ats_ip_set(sockaddr * dst,IpAddr const & addr,uint16_t port)427 ats_ip_set(sockaddr *dst, IpAddr const &addr, uint16_t port)
428 {
429 if (AF_INET == addr._family) {
430 ats_ip4_set(dst, addr._addr._ip4, port);
431 } else if (AF_INET6 == addr._family) {
432 ats_ip6_set(dst, addr._addr._ip6, port);
433 } else {
434 ats_ip_invalidate(dst);
435 }
436 return dst;
437 }
438
439 int
load(const char * text)440 IpAddr::load(const char *text)
441 {
442 IpEndpoint ip;
443 int zret = ats_ip_pton(text, &ip);
444 *this = ip;
445 return zret;
446 }
447
448 int
load(std::string_view const & text)449 IpAddr::load(std::string_view const &text)
450 {
451 IpEndpoint ip;
452 int zret = ats_ip_pton(text, &ip.sa);
453 this->assign(&ip.sa);
454 return zret;
455 }
456
457 char *
toString(char * dest,size_t len) const458 IpAddr::toString(char *dest, size_t len) const
459 {
460 IpEndpoint ip;
461 ip.assign(*this);
462 ats_ip_ntop(&ip, dest, len);
463 return dest;
464 }
465
466 bool
isMulticast() const467 IpAddr::isMulticast() const
468 {
469 return (AF_INET == _family && 0xe == (_addr._byte[0] >> 4)) || (AF_INET6 == _family && IN6_IS_ADDR_MULTICAST(&_addr._ip6));
470 }
471
472 bool
operator ==(IpAddr const & lhs,sockaddr const * rhs)473 operator==(IpAddr const &lhs, sockaddr const *rhs)
474 {
475 bool zret = false;
476 if (lhs._family == rhs->sa_family) {
477 if (AF_INET == lhs._family) {
478 zret = lhs._addr._ip4 == ats_ip4_addr_cast(rhs);
479 } else if (AF_INET6 == lhs._family) {
480 zret = 0 == memcmp(&lhs._addr._ip6, &ats_ip6_addr_cast(rhs), sizeof(in6_addr));
481 } else { // map all non-IP to the same thing.
482 zret = true;
483 }
484 } // else different families, not equal.
485 return zret;
486 }
487
488 /** Compare two IP addresses.
489 This is useful for IPv4, IPv6, and the unspecified address type.
490 If the addresses are of different types they are ordered
491
492 Non-IP < IPv4 < IPv6
493
494 - all non-IP addresses are the same ( including @c AF_UNSPEC )
495 - IPv4 addresses are compared numerically (host order)
496 - IPv6 addresses are compared byte wise in network order (MSB to LSB)
497
498 @return
499 - -1 if @a lhs is less than @a rhs.
500 - 0 if @a lhs is identical to @a rhs.
501 - 1 if @a lhs is greater than @a rhs.
502 */
503 int
cmp(self const & that) const504 IpAddr::cmp(self const &that) const
505 {
506 int zret = 0;
507 uint16_t rtype = that._family;
508 uint16_t ltype = _family;
509
510 // We lump all non-IP addresses into a single equivalence class
511 // that is less than an IP address. This includes AF_UNSPEC.
512 if (AF_INET == ltype) {
513 if (AF_INET == rtype) {
514 in_addr_t la = ntohl(_addr._ip4);
515 in_addr_t ra = ntohl(that._addr._ip4);
516 if (la < ra) {
517 zret = -1;
518 } else if (la > ra) {
519 zret = 1;
520 } else {
521 zret = 0;
522 }
523 } else if (AF_INET6 == rtype) { // IPv4 < IPv6
524 zret = -1;
525 } else { // IP > not IP
526 zret = 1;
527 }
528 } else if (AF_INET6 == ltype) {
529 if (AF_INET6 == rtype) {
530 zret = memcmp(&_addr._ip6, &that._addr._ip6, TS_IP6_SIZE);
531 } else {
532 zret = 1; // IPv6 greater than any other type.
533 }
534 } else if (AF_INET == rtype || AF_INET6 == rtype) {
535 // ltype is non-IP so it's less than either IP type.
536 zret = -1;
537 } else { // Both types are non-IP so they're equal.
538 zret = 0;
539 }
540
541 return zret;
542 }
543
544 int
ats_ip_getbestaddrinfo(const char * host,IpEndpoint * ip4,IpEndpoint * ip6)545 ats_ip_getbestaddrinfo(const char *host, IpEndpoint *ip4, IpEndpoint *ip6)
546 {
547 int zret = -1;
548 int port = 0; // port value to assign if we find an address.
549 addrinfo ai_hints;
550 addrinfo *ai_result;
551 std::string_view addr_text, port_text;
552 std::string_view src(host, strlen(host) + 1);
553
554 if (ip4) {
555 ats_ip_invalidate(ip4);
556 }
557 if (ip6) {
558 ats_ip_invalidate(ip6);
559 }
560
561 if (0 == ats_ip_parse(src, &addr_text, &port_text)) {
562 // Copy if not terminated.
563 if (0 != addr_text[addr_text.size() - 1]) {
564 char *tmp = static_cast<char *>(alloca(addr_text.size() + 1));
565 memcpy(tmp, addr_text.data(), addr_text.size());
566 tmp[addr_text.size()] = 0;
567 addr_text = std::string_view(tmp, addr_text.size());
568 }
569 ink_zero(ai_hints);
570 ai_hints.ai_family = AF_UNSPEC;
571 ai_hints.ai_flags = AI_ADDRCONFIG;
572 zret = getaddrinfo(addr_text.data(), nullptr, &ai_hints, &ai_result);
573
574 if (0 == zret) {
575 // Walk the returned addresses and pick the "best".
576 enum {
577 NA, // Not an (IP) Address.
578 LO, // Loopback.
579 LL, // Link Local.
580 PR, // Private.
581 MC, // Multicast.
582 GL // Global.
583 } spot_type = NA,
584 ip4_type = NA, ip6_type = NA;
585 sockaddr const *ip4_src = nullptr;
586 sockaddr const *ip6_src = nullptr;
587
588 for (addrinfo *ai_spot = ai_result; ai_spot; ai_spot = ai_spot->ai_next) {
589 sockaddr const *ai_ip = ai_spot->ai_addr;
590 if (!ats_is_ip(ai_ip)) {
591 spot_type = NA;
592 } else if (ats_is_ip_loopback(ai_ip)) {
593 spot_type = LO;
594 } else if (ats_is_ip_linklocal(ai_ip)) {
595 spot_type = LL;
596 } else if (ats_is_ip_private(ai_ip)) {
597 spot_type = PR;
598 } else if (ats_is_ip_multicast(ai_ip)) {
599 spot_type = MC;
600 } else {
601 spot_type = GL;
602 }
603
604 if (spot_type == NA) {
605 continue; // Next!
606 }
607
608 if (ats_is_ip4(ai_ip)) {
609 if (spot_type > ip4_type) {
610 ip4_src = ai_ip;
611 ip4_type = spot_type;
612 }
613 } else if (ats_is_ip6(ai_ip)) {
614 if (spot_type > ip6_type) {
615 ip6_src = ai_ip;
616 ip6_type = spot_type;
617 }
618 }
619 }
620 if (ip4 && ip4_type > NA) {
621 ats_ip_copy(ip4, ip4_src);
622 }
623 if (ip6 && ip6_type > NA) {
624 ats_ip_copy(ip6, ip6_src);
625 }
626 freeaddrinfo(ai_result); // free *after* the copy.
627 }
628 }
629
630 // We don't really care if the port is null terminated - the parser
631 // would get all the digits so the next character is a non-digit (null or
632 // not) and atoi will do the right thing in either case.
633 if (port_text.size()) {
634 port = htons(atoi(port_text.data()));
635 }
636 if (ats_is_ip(ip4)) {
637 ats_ip_port_cast(ip4) = port;
638 }
639 if (ats_is_ip(ip6)) {
640 ats_ip_port_cast(ip6) = port;
641 }
642
643 if (!ats_is_ip(ip4) && !ats_is_ip(ip6)) {
644 zret = -1;
645 }
646
647 return zret;
648 }
649
650 int
ats_ip_check_characters(std::string_view text)651 ats_ip_check_characters(std::string_view text)
652 {
653 bool found_colon = false;
654 bool found_hex = false;
655 for (char c : text) {
656 if (':' == c) {
657 found_colon = true;
658 } else if ('.' == c || isdigit(c)) { /* empty */
659 ;
660 } else if (isxdigit(c)) {
661 found_hex = true;
662 } else {
663 return AF_UNSPEC;
664 }
665 }
666
667 return found_hex && !found_colon ? AF_UNSPEC : found_colon ? AF_INET6 : AF_INET;
668 }
669
670 int
ats_tcp_somaxconn()671 ats_tcp_somaxconn()
672 {
673 int value = 0;
674
675 /* Darwin version ... */
676 #if HAVE_SYSCTLBYNAME
677 size_t value_size = sizeof(value);
678 if (sysctlbyname("kern.ipc.somaxconn", &value, &value_size, nullptr, 0) < 0) {
679 value = 0;
680 }
681 #else
682 std::ifstream f("/proc/sys/net/core/somaxconn", std::ifstream::in);
683 if (f.good()) {
684 f >> value;
685 }
686 #endif
687
688 // Default to the compatible value we used before detection. SOMAXCONN is the right
689 // macro to use, but most systems set this to 128, which is just too small.
690 if (value <= 0 || value > 65535) {
691 value = 1024;
692 }
693
694 return value;
695 }
696
697 namespace ts
698 {
699 BufferWriter &
bwformat(BufferWriter & w,BWFSpec const & spec,in_addr_t addr)700 bwformat(BufferWriter &w, BWFSpec const &spec, in_addr_t addr)
701 {
702 uint8_t *ptr = reinterpret_cast<uint8_t *>(&addr);
703 BWFSpec local_spec{spec}; // Format for address elements.
704 bool align_p = false;
705
706 if (spec._ext.size()) {
707 if (spec._ext.front() == '=') {
708 align_p = true;
709 local_spec._fill = '0';
710 } else if (spec._ext.size() > 1 && spec._ext[1] == '=') {
711 align_p = true;
712 local_spec._fill = spec._ext[0];
713 }
714 }
715
716 if (align_p) {
717 local_spec._min = 3;
718 local_spec._align = BWFSpec::Align::RIGHT;
719 } else {
720 local_spec._min = 0;
721 }
722
723 bwformat(w, local_spec, ptr[0]);
724 w.write('.');
725 bwformat(w, local_spec, ptr[1]);
726 w.write('.');
727 bwformat(w, local_spec, ptr[2]);
728 w.write('.');
729 bwformat(w, local_spec, ptr[3]);
730 return w;
731 }
732
733 BufferWriter &
bwformat(BufferWriter & w,BWFSpec const & spec,in6_addr const & addr)734 bwformat(BufferWriter &w, BWFSpec const &spec, in6_addr const &addr)
735 {
736 using QUAD = uint16_t const;
737 BWFSpec local_spec{spec}; // Format for address elements.
738 uint8_t const *ptr = addr.s6_addr;
739 uint8_t const *limit = ptr + sizeof(addr.s6_addr);
740 QUAD *lower = nullptr; // the best zero range
741 QUAD *upper = nullptr;
742 bool align_p = false;
743
744 if (spec._ext.size()) {
745 if (spec._ext.front() == '=') {
746 align_p = true;
747 local_spec._fill = '0';
748 } else if (spec._ext.size() > 1 && spec._ext[1] == '=') {
749 align_p = true;
750 local_spec._fill = spec._ext[0];
751 }
752 }
753
754 if (align_p) {
755 local_spec._min = 4;
756 local_spec._align = BWFSpec::Align::RIGHT;
757 } else {
758 local_spec._min = 0;
759 // do 0 compression if there's no internal fill.
760 for (QUAD *spot = reinterpret_cast<QUAD *>(ptr), *last = reinterpret_cast<QUAD *>(limit), *current = nullptr; spot < last;
761 ++spot) {
762 if (0 == *spot) {
763 if (current) {
764 // If there's no best, or this is better, remember it.
765 if (!lower || (upper - lower < spot - current)) {
766 lower = current;
767 upper = spot;
768 }
769 } else {
770 current = spot;
771 }
772 } else {
773 current = nullptr;
774 }
775 }
776 }
777
778 if (!local_spec.has_numeric_type()) {
779 local_spec._type = 'x';
780 }
781
782 for (; ptr < limit; ptr += 2) {
783 if (reinterpret_cast<uint8_t const *>(lower) <= ptr && ptr <= reinterpret_cast<uint8_t const *>(upper)) {
784 if (ptr == addr.s6_addr) {
785 w.write(':'); // only if this is the first quad.
786 }
787 if (ptr == reinterpret_cast<uint8_t const *>(upper)) {
788 w.write(':');
789 }
790 } else {
791 uint16_t f = (ptr[0] << 8) + ptr[1];
792 bwformat(w, local_spec, f);
793 if (ptr != limit - 2) {
794 w.write(':');
795 }
796 }
797 }
798 return w;
799 }
800
801 BufferWriter &
bwformat(BufferWriter & w,BWFSpec const & spec,IpAddr const & addr)802 bwformat(BufferWriter &w, BWFSpec const &spec, IpAddr const &addr)
803 {
804 BWFSpec local_spec{spec}; // Format for address elements and port.
805 bool addr_p{true};
806 bool family_p{false};
807
808 if (spec._ext.size()) {
809 if (spec._ext.front() == '=') {
810 local_spec._ext.remove_prefix(1);
811 } else if (spec._ext.size() > 1 && spec._ext[1] == '=') {
812 local_spec._ext.remove_prefix(2);
813 }
814 }
815 if (local_spec._ext.size()) {
816 addr_p = false;
817 for (char c : local_spec._ext) {
818 switch (c) {
819 case 'a':
820 case 'A':
821 addr_p = true;
822 break;
823 case 'f':
824 case 'F':
825 family_p = true;
826 break;
827 }
828 }
829 }
830
831 if (addr_p) {
832 if (addr.isIp4()) {
833 bwformat(w, spec, addr._addr._ip4);
834 } else if (addr.isIp6()) {
835 bwformat(w, spec, addr._addr._ip6);
836 } else {
837 w.print("*Not IP address [{}]*", addr.family());
838 }
839 }
840
841 if (family_p) {
842 local_spec._min = 0;
843 if (addr_p) {
844 w.write(' ');
845 }
846 if (spec.has_numeric_type()) {
847 bwformat(w, local_spec, static_cast<uintmax_t>(addr.family()));
848 } else {
849 bwformat(w, local_spec, ats_ip_family_name(addr.family()));
850 }
851 }
852 return w;
853 }
854
855 BufferWriter &
bwformat(BufferWriter & w,BWFSpec const & spec,sockaddr const * addr)856 bwformat(BufferWriter &w, BWFSpec const &spec, sockaddr const *addr)
857 {
858 BWFSpec local_spec{spec}; // Format for address elements and port.
859 bool port_p{true};
860 bool addr_p{true};
861 bool family_p{false};
862 bool local_numeric_fill_p{false};
863 char local_numeric_fill_char{'0'};
864
865 if (spec._type == 'p' || spec._type == 'P') {
866 bwformat(w, spec, static_cast<void const *>(addr));
867 return w;
868 }
869
870 if (spec._ext.size()) {
871 if (spec._ext.front() == '=') {
872 local_numeric_fill_p = true;
873 local_spec._ext.remove_prefix(1);
874 } else if (spec._ext.size() > 1 && spec._ext[1] == '=') {
875 local_numeric_fill_p = true;
876 local_numeric_fill_char = spec._ext.front();
877 local_spec._ext.remove_prefix(2);
878 }
879 }
880 if (local_spec._ext.size()) {
881 addr_p = port_p = false;
882 for (char c : local_spec._ext) {
883 switch (c) {
884 case 'a':
885 case 'A':
886 addr_p = true;
887 break;
888 case 'p':
889 case 'P':
890 port_p = true;
891 break;
892 case 'f':
893 case 'F':
894 family_p = true;
895 break;
896 }
897 }
898 }
899
900 if (addr_p) {
901 bool bracket_p = false;
902 switch (addr->sa_family) {
903 case AF_INET:
904 bwformat(w, spec, ats_ip4_addr_cast(addr));
905 break;
906 case AF_INET6:
907 if (port_p) {
908 w.write('[');
909 bracket_p = true; // take a note - put in the trailing bracket.
910 }
911 bwformat(w, spec, ats_ip6_addr_cast(addr));
912 break;
913 default:
914 w.print("*Not IP address [{}]*", addr->sa_family);
915 break;
916 }
917 if (bracket_p) {
918 w.write(']');
919 }
920 if (port_p) {
921 w.write(':');
922 }
923 }
924 if (port_p) {
925 if (local_numeric_fill_p) {
926 local_spec._min = 5;
927 local_spec._fill = local_numeric_fill_char;
928 local_spec._align = BWFSpec::Align::RIGHT;
929 } else {
930 local_spec._min = 0;
931 }
932 bwformat(w, local_spec, static_cast<uintmax_t>(ats_ip_port_host_order(addr)));
933 }
934 if (family_p) {
935 local_spec._min = 0;
936 if (addr_p || port_p) {
937 w.write(' ');
938 }
939 if (spec.has_numeric_type()) {
940 bwformat(w, local_spec, static_cast<uintmax_t>(addr->sa_family));
941 } else {
942 bwformat(w, local_spec, ats_ip_family_name(addr->sa_family));
943 }
944 }
945 return w;
946 }
947
948 namespace bwf
949 {
950 detail::MemDump
Hex_Dump(IpEndpoint const & addr)951 Hex_Dump(IpEndpoint const &addr)
952 {
953 return detail::MemDump(ats_ip_addr8_cast(&addr), ats_ip_addr_size(&addr));
954 }
955 } // namespace bwf
956
957 } // namespace ts
958