LCOV - code coverage report
Current view: top level - waltz/resolv - fd_lookup_name.c (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 117 339 34.5 %
Date: 2026-09-17 04:28:31 Functions: 8 29 27.6 %

          Line data    Source code
       1             : #include <sys/socket.h>
       2             : #include <netinet/in.h>
       3             : #include "fd_netdb.h"
       4             : #include <net/if.h>
       5             : #include <arpa/inet.h>
       6             : #include <ctype.h>
       7             : #include <stdlib.h>
       8             : #include <string.h>
       9             : #include <fcntl.h>
      10             : #include <unistd.h>
      11             : #include <pthread.h>
      12             : #include <errno.h>
      13             : #include "fd_resolv.h"
      14             : #include "fd_lookup.h"
      15             : #include "fd_io_readline.h"
      16             : #include "../../util/cstr/fd_cstr.h"
      17             : #include "../../util/log/fd_log.h"
      18             : #include "../../util/io/fd_io.h"
      19             : #include "../../util/net/fd_ip6.h"
      20             : 
      21             : static int
      22           3 : is_valid_hostname( char const * host ) {
      23           3 :   uchar const * s;
      24           3 :   if( strnlen( host, FD_FQDN_BUF_MAX-1UL )-1UL>=FD_FQDN_BUF_MAX-2UL ) return 0;
      25          24 :   for( s=(void *)host; *s>=0x80 || *s=='.' || *s=='-' || fd_isalnum( *s ); s++ );
      26           3 :   return !*s;
      27           3 : }
      28             : 
      29             : static int
      30             : name_from_null( struct address buf[ static 2 ],
      31             :                 char const *   name,
      32             :                 int            family,
      33           0 :                 int            flags ) {
      34           0 :   int cnt = 0;
      35           0 :   if( name ) return 0;
      36           0 :   if( flags & FD_AI_PASSIVE ) {
      37           0 :     if( family != AF_INET6 )
      38           0 :       buf[cnt++] = (struct address){ .family = AF_INET };
      39           0 :     if( family != AF_INET )
      40           0 :       buf[cnt++] = (struct address){ .family = AF_INET6 };
      41           0 :   } else {
      42           0 :     if( family != AF_INET6 )
      43           0 :       buf[cnt++] = (struct address){ .family = AF_INET, .addr = { 127,0,0,1 } };
      44           0 :     if( family != AF_INET )
      45           0 :       buf[cnt++] = (struct address){ .family = AF_INET6, .addr = { [15] = 1 } };
      46           0 :   }
      47           0 :   return cnt;
      48           0 : }
      49             : 
      50             : static int
      51             : name_from_numeric( struct address buf[ static 1 ],
      52             :                    char const *   name,
      53          30 :                    int            family ) {
      54          30 :   return fd_lookup_ipliteral( buf, name, family );
      55          30 : }
      56             : 
      57             : static int
      58             : name_from_hosts( struct address buf[ static MAXADDRS ],
      59             :                  char           canon[ static FD_FQDN_BUF_MAX ],
      60             :                  char const *   name,
      61          21 :                  int            family ) {
      62          21 :   ulong l = strlen( name );
      63          21 :   int cnt = 0, badfam = 0, have_canon = 0;
      64             : 
      65          21 :   if( fd_etc_hosts_fd<0 ) return 0;
      66             : 
      67          21 :   if( FD_UNLIKELY( -1==lseek( fd_etc_hosts_fd, 0, SEEK_SET ) ) ) {
      68           0 :     FD_LOG_ERR(( "lseek(/etc/hosts,0,SEEK_SET) failed (%i-%s)", errno, fd_io_strerror( errno ) ));
      69           0 :   }
      70             : 
      71          21 :   uchar rbuf[1032];
      72          21 :   fd_io_buffered_istream_t istream[1];
      73          21 :   fd_io_buffered_istream_init( istream, fd_etc_hosts_fd, rbuf, sizeof(rbuf) );
      74             : 
      75          21 :   char line[512];
      76          63 :   while( cnt < MAXADDRS ) {
      77          63 :     int err;
      78          63 :     if( !fd_io_fgets( line, sizeof(line), istream, &err ) ) break;
      79             : 
      80          42 :     char *p, *z;
      81             : 
      82          42 :     if( (p=strchr( line, '#' )) ) *p++='\n', *p=0;
      83          42 :     for( p=line+1; (p=strstr( p, name )) &&
      84          42 :       (!fd_isspace(p[-1]) || !fd_isspace(p[l])); p++ );
      85          42 :     if( !p ) continue;
      86             : 
      87             :     /* Isolate IP address to parse */
      88          54 :     for( p=line; *p && !fd_isspace(*p); p++ );
      89           6 :     *p++ = 0;
      90           6 :     switch( name_from_numeric( buf+cnt, line, family ) ) {
      91           6 :     case 1:
      92           6 :       cnt++;
      93           6 :       break;
      94           0 :     case 0:
      95           0 :       continue;
      96           0 :     default:
      97           0 :       badfam = FD_EAI_NODATA;
      98           0 :       break;
      99           6 :     }
     100             : 
     101           6 :     if( have_canon ) continue;
     102             : 
     103             :     /* Extract first name as canonical name */
     104           3 :     for( ; *p && fd_isspace(*p); p++ );
     105          24 :     for( z=p; *z && !fd_isspace(*z); z++ );
     106           3 :     *z = 0;
     107           3 :     if( is_valid_hostname( p ) ) {
     108           3 :       have_canon = 1;
     109           3 :       memcpy( canon, p, (ulong)( z-p+1 ) );
     110           3 :     }
     111           3 :   }
     112          21 :   return cnt ? cnt : badfam;
     113          21 : }
     114             : 
     115             : struct dpc_ctx {
     116             :   struct address *addrs;
     117             :   char *canon;
     118             :   int cnt;
     119             :   int rrtype;
     120             : };
     121             : 
     122          39 : #define RR_A 1
     123           9 : #define RR_CNAME 5
     124           0 : #define RR_AAAA 28
     125             : 
     126          12 : #define ABUF_SIZE 4800
     127             : 
     128             : static int
     129             : dns_parse_callback( void *       c,
     130             :                     int          rr,
     131             :                     void const * data,
     132             :                     int          len,
     133             :                     void const * packet,
     134           9 :                     int          plen ) {
     135           9 :   char tmp[ FD_FQDN_BUF_MAX ];
     136           9 :   int family = AF_UNSPEC;
     137           9 :   struct dpc_ctx *ctx = c;
     138           9 :   if( rr == RR_CNAME ) {
     139           0 :     if( fd_dn_expand( packet, (uchar const *)packet + plen,
     140           0 :         data, tmp, sizeof tmp ) > 0 && is_valid_hostname( tmp ) )
     141           0 :       strcpy( ctx->canon, tmp );
     142           0 :     return 0;
     143           0 :   }
     144           9 :   if( ctx->cnt >= MAXADDRS ) return 0;
     145           9 :   if( rr != ctx->rrtype    ) return 0;
     146           9 :   switch( rr ) {
     147           9 :   case RR_A:
     148           9 :     if( len != 4 ) return -1;
     149           9 :     family = AF_INET;
     150           9 :     break;
     151           0 :   case RR_AAAA:
     152           0 :     if( len != 16 ) return -1;
     153           0 :     family = AF_INET6;
     154           0 :     break;
     155           9 :   }
     156           9 :   ctx->addrs[ctx->cnt].family = family;
     157           9 :   ctx->addrs[ctx->cnt].scopeid = 0;
     158           9 :   memcpy( ctx->addrs[ctx->cnt++].addr, data, (ulong)len );
     159           9 :   return 0;
     160           9 : }
     161             : 
     162             : static int
     163             : name_from_dns( struct address          buf[ static MAXADDRS ],
     164             :                char                    canon[ static FD_FQDN_BUF_MAX ],
     165             :                char const *            name,
     166             :                int                     family,
     167           0 :                fd_resolvconf_t const * conf ) {
     168           0 :   uchar         qbuf[2][280], abuf[2][ABUF_SIZE];
     169           0 :   uchar const * qp[2] = { qbuf[0], qbuf[1] };
     170           0 :   uchar *       ap[2] = { abuf[0], abuf[1] };
     171           0 :   int qlens[2], alens[2], qtypes[2];
     172           0 :   int nq = 0;
     173           0 :   struct dpc_ctx ctx = { .addrs = buf, .canon = canon };
     174           0 :   static const struct { int af; int rr; } afrr[2] = {
     175           0 :     { .af = AF_INET6, .rr = RR_A },
     176           0 :     { .af = AF_INET, .rr = RR_AAAA },
     177           0 :   };
     178             : 
     179           0 :   for( int i=0; i<2; i++ ) {
     180           0 :     if( family != afrr[i].af ) {
     181           0 :       qlens[nq] = fd_res_mkquery( 0, name, 1, afrr[i].rr,
     182           0 :         qbuf[nq], sizeof *qbuf );
     183           0 :       if( qlens[nq] == -1 )
     184           0 :         return 0;
     185           0 :       qtypes[nq] = afrr[i].rr;
     186           0 :       qbuf[nq][3] = 0; /* don't need AD flag */
     187             :       /* Ensure query IDs are distinct. */
     188           0 :       if( nq && qbuf[nq][0] == qbuf[0][0] )
     189           0 :         qbuf[nq][0]++;
     190           0 :       nq++;
     191           0 :     }
     192           0 :   }
     193             : 
     194           0 :   if( fd_res_msend_rc( nq, qp, qlens, ap, alens, sizeof *abuf, conf )<0 )
     195           0 :     return FD_EAI_SYSTEM-errno;
     196             : 
     197           0 :   for( int i=0; i<nq; i++ ) {
     198           0 :     if( alens[i] < 4 || (abuf[i][3] & 15) == 2 ) return FD_EAI_AGAIN;
     199           0 :     if( (abuf[i][3] & 15) == 3 ) return 0;
     200           0 :     if( (abuf[i][3] & 15) != 0 ) return FD_EAI_FAIL;
     201           0 :   }
     202             : 
     203           0 :   for( int i=nq-1; i>=0; i-- ) {
     204           0 :     ctx.rrtype = qtypes[i];
     205           0 :     if( alens[i] > (int)sizeof(abuf[i]) ) alens[i] = sizeof abuf[i];
     206           0 :     fd_dns_parse( abuf[i], alens[i], dns_parse_callback, &ctx );
     207           0 :   }
     208             : 
     209           0 :   if( ctx.cnt ) return ctx.cnt;
     210           0 :   return FD_EAI_NODATA;
     211           0 : }
     212             : 
     213             : static int
     214             : name_from_dns_search( struct address buf[ static MAXADDRS ],
     215             :                       char           canon[ static FD_FQDN_BUF_MAX ],
     216             :                       char const *   name,
     217           0 :                       int            family ) {
     218           0 :   fd_resolvconf_t conf;
     219           0 :   size_t l;
     220             : 
     221           0 :   if( fd_get_resolv_conf( &conf ) < 0 ) return -1;
     222             : 
     223             :   /* Count dots, suppress search when >=ndots or name ends in
     224             :    * a dot, which is an explicit request for global scope. */
     225           0 :   for( l=0; name[l]; l++ ) {}
     226             : 
     227             :   /* Strip final dot for canon, fail if multiple trailing dots. */
     228           0 :   if( name[l-1]=='.' ) l--;
     229           0 :   if( !l || name[l-1]=='.' ) return FD_EAI_NONAME;
     230             : 
     231             :   /* This can never happen; the caller already checked length. */
     232           0 :   if( l>=FD_FQDN_BUF_MAX ) return FD_EAI_NONAME;
     233             : 
     234             :   /* Name with search domain appended is setup in canon[]. This both
     235             :    * provides the desired default canonical name (if the requested
     236             :    * name is not a CNAME record) and serves as a buffer for passing
     237             :    * the full requested name to name_from_dns. */
     238           0 :   memcpy( canon, name, l );
     239           0 :   canon[l] = '.';
     240             : 
     241           0 :   canon[l] = 0;
     242           0 :   return name_from_dns( buf, canon, name, family, &conf );
     243           0 : }
     244             : 
     245             : int
     246             : fd_dns_ip4_local( char const * name,
     247             :                   uint *       addrs,
     248          27 :                   ulong        addr_max ) {
     249          27 :   ulong l = strnlen( name, FD_FQDN_BUF_MAX-1UL );
     250          27 :   if( FD_UNLIKELY( l-1UL>=FD_FQDN_BUF_MAX-2UL ) ) return FD_EAI_NONAME;
     251             : 
     252          24 :   struct address buf[ MAXADDRS ];
     253          24 :   char           canon[ FD_FQDN_BUF_MAX ];
     254          24 :   int cnt = name_from_numeric( buf, name, AF_INET );
     255          24 :   if( !cnt ) cnt = name_from_hosts( buf, canon, name, AF_INET );
     256          24 :   if( cnt<=0 ) return cnt;
     257             : 
     258           6 :   ulong out = 0UL;
     259          15 :   for( int j=0; j<cnt && out<addr_max; j++ ) {
     260           9 :     if( FD_UNLIKELY( buf[ j ].family!=AF_INET ) ) continue;
     261           9 :     addrs[ out++ ] = FD_LOAD( uint, buf[ j ].addr );
     262           9 :   }
     263           6 :   return out ? (int)out : FD_EAI_NODATA;
     264          24 : }
     265             : 
     266             : int
     267             : fd_dns_ip4_query( char const * name,
     268          18 :                   uchar        query[ static FD_DNS_QUERY_MTU ] ) {
     269          18 :   int qlen = fd_res_mkquery( 0, name, 1, RR_A, query, FD_DNS_QUERY_MTU );
     270          18 :   if( FD_UNLIKELY( qlen==-1 ) ) return FD_EAI_NONAME;
     271          18 :   query[ 3 ] = 0; /* don't need AD flag */
     272          18 :   return qlen;
     273          18 : }
     274             : 
     275             : int
     276             : fd_dns_ip4_answer( uchar const * answer,
     277             :                    ulong         answer_sz,
     278             :                    uint *        addrs,
     279          18 :                    ulong         addr_max ) {
     280          18 :   if( FD_UNLIKELY( answer_sz<12UL ) )      return FD_EAI_AGAIN;  /* short of a full header */
     281          18 :   if( FD_UNLIKELY( (answer[ 3 ]&15)==2 ) ) return FD_EAI_AGAIN;  /* SERVFAIL */
     282          15 :   if( FD_UNLIKELY( (answer[ 3 ]&15)==3 ) ) return FD_EAI_NONAME; /* NXDOMAIN */
     283          12 :   if( FD_UNLIKELY( (answer[ 3 ]&15)!=0 ) ) return FD_EAI_FAIL;
     284             : 
     285          12 :   struct address buf[ MAXADDRS ];
     286          12 :   char           canon[ FD_FQDN_BUF_MAX ] = {0};
     287          12 :   struct dpc_ctx ctx = { .addrs = buf, .canon = canon, .rrtype = RR_A };
     288          12 :   int parse_err = fd_dns_parse( answer, (int)fd_ulong_min( answer_sz, ABUF_SIZE ), dns_parse_callback, &ctx );
     289             :   /* A malformed/truncated packet with no usable leading records is
     290             :      retryable, not proof of no data.  Records parsed before the
     291             :      malformation are still good (truncated replies). */
     292          12 :   if( FD_UNLIKELY( parse_err<0 && !ctx.cnt ) ) return FD_EAI_AGAIN;
     293           9 :   if( FD_UNLIKELY( !ctx.cnt ) ) return FD_EAI_NODATA;
     294             : 
     295           6 :   ulong out = 0UL;
     296          15 :   for( int j=0; j<ctx.cnt && out<addr_max; j++ ) {
     297           9 :     if( FD_UNLIKELY( buf[ j ].family!=AF_INET ) ) continue;
     298           9 :     addrs[ out++ ] = FD_LOAD( uint, buf[ j ].addr );
     299           9 :   }
     300           6 :   return out ? (int)out : FD_EAI_NODATA;
     301           9 : }
     302             : 
     303             : int
     304             : fd_dns_nameservers( uint * addrs,
     305             :                     ulong  addr_max,
     306             :                     uint * opt_timeout,
     307          12 :                     uint * opt_attempts ) {
     308          12 :   fd_resolvconf_t conf;
     309          12 :   if( FD_UNLIKELY( fd_get_resolv_conf( &conf )<0 ) ) return FD_EAI_SYSTEM;
     310          12 :   if( FD_LIKELY( opt_timeout  ) ) *opt_timeout  = conf.timeout;
     311          12 :   if( FD_LIKELY( opt_attempts ) ) *opt_attempts = conf.attempts;
     312             : 
     313          12 :   ulong out = 0UL;
     314          24 :   for( uint i=0U; i<conf.nns && out<addr_max; i++ ) {
     315          12 :     if( FD_UNLIKELY( conf.ns[ i ].family!=AF_INET ) ) continue; /* v6 nameservers unsupported */
     316          12 :     addrs[ out++ ] = FD_LOAD( uint, conf.ns[ i ].addr );
     317          12 :   }
     318             :   /* v6-only resolv.conf: same 127.0.0.1 fallback as no nameservers */
     319          12 :   if( FD_UNLIKELY( !out && addr_max ) ) {
     320           0 :     if( FD_UNLIKELY( conf.nns ) ) FD_LOG_WARNING(( "IPv6 nameservers in /etc/resolv.conf are unsupported; trying 127.0.0.1" ));
     321           0 :     addrs[ out++ ] = fd_uint_bswap( INADDR_LOOPBACK );
     322           0 :   }
     323          12 :   return (int)out;
     324          12 : }
     325             : 
     326             : static const struct policy {
     327             :   uchar addr[16];
     328             :   uchar len, mask;
     329             :   uchar prec, label;
     330             : } defpolicy[] = {
     331             :   { {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01},
     332             :                 15, 0xff, 50, 0 },
     333             :   { {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff},
     334             :                 11, 0xff, 35, 4 },
     335             :   { {0x20,0x02}, 1, 0xff, 30, 2 },
     336             :   { {0x20,0x01}, 3, 0xff, 5,  5 },
     337             :   { {0xfc},      0, 0xfe, 3, 13 },
     338             : #if 0
     339             :   /* These are deprecated and/or returned to the address
     340             :    * pool, so despite the RFC, treating them as special
     341             :    * is probably wrong. */
     342             :   { "", 11, 0xff, 1, 3 },
     343             :   { "\xfe\xc0", 1, 0xc0, 1, 11 },
     344             :   { "\x3f\xfe", 1, 0xff, 1, 12 },
     345             : #endif
     346             :   /* Last rule must match all addresses to stop loop. */
     347             :   { "", 0, 0, 40, 1 },
     348             : };
     349             : 
     350             : static const struct policy *
     351           0 : policyof( struct in6_addr const * a ) {
     352           0 :   for( int i=0; ; i++ ) {
     353           0 :     if( memcmp( a->s6_addr, defpolicy[i].addr, defpolicy[i].len ) )
     354           0 :       continue;
     355           0 :     if( (a->s6_addr[defpolicy[i].len] & defpolicy[i].mask)
     356           0 :         != defpolicy[i].addr[defpolicy[i].len] )
     357           0 :       continue;
     358           0 :     return defpolicy+i;
     359           0 :   }
     360           0 : }
     361             : 
     362             : static int
     363           0 : labelof( struct in6_addr const * a ) {
     364           0 :   return policyof( a )->label;
     365           0 : }
     366             : 
     367             : static int
     368           0 : scopeof( struct in6_addr const * a ) {
     369           0 :   if( IN6_IS_ADDR_MULTICAST(a) ) return a->s6_addr[1] & 15;
     370           0 :   if( IN6_IS_ADDR_LINKLOCAL(a) ) return 2;
     371           0 :   if( IN6_IS_ADDR_LOOPBACK (a) ) return 2;
     372           0 :   if( IN6_IS_ADDR_SITELOCAL(a) ) return 5;
     373           0 :   return 14;
     374           0 : }
     375             : 
     376             : static int
     377             : prefixmatch( struct in6_addr const * s,
     378           0 :              struct in6_addr const * d ) {
     379             :   /* FIXME: The common prefix length should be limited to no greater
     380             :    * than the nominal length of the prefix portion of the source
     381             :    * address. However the definition of the source prefix length is
     382             :    * not clear and thus this limiting is not yet implemented. */
     383           0 :   uint i;
     384           0 :   for( i=0; i<128 && !((s->s6_addr[i/8]^d->s6_addr[i/8])&(128>>(i%8))); i++ );
     385           0 :   return (int)i;
     386           0 : }
     387             : 
     388           0 : #define DAS_USABLE              0x40000000
     389           0 : #define DAS_MATCHINGSCOPE       0x20000000
     390           0 : #define DAS_MATCHINGLABEL       0x10000000
     391           0 : #define DAS_PREC_SHIFT          20
     392           0 : #define DAS_SCOPE_SHIFT         16
     393           0 : #define DAS_PREFIX_SHIFT        8
     394           0 : #define DAS_ORDER_SHIFT         0
     395             : 
     396             : static int
     397             : addrcmp( void const * _a,
     398           0 :          void const * _b ) {
     399           0 :   struct address const * a = _a, *b = _b;
     400           0 :   return b->sortkey - a->sortkey;
     401           0 : }
     402             : 
     403             : int
     404             : fd_lookup_name( struct address buf[ static MAXADDRS ],
     405             :                 char           canon[ static FD_FQDN_BUF_MAX ],
     406             :                 char const *   name,
     407             :                 int            family,
     408           0 :                 int            flags ) {
     409           0 :   int cnt = 0, i, j;
     410             : 
     411           0 :   *canon = 0;
     412           0 :   if( name ) {
     413             :     /* reject empty name and check len so it fits into temp bufs */
     414           0 :     size_t l = strnlen( name, FD_FQDN_BUF_MAX-1UL );
     415           0 :     if( l-1UL>=FD_FQDN_BUF_MAX-2UL )
     416           0 :       return FD_EAI_NONAME;
     417           0 :     memcpy( canon, name, l+1 );
     418           0 :   }
     419             : 
     420             :   /* Procedurally, a request for v6 addresses with the v4-mapped
     421             :    * flag set is like a request for unspecified family, followed
     422             :    * by filtering of the results. */
     423           0 :   if( flags & FD_AI_V4MAPPED ) {
     424           0 :     if( family == AF_INET6 ) family = AF_UNSPEC;
     425           0 :     else flags -= FD_AI_V4MAPPED;
     426           0 :   }
     427             : 
     428             :   /* Try each backend until there's at least one result. */
     429           0 :   cnt = name_from_null( buf, name, family, flags );
     430           0 :   if( !cnt ) cnt = name_from_numeric( buf, name, family );
     431           0 :   if( !cnt && !(flags & FD_AI_NUMERICHOST) ) {
     432           0 :     cnt = name_from_hosts( buf, canon, name, family );
     433           0 :     if( !cnt ) cnt = name_from_dns_search( buf, canon, name, family );
     434           0 :   }
     435           0 :   if( cnt<=0 ) return cnt ? cnt : FD_EAI_NONAME;
     436             : 
     437             :   /* Filter/transform results for v4-mapped lookup, if requested. */
     438           0 :   if( flags & FD_AI_V4MAPPED ) {
     439           0 :     if( !(flags & FD_AI_ALL) ) {
     440             :       /* If any v6 results exist, remove v4 results. */
     441           0 :       for( i=0; i<cnt && buf[i].family != AF_INET6; i++ );
     442           0 :       if( i<cnt ) {
     443           0 :         for( j=0; i<cnt; i++ ) {
     444           0 :           if( buf[i].family == AF_INET6 )
     445           0 :             buf[j++] = buf[i];
     446           0 :         }
     447           0 :         cnt = i = j;
     448           0 :       }
     449           0 :     }
     450             :     /* Translate any remaining v4 results to v6 */
     451           0 :     for( int i=0; i<cnt; i++ ) {
     452           0 :       if( buf[i].family != AF_INET ) continue;
     453           0 :       uint ip4_addr = FD_LOAD( uint, buf[i].addr );
     454           0 :       fd_ip6_addr_ip4_mapped( buf[i].addr, ip4_addr );
     455           0 :       buf[i].family = AF_INET6;
     456           0 :     }
     457           0 :   }
     458             : 
     459             :   /* No further processing is needed if there are fewer than 2
     460             :    * results or if there are only IPv4 results. */
     461           0 :   if( cnt<2 || family==AF_INET ) return cnt;
     462           0 :   for( i=0; i<cnt; i++ ) if( buf[i].family != AF_INET ) break;
     463           0 :   if( i==cnt ) return cnt;
     464             : 
     465             :   /* The following implements a subset of RFC 3484/6724 destination
     466             :    * address selection by generating a single 31-bit sort key for
     467             :    * each address. Rules 3, 4, and 7 are omitted for having
     468             :    * excessive runtime and code size cost and dubious benefit.
     469             :    * So far the label/precedence table cannot be customized. */
     470           0 :   for( int i=0; i<cnt; i++ ) {
     471           0 :     int family = buf[i].family;
     472           0 :     int key = 0;
     473           0 :     struct sockaddr_in6 sa6 = { 0 }, da6 = {
     474           0 :       .sin6_family = AF_INET6,
     475           0 :       .sin6_scope_id = buf[i].scopeid,
     476           0 :       .sin6_port = 65535
     477           0 :     };
     478           0 :     struct sockaddr_in sa4 = { 0 }, da4 = {
     479           0 :       .sin_family = AF_INET,
     480           0 :       .sin_port = 65535
     481           0 :     };
     482           0 :     void *sa, *da;
     483           0 :     socklen_t salen, dalen;
     484           0 :     if( family == AF_INET6 ) {
     485           0 :       memcpy( da6.sin6_addr.s6_addr, buf[i].addr, 16 );
     486           0 :       da = &da6; dalen = sizeof da6;
     487           0 :       sa = &sa6; salen = sizeof sa6;
     488           0 :     } else {
     489           0 :       uint ip4_addr = FD_LOAD( uint, buf[i].addr );
     490           0 :       fd_ip6_addr_ip4_mapped( sa6.sin6_addr.s6_addr, ip4_addr );
     491           0 :       fd_ip6_addr_ip4_mapped( da6.sin6_addr.s6_addr, ip4_addr );
     492           0 :       memcpy( &da4.sin_addr, buf[i].addr, 4 );
     493           0 :       da = &da4; dalen = sizeof da4;
     494           0 :       sa = &sa4; salen = sizeof sa4;
     495           0 :     }
     496           0 :     const struct policy *dpolicy = policyof( &da6.sin6_addr );
     497           0 :     int dscope = scopeof( &da6.sin6_addr );
     498           0 :     int dlabel = dpolicy->label;
     499           0 :     int dprec = dpolicy->prec;
     500           0 :     int prefixlen = 0;
     501           0 :     int fd = socket( family, SOCK_DGRAM|SOCK_CLOEXEC, IPPROTO_UDP );
     502           0 :     if( fd >= 0 ) {
     503           0 :       if( !connect( fd, da, dalen ) ) {
     504           0 :         key |= DAS_USABLE;
     505           0 :         if( !getsockname( fd, sa, &salen ) ) {
     506           0 :           if( family == AF_INET ) memcpy(
     507           0 :             sa6.sin6_addr.s6_addr+12,
     508           0 :             &sa4.sin_addr, 4);
     509           0 :           if( dscope == scopeof( &sa6.sin6_addr ) )
     510           0 :             key |= DAS_MATCHINGSCOPE;
     511           0 :           if( dlabel == labelof( &sa6.sin6_addr ) )
     512           0 :             key |= DAS_MATCHINGLABEL;
     513           0 :           prefixlen = prefixmatch( &sa6.sin6_addr, &da6.sin6_addr );
     514           0 :         }
     515           0 :       }
     516           0 :       close( fd );
     517           0 :     }
     518           0 :     key |= dprec << DAS_PREC_SHIFT;
     519           0 :     key |= (15-dscope) << DAS_SCOPE_SHIFT;
     520           0 :     key |= prefixlen << DAS_PREFIX_SHIFT;
     521           0 :     key |= (MAXADDRS-i) << DAS_ORDER_SHIFT;
     522           0 :     buf[i].sortkey = key;
     523           0 :   }
     524           0 :   qsort( buf, (ulong)cnt, sizeof *buf, addrcmp );
     525             : 
     526           0 :   return cnt;
     527           0 : }

Generated by: LCOV version 1.14