VirtualBox

source: vbox/trunk/src/libs/curl-7.83.1/lib/vquic/msh3.c@ 97623

Last change on this file since 97623 was 95312, checked in by vboxsync, 3 years ago

libs/{curl,libxml2}: OSE export fixes, bugref:8515

  • Property svn:eol-style set to native
File size: 16.1 KB
Line 
1/***************************************************************************
2 * _ _ ____ _
3 * Project ___| | | | _ \| |
4 * / __| | | | |_) | |
5 * | (__| |_| | _ <| |___
6 * \___|\___/|_| \_\_____|
7 *
8 * Copyright (C) 1998 - 2022, Daniel Stenberg, <[email protected]>, et al.
9 *
10 * This software is licensed as described in the file COPYING, which
11 * you should have received as part of this distribution. The terms
12 * are also available at https://curl.se/docs/copyright.html.
13 *
14 * You may opt to use, copy, modify, merge, publish, distribute and/or sell
15 * copies of the Software, and permit persons to whom the Software is
16 * furnished to do so, under the terms of the COPYING file.
17 *
18 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
19 * KIND, either express or implied.
20 *
21 ***************************************************************************/
22
23#include "curl_setup.h"
24
25#ifdef USE_MSH3
26
27#include "urldata.h"
28#include "curl_printf.h"
29#include "timeval.h"
30#include "multiif.h"
31#include "sendf.h"
32#include "connect.h"
33#include "h2h3.h"
34#include "msh3.h"
35
36/* #define DEBUG_HTTP3 1 */
37#ifdef DEBUG_HTTP3
38#define H3BUGF(x) x
39#else
40#define H3BUGF(x) do { } while(0)
41#endif
42
43#define MSH3_REQ_INIT_BUF_LEN 8192
44
45static CURLcode msh3_do_it(struct Curl_easy *data, bool *done);
46static int msh3_getsock(struct Curl_easy *data,
47 struct connectdata *conn, curl_socket_t *socks);
48static CURLcode msh3_disconnect(struct Curl_easy *data,
49 struct connectdata *conn,
50 bool dead_connection);
51static unsigned int msh3_conncheck(struct Curl_easy *data,
52 struct connectdata *conn,
53 unsigned int checks_to_perform);
54static Curl_recv msh3_stream_recv;
55static Curl_send msh3_stream_send;
56static void MSH3_CALL msh3_header_received(MSH3_REQUEST *Request,
57 void *IfContext,
58 const MSH3_HEADER *Header);
59static void MSH3_CALL msh3_data_received(MSH3_REQUEST *Request,
60 void *IfContext, uint32_t Length,
61 const uint8_t *Data);
62static void MSH3_CALL msh3_complete(MSH3_REQUEST *Request, void *IfContext,
63 bool Aborted, uint64_t AbortError);
64static void MSH3_CALL msh3_shutdown(MSH3_REQUEST *Request, void *IfContext);
65
66static const struct Curl_handler msh3_curl_handler_http3 = {
67 "HTTPS", /* scheme */
68 ZERO_NULL, /* setup_connection */
69 msh3_do_it, /* do_it */
70 Curl_http_done, /* done */
71 ZERO_NULL, /* do_more */
72 ZERO_NULL, /* connect_it */
73 ZERO_NULL, /* connecting */
74 ZERO_NULL, /* doing */
75 msh3_getsock, /* proto_getsock */
76 msh3_getsock, /* doing_getsock */
77 ZERO_NULL, /* domore_getsock */
78 msh3_getsock, /* perform_getsock */
79 msh3_disconnect, /* disconnect */
80 ZERO_NULL, /* readwrite */
81 msh3_conncheck, /* connection_check */
82 ZERO_NULL, /* attach connection */
83 PORT_HTTP, /* defport */
84 CURLPROTO_HTTPS, /* protocol */
85 CURLPROTO_HTTP, /* family */
86 PROTOPT_SSL | PROTOPT_STREAM /* flags */
87};
88
89static const MSH3_REQUEST_IF msh3_request_if = {
90 msh3_header_received,
91 msh3_data_received,
92 msh3_complete,
93 msh3_shutdown
94};
95
96void Curl_quic_ver(char *p, size_t len)
97{
98 uint32_t v[4];
99 MsH3Version(v);
100 (void)msnprintf(p, len, "msh3/%d.%d.%d.%d", v[0], v[1], v[2], v[3]);
101}
102
103CURLcode Curl_quic_connect(struct Curl_easy *data,
104 struct connectdata *conn,
105 curl_socket_t sockfd,
106 int sockindex,
107 const struct sockaddr *addr,
108 socklen_t addrlen)
109{
110 struct quicsocket *qs = &conn->hequic[sockindex];
111 bool unsecure = !conn->ssl_config.verifypeer;
112 memset(qs, 0, sizeof(*qs));
113
114 (void)sockfd;
115 (void)addr; /* TODO - Pass address along */
116 (void)addrlen;
117
118 H3BUGF(infof(data, "creating new api/connection"));
119
120 qs->api = MsH3ApiOpen();
121 if(!qs->api) {
122 failf(data, "can't create msh3 api");
123 return CURLE_FAILED_INIT;
124 }
125
126 qs->conn = MsH3ConnectionOpen(qs->api,
127 conn->host.name,
128 (uint16_t)conn->remote_port,
129 unsecure);
130 if(!qs->conn) {
131 failf(data, "can't create msh3 connection");
132 if(qs->api) {
133 MsH3ApiClose(qs->api);
134 }
135 return CURLE_FAILED_INIT;
136 }
137
138 return CURLE_OK;
139}
140
141CURLcode Curl_quic_is_connected(struct Curl_easy *data,
142 struct connectdata *conn,
143 int sockindex,
144 bool *connected)
145{
146 struct quicsocket *qs = &conn->hequic[sockindex];
147 MSH3_CONNECTION_STATE state;
148
149 state = MsH3ConnectionGetState(qs->conn, false);
150 if(state == MSH3_CONN_HANDSHAKE_FAILED || state == MSH3_CONN_DISCONNECTED) {
151 failf(data, "failed to connect, state=%u", (uint32_t)state);
152 return CURLE_COULDNT_CONNECT;
153 }
154
155 if(state == MSH3_CONN_CONNECTED) {
156 H3BUGF(infof(data, "connection connected"));
157 *connected = true;
158 conn->quic = qs;
159 conn->recv[sockindex] = msh3_stream_recv;
160 conn->send[sockindex] = msh3_stream_send;
161 conn->handler = &msh3_curl_handler_http3;
162 conn->bits.multiplex = TRUE; /* at least potentially multiplexed */
163 conn->httpversion = 30;
164 conn->bundle->multiuse = BUNDLE_MULTIPLEX;
165 /* TODO - Clean up other happy-eyeballs connection(s)? */
166 }
167
168 return CURLE_OK;
169}
170
171static int msh3_getsock(struct Curl_easy *data,
172 struct connectdata *conn, curl_socket_t *socks)
173{
174 struct HTTP *stream = data->req.p.http;
175 int bitmap = GETSOCK_BLANK;
176
177 socks[0] = conn->sock[FIRSTSOCKET];
178
179 if(stream->recv_error) {
180 bitmap |= GETSOCK_READSOCK(FIRSTSOCKET);
181 data->state.drain++;
182 }
183 else if(stream->recv_header_len || stream->recv_data_len) {
184 bitmap |= GETSOCK_READSOCK(FIRSTSOCKET);
185 data->state.drain++;
186 }
187
188 H3BUGF(infof(data, "msh3_getsock %u", (uint32_t)data->state.drain));
189
190 return bitmap;
191}
192
193static CURLcode msh3_do_it(struct Curl_easy *data, bool *done)
194{
195 struct HTTP *stream = data->req.p.http;
196 H3BUGF(infof(data, "msh3_do_it"));
197 stream->recv_buf = malloc(MSH3_REQ_INIT_BUF_LEN);
198 if(!stream->recv_buf) {
199 return CURLE_OUT_OF_MEMORY;
200 }
201 stream->req = ZERO_NULL;
202 msh3_lock_initialize(&stream->recv_lock);
203 stream->recv_buf_alloc = MSH3_REQ_INIT_BUF_LEN;
204 stream->recv_header_len = 0;
205 stream->recv_header_complete = false;
206 stream->recv_data_len = 0;
207 stream->recv_data_complete = false;
208 stream->recv_error = CURLE_OK;
209 return Curl_http(data, done);
210}
211
212static unsigned int msh3_conncheck(struct Curl_easy *data,
213 struct connectdata *conn,
214 unsigned int checks_to_perform)
215{
216 (void)data;
217 (void)conn;
218 (void)checks_to_perform;
219 H3BUGF(infof(data, "msh3_conncheck"));
220 return CONNRESULT_NONE;
221}
222
223static void msh3_cleanup(struct quicsocket *qs, struct HTTP *stream)
224{
225 if(stream && stream->recv_buf) {
226 free(stream->recv_buf);
227 stream->recv_buf = ZERO_NULL;
228 msh3_lock_uninitialize(&stream->recv_lock);
229 }
230 if(qs->conn) {
231 MsH3ConnectionClose(qs->conn);
232 qs->conn = ZERO_NULL;
233 }
234 if(qs->api) {
235 MsH3ApiClose(qs->api);
236 qs->api = ZERO_NULL;
237 }
238}
239
240static CURLcode msh3_disconnect(struct Curl_easy *data,
241 struct connectdata *conn, bool dead_connection)
242{
243 (void)dead_connection;
244 H3BUGF(infof(data, "disconnecting (msh3)"));
245 msh3_cleanup(conn->quic, data->req.p.http);
246 return CURLE_OK;
247}
248
249void Curl_quic_disconnect(struct Curl_easy *data, struct connectdata *conn,
250 int tempindex)
251{
252 if(conn->transport == TRNSPRT_QUIC) {
253 H3BUGF(infof(data, "disconnecting (curl)"));
254 msh3_cleanup(&conn->hequic[tempindex], data->req.p.http);
255 }
256}
257
258/* Requires stream->recv_lock to be held */
259static bool msh3request_ensure_room(struct HTTP *stream, size_t len)
260{
261 uint8_t *new_recv_buf;
262 const size_t cur_recv_len = stream->recv_header_len + stream->recv_data_len;
263 if(cur_recv_len + len > stream->recv_buf_alloc) {
264 size_t new_recv_buf_alloc_len = stream->recv_buf_alloc;
265 do {
266 new_recv_buf_alloc_len <<= 1; /* TODO - handle overflow */
267 } while(cur_recv_len + len > new_recv_buf_alloc_len);
268 new_recv_buf = malloc(new_recv_buf_alloc_len);
269 if(!new_recv_buf) {
270 return false;
271 }
272 if(cur_recv_len) {
273 memcpy(new_recv_buf, stream->recv_buf, cur_recv_len);
274 }
275 stream->recv_buf_alloc = new_recv_buf_alloc_len;
276 free(stream->recv_buf);
277 stream->recv_buf = new_recv_buf;
278 }
279 return true;
280}
281
282static void MSH3_CALL msh3_header_received(MSH3_REQUEST *Request,
283 void *IfContext,
284 const MSH3_HEADER *Header)
285{
286 struct HTTP *stream = IfContext;
287 size_t total_len;
288 (void)Request;
289 H3BUGF(printf("* msh3_header_received\n"));
290
291 if(stream->recv_header_complete) {
292 H3BUGF(printf("* ignoring header after data\n"));
293 return;
294 }
295
296 msh3_lock_acquire(&stream->recv_lock);
297
298 if((Header->NameLength == 7) &&
299 !strncmp(H2H3_PSEUDO_STATUS, (char *)Header->Name, 7)) {
300 total_len = 9 + Header->ValueLength;
301 if(!msh3request_ensure_room(stream, total_len)) {
302 /* TODO - handle error */
303 goto release_lock;
304 }
305 msnprintf((char *)stream->recv_buf + stream->recv_header_len,
306 stream->recv_buf_alloc - stream->recv_header_len,
307 "HTTP/3 %.*s\n", (int)Header->ValueLength, Header->Value);
308 }
309 else {
310 total_len = Header->NameLength + 4 + Header->ValueLength;
311 if(!msh3request_ensure_room(stream, total_len)) {
312 /* TODO - handle error */
313 goto release_lock;
314 }
315 msnprintf((char *)stream->recv_buf + stream->recv_header_len,
316 stream->recv_buf_alloc - stream->recv_header_len,
317 "%.*s: %.*s\n",
318 (int)Header->NameLength, Header->Name,
319 (int)Header->ValueLength, Header->Value);
320 }
321
322 stream->recv_header_len += total_len - 1; /* don't include null-terminator */
323
324release_lock:
325 msh3_lock_release(&stream->recv_lock);
326}
327
328static void MSH3_CALL msh3_data_received(MSH3_REQUEST *Request,
329 void *IfContext, uint32_t Length,
330 const uint8_t *Data)
331{
332 struct HTTP *stream = IfContext;
333 size_t cur_recv_len = stream->recv_header_len + stream->recv_data_len;
334 (void)Request;
335 H3BUGF(printf("* msh3_data_received %u. %zu buffered, %zu allocated\n",
336 Length, cur_recv_len, stream->recv_buf_alloc));
337 msh3_lock_acquire(&stream->recv_lock);
338 if(!stream->recv_header_complete) {
339 H3BUGF(printf("* Headers complete!\n"));
340 if(!msh3request_ensure_room(stream, 2)) {
341 /* TODO - handle error */
342 goto release_lock;
343 }
344 stream->recv_buf[stream->recv_header_len++] = '\r';
345 stream->recv_buf[stream->recv_header_len++] = '\n';
346 stream->recv_header_complete = true;
347 cur_recv_len += 2;
348 }
349 if(!msh3request_ensure_room(stream, Length)) {
350 /* TODO - handle error */
351 goto release_lock;
352 }
353 memcpy(stream->recv_buf + cur_recv_len, Data, Length);
354 stream->recv_data_len += (size_t)Length;
355release_lock:
356 msh3_lock_release(&stream->recv_lock);
357}
358
359static void MSH3_CALL msh3_complete(MSH3_REQUEST *Request, void *IfContext,
360 bool Aborted, uint64_t AbortError)
361{
362 struct HTTP *stream = IfContext;
363 (void)Request;
364 (void)AbortError;
365 H3BUGF(printf("* msh3_complete, aborted=%s\n", Aborted ? "true" : "false"));
366 msh3_lock_acquire(&stream->recv_lock);
367 if(Aborted) {
368 stream->recv_error = CURLE_HTTP3; /* TODO - how do we pass AbortError? */
369 }
370 stream->recv_header_complete = true;
371 stream->recv_data_complete = true;
372 msh3_lock_release(&stream->recv_lock);
373}
374
375static void MSH3_CALL msh3_shutdown(MSH3_REQUEST *Request, void *IfContext)
376{
377 struct HTTP *stream = IfContext;
378 (void)Request;
379 (void)stream;
380}
381
382static_assert(sizeof(MSH3_HEADER) == sizeof(struct h2h3pseudo),
383 "Sizes must match for cast below to work");
384
385static ssize_t msh3_stream_send(struct Curl_easy *data,
386 int sockindex,
387 const void *mem,
388 size_t len,
389 CURLcode *curlcode)
390{
391 struct connectdata *conn = data->conn;
392 struct HTTP *stream = data->req.p.http;
393 struct quicsocket *qs = conn->quic;
394 struct h2h3req *hreq;
395
396 (void)sockindex;
397 H3BUGF(infof(data, "msh3_stream_send %zu", len));
398
399 if(!stream->req) {
400 *curlcode = Curl_pseudo_headers(data, mem, len, &hreq);
401 if(*curlcode) {
402 failf(data, "Curl_pseudo_headers failed");
403 return -1;
404 }
405 H3BUGF(infof(data, "starting request with %zu headers", hreq->entries));
406 stream->req = MsH3RequestOpen(qs->conn, &msh3_request_if, stream,
407 (MSH3_HEADER*)hreq->header, hreq->entries);
408 Curl_pseudo_free(hreq);
409 if(!stream->req) {
410 failf(data, "request open failed");
411 *curlcode = CURLE_SEND_ERROR;
412 return -1;
413 }
414 *curlcode = CURLE_OK;
415 return len;
416 }
417 H3BUGF(infof(data, "send %zd body bytes on request %p", len,
418 (void *)stream->req));
419 *curlcode = CURLE_SEND_ERROR;
420 return -1;
421}
422
423static ssize_t msh3_stream_recv(struct Curl_easy *data,
424 int sockindex,
425 char *buf,
426 size_t buffersize,
427 CURLcode *curlcode)
428{
429 struct HTTP *stream = data->req.p.http;
430 size_t outsize = 0;
431 (void)sockindex;
432 H3BUGF(infof(data, "msh3_stream_recv %zu", buffersize));
433
434 if(stream->recv_error) {
435 failf(data, "request aborted");
436 *curlcode = stream->recv_error;
437 return -1;
438 }
439
440 msh3_lock_acquire(&stream->recv_lock);
441
442 if(stream->recv_header_len) {
443 outsize = buffersize;
444 if(stream->recv_header_len < outsize) {
445 outsize = stream->recv_header_len;
446 }
447 memcpy(buf, stream->recv_buf, outsize);
448 if(outsize < stream->recv_header_len + stream->recv_data_len) {
449 memmove(stream->recv_buf, stream->recv_buf + outsize,
450 stream->recv_header_len + stream->recv_data_len - outsize);
451 }
452 stream->recv_header_len -= outsize;
453 H3BUGF(infof(data, "returned %zu bytes of headers", outsize));
454 }
455 else if(stream->recv_data_len) {
456 outsize = buffersize;
457 if(stream->recv_data_len < outsize) {
458 outsize = stream->recv_data_len;
459 }
460 memcpy(buf, stream->recv_buf, outsize);
461 if(outsize < stream->recv_data_len) {
462 memmove(stream->recv_buf, stream->recv_buf + outsize,
463 stream->recv_data_len - outsize);
464 }
465 stream->recv_data_len -= outsize;
466 H3BUGF(infof(data, "returned %zu bytes of data", outsize));
467 }
468 else if(stream->recv_data_complete) {
469 H3BUGF(infof(data, "receive complete"));
470 }
471
472 msh3_lock_release(&stream->recv_lock);
473
474 return (ssize_t)outsize;
475}
476
477CURLcode Curl_quic_done_sending(struct Curl_easy *data)
478{
479 struct connectdata *conn = data->conn;
480 H3BUGF(infof(data, "Curl_quic_done_sending"));
481 if(conn->handler == &msh3_curl_handler_http3) {
482 struct HTTP *stream = data->req.p.http;
483 stream->upload_done = TRUE;
484 }
485
486 return CURLE_OK;
487}
488
489void Curl_quic_done(struct Curl_easy *data, bool premature)
490{
491 (void)data;
492 (void)premature;
493 H3BUGF(infof(data, "Curl_quic_done"));
494}
495
496bool Curl_quic_data_pending(const struct Curl_easy *data)
497{
498 struct HTTP *stream = data->req.p.http;
499 H3BUGF(infof((struct Curl_easy *)data, "Curl_quic_data_pending"));
500 return stream->recv_header_len || stream->recv_data_len;
501}
502
503#endif /* USE_MSH3 */
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette