LCOV - code coverage report
Current view: top level - waltz/resolv - fd_lookup_name.c (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 0 283 0.0 %
Date: 2025-07-01 05:00:49 Functions: 0 25 0.0 %

          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           0 : is_valid_hostname( char const * host ) {
      23           0 :   uchar const * s;
      24           0 :   if( strnlen( host, 255 )-1 >= 254 ) return 0;
      25           0 :   for( s=(void *)host; *s>=0x80 || *s=='.' || *s=='-' || fd_isalnum( *s ); s++ );
      26           0 :   return !*s;
      27           0 : }
      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           0 :                    int            family ) {
      54           0 :   return fd_lookup_ipliteral( buf, name, family );
      55           0 : }
      56             : 
      57             : static int
      58             : name_from_hosts( struct address buf[ static MAXADDRS ],
      59             :                  char           canon[ static 256 ],
      60             :                  char const *   name,
      61           0 :                  int            family ) {
      62           0 :   ulong l = strlen( name );
      63           0 :   int cnt = 0, badfam = 0, have_canon = 0;
      64             : 
      65           0 :   if( fd_etc_hosts_fd<0 ) return 0;
      66             : 
      67           0 :   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           0 :   uchar rbuf[1032];
      72           0 :   fd_io_buffered_istream_t istream[1];
      73           0 :   fd_io_buffered_istream_init( istream, fd_etc_hosts_fd, rbuf, sizeof(rbuf) );
      74             : 
      75           0 :   char line[512];
      76           0 :   while( cnt < MAXADDRS ) {
      77           0 :     int err;
      78           0 :     if( !fd_io_fgets( line, sizeof(line), istream, &err ) ) break;
      79             : 
      80           0 :     char *p, *z;
      81             : 
      82           0 :     if( (p=strchr( line, '#' )) ) *p++='\n', *p=0;
      83           0 :     for( p=line+1; (p=strstr( p, name )) &&
      84           0 :       (!fd_isspace(p[-1]) || !fd_isspace(p[l])); p++ );
      85           0 :     if( !p ) continue;
      86             : 
      87             :     /* Isolate IP address to parse */
      88           0 :     for( p=line; *p && !fd_isspace(*p); p++ );
      89           0 :     *p++ = 0;
      90           0 :     switch( name_from_numeric( buf+cnt, line, family ) ) {
      91           0 :     case 1:
      92           0 :       cnt++;
      93           0 :       break;
      94           0 :     case 0:
      95           0 :       continue;
      96           0 :     default:
      97           0 :       badfam = FD_EAI_NODATA;
      98           0 :       break;
      99           0 :     }
     100             : 
     101           0 :     if( have_canon ) continue;
     102             : 
     103             :     /* Extract first name as canonical name */
     104           0 :     for( ; *p && fd_isspace(*p); p++ );
     105           0 :     for( z=p; *z && !fd_isspace(*z); z++ );
     106           0 :     *z = 0;
     107           0 :     if( is_valid_hostname( p ) ) {
     108           0 :       have_canon = 1;
     109           0 :       memcpy( canon, p, (ulong)( z-p+1 ) );
     110           0 :     }
     111           0 :   }
     112           0 :   return cnt ? cnt : badfam;
     113           0 : }
     114             : 
     115             : struct dpc_ctx {
     116             :   struct address *addrs;
     117             :   char *canon;
     118             :   int cnt;
     119             :   int rrtype;
     120             : };
     121             : 
     122           0 : #define RR_A 1
     123           0 : #define RR_CNAME 5
     124           0 : #define RR_AAAA 28
     125             : 
     126             : #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           0 :                     int          plen ) {
     135           0 :   char tmp[256];
     136           0 :   int family = AF_UNSPEC;
     137           0 :   struct dpc_ctx *ctx = c;
     138           0 :   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           0 :   if( ctx->cnt >= MAXADDRS ) return 0;
     145           0 :   if( rr != ctx->rrtype    ) return 0;
     146           0 :   switch( rr ) {
     147           0 :   case RR_A:
     148           0 :     if( len != 4 ) return -1;
     149           0 :     family = AF_INET;
     150           0 :     break;
     151           0 :   case RR_AAAA:
     152           0 :     if( len != 16 ) return -1;
     153           0 :     family = AF_INET6;
     154           0 :     break;
     155           0 :   }
     156           0 :   ctx->addrs[ctx->cnt].family = family;
     157           0 :   ctx->addrs[ctx->cnt].scopeid = 0;
     158           0 :   memcpy( ctx->addrs[ctx->cnt++].addr, data, (ulong)len );
     159           0 :   return 0;
     160           0 : }
     161             : 
     162             : static int
     163             : name_from_dns( struct address          buf[ static MAXADDRS ],
     164             :                char                    canon[ static 256 ],
     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 256 ],
     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 >= 256 ) 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             : static const struct policy {
     246             :   uchar addr[16];
     247             :   uchar len, mask;
     248             :   uchar prec, label;
     249             : } defpolicy[] = {
     250             :   { {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01},
     251             :                 15, 0xff, 50, 0 },
     252             :   { {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff},
     253             :                 11, 0xff, 35, 4 },
     254             :   { {0x20,0x02}, 1, 0xff, 30, 2 },
     255             :   { {0x20,0x01}, 3, 0xff, 5,  5 },
     256             :   { {0xfc},      0, 0xfe, 3, 13 },
     257             : #if 0
     258             :   /* These are deprecated and/or returned to the address
     259             :    * pool, so despite the RFC, treating them as special
     260             :    * is probably wrong. */
     261             :   { "", 11, 0xff, 1, 3 },
     262             :   { "\xfe\xc0", 1, 0xc0, 1, 11 },
     263             :   { "\x3f\xfe", 1, 0xff, 1, 12 },
     264             : #endif
     265             :   /* Last rule must match all addresses to stop loop. */
     266             :   { "", 0, 0, 40, 1 },
     267             : };
     268             : 
     269             : static const struct policy *
     270           0 : policyof( struct in6_addr const * a ) {
     271           0 :   for( int i=0; ; i++ ) {
     272           0 :     if( memcmp( a->s6_addr, defpolicy[i].addr, defpolicy[i].len ) )
     273           0 :       continue;
     274           0 :     if( (a->s6_addr[defpolicy[i].len] & defpolicy[i].mask)
     275           0 :         != defpolicy[i].addr[defpolicy[i].len] )
     276           0 :       continue;
     277           0 :     return defpolicy+i;
     278           0 :   }
     279           0 : }
     280             : 
     281             : static int
     282           0 : labelof( struct in6_addr const * a ) {
     283           0 :   return policyof( a )->label;
     284           0 : }
     285             : 
     286             : static int
     287           0 : scopeof( struct in6_addr const * a ) {
     288           0 :   if( IN6_IS_ADDR_MULTICAST(a) ) return a->s6_addr[1] & 15;
     289           0 :   if( IN6_IS_ADDR_LINKLOCAL(a) ) return 2;
     290           0 :   if( IN6_IS_ADDR_LOOPBACK (a) ) return 2;
     291           0 :   if( IN6_IS_ADDR_SITELOCAL(a) ) return 5;
     292           0 :   return 14;
     293           0 : }
     294             : 
     295             : static int
     296             : prefixmatch( struct in6_addr const * s,
     297           0 :              struct in6_addr const * d ) {
     298             :   /* FIXME: The common prefix length should be limited to no greater
     299             :    * than the nominal length of the prefix portion of the source
     300             :    * address. However the definition of the source prefix length is
     301             :    * not clear and thus this limiting is not yet implemented. */
     302           0 :   uint i;
     303           0 :   for( i=0; i<128 && !((s->s6_addr[i/8]^d->s6_addr[i/8])&(128>>(i%8))); i++ );
     304           0 :   return (int)i;
     305           0 : }
     306             : 
     307           0 : #define DAS_USABLE              0x40000000
     308           0 : #define DAS_MATCHINGSCOPE       0x20000000
     309           0 : #define DAS_MATCHINGLABEL       0x10000000
     310           0 : #define DAS_PREC_SHIFT          20
     311           0 : #define DAS_SCOPE_SHIFT         16
     312           0 : #define DAS_PREFIX_SHIFT        8
     313           0 : #define DAS_ORDER_SHIFT         0
     314             : 
     315             : static int
     316             : addrcmp( void const * _a,
     317           0 :          void const * _b ) {
     318           0 :   struct address const * a = _a, *b = _b;
     319           0 :   return b->sortkey - a->sortkey;
     320           0 : }
     321             : 
     322             : int
     323             : fd_lookup_name( struct address buf[ static MAXADDRS ],
     324             :                 char           canon[ static 256 ],
     325             :                 char const *   name,
     326             :                 int            family,
     327           0 :                 int            flags ) {
     328           0 :   int cnt = 0, i, j;
     329             : 
     330           0 :   *canon = 0;
     331           0 :   if( name ) {
     332             :     /* reject empty name and check len so it fits into temp bufs */
     333           0 :     size_t l = strnlen( name, 255 );
     334           0 :     if( l-1 >= 254 )
     335           0 :       return FD_EAI_NONAME;
     336           0 :     memcpy( canon, name, l+1 );
     337           0 :   }
     338             : 
     339             :   /* Procedurally, a request for v6 addresses with the v4-mapped
     340             :    * flag set is like a request for unspecified family, followed
     341             :    * by filtering of the results. */
     342           0 :   if( flags & FD_AI_V4MAPPED ) {
     343           0 :     if( family == AF_INET6 ) family = AF_UNSPEC;
     344           0 :     else flags -= FD_AI_V4MAPPED;
     345           0 :   }
     346             : 
     347             :   /* Try each backend until there's at least one result. */
     348           0 :   cnt = name_from_null( buf, name, family, flags );
     349           0 :   if( !cnt ) cnt = name_from_numeric( buf, name, family );
     350           0 :   if( !cnt && !(flags & FD_AI_NUMERICHOST) ) {
     351           0 :     cnt = name_from_hosts( buf, canon, name, family );
     352           0 :     if( !cnt ) cnt = name_from_dns_search( buf, canon, name, family );
     353           0 :   }
     354           0 :   if( cnt<=0 ) return cnt ? cnt : FD_EAI_NONAME;
     355             : 
     356             :   /* Filter/transform results for v4-mapped lookup, if requested. */
     357           0 :   if( flags & FD_AI_V4MAPPED ) {
     358           0 :     if( !(flags & FD_AI_ALL) ) {
     359             :       /* If any v6 results exist, remove v4 results. */
     360           0 :       for( i=0; i<cnt && buf[i].family != AF_INET6; i++ );
     361           0 :       if( i<cnt ) {
     362           0 :         for( j=0; i<cnt; i++ ) {
     363           0 :           if( buf[i].family == AF_INET6 )
     364           0 :             buf[j++] = buf[i];
     365           0 :         }
     366           0 :         cnt = i = j;
     367           0 :       }
     368           0 :     }
     369             :     /* Translate any remaining v4 results to v6 */
     370           0 :     for( int i=0; i<cnt; i++ ) {
     371           0 :       if( buf[i].family != AF_INET ) continue;
     372           0 :       uint ip4_addr = FD_LOAD( uint, buf[i].addr );
     373           0 :       fd_ip6_addr_ip4_mapped( buf[i].addr, ip4_addr );
     374           0 :       buf[i].family = AF_INET6;
     375           0 :     }
     376           0 :   }
     377             : 
     378             :   /* No further processing is needed if there are fewer than 2
     379             :    * results or if there are only IPv4 results. */
     380           0 :   if( cnt<2 || family==AF_INET ) return cnt;
     381           0 :   for( i=0; i<cnt; i++ ) if( buf[i].family != AF_INET ) break;
     382           0 :   if( i==cnt ) return cnt;
     383             : 
     384             :   /* The following implements a subset of RFC 3484/6724 destination
     385             :    * address selection by generating a single 31-bit sort key for
     386             :    * each address. Rules 3, 4, and 7 are omitted for having
     387             :    * excessive runtime and code size cost and dubious benefit.
     388             :    * So far the label/precedence table cannot be customized. */
     389           0 :   for( int i=0; i<cnt; i++ ) {
     390           0 :     int family = buf[i].family;
     391           0 :     int key = 0;
     392           0 :     struct sockaddr_in6 sa6 = { 0 }, da6 = {
     393           0 :       .sin6_family = AF_INET6,
     394           0 :       .sin6_scope_id = buf[i].scopeid,
     395           0 :       .sin6_port = 65535
     396           0 :     };
     397           0 :     struct sockaddr_in sa4 = { 0 }, da4 = {
     398           0 :       .sin_family = AF_INET,
     399           0 :       .sin_port = 65535
     400           0 :     };
     401           0 :     void *sa, *da;
     402           0 :     socklen_t salen, dalen;
     403           0 :     if( family == AF_INET6 ) {
     404           0 :       memcpy( da6.sin6_addr.s6_addr, buf[i].addr, 16 );
     405           0 :       da = &da6; dalen = sizeof da6;
     406           0 :       sa = &sa6; salen = sizeof sa6;
     407           0 :     } else {
     408           0 :       uint ip4_addr = FD_LOAD( uint, buf[i].addr );
     409           0 :       fd_ip6_addr_ip4_mapped( sa6.sin6_addr.s6_addr, ip4_addr );
     410           0 :       fd_ip6_addr_ip4_mapped( da6.sin6_addr.s6_addr, ip4_addr );
     411           0 :       memcpy( &da4.sin_addr, buf[i].addr, 4 );
     412           0 :       da = &da4; dalen = sizeof da4;
     413           0 :       sa = &sa4; salen = sizeof sa4;
     414           0 :     }
     415           0 :     const struct policy *dpolicy = policyof( &da6.sin6_addr );
     416           0 :     int dscope = scopeof( &da6.sin6_addr );
     417           0 :     int dlabel = dpolicy->label;
     418           0 :     int dprec = dpolicy->prec;
     419           0 :     int prefixlen = 0;
     420           0 :     int fd = socket( family, SOCK_DGRAM|SOCK_CLOEXEC, IPPROTO_UDP );
     421           0 :     if( fd >= 0 ) {
     422           0 :       if( !connect( fd, da, dalen ) ) {
     423           0 :         key |= DAS_USABLE;
     424           0 :         if( !getsockname( fd, sa, &salen ) ) {
     425           0 :           if( family == AF_INET ) memcpy(
     426           0 :             sa6.sin6_addr.s6_addr+12,
     427           0 :             &sa4.sin_addr, 4);
     428           0 :           if( dscope == scopeof( &sa6.sin6_addr ) )
     429           0 :             key |= DAS_MATCHINGSCOPE;
     430           0 :           if( dlabel == labelof( &sa6.sin6_addr ) )
     431           0 :             key |= DAS_MATCHINGLABEL;
     432           0 :           prefixlen = prefixmatch( &sa6.sin6_addr, &da6.sin6_addr );
     433           0 :         }
     434           0 :       }
     435           0 :       close( fd );
     436           0 :     }
     437           0 :     key |= dprec << DAS_PREC_SHIFT;
     438           0 :     key |= (15-dscope) << DAS_SCOPE_SHIFT;
     439           0 :     key |= prefixlen << DAS_PREFIX_SHIFT;
     440           0 :     key |= (MAXADDRS-i) << DAS_ORDER_SHIFT;
     441           0 :     buf[i].sortkey = key;
     442           0 :   }
     443           0 :   qsort( buf, (ulong)cnt, sizeof *buf, addrcmp );
     444             : 
     445           0 :   return cnt;
     446           0 : }

Generated by: LCOV version 1.14