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

Generated by: LCOV version 1.14