Line data Source code
1 : #define _GNU_SOURCE
2 : #include "fd_http_resolver.h"
3 : #include "fd_ssresolve.h"
4 :
5 : #include "../../../util/log/fd_log.h"
6 : #include "../../../util/fd_util.h"
7 :
8 : #include <poll.h>
9 : #include <errno.h>
10 : #include <unistd.h>
11 : #include <string.h>
12 : #include <sys/socket.h>
13 : #include <sys/random.h>
14 : #include <netinet/in.h>
15 : #include <netinet/tcp.h>
16 :
17 : #include "../../../waltz/tls/fd_tls.h"
18 : #include "../../../ballet/ed25519/fd_x25519.h"
19 :
20 0 : #define PEER_STATE_UNRESOLVED (0)
21 0 : #define PEER_STATE_REFRESHING (1)
22 0 : #define PEER_STATE_VALID (2)
23 0 : #define PEER_STATE_INVALID (3)
24 :
25 0 : #define PEER_DEADLINE_NANOS_VALID (5L*1000L*1000L*1000L) /* 5 seconds */
26 0 : #define PEER_DEADLINE_NANOS_RESOLVE (2L*1000L*1000L*1000L) /* 2 seconds */
27 0 : #define PEER_DEADLINE_NANOS_INVALID (5L*1000L*1000L*1000L) /* 5 seconds */
28 :
29 : /* FIXME: The fds/fds_len/idx logic is fragile, replace with something
30 : that duplicates less state / etc. */
31 :
32 : struct fd_ssresolve_peer {
33 : fd_sspeer_key_t key;
34 : fd_ip4_port_t addr;
35 : int is_https;
36 : ulong full_slot;
37 : ulong incr_slot;
38 : uchar full_hash[ FD_HASH_FOOTPRINT ];
39 : uchar incr_hash[ FD_HASH_FOOTPRINT ];
40 :
41 : fd_ssresolve_t * full_ssresolve;
42 : fd_ssresolve_t * inc_ssresolve;
43 :
44 : struct {
45 : ulong next;
46 : } pool;
47 :
48 : struct {
49 : ulong next;
50 : ulong prev;
51 : } deadline;
52 :
53 : struct {
54 : ulong idx;
55 : } fd;
56 :
57 : int state;
58 : long deadline_nanos;
59 : };
60 : typedef struct fd_ssresolve_peer fd_ssresolve_peer_t;
61 :
62 : #define POOL_NAME peer_pool
63 0 : #define POOL_T fd_ssresolve_peer_t
64 : #define POOL_IDX_T ulong
65 0 : #define POOL_NEXT pool.next
66 : #include "../../../util/tmpl/fd_pool.c"
67 :
68 : #define DLIST_NAME deadline_list
69 : #define DLIST_ELE_T fd_ssresolve_peer_t
70 0 : #define DLIST_PREV deadline.prev
71 0 : #define DLIST_NEXT deadline.next
72 : #include "../../../util/tmpl/fd_dlist.c"
73 :
74 : static inline void
75 0 : clear_peer_snapshot_data( fd_ssresolve_peer_t * peer ) {
76 0 : peer->full_slot = FD_SSPEER_SLOT_UNKNOWN;
77 0 : peer->incr_slot = FD_SSPEER_SLOT_UNKNOWN;
78 0 : fd_memset( peer->full_hash, 0, FD_HASH_FOOTPRINT );
79 0 : fd_memset( peer->incr_hash, 0, FD_HASH_FOOTPRINT );
80 0 : }
81 :
82 : struct fd_http_resolver_private {
83 : fd_ssresolve_peer_t * pool;
84 : deadline_list_t * unresolved;
85 : deadline_list_t * resolving;
86 : deadline_list_t * valid;
87 : deadline_list_t * invalid;
88 :
89 : ulong fds_len;
90 : struct pollfd * fds;
91 : ulong * fds_idx;
92 :
93 : int incremental_snapshot_fetch;
94 :
95 : void * cb_arg;
96 : fd_http_resolver_on_resolve_fn_t on_resolve_cb;
97 :
98 : fd_tls_t tls;
99 : fd_chacha_rng_t rng[1];
100 :
101 : fd_x509_ca_store_t ca_store;
102 :
103 : ulong magic; /* ==FD_HTTP_RESOLVER_MAGIC */
104 : };
105 :
106 : FD_FN_CONST ulong
107 294 : fd_http_resolver_align( void ) {
108 294 : return fd_ulong_max( alignof(fd_http_resolver_t), fd_ulong_max( peer_pool_align(), fd_ulong_max( deadline_list_align(), fd_ulong_max( alignof(struct pollfd), alignof(ulong) ) ) ) );
109 294 : }
110 :
111 : FD_FN_CONST ulong
112 42 : fd_http_resolver_footprint( ulong peers_cnt ) {
113 42 : ulong l;
114 42 : l = FD_LAYOUT_INIT;
115 42 : l = FD_LAYOUT_APPEND( l, alignof(fd_http_resolver_t), sizeof(fd_http_resolver_t) );
116 42 : l = FD_LAYOUT_APPEND( l, peer_pool_align(), peer_pool_footprint( peers_cnt ) );
117 42 : l = FD_LAYOUT_APPEND( l, deadline_list_align(), deadline_list_footprint() );
118 42 : l = FD_LAYOUT_APPEND( l, deadline_list_align(), deadline_list_footprint() );
119 42 : l = FD_LAYOUT_APPEND( l, deadline_list_align(), deadline_list_footprint() );
120 42 : l = FD_LAYOUT_APPEND( l, deadline_list_align(), deadline_list_footprint() );
121 42 : l = FD_LAYOUT_APPEND( l, alignof(struct pollfd), 2UL*peers_cnt*sizeof(struct pollfd) );
122 42 : l = FD_LAYOUT_APPEND( l, alignof(ulong), 2UL*peers_cnt*sizeof(ulong) );
123 :
124 5418 : for( ulong i=0UL; i<peers_cnt*2UL; i++ ) {
125 5376 : l = FD_LAYOUT_APPEND( l, fd_ssresolve_align(), fd_ssresolve_footprint() );
126 5376 : }
127 42 : return FD_LAYOUT_FINI( l, fd_http_resolver_align() );
128 42 : }
129 :
130 : void *
131 : fd_http_resolver_new( void * shmem,
132 : ulong peers_cnt,
133 : int incremental_snapshot_fetch,
134 : int load_ca_store,
135 : fd_http_resolver_on_resolve_fn_t on_resolve_cb,
136 0 : void * cb_arg ) {
137 0 : if( FD_UNLIKELY( !shmem ) ) {
138 0 : FD_LOG_WARNING(( "NULL shmem" ));
139 0 : return NULL;
140 0 : }
141 :
142 0 : if( FD_UNLIKELY( !fd_ulong_is_aligned( (ulong)shmem, fd_http_resolver_align() ) ) ) {
143 0 : FD_LOG_WARNING(( "unaligned shmem" ));
144 0 : return NULL;
145 0 : }
146 :
147 0 : if( FD_UNLIKELY( peers_cnt<1UL ) ) {
148 0 : FD_LOG_WARNING(( "max_peers must be at least 1" ));
149 0 : return NULL;
150 0 : }
151 :
152 0 : FD_SCRATCH_ALLOC_INIT( l, shmem );
153 0 : fd_http_resolver_t * resolver = FD_SCRATCH_ALLOC_APPEND( l, fd_http_resolver_align(), sizeof(fd_http_resolver_t) );
154 0 : void * _pool = FD_SCRATCH_ALLOC_APPEND( l, peer_pool_align(), peer_pool_footprint( peers_cnt ) );
155 0 : void * _unresolved = FD_SCRATCH_ALLOC_APPEND( l, deadline_list_align(), deadline_list_footprint() );
156 0 : void * _resolving = FD_SCRATCH_ALLOC_APPEND( l, deadline_list_align(), deadline_list_footprint() );
157 0 : void * _invalid = FD_SCRATCH_ALLOC_APPEND( l, deadline_list_align(), deadline_list_footprint() );
158 0 : void * _valid = FD_SCRATCH_ALLOC_APPEND( l, deadline_list_align(), deadline_list_footprint() );
159 0 : struct pollfd * fds = FD_SCRATCH_ALLOC_APPEND( l, alignof(struct pollfd), 2UL*peers_cnt*sizeof(struct pollfd) );
160 0 : ulong * fds_idx = FD_SCRATCH_ALLOC_APPEND( l, alignof(ulong), 2UL*peers_cnt*sizeof(ulong) );
161 :
162 0 : resolver->pool = peer_pool_join( peer_pool_new( _pool, peers_cnt ) );
163 0 : resolver->unresolved = deadline_list_join( deadline_list_new( _unresolved ) );
164 0 : resolver->resolving = deadline_list_join( deadline_list_new( _resolving ) );
165 0 : resolver->invalid = deadline_list_join( deadline_list_new( _invalid ) );
166 0 : resolver->valid = deadline_list_join( deadline_list_new( _valid ) );
167 :
168 0 : resolver->fds_len = 0UL;
169 0 : resolver->fds = fds;
170 0 : resolver->fds_idx = fds_idx;
171 :
172 0 : for( ulong i=0UL; i<peer_pool_max( resolver->pool ); i++ ) {
173 0 : void * _full_ssresolve = FD_SCRATCH_ALLOC_APPEND( l, fd_ssresolve_align(), fd_ssresolve_footprint() );
174 0 : void * _inc_ssresolve = FD_SCRATCH_ALLOC_APPEND( l, fd_ssresolve_align(), fd_ssresolve_footprint() );
175 0 : resolver->pool[ i ].full_ssresolve = fd_ssresolve_join( fd_ssresolve_new( _full_ssresolve ) );
176 0 : resolver->pool[ i ].inc_ssresolve = fd_ssresolve_join( fd_ssresolve_new( _inc_ssresolve ) );
177 0 : }
178 :
179 0 : resolver->incremental_snapshot_fetch = incremental_snapshot_fetch;
180 0 : resolver->cb_arg = cb_arg;
181 0 : resolver->on_resolve_cb = on_resolve_cb;
182 :
183 0 : {
184 0 : fd_tls_t * tls = &resolver->tls;
185 0 : fd_memset( tls, 0, sizeof(fd_tls_t) );
186 :
187 : /* Seed the CSPRNG that fd_tls draws handshake randomness from */
188 :
189 0 : uchar rng_key[ FD_CHACHA_KEY_SZ ];
190 0 : if( FD_UNLIKELY( !fd_rng_secure( rng_key, sizeof(rng_key) ) ) ) FD_LOG_CRIT(( "fd_rng_secure failed" ));
191 0 : fd_chacha_rng_init( resolver->rng, rng_key, FD_CHACHA_RNG_ALGO_CHACHA8 );
192 0 : fd_memzero_explicit( rng_key, sizeof(rng_key) );
193 0 : tls->rng = resolver->rng;
194 :
195 : /* Generate ephemeral X25519 key */
196 :
197 0 : if( FD_UNLIKELY( !fd_rng_secure( tls->kex_private_key, 32UL ) ) ) FD_LOG_CRIT(( "fd_rng_secure failed" ));
198 0 : fd_x25519_public( tls->kex_public_key, tls->kex_private_key );
199 :
200 0 : static uchar const alpn[] = { 8, 'h', 't', 't', 'p', '/', '1', '.', '1' };
201 0 : fd_memcpy( tls->alpn, alpn, sizeof(alpn) );
202 0 : tls->alpn_sz = sizeof(alpn);
203 :
204 0 : tls->quic = 0;
205 0 : }
206 :
207 0 : resolver->ca_store.cnt = 0UL;
208 0 : if( load_ca_store ) {
209 0 : if( FD_UNLIKELY( fd_x509_ca_store_load_system( &resolver->ca_store )<0L ) ) {
210 0 : FD_LOG_ERR(( "No CA certificate bundle found, cannot verify HTTPS snapshot "
211 0 : "server certificates. Install the system CA certificates, or "
212 0 : "use http:// [snapshots.sources.servers] entries." ));
213 0 : }
214 0 : }
215 :
216 0 : FD_COMPILER_MFENCE();
217 0 : FD_VOLATILE( resolver->magic ) = FD_HTTP_RESOLVER_MAGIC;
218 0 : FD_COMPILER_MFENCE();
219 :
220 0 : return (void *)resolver;
221 0 : }
222 :
223 : fd_http_resolver_t *
224 0 : fd_http_resolver_join( void * shresolver ) {
225 0 : if( FD_UNLIKELY( !shresolver ) ) {
226 0 : FD_LOG_WARNING(( "NULL shresolver" ));
227 0 : return NULL;
228 0 : }
229 :
230 0 : if( FD_UNLIKELY( !fd_ulong_is_aligned( (ulong)shresolver, fd_http_resolver_align() ) ) ) {
231 0 : FD_LOG_WARNING(( "misaligned shresolver" ));
232 0 : return NULL;
233 0 : }
234 :
235 0 : fd_http_resolver_t * resolver = (fd_http_resolver_t *)shresolver;
236 :
237 0 : if( FD_UNLIKELY( resolver->magic!=FD_HTTP_RESOLVER_MAGIC ) ) {
238 0 : FD_LOG_WARNING(( "bad magic" ));
239 0 : return NULL;
240 0 : }
241 :
242 0 : return resolver;
243 0 : }
244 :
245 : int
246 : fd_http_resolver_add( fd_http_resolver_t * resolver,
247 : fd_ip4_port_t addr,
248 : char const * hostname,
249 : int is_https,
250 0 : fd_sspeer_selector_t * selector ) {
251 0 : if( !peer_pool_free( resolver->pool ) ) {
252 0 : FD_LOG_WARNING(( "peer pool exhausted" ));
253 0 : return -1;
254 0 : }
255 0 : fd_ssresolve_peer_t * peer = peer_pool_ele_acquire( resolver->pool );
256 0 : memset( &peer->key.url, 0, sizeof(peer->key.url) );
257 0 : if( FD_LIKELY( hostname ) ) {
258 0 : strncpy( peer->key.url.hostname, hostname, sizeof(peer->key.url.hostname) - 1UL );
259 0 : peer->key.url.hostname[ sizeof(peer->key.url.hostname) - 1UL ] = '\0';
260 0 : } else {
261 0 : peer->key.url.hostname[ 0 ] = '\0';
262 0 : }
263 0 : peer->key.url.resolved_addr = addr;
264 0 : peer->key.is_url = 1;
265 0 : peer->state = PEER_STATE_UNRESOLVED;
266 0 : peer->addr = addr;
267 0 : peer->is_https = is_https;
268 0 : peer->fd.idx = ULONG_MAX;
269 0 : peer->full_slot = FD_SSPEER_SLOT_UNKNOWN;
270 0 : peer->incr_slot = FD_SSPEER_SLOT_UNKNOWN;
271 0 : fd_memset( peer->full_hash, 0, FD_HASH_FOOTPRINT );
272 0 : fd_memset( peer->incr_hash, 0, FD_HASH_FOOTPRINT );
273 :
274 : /* Create the selector entry now. Latency, full/incr slot, and
275 : full/incr hash are unknown at this point, so the peer only
276 : becomes selectable by best() after on_resolve updates them once
277 : resolution succeeds. */
278 0 : ulong score = fd_sspeer_selector_add( selector, &peer->key, addr, FD_SSPEER_LATENCY_UNKNOWN,
279 0 : FD_SSPEER_SLOT_UNKNOWN, FD_SSPEER_SLOT_UNKNOWN, NULL, NULL );
280 0 : if( FD_UNLIKELY( score==FD_SSPEER_SCORE_INVALID ) ) {
281 : /* If unable to add, then release the element back to the pool. */
282 0 : FD_LOG_WARNING(( "failed to add peer to selector (hostname \"%s\" addr=" FD_IP4_ADDR_FMT ":%hu score=%lu)",
283 0 : peer->key.url.hostname[ 0 ] ? peer->key.url.hostname : "(none)",
284 0 : FD_IP4_ADDR_FMT_ARGS( peer->addr.addr ), fd_ushort_bswap( peer->addr.port ), score ));
285 0 : peer_pool_ele_release( resolver->pool, peer );
286 0 : return -1;
287 0 : }
288 : /* Add to the unresolved list. */
289 0 : deadline_list_ele_push_tail( resolver->unresolved, peer, resolver->pool );
290 0 : return 0;
291 0 : }
292 :
293 : static int
294 : create_socket( fd_http_resolver_t * resolver,
295 0 : fd_ssresolve_peer_t * peer ) {
296 0 : int sockfd = socket( AF_INET, SOCK_STREAM|SOCK_NONBLOCK, 0 );
297 0 : if( FD_UNLIKELY( -1==sockfd ) ) FD_LOG_ERR(( "socket failed (%i-%s)", errno, strerror( errno ) ));
298 :
299 0 : int optval = 1;
300 0 : if( FD_UNLIKELY( -1==setsockopt( sockfd, IPPROTO_TCP, TCP_NODELAY, &optval, sizeof(int) ) ) ) {
301 0 : FD_LOG_ERR(( "setsockopt() failed (%d-%s)", errno, fd_io_strerror( errno ) ));
302 0 : }
303 :
304 0 : struct sockaddr_in addr = {
305 0 : .sin_family = AF_INET,
306 0 : .sin_port = peer->addr.port,
307 0 : .sin_addr = { .s_addr = peer->addr.addr }
308 0 : };
309 :
310 0 : if( FD_UNLIKELY( -1==connect( sockfd, fd_type_pun( &addr ), sizeof(addr) ) && errno!=EINPROGRESS ) ) {
311 0 : if( FD_UNLIKELY( -1==close( sockfd ) ) ) FD_LOG_ERR(( "close() failed (%i-%s)", errno, fd_io_strerror( errno ) ));
312 0 : return -1;
313 0 : }
314 :
315 0 : resolver->fds[ resolver->fds_len ] = (struct pollfd){
316 0 : .fd = sockfd,
317 0 : .events = POLLIN|POLLOUT,
318 0 : .revents = 0
319 0 : };
320 :
321 0 : return 0;
322 0 : }
323 :
324 : static int
325 : peer_connect( fd_http_resolver_t * resolver,
326 0 : fd_ssresolve_peer_t * peer ) {
327 0 : int err;
328 0 : err = create_socket( resolver, peer ); /* full */
329 0 : if( FD_UNLIKELY( err ) ) return err;
330 0 : resolver->fds_idx[ resolver->fds_len ] = peer_pool_idx( resolver->pool, peer );
331 0 : peer->fd.idx = resolver->fds_len;
332 0 : resolver->fds_len++;
333 :
334 0 : if( FD_UNLIKELY( peer->is_https ) ) {
335 0 : fd_ssresolve_init_https( peer->full_ssresolve, peer->addr, resolver->fds[ peer->fd.idx ].fd, 1, peer->key.url.hostname, &resolver->tls,
336 0 : &resolver->ca_store );
337 0 : } else {
338 0 : fd_ssresolve_init( peer->full_ssresolve, peer->addr, resolver->fds[ peer->fd.idx ].fd, 1, peer->key.url.hostname );
339 0 : }
340 :
341 0 : if( FD_LIKELY( resolver->incremental_snapshot_fetch ) ) {
342 0 : err = create_socket( resolver, peer ); /* incremental */
343 0 : if( FD_UNLIKELY( err ) ) {
344 : /* Undo the full socket setup to avoid leaking the fd and
345 : corrupting the fds array (entries must always come in pairs). */
346 0 : fd_ssresolve_cancel( peer->full_ssresolve );
347 0 : resolver->fds_len--;
348 0 : peer->fd.idx = ULONG_MAX;
349 0 : return err;
350 0 : }
351 0 : resolver->fds_idx[ resolver->fds_len ] = peer_pool_idx( resolver->pool, peer );
352 0 : resolver->fds_len++;
353 0 : if( FD_UNLIKELY( peer->is_https ) ) {
354 0 : fd_ssresolve_init_https( peer->inc_ssresolve, peer->addr, resolver->fds[ peer->fd.idx+1UL ].fd, 0, peer->key.url.hostname, &resolver->tls,
355 0 : &resolver->ca_store );
356 0 : } else {
357 0 : fd_ssresolve_init( peer->inc_ssresolve, peer->addr, resolver->fds[ peer->fd.idx+1UL ].fd, 0, peer->key.url.hostname );
358 0 : }
359 0 : } else {
360 0 : resolver->fds[ resolver->fds_len ] = (struct pollfd) {
361 0 : .fd = -1,
362 0 : .events = 0,
363 0 : .revents = 0
364 0 : };
365 0 : resolver->fds_idx[ resolver->fds_len ] = ULONG_MAX;
366 0 : resolver->fds_len++;
367 0 : }
368 :
369 0 : return 0;
370 0 : }
371 :
372 : static inline void
373 : remove_peer( fd_http_resolver_t * resolver,
374 0 : ulong idx ) {
375 0 : FD_TEST( idx<resolver->fds_len );
376 :
377 0 : fd_ssresolve_peer_t * cur_peer = peer_pool_ele( resolver->pool, resolver->fds_idx[ idx ] );
378 0 : fd_ssresolve_cancel( cur_peer->full_ssresolve );
379 0 : fd_ssresolve_cancel( cur_peer->inc_ssresolve );
380 :
381 0 : if( FD_UNLIKELY( resolver->fds_len==2UL ) ) {
382 0 : resolver->fds_len = 0UL;
383 0 : return;
384 0 : }
385 :
386 0 : resolver->fds[ idx ] = resolver->fds[ resolver->fds_len-2UL ];
387 0 : resolver->fds_idx[ idx ] = resolver->fds_idx[ resolver->fds_len-2UL ];
388 :
389 0 : resolver->fds[ idx+1UL ] = resolver->fds[ resolver->fds_len-1UL ];
390 0 : resolver->fds_idx[ idx+1UL ] = resolver->fds_idx[ resolver->fds_len-1UL ];
391 :
392 0 : fd_ssresolve_peer_t * peer = peer_pool_ele( resolver->pool, resolver->fds_idx[ idx ] );
393 0 : peer->fd.idx = idx;
394 :
395 0 : resolver->fds_len -= 2UL;
396 0 : }
397 :
398 : static inline void
399 : unresolve_peer( fd_http_resolver_t * resolver,
400 : fd_ssresolve_peer_t * peer,
401 0 : long now ) {
402 0 : FD_TEST( peer->state==PEER_STATE_UNRESOLVED || peer->state==PEER_STATE_REFRESHING );
403 0 : remove_peer( resolver, peer->fd.idx );
404 0 : deadline_list_ele_remove( resolver->resolving, peer, resolver->pool );
405 0 : peer->state = PEER_STATE_INVALID;
406 0 : peer->deadline_nanos = now + PEER_DEADLINE_NANOS_INVALID;
407 0 : deadline_list_ele_push_tail( resolver->invalid, peer, resolver->pool );
408 0 : }
409 :
410 : static inline int
411 : poll_resolve( fd_http_resolver_t * resolver,
412 : struct pollfd * pfd,
413 : fd_ssresolve_peer_t * peer,
414 : fd_ssresolve_t * ssresolve,
415 : ulong idx,
416 0 : long now ) {
417 0 : FD_TEST( !fd_ssresolve_is_done( ssresolve ) );
418 0 : if( FD_LIKELY( pfd->revents & POLLOUT ) ) {
419 0 : int res = fd_ssresolve_advance_poll_out( ssresolve );
420 :
421 0 : if( FD_UNLIKELY( res==FD_SSRESOLVE_ADVANCE_ERROR ) ) {
422 0 : unresolve_peer( resolver, peer_pool_ele( resolver->pool, resolver->fds_idx[ idx ] ), now );
423 0 : return -1;
424 0 : }
425 0 : }
426 :
427 0 : if( FD_LIKELY( pfd->revents & POLLIN ) ) {
428 0 : fd_ssresolve_result_t resolve_result;
429 0 : int res = fd_ssresolve_advance_poll_in( ssresolve, &resolve_result );
430 :
431 0 : if( FD_UNLIKELY( res==FD_SSRESOLVE_ADVANCE_ERROR ) ) {
432 0 : unresolve_peer( resolver, peer_pool_ele( resolver->pool, resolver->fds_idx[ idx ] ), now );
433 0 : return -1;
434 0 : } else if( FD_UNLIKELY( res==FD_SSRESOLVE_ADVANCE_AGAIN ) ) {
435 0 : return -1;
436 0 : } else if( FD_LIKELY( res==FD_SSRESOLVE_ADVANCE_RESULT ) ) {
437 0 : FD_TEST( peer->deadline_nanos>now );
438 :
439 0 : if( resolve_result.base_slot==ULONG_MAX ) {
440 0 : peer->full_slot = resolve_result.slot;
441 0 : fd_memcpy( peer->full_hash, resolve_result.hash, FD_HASH_FOOTPRINT );
442 0 : } else {
443 0 : peer->full_slot = resolve_result.base_slot;
444 0 : peer->incr_slot = resolve_result.slot;
445 0 : fd_memcpy( peer->incr_hash, resolve_result.hash, FD_HASH_FOOTPRINT );
446 0 : }
447 0 : }
448 0 : }
449 :
450 0 : return 0;
451 0 : }
452 :
453 : static inline void
454 : poll_advance( fd_http_resolver_t * resolver,
455 0 : long now ) {
456 0 : if( FD_LIKELY( !resolver->fds_len ) ) return;
457 :
458 0 : int nfds = fd_syscall_poll( resolver->fds, (uint)resolver->fds_len, 0 );
459 0 : if( FD_LIKELY( !nfds ) ) return;
460 0 : else if( FD_UNLIKELY( -1==nfds && errno==EINTR ) ) return;
461 0 : else if( FD_UNLIKELY( -1==nfds ) ) FD_LOG_ERR(( "poll failed (%i-%s)", errno, strerror( errno ) ));
462 :
463 0 : for( ulong i=0UL; i<resolver->fds_len; i++) {
464 :
465 0 : struct pollfd * pfd = &resolver->fds[ i ];
466 0 : if( FD_UNLIKELY( pfd->fd==-1 ) ) continue;
467 :
468 0 : fd_ssresolve_peer_t * peer = peer_pool_ele( resolver->pool, resolver->fds_idx[ i ] );
469 0 : int full = i&1UL ? 0 : 1; /* even indices are full, odd indices are incremental */
470 0 : fd_ssresolve_t * ssresolve = full ? peer->full_ssresolve : peer->inc_ssresolve;
471 :
472 : /* Process pending I/O before checking for errors. POLLIN can
473 : coexist with POLLHUP when the server sends a response and then
474 : closes the connection (common for HTTP HEAD redirects). */
475 0 : if( FD_LIKELY( !fd_ssresolve_is_done( ssresolve ) ) ) {
476 0 : int res = poll_resolve( resolver, pfd, peer, ssresolve, i, now );
477 0 : if( FD_UNLIKELY( res ) ) continue;
478 0 : }
479 :
480 : /* Only react to POLLERR/POLLHUP if the ssresolve hasn't completed
481 : yet. After a redirect is parsed the server often closes the
482 : connection, which is harmless. */
483 0 : if( FD_UNLIKELY( (pfd->revents & (POLLERR|POLLHUP)) && !fd_ssresolve_is_done( ssresolve ) ) ) {
484 0 : unresolve_peer( resolver, peer_pool_ele( resolver->pool, resolver->fds_idx[ i ] ), now );
485 0 : continue;
486 0 : }
487 :
488 : /* Once both the full and incremental snapshots are resolved, we can
489 : mark the peer valid and remove the peer from the list of peers to
490 : ping. */
491 0 : if( FD_LIKELY( fd_ssresolve_is_done( peer->full_ssresolve ) &&
492 0 : (!resolver->incremental_snapshot_fetch || fd_ssresolve_is_done( peer->inc_ssresolve ) ) ) ) {
493 0 : peer->state = PEER_STATE_VALID;
494 0 : peer->deadline_nanos = now + PEER_DEADLINE_NANOS_VALID;
495 :
496 0 : deadline_list_ele_remove( resolver->resolving, peer, resolver->pool );
497 0 : deadline_list_ele_push_tail( resolver->valid, peer, resolver->pool );
498 0 : remove_peer( resolver, peer->fd.idx );
499 :
500 0 : resolver->on_resolve_cb( resolver->cb_arg, &peer->key, peer->addr, peer->full_slot, peer->incr_slot, peer->full_hash,
501 0 : peer->incr_slot!=FD_SSPEER_SLOT_UNKNOWN ? peer->incr_hash : NULL );
502 0 : }
503 0 : }
504 0 : }
505 :
506 : void
507 : fd_http_resolver_advance( fd_http_resolver_t * resolver,
508 : long now,
509 0 : fd_sspeer_selector_t * selector ) {
510 0 : while( !deadline_list_is_empty( resolver->unresolved, resolver->pool ) ) {
511 0 : fd_ssresolve_peer_t * peer = deadline_list_ele_pop_head( resolver->unresolved, resolver->pool );
512 :
513 0 : FD_LOG_INFO(( "resolving " FD_IP4_ADDR_FMT ":%hu", FD_IP4_ADDR_FMT_ARGS( peer->addr.addr ), fd_ushort_bswap( peer->addr.port ) ));
514 : /* Clear stale snapshot data so the new resolve cycle starts clean.
515 : Without this, a previously-valid peer could carry stale
516 : incr_slot/incr_hash through the invalid->unresolved cycle. */
517 0 : clear_peer_snapshot_data( peer );
518 : /* Peers that were already removed from the selector due to
519 : timeout or blacklist handling stay absent while resolving.
520 : Re-adding such peers here with unknown slots would bypass
521 : blacklist checks and contribute no useful data. The
522 : on_resolve callback re-adds them once resolution succeeds. */
523 0 : int result = peer_connect( resolver, peer );
524 0 : if( FD_UNLIKELY( -1==result ) ) {
525 0 : peer->state = PEER_STATE_INVALID;
526 0 : peer->deadline_nanos = now + PEER_DEADLINE_NANOS_INVALID;
527 0 : deadline_list_ele_push_tail( resolver->invalid, peer, resolver->pool );
528 0 : } else {
529 0 : peer->state = PEER_STATE_REFRESHING;
530 0 : peer->deadline_nanos = now + PEER_DEADLINE_NANOS_RESOLVE;
531 0 : deadline_list_ele_push_tail( resolver->resolving, peer, resolver->pool );
532 0 : }
533 0 : }
534 :
535 0 : while( !deadline_list_is_empty( resolver->resolving, resolver->pool ) ) {
536 0 : fd_ssresolve_peer_t * peer = deadline_list_ele_peek_head( resolver->resolving, resolver->pool );
537 0 : if( FD_LIKELY( peer->deadline_nanos>now ) ) break;
538 :
539 0 : deadline_list_ele_pop_head( resolver->resolving, resolver->pool );
540 0 : peer->state = PEER_STATE_INVALID;
541 0 : peer->deadline_nanos = now + PEER_DEADLINE_NANOS_INVALID;
542 0 : deadline_list_ele_push_tail( resolver->invalid, peer, resolver->pool );
543 0 : remove_peer( resolver, peer->fd.idx );
544 :
545 0 : fd_sspeer_selector_remove( selector, &peer->key );
546 0 : }
547 :
548 0 : while( !deadline_list_is_empty( resolver->invalid, resolver->pool ) ) {
549 0 : fd_ssresolve_peer_t * peer = deadline_list_ele_peek_head( resolver->invalid, resolver->pool );
550 0 : if( FD_LIKELY( peer->deadline_nanos>now ) ) break;
551 :
552 0 : deadline_list_ele_pop_head( resolver->invalid, resolver->pool );
553 :
554 0 : peer->state = PEER_STATE_UNRESOLVED;
555 0 : peer->deadline_nanos = 0L;
556 0 : deadline_list_ele_push_tail( resolver->unresolved, peer, resolver->pool );
557 0 : }
558 :
559 0 : while( !deadline_list_is_empty( resolver->valid, resolver->pool ) ) {
560 0 : fd_ssresolve_peer_t * peer = deadline_list_ele_peek_head( resolver->valid, resolver->pool );
561 0 : if( FD_LIKELY( peer->deadline_nanos>now ) ) break;
562 :
563 0 : deadline_list_ele_pop_head( resolver->valid, resolver->pool );
564 :
565 : /* Clear stale snapshot data before re-resolving so the peer
566 : does not carry data from the previous resolve cycle. */
567 0 : clear_peer_snapshot_data( peer );
568 0 : int result = peer_connect( resolver, peer );
569 0 : if( FD_UNLIKELY( -1==result ) ) {
570 0 : peer->state = PEER_STATE_INVALID;
571 0 : peer->deadline_nanos = now + PEER_DEADLINE_NANOS_INVALID;
572 0 : deadline_list_ele_push_tail( resolver->invalid, peer, resolver->pool );
573 0 : fd_sspeer_selector_remove( selector, &peer->key );
574 0 : } else {
575 0 : peer->state = PEER_STATE_REFRESHING;
576 0 : peer->deadline_nanos = now + PEER_DEADLINE_NANOS_RESOLVE;
577 0 : deadline_list_ele_push_tail( resolver->resolving, peer, resolver->pool );
578 0 : }
579 0 : }
580 :
581 0 : poll_advance( resolver, now );
582 0 : }
|