LCOV - code coverage report
Current view: top level - disco/topo - fd_dns_resolve.c (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 0 113 0.0 %
Date: 2026-08-14 04:54:57 Functions: 0 4 0.0 %

          Line data    Source code
       1             : #include "fd_dns_resolve.h"
       2             : #include "../../waltz/resolv/fd_adns.h"
       3             : #include "../../waltz/resolv/fd_netdb.h"
       4             : #include "../../util/fd_util.h"
       5             : 
       6             : #include <sys/socket.h>
       7             : #include <netinet/in.h>
       8             : #include <string.h>
       9             : #include <stdlib.h>
      10             : 
      11           0 : #define FD_DNS_RESOLVE_TIMEOUT_NANOS (90L*1000L*1000L*1000L)
      12             : 
      13             : int
      14             : fd_dns_resolve_address( char const * address,
      15           0 :                         uint *       ip_addr ) {
      16           0 :   if( FD_LIKELY( fd_cstr_to_ip4_addr( address, ip_addr ) ) ) return 1;
      17             : 
      18           0 :   struct addrinfo hints = { .ai_family = AF_INET };
      19           0 :   struct addrinfo * res;
      20           0 :   int err = getaddrinfo( address, NULL, &hints, &res );
      21           0 :   if( FD_UNLIKELY( err ) ) {
      22           0 :     FD_LOG_WARNING(( "cannot resolve address \"%s\": %i-%s", address, err, gai_strerror( err ) ));
      23           0 :     return 0;
      24           0 :   }
      25             : 
      26           0 :   int resolved = 0;
      27           0 :   for( struct addrinfo * cur=res; cur; cur=cur->ai_next ) {
      28           0 :     if( FD_UNLIKELY( cur->ai_addr->sa_family!=AF_INET ) ) continue;
      29           0 :     struct sockaddr_in const * addr = (struct sockaddr_in const *)cur->ai_addr;
      30           0 :     *ip_addr = addr->sin_addr.s_addr;
      31           0 :     resolved = 1;
      32           0 :     break;
      33           0 :   }
      34             : 
      35           0 :   freeaddrinfo( res );
      36           0 :   return resolved;
      37           0 : }
      38             : 
      39             : int
      40             : fd_dns_resolve_addresses( char const *            address,
      41             :                           struct addrinfo const * hints,
      42             :                           fd_ip4_port_t *         ip_addrs,
      43           0 :                           ulong                   ip_addr_cnt ) {
      44           0 :   struct addrinfo default_hints = { .ai_family = AF_INET };
      45           0 :   if( FD_UNLIKELY( !hints ) ) {
      46           0 :     hints = &default_hints;
      47           0 :   }
      48             : 
      49           0 :   struct addrinfo * res;
      50           0 :   int err = getaddrinfo( address, NULL, hints, &res );
      51           0 :   if( FD_UNLIKELY( err ) ) {
      52           0 :     FD_LOG_WARNING(( "cannot resolve address \"%s\": %i-%s", address, err, gai_strerror( err ) ));
      53           0 :     return 0;
      54           0 :   }
      55             : 
      56           0 :   int resolved = 0;
      57           0 :   for( struct addrinfo * cur=res; cur; cur=cur->ai_next ) {
      58           0 :     if( FD_UNLIKELY( (ulong)resolved>=ip_addr_cnt ) ) break;
      59           0 :     if( FD_UNLIKELY( cur->ai_addr->sa_family!=AF_INET ) ) continue;
      60           0 :     struct sockaddr_in const * addr = (struct sockaddr_in const *)cur->ai_addr;
      61           0 :     ip_addrs[ resolved ].addr = addr->sin_addr.s_addr;
      62           0 :     resolved++;
      63           0 :   }
      64             : 
      65           0 :   freeaddrinfo( res );
      66           0 :   return resolved;
      67           0 : }
      68             : 
      69             : void
      70             : fd_dns_peer_parse( char const * peer,
      71             :                    char const * config_str,
      72             :                    char         hostname[ static 256UL ],
      73             :                    ushort *     port,
      74           0 :                    int *        is_https ) {
      75             : 
      76             :   /* Split host:port */
      77           0 :   int          https     = 0;
      78           0 :   char const * host_port = peer;
      79           0 :   if( FD_LIKELY( strncmp( peer, "http://", 7UL )==0 ) ) {
      80           0 :     host_port += 7UL;
      81           0 :   } else if( FD_LIKELY( strncmp( peer, "https://", 8UL )==0 ) ) {
      82           0 :     host_port += 8UL;
      83           0 :     https      = 1;
      84           0 :   }
      85           0 :   if( FD_LIKELY( is_https ) ) *is_https = https;
      86             : 
      87           0 :   char const * colon    = strrchr( host_port, ':' );
      88           0 :   char const * host_end = colon;
      89           0 :   if( FD_UNLIKELY( !colon && !https ) ) {
      90           0 :     FD_LOG_ERR(( "invalid [%s] entry \"%s\": no port number", config_str, host_port ));
      91           0 :   } else if( FD_LIKELY( !colon && https ) ) {
      92           0 :     host_end = host_port + strlen( host_port );
      93           0 :   }
      94             : 
      95           0 :   ulong fqdn_len = (ulong)( host_end-host_port );
      96           0 :   if( FD_UNLIKELY( !fqdn_len ) ) {
      97           0 :     FD_LOG_ERR(( "invalid [%s] entry \"%s\": no hostname", config_str, host_port ));
      98           0 :   }
      99           0 :   if( FD_UNLIKELY( fqdn_len>255 ) ) {
     100           0 :     FD_LOG_ERR(( "invalid [%s] entry \"%s\": hostname too long", config_str, host_port ));
     101           0 :   }
     102           0 :   fd_memcpy( hostname, host_port, fqdn_len );
     103           0 :   hostname[ fqdn_len ] = '\0';
     104             : 
     105           0 :   if( FD_LIKELY( colon ) ) {
     106           0 :     char const * port_str = host_end+1;
     107           0 :     char const * endptr   = NULL;
     108           0 :     ulong _port = strtoul( port_str, (char **)&endptr, 10 );
     109           0 :     if( FD_UNLIKELY( endptr==port_str || !_port || _port>USHORT_MAX || *endptr!='\0' ) ) {
     110           0 :       FD_LOG_ERR(( "invalid [%s] entry \"%s\": invalid port number", config_str, host_port ));
     111           0 :     }
     112           0 :     *port = fd_ushort_bswap( (ushort)_port );
     113           0 :   } else {
     114             :     /* use default https port */
     115           0 :     *port = fd_ushort_bswap( 443U );
     116           0 :   }
     117           0 : }
     118             : 
     119             : void
     120             : fd_dns_resolve_peers( char const *    peers,
     121             :                       ulong           peer_stride,
     122             :                       ulong           peer_cnt,
     123             :                       char const *    config_str,
     124           0 :                       fd_ip4_port_t * out ) {
     125           0 :   if( FD_UNLIKELY( peer_cnt>FD_DNS_RESOLVE_PEERS_MAX ) ) FD_LOG_ERR(( "too many [%s] entries (%lu)", config_str, peer_cnt ));
     126           0 :   if( FD_UNLIKELY( !peer_cnt ) ) return;
     127             : 
     128           0 :   char hostname[ FD_DNS_RESOLVE_PEERS_MAX ][ 256UL ];
     129           0 :   for( ulong i=0UL; i<peer_cnt; i++ ) {
     130           0 :     fd_dns_peer_parse( peers+i*peer_stride, config_str, hostname[ i ], &out[ i ].port, NULL );
     131           0 :   }
     132             : 
     133             :   /* All queries in flight concurrently on one socket, so the wall
     134             :      clock cost is that of the slowest single lookup.  The fds are
     135             :      opened and closed within this call: callers are about to enter a
     136             :      sandbox that requires no stray fds. */
     137           0 :   fd_netdb_fds_t netdb_fds[1];
     138           0 :   int netdb_opened = !!fd_netdb_open_fds( netdb_fds ); /* NULL if already open (owned by caller) */
     139             : 
     140             :   /* Thread local: tiles resolve concurrently under
     141             :      fd_topo_run_single_process (and netdb fds are FD_TL already) */
     142           0 :   static FD_TL uchar shadns[ 16384UL ] __attribute__((aligned(32UL)));
     143           0 :   FD_TEST( fd_adns_footprint( FD_DNS_RESOLVE_PEERS_MAX )<=sizeof(shadns) );
     144           0 :   fd_adns_t * adns = fd_adns_join( fd_adns_new( shadns, peer_cnt ) );
     145           0 :   FD_TEST( adns );
     146             : 
     147           0 :   for( ulong i=0UL; i<peer_cnt; i++ ) FD_TEST( !fd_adns_resolve( adns, hostname[ i ], i ) );
     148             : 
     149           0 :   ulong resolved_cnt = 0UL;
     150           0 :   long  deadline     = fd_log_wallclock()+FD_DNS_RESOLVE_TIMEOUT_NANOS;
     151           0 :   for(;;) {
     152           0 :     long now = fd_log_wallclock();
     153           0 :     fd_adns_result_t res[ 1 ];
     154           0 :     while( fd_adns_advance( adns, now, res ) ) {
     155           0 :       if( FD_UNLIKELY( res->err ) )
     156           0 :         FD_LOG_ERR(( "failed to resolve [%s] entry \"%s\" (%s)", config_str, peers+res->req_id*peer_stride, fd_gai_strerror( res->err ) ));
     157           0 :       out[ res->req_id ].addr = res->addrs[ 0 ];
     158           0 :       resolved_cnt++;
     159           0 :     }
     160           0 :     if( FD_LIKELY( resolved_cnt==peer_cnt ) ) break;
     161           0 :     if( FD_UNLIKELY( now>deadline ) ) FD_LOG_ERR(( "timed out resolving [%s]", config_str ));
     162           0 :     FD_SPIN_PAUSE();
     163           0 :   }
     164             : 
     165           0 :   fd_adns_delete( fd_adns_leave( adns ) );
     166           0 :   if( FD_LIKELY( netdb_opened ) ) fd_netdb_close_fds();
     167           0 : }

Generated by: LCOV version 1.14