f7bd315be1bab005e9bf6d7072e73f34d94c14ba
[platform/upstream/cmake.git] / Utilities / cmcurl / lib / vquic / msh3.c
1 /***************************************************************************
2  *                                  _   _ ____  _
3  *  Project                     ___| | | |  _ \| |
4  *                             / __| | | | |_) | |
5  *                            | (__| |_| |  _ <| |___
6  *                             \___|\___/|_| \_\_____|
7  *
8  * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, 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
45 static CURLcode msh3_do_it(struct Curl_easy *data, bool *done);
46 static int msh3_getsock(struct Curl_easy *data,
47                         struct connectdata *conn, curl_socket_t *socks);
48 static CURLcode msh3_disconnect(struct Curl_easy *data,
49                                 struct connectdata *conn,
50                                 bool dead_connection);
51 static unsigned int msh3_conncheck(struct Curl_easy *data,
52                                    struct connectdata *conn,
53                                    unsigned int checks_to_perform);
54 static Curl_recv msh3_stream_recv;
55 static Curl_send msh3_stream_send;
56 static void MSH3_CALL msh3_header_received(MSH3_REQUEST *Request,
57                                            void *IfContext,
58                                            const MSH3_HEADER *Header);
59 static void MSH3_CALL msh3_data_received(MSH3_REQUEST *Request,
60                                         void *IfContext, uint32_t Length,
61                                         const uint8_t *Data);
62 static void MSH3_CALL msh3_complete(MSH3_REQUEST *Request, void *IfContext,
63                                     bool Aborted, uint64_t AbortError);
64 static void MSH3_CALL msh3_shutdown(MSH3_REQUEST *Request, void *IfContext);
65
66 static 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
89 static const MSH3_REQUEST_IF msh3_request_if = {
90   msh3_header_received,
91   msh3_data_received,
92   msh3_complete,
93   msh3_shutdown
94 };
95
96 void 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
103 CURLcode 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
141 CURLcode 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
171 static 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
193 static 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
212 static 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
223 static 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
240 static 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
249 void 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 */
259 static 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
282 static 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
324 release_lock:
325   msh3_lock_release(&stream->recv_lock);
326 }
327
328 static 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;
355 release_lock:
356   msh3_lock_release(&stream->recv_lock);
357 }
358
359 static 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
375 static void MSH3_CALL msh3_shutdown(MSH3_REQUEST *Request, void *IfContext)
376 {
377   struct HTTP *stream = IfContext;
378   (void)Request;
379   (void)stream;
380 }
381
382 static_assert(sizeof(MSH3_HEADER) == sizeof(struct h2h3pseudo),
383               "Sizes must match for cast below to work");
384
385 static 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
423 static 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
477 CURLcode 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
489 void 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
496 bool 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 */