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 : }
|