Line data Source code
1 : #include "fd_ssresolve.h"
2 : #include "fd_ssarchive.h"
3 :
4 : #include "../../../waltz/http/picohttpparser.h"
5 : #include "../../../util/log/fd_log.h"
6 :
7 : #include <unistd.h>
8 : #include <errno.h>
9 : #include <stdlib.h>
10 : #include <strings.h>
11 :
12 : #include <sys/socket.h>
13 : #include <netinet/tcp.h>
14 : #include <netinet/in.h>
15 :
16 0 : #define FD_SSRESOLVE_STATE_REQ (0) /* sending request for snapshot */
17 0 : #define FD_SSRESOLVE_STATE_RESP (1) /* receiving snapshot response */
18 0 : #define FD_SSRESOLVE_STATE_DONE (2) /* done */
19 :
20 : struct fd_ssresolve_private {
21 : int state;
22 : long deadline;
23 :
24 : fd_ip4_port_t addr;
25 : int sockfd;
26 : int full;
27 :
28 : char request[ 4096UL ];
29 : ulong request_sent;
30 : ulong request_len;
31 :
32 : ulong response_len;
33 : char response[ USHORT_MAX ];
34 :
35 : ulong magic;
36 : };
37 :
38 : FD_FN_CONST ulong
39 0 : fd_ssresolve_align( void ) {
40 0 : return FD_SSRESOLVE_ALIGN;
41 0 : }
42 :
43 : FD_FN_CONST ulong
44 0 : fd_ssresolve_footprint( void ) {
45 0 : ulong l;
46 0 : l = FD_LAYOUT_INIT;
47 0 : l = FD_LAYOUT_APPEND( l, FD_SSRESOLVE_ALIGN, sizeof(fd_ssresolve_t) );
48 0 : return FD_LAYOUT_FINI( l, FD_SSRESOLVE_ALIGN );
49 0 : }
50 :
51 : void *
52 0 : fd_ssresolve_new( void * shmem ) {
53 0 : if( FD_UNLIKELY( !shmem ) ) {
54 0 : FD_LOG_WARNING(( "NULL shmem" ));
55 0 : return NULL;
56 0 : }
57 :
58 0 : if( FD_UNLIKELY( !fd_ulong_is_aligned( (ulong)shmem, fd_ssresolve_align() ) ) ) {
59 0 : FD_LOG_WARNING(( "unaligned shmem" ));
60 0 : return NULL;
61 0 : }
62 :
63 0 : FD_SCRATCH_ALLOC_INIT( l, shmem );
64 0 : fd_ssresolve_t * ssresolve = FD_SCRATCH_ALLOC_APPEND( l, FD_SSRESOLVE_ALIGN, sizeof(fd_ssresolve_t) );
65 :
66 0 : ssresolve->state = FD_SSRESOLVE_STATE_REQ;
67 0 : ssresolve->request_sent = 0UL;
68 0 : ssresolve->request_len = 0UL;
69 0 : ssresolve->response_len = 0UL;
70 :
71 0 : FD_COMPILER_MFENCE();
72 0 : FD_VOLATILE( ssresolve->magic ) = FD_SSRESOLVE_MAGIC;
73 0 : FD_COMPILER_MFENCE();
74 :
75 0 : return (void *)ssresolve;
76 0 : }
77 :
78 : fd_ssresolve_t *
79 0 : fd_ssresolve_join( void * _ssresolve ) {
80 0 : if( FD_UNLIKELY( !_ssresolve ) ) {
81 0 : FD_LOG_WARNING(( "NULL ssresolve" ));
82 0 : return NULL;
83 0 : }
84 :
85 0 : if( FD_UNLIKELY( !fd_ulong_is_aligned( (ulong)_ssresolve, fd_ssresolve_align() ) ) ) {
86 0 : FD_LOG_WARNING(( "misaligned ssresolve" ));
87 0 : return NULL;
88 0 : }
89 :
90 0 : fd_ssresolve_t * ssresolve = (fd_ssresolve_t *)_ssresolve;
91 :
92 0 : if( FD_UNLIKELY( ssresolve->magic!=FD_SSRESOLVE_MAGIC ) ) {
93 0 : FD_LOG_WARNING(( "bad magic" ));
94 0 : return NULL;
95 0 : }
96 :
97 0 : return ssresolve;
98 0 : }
99 :
100 : void
101 : fd_ssresolve_init( fd_ssresolve_t * ssresolve,
102 : fd_ip4_port_t addr,
103 : int sockfd,
104 0 : int full ) {
105 0 : ssresolve->addr = addr;
106 0 : ssresolve->sockfd = sockfd;
107 0 : ssresolve->full = full;
108 :
109 0 : ssresolve->state = FD_SSRESOLVE_STATE_REQ;
110 0 : ssresolve->request_sent = 0UL;
111 0 : ssresolve->request_len = 0UL;
112 0 : ssresolve->response_len = 0UL;
113 0 : }
114 :
115 : static void
116 : fd_ssresolve_render_req( fd_ssresolve_t * ssresolve,
117 0 : fd_ip4_port_t addr ) {
118 0 : if( FD_LIKELY( ssresolve->full ) ) {
119 0 : FD_TEST( fd_cstr_printf_check( ssresolve->request, sizeof(ssresolve->request), &ssresolve->request_len,
120 0 : "GET /snapshot.tar.bz2 HTTP/1.1\r\n"
121 0 : "User-Agent: Firedancer\r\n"
122 0 : "Accept: */*\r\n"
123 0 : "Accept-Encoding: identity\r\n"
124 0 : "Host: " FD_IP4_ADDR_FMT "\r\n\r\n",
125 0 : FD_IP4_ADDR_FMT_ARGS( addr.addr ) ) );
126 0 : } else {
127 0 : FD_TEST( fd_cstr_printf_check( ssresolve->request, sizeof(ssresolve->request), &ssresolve->request_len,
128 0 : "GET /incremental-snapshot.tar.bz2 HTTP/1.1\r\n"
129 0 : "User-Agent: Firedancer\r\n"
130 0 : "Accept: */*\r\n"
131 0 : "Accept-Encoding: identity\r\n"
132 0 : "Host: " FD_IP4_ADDR_FMT "\r\n\r\n",
133 0 : FD_IP4_ADDR_FMT_ARGS( addr.addr ) ) );
134 0 : }
135 0 : }
136 :
137 : static int
138 0 : fd_ssresolve_send_request( fd_ssresolve_t * ssresolve ) {
139 0 : FD_TEST( ssresolve->state==FD_SSRESOLVE_STATE_REQ );
140 :
141 0 : if( FD_UNLIKELY( !ssresolve->request_len ) ) {
142 0 : fd_ssresolve_render_req( ssresolve, ssresolve->addr );
143 0 : }
144 :
145 0 : long sent = sendto( ssresolve->sockfd, ssresolve->request+ssresolve->request_sent, ssresolve->request_len-ssresolve->request_sent, 0, NULL, 0 );
146 0 : if( FD_UNLIKELY( -1==sent && errno==EAGAIN ) ) return FD_SSRESOLVE_ADVANCE_AGAIN;
147 0 : else if( FD_UNLIKELY( -1==sent ) ) {
148 0 : return FD_SSRESOLVE_ADVANCE_ERROR;
149 0 : }
150 :
151 0 : ssresolve->request_sent += (ulong)sent;
152 0 : if( FD_UNLIKELY( ssresolve->request_sent==ssresolve->request_len ) ) {
153 0 : ssresolve->state = FD_SSRESOLVE_STATE_RESP;
154 0 : return FD_SSRESOLVE_ADVANCE_SUCCESS;
155 0 : }
156 :
157 0 : return FD_SSRESOLVE_ADVANCE_AGAIN;
158 0 : }
159 :
160 : static int
161 : fd_ssresolve_parse_redirect( fd_ssresolve_t * ssresolve,
162 : struct phr_header * headers,
163 : ulong header_cnt,
164 0 : fd_ssresolve_result_t * result ) {
165 0 : ulong location_len = 0UL;
166 0 : char const * location = NULL;
167 :
168 0 : for( ulong i=0UL; i<header_cnt; i++ ) {
169 0 : if( FD_UNLIKELY( !strncasecmp( headers[ i ].name, "location", headers[ i ].name_len ) ) ) {
170 0 : if( FD_UNLIKELY( !headers [ i ].value_len || headers[ i ].value[ 0 ]!='/' ) ) {
171 0 : FD_LOG_WARNING(( "invalid location header `%.*s`", (int)headers[ i ].value_len, headers[ i ].value ));
172 0 : return FD_SSRESOLVE_ADVANCE_ERROR;
173 0 : }
174 :
175 0 : location_len = headers[ i ].value_len;
176 0 : location = headers[ i ].value;
177 0 : break;
178 0 : }
179 0 : }
180 :
181 0 : if( FD_UNLIKELY( location_len>=PATH_MAX-1UL ) ) return FD_SSRESOLVE_ADVANCE_ERROR;
182 :
183 0 : char snapshot_name[ PATH_MAX ];
184 0 : fd_memcpy( snapshot_name, location+1UL, location_len-1UL );
185 0 : snapshot_name[ location_len-1UL ] = '\0';
186 :
187 0 : ulong full_entry_slot, incremental_entry_slot;
188 0 : uchar decoded_hash[ FD_HASH_FOOTPRINT ];
189 0 : int err = fd_ssarchive_parse_filename( snapshot_name, &full_entry_slot, &incremental_entry_slot, decoded_hash );
190 :
191 0 : if( FD_UNLIKELY( err ) ) {
192 0 : FD_LOG_WARNING(( "unrecognized snapshot file `%s` in redirect location header", snapshot_name ));
193 0 : return FD_SSRESOLVE_ADVANCE_ERROR;
194 0 : }
195 :
196 0 : if( FD_LIKELY( incremental_entry_slot==ULONG_MAX ) ) {
197 0 : result->slot = full_entry_slot;
198 0 : result->base_slot = ULONG_MAX;
199 0 : } else {
200 0 : result->slot = incremental_entry_slot;
201 0 : result->base_slot = full_entry_slot;
202 0 : }
203 :
204 0 : ssresolve->state = FD_SSRESOLVE_STATE_DONE;
205 0 : return FD_SSRESOLVE_ADVANCE_SUCCESS;
206 0 : }
207 :
208 : static int
209 : fd_ssresolve_read_response( fd_ssresolve_t * ssresolve,
210 0 : fd_ssresolve_result_t * result ) {
211 0 : FD_TEST( ssresolve->state==FD_SSRESOLVE_STATE_RESP );
212 0 : long read = recvfrom( ssresolve->sockfd, ssresolve->response+ssresolve->response_len, sizeof(ssresolve->response)-ssresolve->response_len, 0, NULL, NULL );
213 0 : if( FD_UNLIKELY( -1==read && errno==EAGAIN ) ) return FD_SSRESOLVE_ADVANCE_AGAIN;
214 0 : else if( FD_UNLIKELY( -1==read ) ) {
215 0 : FD_LOG_WARNING(( "recvfrom() failed (%d-%s)", errno, fd_io_strerror( errno ) ));
216 0 : return FD_SSRESOLVE_ADVANCE_ERROR;
217 0 : }
218 :
219 0 : ssresolve->response_len += (ulong)read;
220 :
221 0 : int minor_version;
222 0 : int status;
223 0 : const char * message;
224 0 : ulong message_len;
225 0 : struct phr_header headers[ 128UL ];
226 0 : ulong header_cnt = 128UL;
227 0 : int parsed = phr_parse_response( ssresolve->response,
228 0 : ssresolve->response_len,
229 0 : &minor_version,
230 0 : &status,
231 0 : &message,
232 0 : &message_len,
233 0 : headers,
234 0 : &header_cnt,
235 0 : ssresolve->response_len - (ulong)read );
236 0 : if( FD_UNLIKELY( parsed==-1 ) ) {
237 0 : FD_LOG_WARNING(( "malformed response body" ));
238 0 : return FD_SSRESOLVE_ADVANCE_ERROR;
239 0 : } else if( parsed==-2 ) {
240 0 : return FD_SSRESOLVE_ADVANCE_AGAIN;
241 0 : }
242 :
243 0 : int is_redirect = (status==301) | (status==302) | (status==303) | (status==304) | (status==307) | (status==308);
244 0 : if( FD_UNLIKELY( is_redirect ) ) {
245 0 : return fd_ssresolve_parse_redirect( ssresolve, headers, header_cnt, result );
246 0 : }
247 :
248 0 : if( FD_UNLIKELY( status!=200 ) ) {
249 0 : FD_LOG_WARNING(( "unexpected response status %d", status ));
250 0 : return FD_SSRESOLVE_ADVANCE_ERROR;
251 0 : }
252 :
253 0 : return FD_SSRESOLVE_ADVANCE_ERROR;
254 0 : }
255 :
256 : int
257 0 : fd_ssresolve_advance_poll_out( fd_ssresolve_t * ssresolve ) {
258 0 : int res;
259 0 : switch( ssresolve->state ) {
260 0 : case FD_SSRESOLVE_STATE_REQ: {
261 0 : res = fd_ssresolve_send_request( ssresolve );
262 0 : break;
263 0 : }
264 0 : case FD_SSRESOLVE_STATE_RESP: {
265 0 : res = FD_SSRESOLVE_ADVANCE_AGAIN;
266 0 : break;
267 0 : }
268 0 : default: {
269 0 : FD_LOG_ERR(( "unexpected state %d", ssresolve->state ));
270 0 : return FD_SSRESOLVE_ADVANCE_ERROR;
271 0 : }
272 0 : }
273 0 : return res;
274 0 : }
275 :
276 : int
277 : fd_ssresolve_advance_poll_in( fd_ssresolve_t * ssresolve,
278 0 : fd_ssresolve_result_t * result ) {
279 0 : int res;
280 0 : switch( ssresolve->state ) {
281 0 : case FD_SSRESOLVE_STATE_RESP: {
282 0 : res = fd_ssresolve_read_response( ssresolve, result );
283 0 : break;
284 0 : }
285 0 : case FD_SSRESOLVE_STATE_REQ: {
286 0 : res = FD_SSRESOLVE_ADVANCE_AGAIN;
287 0 : break;
288 0 : }
289 0 : default: {
290 0 : FD_LOG_ERR(( "unexpected state %d", ssresolve->state ));
291 0 : return FD_SSRESOLVE_ADVANCE_ERROR;
292 0 : }
293 0 : }
294 :
295 0 : return res;
296 0 : }
297 :
298 : int
299 0 : fd_ssresolve_is_done( fd_ssresolve_t * ssresolve ) {
300 0 : return ssresolve->state==FD_SSRESOLVE_STATE_DONE;
301 0 : }
|