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