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