Imported Upstream version 3.25.0
[platform/upstream/cmake.git] / Utilities / cmcurl / lib / vquic / quiche.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  * SPDX-License-Identifier: curl
22  *
23  ***************************************************************************/
24
25 #include "curl_setup.h"
26
27 #ifdef USE_QUICHE
28 #include <quiche.h>
29 #include <openssl/err.h>
30 #include <openssl/ssl.h>
31 #include "urldata.h"
32 #include "sendf.h"
33 #include "strdup.h"
34 #include "rand.h"
35 #include "quic.h"
36 #include "strcase.h"
37 #include "multiif.h"
38 #include "connect.h"
39 #include "strerror.h"
40 #include "vquic.h"
41 #include "transfer.h"
42 #include "h2h3.h"
43 #include "vtls/openssl.h"
44 #include "vtls/keylog.h"
45
46 /* The last 3 #include files should be in this order */
47 #include "curl_printf.h"
48 #include "curl_memory.h"
49 #include "memdebug.h"
50
51 #define DEBUG_HTTP3
52 /* #define DEBUG_QUICHE */
53 #ifdef DEBUG_HTTP3
54 #define H3BUGF(x) x
55 #else
56 #define H3BUGF(x) do { } while(0)
57 #endif
58
59 #define QUIC_MAX_STREAMS (256*1024)
60 #define QUIC_MAX_DATA (1*1024*1024)
61 #define QUIC_IDLE_TIMEOUT (60 * 1000) /* milliseconds */
62
63 static CURLcode process_ingress(struct Curl_easy *data,
64                                 curl_socket_t sockfd,
65                                 struct quicsocket *qs);
66
67 static CURLcode flush_egress(struct Curl_easy *data, curl_socket_t sockfd,
68                              struct quicsocket *qs);
69
70 static CURLcode http_request(struct Curl_easy *data, const void *mem,
71                              size_t len);
72 static Curl_recv h3_stream_recv;
73 static Curl_send h3_stream_send;
74
75 static int quiche_getsock(struct Curl_easy *data,
76                           struct connectdata *conn, curl_socket_t *socks)
77 {
78   struct SingleRequest *k = &data->req;
79   int bitmap = GETSOCK_BLANK;
80
81   socks[0] = conn->sock[FIRSTSOCKET];
82
83   /* in a HTTP/2 connection we can basically always get a frame so we should
84      always be ready for one */
85   bitmap |= GETSOCK_READSOCK(FIRSTSOCKET);
86
87   /* we're still uploading or the HTTP/2 layer wants to send data */
88   if((k->keepon & (KEEP_SEND|KEEP_SEND_PAUSE)) == KEEP_SEND)
89     bitmap |= GETSOCK_WRITESOCK(FIRSTSOCKET);
90
91   return bitmap;
92 }
93
94 static CURLcode qs_disconnect(struct Curl_easy *data,
95                               struct quicsocket *qs)
96 {
97   DEBUGASSERT(qs);
98   if(qs->conn) {
99     (void)quiche_conn_close(qs->conn, TRUE, 0, NULL, 0);
100     /* flushing the egress is not a failsafe way to deliver all the
101        outstanding packets, but we also don't want to get stuck here... */
102     (void)flush_egress(data, qs->sockfd, qs);
103     quiche_conn_free(qs->conn);
104     qs->conn = NULL;
105   }
106   if(qs->h3config)
107     quiche_h3_config_free(qs->h3config);
108   if(qs->h3c)
109     quiche_h3_conn_free(qs->h3c);
110   if(qs->cfg) {
111     quiche_config_free(qs->cfg);
112     qs->cfg = NULL;
113   }
114   return CURLE_OK;
115 }
116
117 static CURLcode quiche_disconnect(struct Curl_easy *data,
118                                   struct connectdata *conn,
119                                   bool dead_connection)
120 {
121   struct quicsocket *qs = conn->quic;
122   (void)dead_connection;
123   return qs_disconnect(data, qs);
124 }
125
126 void Curl_quic_disconnect(struct Curl_easy *data,
127                           struct connectdata *conn,
128                           int tempindex)
129 {
130   if(conn->transport == TRNSPRT_QUIC)
131     qs_disconnect(data, &conn->hequic[tempindex]);
132 }
133
134 static unsigned int quiche_conncheck(struct Curl_easy *data,
135                                      struct connectdata *conn,
136                                      unsigned int checks_to_perform)
137 {
138   (void)data;
139   (void)conn;
140   (void)checks_to_perform;
141   return CONNRESULT_NONE;
142 }
143
144 static CURLcode quiche_do(struct Curl_easy *data, bool *done)
145 {
146   struct HTTP *stream = data->req.p.http;
147   stream->h3req = FALSE; /* not sent */
148   return Curl_http(data, done);
149 }
150
151 static const struct Curl_handler Curl_handler_http3 = {
152   "HTTPS",                              /* scheme */
153   ZERO_NULL,                            /* setup_connection */
154   quiche_do,                            /* do_it */
155   Curl_http_done,                       /* done */
156   ZERO_NULL,                            /* do_more */
157   ZERO_NULL,                            /* connect_it */
158   ZERO_NULL,                            /* connecting */
159   ZERO_NULL,                            /* doing */
160   quiche_getsock,                       /* proto_getsock */
161   quiche_getsock,                       /* doing_getsock */
162   ZERO_NULL,                            /* domore_getsock */
163   quiche_getsock,                       /* perform_getsock */
164   quiche_disconnect,                    /* disconnect */
165   ZERO_NULL,                            /* readwrite */
166   quiche_conncheck,                     /* connection_check */
167   ZERO_NULL,                            /* attach connection */
168   PORT_HTTP,                            /* defport */
169   CURLPROTO_HTTPS,                      /* protocol */
170   CURLPROTO_HTTP,                       /* family */
171   PROTOPT_SSL | PROTOPT_STREAM          /* flags */
172 };
173
174 #ifdef DEBUG_QUICHE
175 static void quiche_debug_log(const char *line, void *argp)
176 {
177   (void)argp;
178   fprintf(stderr, "%s\n", line);
179 }
180 #endif
181
182 static void keylog_callback(const SSL *ssl, const char *line)
183 {
184   (void)ssl;
185   Curl_tls_keylog_write_line(line);
186 }
187
188 static SSL_CTX *quic_ssl_ctx(struct Curl_easy *data)
189 {
190   SSL_CTX *ssl_ctx = SSL_CTX_new(TLS_method());
191
192   SSL_CTX_set_alpn_protos(ssl_ctx,
193                           (const uint8_t *)QUICHE_H3_APPLICATION_PROTOCOL,
194                           sizeof(QUICHE_H3_APPLICATION_PROTOCOL) - 1);
195
196   SSL_CTX_set_default_verify_paths(ssl_ctx);
197
198   /* Open the file if a TLS or QUIC backend has not done this before. */
199   Curl_tls_keylog_open();
200   if(Curl_tls_keylog_enabled()) {
201     SSL_CTX_set_keylog_callback(ssl_ctx, keylog_callback);
202   }
203
204   {
205     struct connectdata *conn = data->conn;
206     if(conn->ssl_config.verifypeer) {
207       const char * const ssl_cafile = conn->ssl_config.CAfile;
208       const char * const ssl_capath = conn->ssl_config.CApath;
209       if(ssl_cafile || ssl_capath) {
210         SSL_CTX_set_verify(ssl_ctx, SSL_VERIFY_PEER, NULL);
211         /* tell OpenSSL where to find CA certificates that are used to verify
212            the server's certificate. */
213         if(!SSL_CTX_load_verify_locations(ssl_ctx, ssl_cafile, ssl_capath)) {
214           /* Fail if we insist on successfully verifying the server. */
215           failf(data, "error setting certificate verify locations:"
216                 "  CAfile: %s CApath: %s",
217                 ssl_cafile ? ssl_cafile : "none",
218                 ssl_capath ? ssl_capath : "none");
219           return NULL;
220         }
221         infof(data, " CAfile: %s", ssl_cafile ? ssl_cafile : "none");
222         infof(data, " CApath: %s", ssl_capath ? ssl_capath : "none");
223       }
224 #ifdef CURL_CA_FALLBACK
225       else {
226         /* verifying the peer without any CA certificates won't work so
227            use openssl's built-in default as fallback */
228         SSL_CTX_set_default_verify_paths(ssl_ctx);
229       }
230 #endif
231     }
232   }
233   return ssl_ctx;
234 }
235
236 static int quic_init_ssl(struct quicsocket *qs, struct connectdata *conn)
237 {
238   /* this will need some attention when HTTPS proxy over QUIC get fixed */
239   const char * const hostname = conn->host.name;
240
241   DEBUGASSERT(!qs->ssl);
242   qs->ssl = SSL_new(qs->sslctx);
243
244   SSL_set_app_data(qs->ssl, qs);
245
246   /* set SNI */
247   SSL_set_tlsext_host_name(qs->ssl, hostname);
248   return 0;
249 }
250
251
252 CURLcode Curl_quic_connect(struct Curl_easy *data,
253                            struct connectdata *conn, curl_socket_t sockfd,
254                            int sockindex,
255                            const struct sockaddr *addr, socklen_t addrlen)
256 {
257   CURLcode result;
258   struct quicsocket *qs = &conn->hequic[sockindex];
259   char ipbuf[40];
260   int port;
261   int rv;
262
263 #ifdef DEBUG_QUICHE
264   /* initialize debug log callback only once */
265   static int debug_log_init = 0;
266   if(!debug_log_init) {
267     quiche_enable_debug_logging(quiche_debug_log, NULL);
268     debug_log_init = 1;
269   }
270 #endif
271
272   (void)addr;
273   (void)addrlen;
274
275   qs->sockfd = sockfd;
276   qs->cfg = quiche_config_new(QUICHE_PROTOCOL_VERSION);
277   if(!qs->cfg) {
278     failf(data, "can't create quiche config");
279     return CURLE_FAILED_INIT;
280   }
281
282   quiche_config_set_max_idle_timeout(qs->cfg, QUIC_IDLE_TIMEOUT);
283   quiche_config_set_initial_max_data(qs->cfg, QUIC_MAX_DATA);
284   quiche_config_set_initial_max_stream_data_bidi_local(qs->cfg, QUIC_MAX_DATA);
285   quiche_config_set_initial_max_stream_data_bidi_remote(qs->cfg,
286                                                         QUIC_MAX_DATA);
287   quiche_config_set_initial_max_stream_data_uni(qs->cfg, QUIC_MAX_DATA);
288   quiche_config_set_initial_max_streams_bidi(qs->cfg, QUIC_MAX_STREAMS);
289   quiche_config_set_initial_max_streams_uni(qs->cfg, QUIC_MAX_STREAMS);
290   quiche_config_set_application_protos(qs->cfg,
291                                        (uint8_t *)
292                                        QUICHE_H3_APPLICATION_PROTOCOL,
293                                        sizeof(QUICHE_H3_APPLICATION_PROTOCOL)
294                                        - 1);
295
296   qs->sslctx = quic_ssl_ctx(data);
297   if(!qs->sslctx)
298     return CURLE_QUIC_CONNECT_ERROR;
299
300   if(quic_init_ssl(qs, conn))
301     return CURLE_QUIC_CONNECT_ERROR;
302
303   result = Curl_rand(data, qs->scid, sizeof(qs->scid));
304   if(result)
305     return result;
306
307   qs->local_addrlen = sizeof(qs->local_addr);
308   rv = getsockname(sockfd, (struct sockaddr *)&qs->local_addr,
309                    &qs->local_addrlen);
310   if(rv == -1)
311     return CURLE_QUIC_CONNECT_ERROR;
312
313   qs->conn = quiche_conn_new_with_tls((const uint8_t *) qs->scid,
314                                       sizeof(qs->scid), NULL, 0,
315                                       (struct sockaddr *)&qs->local_addr,
316                                       qs->local_addrlen, addr, addrlen,
317                                       qs->cfg, qs->ssl, false);
318   if(!qs->conn) {
319     failf(data, "can't create quiche connection");
320     return CURLE_OUT_OF_MEMORY;
321   }
322
323   /* Known to not work on Windows */
324 #if !defined(WIN32) && defined(HAVE_QUICHE_CONN_SET_QLOG_FD)
325   {
326     int qfd;
327     (void)Curl_qlogdir(data, qs->scid, sizeof(qs->scid), &qfd);
328     if(qfd != -1)
329       quiche_conn_set_qlog_fd(qs->conn, qfd,
330                               "qlog title", "curl qlog");
331   }
332 #endif
333
334   result = flush_egress(data, sockfd, qs);
335   if(result)
336     return result;
337
338   /* extract the used address as a string */
339   if(!Curl_addr2string((struct sockaddr*)addr, addrlen, ipbuf, &port)) {
340     char buffer[STRERROR_LEN];
341     failf(data, "ssrem inet_ntop() failed with errno %d: %s",
342           SOCKERRNO, Curl_strerror(SOCKERRNO, buffer, sizeof(buffer)));
343     return CURLE_BAD_FUNCTION_ARGUMENT;
344   }
345
346   infof(data, "Connect socket %d over QUIC to %s:%ld",
347         sockfd, ipbuf, port);
348
349   Curl_persistconninfo(data, conn, NULL, -1);
350
351   /* for connection reuse purposes: */
352   conn->ssl[FIRSTSOCKET].state = ssl_connection_complete;
353
354   {
355     unsigned char alpn_protocols[] = QUICHE_H3_APPLICATION_PROTOCOL;
356     unsigned alpn_len, offset = 0;
357
358     /* Replace each ALPN length prefix by a comma. */
359     while(offset < sizeof(alpn_protocols) - 1) {
360       alpn_len = alpn_protocols[offset];
361       alpn_protocols[offset] = ',';
362       offset += 1 + alpn_len;
363     }
364
365     infof(data, "Sent QUIC client Initial, ALPN: %s",
366           alpn_protocols + 1);
367   }
368
369   return CURLE_OK;
370 }
371
372 static CURLcode quiche_has_connected(struct Curl_easy *data,
373                                      struct connectdata *conn,
374                                      int sockindex,
375                                      int tempindex)
376 {
377   CURLcode result;
378   struct quicsocket *qs = conn->quic = &conn->hequic[tempindex];
379
380   conn->recv[sockindex] = h3_stream_recv;
381   conn->send[sockindex] = h3_stream_send;
382   conn->handler = &Curl_handler_http3;
383   conn->bits.multiplex = TRUE; /* at least potentially multiplexed */
384   conn->httpversion = 30;
385   conn->bundle->multiuse = BUNDLE_MULTIPLEX;
386
387   if(conn->ssl_config.verifyhost) {
388     X509 *server_cert;
389     server_cert = SSL_get_peer_certificate(qs->ssl);
390     if(!server_cert) {
391       return CURLE_PEER_FAILED_VERIFICATION;
392     }
393     result = Curl_ossl_verifyhost(data, conn, server_cert);
394     X509_free(server_cert);
395     if(result)
396       return result;
397     infof(data, "Verified certificate just fine");
398   }
399   else
400     infof(data, "Skipped certificate verification");
401
402   qs->h3config = quiche_h3_config_new();
403   if(!qs->h3config)
404     return CURLE_OUT_OF_MEMORY;
405
406   /* Create a new HTTP/3 connection on the QUIC connection. */
407   qs->h3c = quiche_h3_conn_new_with_transport(qs->conn, qs->h3config);
408   if(!qs->h3c) {
409     result = CURLE_OUT_OF_MEMORY;
410     goto fail;
411   }
412   if(conn->hequic[1-tempindex].cfg) {
413     qs = &conn->hequic[1-tempindex];
414     quiche_config_free(qs->cfg);
415     quiche_conn_free(qs->conn);
416     qs->cfg = NULL;
417     qs->conn = NULL;
418   }
419   if(data->set.ssl.certinfo)
420     /* asked to gather certificate info */
421     (void)Curl_ossl_certchain(data, qs->ssl);
422
423   return CURLE_OK;
424   fail:
425   quiche_h3_config_free(qs->h3config);
426   quiche_h3_conn_free(qs->h3c);
427   return result;
428 }
429
430 /*
431  * This function gets polled to check if this QUIC connection has connected.
432  */
433 CURLcode Curl_quic_is_connected(struct Curl_easy *data,
434                                 struct connectdata *conn,
435                                 int sockindex,
436                                 bool *done)
437 {
438   CURLcode result;
439   struct quicsocket *qs = &conn->hequic[sockindex];
440   curl_socket_t sockfd = conn->tempsock[sockindex];
441
442   result = process_ingress(data, sockfd, qs);
443   if(result)
444     goto error;
445
446   result = flush_egress(data, sockfd, qs);
447   if(result)
448     goto error;
449
450   if(quiche_conn_is_established(qs->conn)) {
451     *done = TRUE;
452     result = quiche_has_connected(data, conn, 0, sockindex);
453     DEBUGF(infof(data, "quiche established connection"));
454   }
455
456   return result;
457   error:
458   qs_disconnect(data, qs);
459   return result;
460 }
461
462 static CURLcode process_ingress(struct Curl_easy *data, int sockfd,
463                                 struct quicsocket *qs)
464 {
465   ssize_t recvd;
466   uint8_t *buf = (uint8_t *)data->state.buffer;
467   size_t bufsize = data->set.buffer_size;
468   struct sockaddr_storage from;
469   socklen_t from_len;
470   quiche_recv_info recv_info;
471
472   DEBUGASSERT(qs->conn);
473
474   /* in case the timeout expired */
475   quiche_conn_on_timeout(qs->conn);
476
477   do {
478     from_len = sizeof(from);
479
480     recvd = recvfrom(sockfd, buf, bufsize, 0,
481                      (struct sockaddr *)&from, &from_len);
482
483     if((recvd < 0) && ((SOCKERRNO == EAGAIN) || (SOCKERRNO == EWOULDBLOCK)))
484       break;
485
486     if(recvd < 0) {
487       failf(data, "quiche: recvfrom() unexpectedly returned %zd "
488             "(errno: %d, socket %d)", recvd, SOCKERRNO, sockfd);
489       return CURLE_RECV_ERROR;
490     }
491
492     recv_info.from = (struct sockaddr *) &from;
493     recv_info.from_len = from_len;
494     recv_info.to = (struct sockaddr *) &qs->local_addr;
495     recv_info.to_len = qs->local_addrlen;
496
497     recvd = quiche_conn_recv(qs->conn, buf, recvd, &recv_info);
498     if(recvd == QUICHE_ERR_DONE)
499       break;
500
501     if(recvd < 0) {
502       if(QUICHE_ERR_TLS_FAIL == recvd) {
503         long verify_ok = SSL_get_verify_result(qs->ssl);
504         if(verify_ok != X509_V_OK) {
505           failf(data, "SSL certificate problem: %s",
506                 X509_verify_cert_error_string(verify_ok));
507
508           return CURLE_PEER_FAILED_VERIFICATION;
509         }
510       }
511
512       failf(data, "quiche_conn_recv() == %zd", recvd);
513
514       return CURLE_RECV_ERROR;
515     }
516   } while(1);
517
518   return CURLE_OK;
519 }
520
521 /*
522  * flush_egress drains the buffers and sends off data.
523  * Calls failf() on errors.
524  */
525 static CURLcode flush_egress(struct Curl_easy *data, int sockfd,
526                              struct quicsocket *qs)
527 {
528   ssize_t sent;
529   uint8_t out[1200];
530   int64_t timeout_ns;
531   quiche_send_info send_info;
532
533   do {
534     sent = quiche_conn_send(qs->conn, out, sizeof(out), &send_info);
535     if(sent == QUICHE_ERR_DONE)
536       break;
537
538     if(sent < 0) {
539       failf(data, "quiche_conn_send returned %zd", sent);
540       return CURLE_SEND_ERROR;
541     }
542
543     sent = send(sockfd, out, sent, 0);
544     if(sent < 0) {
545       failf(data, "send() returned %zd", sent);
546       return CURLE_SEND_ERROR;
547     }
548   } while(1);
549
550   /* time until the next timeout event, as nanoseconds. */
551   timeout_ns = quiche_conn_timeout_as_nanos(qs->conn);
552   if(timeout_ns)
553     /* expire uses milliseconds */
554     Curl_expire(data, (timeout_ns + 999999) / 1000000, EXPIRE_QUIC);
555
556   return CURLE_OK;
557 }
558
559 struct h3h1header {
560   char *dest;
561   size_t destlen; /* left to use */
562   size_t nlen; /* used */
563 };
564
565 static int cb_each_header(uint8_t *name, size_t name_len,
566                           uint8_t *value, size_t value_len,
567                           void *argp)
568 {
569   struct h3h1header *headers = (struct h3h1header *)argp;
570   size_t olen = 0;
571
572   if((name_len == 7) && !strncmp(H2H3_PSEUDO_STATUS, (char *)name, 7)) {
573     msnprintf(headers->dest,
574               headers->destlen, "HTTP/3 %.*s\n",
575               (int) value_len, value);
576   }
577   else if(!headers->nlen) {
578     return CURLE_HTTP3;
579   }
580   else {
581     msnprintf(headers->dest,
582               headers->destlen, "%.*s: %.*s\n",
583               (int)name_len, name, (int) value_len, value);
584   }
585   olen = strlen(headers->dest);
586   headers->destlen -= olen;
587   headers->nlen += olen;
588   headers->dest += olen;
589   return 0;
590 }
591
592 static ssize_t h3_stream_recv(struct Curl_easy *data,
593                               int sockindex,
594                               char *buf,
595                               size_t buffersize,
596                               CURLcode *curlcode)
597 {
598   ssize_t recvd = -1;
599   ssize_t rcode;
600   struct connectdata *conn = data->conn;
601   struct quicsocket *qs = conn->quic;
602   curl_socket_t sockfd = conn->sock[sockindex];
603   quiche_h3_event *ev;
604   int rc;
605   struct h3h1header headers;
606   struct HTTP *stream = data->req.p.http;
607   headers.dest = buf;
608   headers.destlen = buffersize;
609   headers.nlen = 0;
610
611   if(process_ingress(data, sockfd, qs)) {
612     infof(data, "h3_stream_recv returns on ingress");
613     *curlcode = CURLE_RECV_ERROR;
614     return -1;
615   }
616
617   if(qs->h3_recving) {
618     /* body receiving state */
619     rcode = quiche_h3_recv_body(qs->h3c, qs->conn, stream->stream3_id,
620                                 (unsigned char *)buf, buffersize);
621     if(rcode <= 0) {
622       recvd = -1;
623       qs->h3_recving = FALSE;
624       /* fall through into the while loop below */
625     }
626     else
627       recvd = rcode;
628   }
629
630   while(recvd < 0) {
631     int64_t s = quiche_h3_conn_poll(qs->h3c, qs->conn, &ev);
632     if(s < 0)
633       /* nothing more to do */
634       break;
635
636     if(s != stream->stream3_id) {
637       /* another transfer, ignore for now */
638       infof(data, "Got h3 for stream %u, expects %u",
639             s, stream->stream3_id);
640       continue;
641     }
642
643     switch(quiche_h3_event_type(ev)) {
644     case QUICHE_H3_EVENT_HEADERS:
645       rc = quiche_h3_event_for_each_header(ev, cb_each_header, &headers);
646       if(rc) {
647         *curlcode = rc;
648         failf(data, "Error in HTTP/3 response header");
649         break;
650       }
651       recvd = headers.nlen;
652       break;
653     case QUICHE_H3_EVENT_DATA:
654       if(!stream->firstbody) {
655         /* add a header-body separator CRLF */
656         buf[0] = '\r';
657         buf[1] = '\n';
658         buf += 2;
659         buffersize -= 2;
660         stream->firstbody = TRUE;
661         recvd = 2; /* two bytes already */
662       }
663       else
664         recvd = 0;
665       rcode = quiche_h3_recv_body(qs->h3c, qs->conn, s, (unsigned char *)buf,
666                                   buffersize);
667       if(rcode <= 0) {
668         recvd = -1;
669         break;
670       }
671       qs->h3_recving = TRUE;
672       recvd += rcode;
673       break;
674
675     case QUICHE_H3_EVENT_RESET:
676       streamclose(conn, "Stream reset");
677       *curlcode = CURLE_PARTIAL_FILE;
678       return -1;
679
680     case QUICHE_H3_EVENT_FINISHED:
681       streamclose(conn, "End of stream");
682       recvd = 0; /* end of stream */
683       break;
684     default:
685       break;
686     }
687
688     quiche_h3_event_free(ev);
689   }
690   if(flush_egress(data, sockfd, qs)) {
691     *curlcode = CURLE_SEND_ERROR;
692     return -1;
693   }
694
695   *curlcode = (-1 == recvd)? CURLE_AGAIN : CURLE_OK;
696   if(recvd >= 0)
697     /* Get this called again to drain the event queue */
698     Curl_expire(data, 0, EXPIRE_QUIC);
699
700   data->state.drain = (recvd >= 0) ? 1 : 0;
701   return recvd;
702 }
703
704 static ssize_t h3_stream_send(struct Curl_easy *data,
705                               int sockindex,
706                               const void *mem,
707                               size_t len,
708                               CURLcode *curlcode)
709 {
710   ssize_t sent;
711   struct connectdata *conn = data->conn;
712   struct quicsocket *qs = conn->quic;
713   curl_socket_t sockfd = conn->sock[sockindex];
714   struct HTTP *stream = data->req.p.http;
715
716   if(!stream->h3req) {
717     CURLcode result = http_request(data, mem, len);
718     if(result) {
719       *curlcode = CURLE_SEND_ERROR;
720       return -1;
721     }
722     sent = len;
723   }
724   else {
725     sent = quiche_h3_send_body(qs->h3c, qs->conn, stream->stream3_id,
726                                (uint8_t *)mem, len, FALSE);
727     if(sent == QUICHE_H3_ERR_DONE) {
728       sent = 0;
729     }
730     else if(sent < 0) {
731       *curlcode = CURLE_SEND_ERROR;
732       return -1;
733     }
734   }
735
736   if(flush_egress(data, sockfd, qs)) {
737     *curlcode = CURLE_SEND_ERROR;
738     return -1;
739   }
740
741   *curlcode = CURLE_OK;
742   return sent;
743 }
744
745 /*
746  * Store quiche version info in this buffer.
747  */
748 void Curl_quic_ver(char *p, size_t len)
749 {
750   (void)msnprintf(p, len, "quiche/%s", quiche_version());
751 }
752
753 /* Index where :authority header field will appear in request header
754    field list. */
755 #define AUTHORITY_DST_IDX 3
756
757 static CURLcode http_request(struct Curl_easy *data, const void *mem,
758                              size_t len)
759 {
760   struct connectdata *conn = data->conn;
761   struct HTTP *stream = data->req.p.http;
762   size_t nheader;
763   int64_t stream3_id;
764   quiche_h3_header *nva = NULL;
765   struct quicsocket *qs = conn->quic;
766   CURLcode result = CURLE_OK;
767   struct h2h3req *hreq = NULL;
768
769   stream->h3req = TRUE; /* senf off! */
770
771   result = Curl_pseudo_headers(data, mem, len, &hreq);
772   if(result)
773     goto fail;
774   nheader = hreq->entries;
775
776   nva = malloc(sizeof(quiche_h3_header) * nheader);
777   if(!nva) {
778     result = CURLE_OUT_OF_MEMORY;
779     goto fail;
780   }
781   else {
782     unsigned int i;
783     for(i = 0; i < nheader; i++) {
784       nva[i].name = (unsigned char *)hreq->header[i].name;
785       nva[i].name_len = hreq->header[i].namelen;
786       nva[i].value = (unsigned char *)hreq->header[i].value;
787       nva[i].value_len = hreq->header[i].valuelen;
788     }
789   }
790
791   switch(data->state.httpreq) {
792   case HTTPREQ_POST:
793   case HTTPREQ_POST_FORM:
794   case HTTPREQ_POST_MIME:
795   case HTTPREQ_PUT:
796     if(data->state.infilesize != -1)
797       stream->upload_left = data->state.infilesize;
798     else
799       /* data sending without specifying the data amount up front */
800       stream->upload_left = -1; /* unknown, but not zero */
801
802     stream3_id = quiche_h3_send_request(qs->h3c, qs->conn, nva, nheader,
803                                         stream->upload_left ? FALSE: TRUE);
804     if((stream3_id >= 0) && data->set.postfields) {
805       ssize_t sent = quiche_h3_send_body(qs->h3c, qs->conn, stream3_id,
806                                          (uint8_t *)data->set.postfields,
807                                          stream->upload_left, TRUE);
808       if(sent <= 0) {
809         failf(data, "quiche_h3_send_body failed");
810         result = CURLE_SEND_ERROR;
811       }
812       stream->upload_left = 0; /* nothing left to send */
813     }
814     break;
815   default:
816     stream3_id = quiche_h3_send_request(qs->h3c, qs->conn, nva, nheader,
817                                         TRUE);
818     break;
819   }
820
821   Curl_safefree(nva);
822
823   if(stream3_id < 0) {
824     H3BUGF(infof(data, "quiche_h3_send_request returned %d",
825                  stream3_id));
826     result = CURLE_SEND_ERROR;
827     goto fail;
828   }
829
830   infof(data, "Using HTTP/3 Stream ID: %x (easy handle %p)",
831         stream3_id, (void *)data);
832   stream->stream3_id = stream3_id;
833
834   Curl_pseudo_free(hreq);
835   return CURLE_OK;
836
837 fail:
838   free(nva);
839   Curl_pseudo_free(hreq);
840   return result;
841 }
842
843 /*
844  * Called from transfer.c:done_sending when we stop HTTP/3 uploading.
845  */
846 CURLcode Curl_quic_done_sending(struct Curl_easy *data)
847 {
848   struct connectdata *conn = data->conn;
849   DEBUGASSERT(conn);
850   if(conn->handler == &Curl_handler_http3) {
851     /* only for HTTP/3 transfers */
852     ssize_t sent;
853     struct HTTP *stream = data->req.p.http;
854     struct quicsocket *qs = conn->quic;
855     stream->upload_done = TRUE;
856     sent = quiche_h3_send_body(qs->h3c, qs->conn, stream->stream3_id,
857                                NULL, 0, TRUE);
858     if(sent < 0)
859       return CURLE_SEND_ERROR;
860   }
861
862   return CURLE_OK;
863 }
864
865 /*
866  * Called from http.c:Curl_http_done when a request completes.
867  */
868 void Curl_quic_done(struct Curl_easy *data, bool premature)
869 {
870   (void)data;
871   (void)premature;
872 }
873
874 /*
875  * Called from transfer.c:data_pending to know if we should keep looping
876  * to receive more data from the connection.
877  */
878 bool Curl_quic_data_pending(const struct Curl_easy *data)
879 {
880   (void)data;
881   return FALSE;
882 }
883
884 /*
885  * Called from transfer.c:Curl_readwrite when neither HTTP level read
886  * nor write is performed. It is a good place to handle timer expiry
887  * for QUIC transport.
888  */
889 CURLcode Curl_quic_idle(struct Curl_easy *data)
890 {
891   (void)data;
892   return CURLE_OK;
893 }
894
895 #endif