• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /***************************************************************************
2  *                                  _   _ ____  _
3  *  Project                     ___| | | |  _ \| |
4  *                             / __| | | | |_) | |
5  *                            | (__| |_| |  _ <| |___
6  *                             \___|\___/|_| \_\_____|
7  *
8  * Copyright (C) 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 "cfilters.h"
33 #include "cf-socket.h"
34 #include "sendf.h"
35 #include "strdup.h"
36 #include "rand.h"
37 #include "strcase.h"
38 #include "multiif.h"
39 #include "connect.h"
40 #include "progress.h"
41 #include "strerror.h"
42 #include "vquic.h"
43 #include "vquic_int.h"
44 #include "curl_quiche.h"
45 #include "transfer.h"
46 #include "h2h3.h"
47 #include "vtls/openssl.h"
48 #include "vtls/keylog.h"
49 
50 /* The last 3 #include files should be in this order */
51 #include "curl_printf.h"
52 #include "curl_memory.h"
53 #include "memdebug.h"
54 
55 
56 #define QUIC_MAX_STREAMS (256*1024)
57 #define QUIC_MAX_DATA (1*1024*1024)
58 #define QUIC_IDLE_TIMEOUT (60 * 1000) /* milliseconds */
59 
60 /* how many UDP packets to send max in one call */
61 #define MAX_PKT_BURST 10
62 #define MAX_UDP_PAYLOAD_SIZE  1452
63 
64 /*
65  * Store quiche version info in this buffer.
66  */
Curl_quiche_ver(char * p,size_t len)67 void Curl_quiche_ver(char *p, size_t len)
68 {
69   (void)msnprintf(p, len, "quiche/%s", quiche_version());
70 }
71 
keylog_callback(const SSL * ssl,const char * line)72 static void keylog_callback(const SSL *ssl, const char *line)
73 {
74   (void)ssl;
75   Curl_tls_keylog_write_line(line);
76 }
77 
quic_ssl_ctx(struct Curl_easy * data)78 static SSL_CTX *quic_ssl_ctx(struct Curl_easy *data)
79 {
80   SSL_CTX *ssl_ctx = SSL_CTX_new(TLS_method());
81 
82   SSL_CTX_set_alpn_protos(ssl_ctx,
83                           (const uint8_t *)QUICHE_H3_APPLICATION_PROTOCOL,
84                           sizeof(QUICHE_H3_APPLICATION_PROTOCOL) - 1);
85 
86   SSL_CTX_set_default_verify_paths(ssl_ctx);
87 
88   /* Open the file if a TLS or QUIC backend has not done this before. */
89   Curl_tls_keylog_open();
90   if(Curl_tls_keylog_enabled()) {
91     SSL_CTX_set_keylog_callback(ssl_ctx, keylog_callback);
92   }
93 
94   {
95     struct connectdata *conn = data->conn;
96     if(conn->ssl_config.verifypeer) {
97       const char * const ssl_cafile = conn->ssl_config.CAfile;
98       const char * const ssl_capath = conn->ssl_config.CApath;
99       if(ssl_cafile || ssl_capath) {
100         SSL_CTX_set_verify(ssl_ctx, SSL_VERIFY_PEER, NULL);
101         /* tell OpenSSL where to find CA certificates that are used to verify
102            the server's certificate. */
103         if(!SSL_CTX_load_verify_locations(ssl_ctx, ssl_cafile, ssl_capath)) {
104           /* Fail if we insist on successfully verifying the server. */
105           failf(data, "error setting certificate verify locations:"
106                 "  CAfile: %s CApath: %s",
107                 ssl_cafile ? ssl_cafile : "none",
108                 ssl_capath ? ssl_capath : "none");
109           return NULL;
110         }
111         infof(data, " CAfile: %s", ssl_cafile ? ssl_cafile : "none");
112         infof(data, " CApath: %s", ssl_capath ? ssl_capath : "none");
113       }
114 #ifdef CURL_CA_FALLBACK
115       else {
116         /* verifying the peer without any CA certificates won't work so
117            use openssl's built-in default as fallback */
118         SSL_CTX_set_default_verify_paths(ssl_ctx);
119       }
120 #endif
121     }
122   }
123   return ssl_ctx;
124 }
125 
126 struct quic_handshake {
127   char *buf;       /* pointer to the buffer */
128   size_t alloclen; /* size of allocation */
129   size_t len;      /* size of content in buffer */
130   size_t nread;    /* how many bytes have been read */
131 };
132 
133 struct h3_event_node {
134   struct h3_event_node *next;
135   quiche_h3_event *ev;
136 };
137 
138 struct cf_quiche_ctx {
139   struct cf_quic_ctx q;
140   quiche_conn *qconn;
141   quiche_config *cfg;
142   quiche_h3_conn *h3c;
143   quiche_h3_config *h3config;
144   uint8_t scid[QUICHE_MAX_CONN_ID_LEN];
145   SSL_CTX *sslctx;
146   SSL *ssl;
147   struct curltime started_at;        /* time the current attempt started */
148   struct curltime handshake_at;      /* time connect handshake finished */
149   struct curltime first_byte_at;     /* when first byte was recvd */
150   struct curltime reconnect_at;      /* time the next attempt should start */
151   BIT(goaway);                       /* got GOAWAY from server */
152   BIT(got_first_byte);               /* if first byte was received */
153 };
154 
155 
156 #ifdef DEBUG_QUICHE
quiche_debug_log(const char * line,void * argp)157 static void quiche_debug_log(const char *line, void *argp)
158 {
159   (void)argp;
160   fprintf(stderr, "%s\n", line);
161 }
162 #endif
163 
h3_clear_pending(struct Curl_easy * data)164 static void h3_clear_pending(struct Curl_easy *data)
165 {
166   struct HTTP *stream = data->req.p.http;
167 
168   if(stream->pending) {
169     struct h3_event_node *node, *next;
170     for(node = stream->pending; node; node = next) {
171       next = node->next;
172       quiche_h3_event_free(node->ev);
173       free(node);
174     }
175     stream->pending = NULL;
176   }
177 }
178 
cf_quiche_ctx_clear(struct cf_quiche_ctx * ctx)179 static void cf_quiche_ctx_clear(struct cf_quiche_ctx *ctx)
180 {
181   if(ctx) {
182     vquic_ctx_free(&ctx->q);
183     if(ctx->qconn)
184       quiche_conn_free(ctx->qconn);
185     if(ctx->h3config)
186       quiche_h3_config_free(ctx->h3config);
187     if(ctx->h3c)
188       quiche_h3_conn_free(ctx->h3c);
189     if(ctx->cfg)
190       quiche_config_free(ctx->cfg);
191     memset(ctx, 0, sizeof(*ctx));
192   }
193 }
194 
notify_drain(struct Curl_cfilter * cf,struct Curl_easy * data)195 static void notify_drain(struct Curl_cfilter *cf,
196                          struct Curl_easy *data)
197 {
198   (void)cf;
199   data->state.drain = 1;
200   Curl_expire(data, 0, EXPIRE_RUN_NOW);
201 }
202 
h3_add_event(struct Curl_cfilter * cf,struct Curl_easy * data,int64_t stream3_id,quiche_h3_event * ev)203 static CURLcode h3_add_event(struct Curl_cfilter *cf,
204                              struct Curl_easy *data,
205                              int64_t stream3_id, quiche_h3_event *ev)
206 {
207   struct Curl_easy *mdata;
208   struct h3_event_node *node, **pnext;
209 
210   DEBUGASSERT(data->multi);
211   for(mdata = data->multi->easyp; mdata; mdata = mdata->next) {
212     if(mdata->req.p.http && mdata->req.p.http->stream3_id == stream3_id) {
213       break;
214     }
215   }
216 
217   if(!mdata) {
218     DEBUGF(LOG_CF(data, cf, "[h3sid=%"PRId64"] event discarded, easy handle "
219                   "not found", stream3_id));
220     quiche_h3_event_free(ev);
221     return CURLE_OK;
222   }
223 
224   node = calloc(sizeof(*node), 1);
225   if(!node) {
226     quiche_h3_event_free(ev);
227     return CURLE_OUT_OF_MEMORY;
228   }
229   node->ev = ev;
230   /* append to process them in order of arrival */
231   pnext = &mdata->req.p.http->pending;
232   while(*pnext) {
233     pnext = &((*pnext)->next);
234   }
235   *pnext = node;
236   notify_drain(cf, mdata);
237   return CURLE_OK;
238 }
239 
240 struct h3h1header {
241   char *dest;
242   size_t destlen; /* left to use */
243   size_t nlen; /* used */
244 };
245 
cb_each_header(uint8_t * name,size_t name_len,uint8_t * value,size_t value_len,void * argp)246 static int cb_each_header(uint8_t *name, size_t name_len,
247                           uint8_t *value, size_t value_len,
248                           void *argp)
249 {
250   struct h3h1header *headers = (struct h3h1header *)argp;
251   size_t olen = 0;
252 
253   if((name_len == 7) && !strncmp(H2H3_PSEUDO_STATUS, (char *)name, 7)) {
254     msnprintf(headers->dest,
255               headers->destlen, "HTTP/3 %.*s \r\n",
256               (int) value_len, value);
257   }
258   else if(!headers->nlen) {
259     return CURLE_HTTP3;
260   }
261   else {
262     msnprintf(headers->dest,
263               headers->destlen, "%.*s: %.*s\r\n",
264               (int)name_len, name, (int) value_len, value);
265   }
266   olen = strlen(headers->dest);
267   headers->destlen -= olen;
268   headers->nlen += olen;
269   headers->dest += olen;
270   return 0;
271 }
272 
cf_recv_body(struct Curl_cfilter * cf,struct Curl_easy * data,char * buf,size_t len,CURLcode * err)273 static ssize_t cf_recv_body(struct Curl_cfilter *cf,
274                                 struct Curl_easy *data,
275                                 char *buf, size_t len,
276                                 CURLcode *err)
277 {
278   struct cf_quiche_ctx *ctx = cf->ctx;
279   struct HTTP *stream = data->req.p.http;
280   ssize_t nread;
281   size_t offset = 0;
282 
283   if(!stream->firstbody) {
284     /* add a header-body separator CRLF */
285     offset = 2;
286   }
287   nread = quiche_h3_recv_body(ctx->h3c, ctx->qconn, stream->stream3_id,
288                               (unsigned char *)buf + offset, len - offset);
289   if(nread >= 0) {
290     DEBUGF(LOG_CF(data, cf, "[h3sid=%"PRId64"][DATA] len=%zd",
291                   stream->stream3_id, nread));
292     if(!stream->firstbody) {
293       stream->firstbody = TRUE;
294       buf[0] = '\r';
295       buf[1] = '\n';
296       nread += offset;
297     }
298   }
299   else if(nread == -1) {
300     *err = CURLE_AGAIN;
301     stream->h3_recving_data = FALSE;
302   }
303   else {
304     failf(data, "Error %zd in HTTP/3 response body for stream[%"PRId64"]",
305           nread, stream->stream3_id);
306     stream->closed = TRUE;
307     stream->reset = TRUE;
308     streamclose(cf->conn, "Reset of stream");
309     stream->h3_recving_data = FALSE;
310     nread = -1;
311     *err = stream->h3_got_header? CURLE_PARTIAL_FILE : CURLE_RECV_ERROR;
312   }
313   return nread;
314 }
315 
316 #ifdef DEBUGBUILD
cf_ev_name(quiche_h3_event * ev)317 static const char *cf_ev_name(quiche_h3_event *ev)
318 {
319   switch(quiche_h3_event_type(ev)) {
320   case QUICHE_H3_EVENT_HEADERS:
321     return "HEADERS";
322   case QUICHE_H3_EVENT_DATA:
323     return "DATA";
324   case QUICHE_H3_EVENT_RESET:
325     return "RESET";
326   case QUICHE_H3_EVENT_FINISHED:
327     return "FINISHED";
328   case QUICHE_H3_EVENT_GOAWAY:
329     return "GOAWAY";
330   default:
331     return "Unknown";
332   }
333 }
334 #else
335 #define cf_ev_name(x)   ""
336 #endif
337 
h3_process_event(struct Curl_cfilter * cf,struct Curl_easy * data,char * buf,size_t len,int64_t stream3_id,quiche_h3_event * ev,CURLcode * err)338 static ssize_t h3_process_event(struct Curl_cfilter *cf,
339                                 struct Curl_easy *data,
340                                 char *buf, size_t len,
341                                 int64_t stream3_id,
342                                 quiche_h3_event *ev,
343                                 CURLcode *err)
344 {
345   struct HTTP *stream = data->req.p.http;
346   ssize_t recvd = 0;
347   int rc;
348   struct h3h1header headers;
349 
350   DEBUGASSERT(stream3_id == stream->stream3_id);
351 
352   *err = CURLE_OK;
353   switch(quiche_h3_event_type(ev)) {
354   case QUICHE_H3_EVENT_HEADERS:
355     stream->h3_got_header = TRUE;
356     headers.dest = buf;
357     headers.destlen = len;
358     headers.nlen = 0;
359     rc = quiche_h3_event_for_each_header(ev, cb_each_header, &headers);
360     if(rc) {
361       failf(data, "Error %d in HTTP/3 response header for stream[%"PRId64"]",
362             rc, stream3_id);
363       *err = CURLE_RECV_ERROR;
364       recvd = -1;
365       break;
366     }
367     recvd = headers.nlen;
368     DEBUGF(LOG_CF(data, cf, "[h3sid=%"PRId64"][HEADERS] len=%zd",
369                   stream3_id, recvd));
370     break;
371 
372   case QUICHE_H3_EVENT_DATA:
373     DEBUGASSERT(!stream->closed);
374     stream->h3_recving_data = TRUE;
375     recvd = cf_recv_body(cf, data, buf, len, err);
376     if(recvd < 0) {
377       if(*err != CURLE_AGAIN)
378         return -1;
379       recvd = 0;
380     }
381     break;
382 
383   case QUICHE_H3_EVENT_RESET:
384       DEBUGF(LOG_CF(data, cf, "[h3sid=%"PRId64"][RESET]", stream3_id));
385     stream->closed = TRUE;
386     stream->reset = TRUE;
387     /* streamclose(cf->conn, "Reset of stream");*/
388     stream->h3_recving_data = FALSE;
389     break;
390 
391   case QUICHE_H3_EVENT_FINISHED:
392     DEBUGF(LOG_CF(data, cf, "[h3sid=%"PRId64"][FINISHED]", stream3_id));
393     stream->closed = TRUE;
394     /* streamclose(cf->conn, "End of stream");*/
395     stream->h3_recving_data = FALSE;
396     break;
397 
398   case QUICHE_H3_EVENT_GOAWAY:
399     DEBUGF(LOG_CF(data, cf, "[h3sid=%"PRId64"][GOAWAY]", stream3_id));
400     break;
401 
402   default:
403     DEBUGF(LOG_CF(data, cf, "[h3sid=%"PRId64"] recv, unhandled event %d",
404                   stream3_id, quiche_h3_event_type(ev)));
405     break;
406   }
407   return recvd;
408 }
409 
h3_process_pending(struct Curl_cfilter * cf,struct Curl_easy * data,char * buf,size_t len,CURLcode * err)410 static ssize_t h3_process_pending(struct Curl_cfilter *cf,
411                                   struct Curl_easy *data,
412                                   char *buf, size_t len,
413                                   CURLcode *err)
414 {
415   struct HTTP *stream = data->req.p.http;
416   struct h3_event_node *node = stream->pending, **pnext = &stream->pending;
417   ssize_t recvd = 0, erecvd;
418 
419   *err = CURLE_OK;
420   DEBUGASSERT(stream);
421   while(node && len) {
422     erecvd = h3_process_event(cf, data, buf, len,
423                               stream->stream3_id, node->ev, err);
424     quiche_h3_event_free(node->ev);
425     *pnext = node->next;
426     free(node);
427     node = *pnext;
428     if(erecvd < 0) {
429       DEBUGF(LOG_CF(data, cf, "[h3sid=%"PRId64"] process event -> %d",
430                     stream->stream3_id, *err));
431       return erecvd;
432     }
433     recvd += erecvd;
434     *err = CURLE_OK;
435     buf += erecvd;
436     len -= erecvd;
437   }
438   return recvd;
439 }
440 
cf_process_ingress(struct Curl_cfilter * cf,struct Curl_easy * data)441 static CURLcode cf_process_ingress(struct Curl_cfilter *cf,
442                                    struct Curl_easy *data)
443 {
444   struct cf_quiche_ctx *ctx = cf->ctx;
445   int64_t stream3_id = data->req.p.http? data->req.p.http->stream3_id : -1;
446   uint8_t buf[65536];
447   int bufsize = (int)sizeof(buf);
448   struct sockaddr_storage remote_addr;
449   socklen_t remote_addrlen;
450   quiche_recv_info recv_info;
451   ssize_t recvd, nread;
452   ssize_t total = 0, pkts = 0;
453 
454   DEBUGASSERT(ctx->qconn);
455 
456   /* in case the timeout expired */
457   quiche_conn_on_timeout(ctx->qconn);
458 
459   do {
460     remote_addrlen = sizeof(remote_addr);
461     while((recvd = recvfrom(ctx->q.sockfd, (char *)buf, bufsize, 0,
462                             (struct sockaddr *)&remote_addr,
463                             &remote_addrlen)) == -1 &&
464           SOCKERRNO == EINTR)
465       ;
466     if(recvd < 0) {
467       if((SOCKERRNO == EAGAIN) || (SOCKERRNO == EWOULDBLOCK)) {
468         break;
469       }
470       if(SOCKERRNO == ECONNREFUSED) {
471         const char *r_ip;
472         int r_port;
473         Curl_cf_socket_peek(cf->next, data, NULL, NULL,
474                             &r_ip, &r_port, NULL, NULL);
475         failf(data, "quiche: connection to %s:%u refused",
476               r_ip, r_port);
477         return CURLE_COULDNT_CONNECT;
478       }
479       failf(data, "quiche: recvfrom() unexpectedly returned %zd "
480             "(errno: %d, socket %d)", recvd, SOCKERRNO, ctx->q.sockfd);
481       return CURLE_RECV_ERROR;
482     }
483 
484     total += recvd;
485     ++pkts;
486     if(recvd > 0 && !ctx->got_first_byte) {
487       ctx->first_byte_at = Curl_now();
488       ctx->got_first_byte = TRUE;
489     }
490     recv_info.from = (struct sockaddr *) &remote_addr;
491     recv_info.from_len = remote_addrlen;
492     recv_info.to = (struct sockaddr *) &ctx->q.local_addr;
493     recv_info.to_len = ctx->q.local_addrlen;
494 
495     nread = quiche_conn_recv(ctx->qconn, buf, recvd, &recv_info);
496     if(nread < 0) {
497       if(QUICHE_ERR_DONE == nread) {
498         DEBUGF(LOG_CF(data, cf, "ingress, quiche is DONE"));
499         return CURLE_OK;
500       }
501       else if(QUICHE_ERR_TLS_FAIL == nread) {
502         long verify_ok = SSL_get_verify_result(ctx->ssl);
503         if(verify_ok != X509_V_OK) {
504           failf(data, "SSL certificate problem: %s",
505                 X509_verify_cert_error_string(verify_ok));
506           return CURLE_PEER_FAILED_VERIFICATION;
507         }
508       }
509       else {
510         failf(data, "quiche_conn_recv() == %zd", nread);
511         return CURLE_RECV_ERROR;
512       }
513     }
514     else if(nread < recvd) {
515       DEBUGF(LOG_CF(data, cf, "[h3sid=%" PRId64 "] ingress, quiche only "
516                     "accepted %zd/%zd bytes",
517                     stream3_id, nread, recvd));
518     }
519 
520   } while(pkts < 1000); /* arbitrary */
521 
522   DEBUGF(LOG_CF(data, cf, "[h3sid=%" PRId64 "] ingress, recvd %zd bytes "
523                 "in %zd packets", stream3_id, total, pkts));
524   return CURLE_OK;
525 }
526 
527 /*
528  * flush_egress drains the buffers and sends off data.
529  * Calls failf() on errors.
530  */
cf_flush_egress(struct Curl_cfilter * cf,struct Curl_easy * data)531 static CURLcode cf_flush_egress(struct Curl_cfilter *cf,
532                                 struct Curl_easy *data)
533 {
534   struct cf_quiche_ctx *ctx = cf->ctx;
535   int64_t stream3_id = data->req.p.http? data->req.p.http->stream3_id : -1;
536   quiche_send_info send_info;
537   ssize_t outlen, total_len = 0;
538   size_t max_udp_payload_size =
539     quiche_conn_max_send_udp_payload_size(ctx->qconn);
540   size_t gsolen = max_udp_payload_size;
541   size_t sent, pktcnt = 0;
542   CURLcode result;
543   int64_t timeout_ns;
544 
545   ctx->q.no_gso = TRUE;
546   if(ctx->q.num_blocked_pkt) {
547     result = vquic_send_blocked_pkt(cf, data, &ctx->q);
548     if(result) {
549       if(result == CURLE_AGAIN) {
550         DEBUGF(LOG_CF(data, cf, "[h3sid=%" PRId64 "] egress, still not "
551                       "able to send blocked packet", stream3_id));
552         Curl_expire(data, 1, EXPIRE_QUIC);
553         return CURLE_OK;
554       }
555       goto out;
556     }
557   }
558 
559   for(;;) {
560     outlen = quiche_conn_send(ctx->qconn, ctx->q.pktbuf, max_udp_payload_size,
561                               &send_info);
562     if(outlen == QUICHE_ERR_DONE) {
563       result = CURLE_OK;
564       goto out;
565     }
566 
567     if(outlen < 0) {
568       failf(data, "quiche_conn_send returned %zd", outlen);
569       result = CURLE_SEND_ERROR;
570       goto out;
571     }
572 
573     /* send the pktbuf *before* the last addition */
574     result = vquic_send_packet(cf, data, &ctx->q, ctx->q.pktbuf,
575                                outlen, gsolen, &sent);
576     ++pktcnt;
577     total_len += outlen;
578     if(result) {
579       if(result == CURLE_AGAIN) {
580         /* blocked, add the pktbuf *before* and *at* the last addition
581          * separately to the blocked packages */
582         DEBUGF(LOG_CF(data, cf, "[h3sid=%" PRId64 "] egress, pushing blocked "
583                       "packet with %zd bytes", stream3_id, outlen));
584         vquic_push_blocked_pkt(cf, &ctx->q, ctx->q.pktbuf, outlen, gsolen);
585         Curl_expire(data, 1, EXPIRE_QUIC);
586         return CURLE_OK;
587       }
588       goto out;
589     }
590   }
591 
592 out:
593   timeout_ns = quiche_conn_timeout_as_nanos(ctx->qconn);
594   if(timeout_ns % 1000000)
595     timeout_ns += 1000000;
596     /* expire resolution is milliseconds */
597   Curl_expire(data, (timeout_ns / 1000000), EXPIRE_QUIC);
598   if(pktcnt)
599     DEBUGF(LOG_CF(data, cf, "[h3sid=%" PRId64 "] egress, sent %zd packets "
600                   "with %zd bytes", stream3_id, pktcnt, total_len));
601   return result;
602 }
603 
recv_closed_stream(struct Curl_cfilter * cf,struct Curl_easy * data,CURLcode * err)604 static ssize_t recv_closed_stream(struct Curl_cfilter *cf,
605                                   struct Curl_easy *data,
606                                   CURLcode *err)
607 {
608   struct HTTP *stream = data->req.p.http;
609   ssize_t nread = -1;
610 
611   if(stream->reset) {
612     failf(data,
613           "HTTP/3 stream %" PRId64 " reset by server", stream->stream3_id);
614     *err = stream->h3_got_header? CURLE_PARTIAL_FILE : CURLE_RECV_ERROR;
615     DEBUGF(LOG_CF(data, cf, "[h3sid=%" PRId64 "] cf_recv, was reset -> %d",
616                   stream->stream3_id, *err));
617     goto out;
618   }
619 
620   if(!stream->h3_got_header) {
621     failf(data,
622           "HTTP/3 stream %" PRId64 " was closed cleanly, but before getting"
623           " all response header fields, treated as error",
624           stream->stream3_id);
625     /* *err = CURLE_PARTIAL_FILE; */
626     *err = CURLE_RECV_ERROR;
627     DEBUGF(LOG_CF(data, cf, "[h3sid=%" PRId64 "] cf_recv, closed incomplete"
628                   " -> %d", stream->stream3_id, *err));
629     goto out;
630   }
631   else {
632     DEBUGF(LOG_CF(data, cf, "[h3sid=%" PRId64 "] cf_recv, closed ok"
633                   " -> %d", stream->stream3_id, *err));
634   }
635   *err = CURLE_OK;
636   nread = 0;
637 
638 out:
639   return nread;
640 }
641 
cf_poll_events(struct Curl_cfilter * cf,struct Curl_easy * data)642 static CURLcode cf_poll_events(struct Curl_cfilter *cf,
643                                struct Curl_easy *data)
644 {
645   struct cf_quiche_ctx *ctx = cf->ctx;
646   struct HTTP *stream = data->req.p.http;
647   quiche_h3_event *ev;
648 
649   /* Take in the events and distribute them to the transfers. */
650   while(1) {
651     int64_t stream3_id = quiche_h3_conn_poll(ctx->h3c, ctx->qconn, &ev);
652     if(stream3_id < 0) {
653       /* nothing more to do */
654       break;
655     }
656     DEBUGF(LOG_CF(data, cf, "[h3sid=%"PRId64"] recv, queue event %s "
657                   "for [h3sid=%"PRId64"]",
658                   stream? stream->stream3_id : -1, cf_ev_name(ev),
659                   stream3_id));
660     if(h3_add_event(cf, data, stream3_id, ev) != CURLE_OK) {
661       return CURLE_OUT_OF_MEMORY;
662     }
663   }
664   return CURLE_OK;
665 }
666 
cf_recv_transfer_data(struct Curl_cfilter * cf,struct Curl_easy * data,char * buf,size_t len,CURLcode * err)667 static ssize_t cf_recv_transfer_data(struct Curl_cfilter *cf,
668                                      struct Curl_easy *data,
669                                       char *buf, size_t len,
670                                       CURLcode *err)
671 {
672   struct HTTP *stream = data->req.p.http;
673   ssize_t recvd = -1;
674   size_t offset = 0;
675 
676   if(stream->h3_recving_data) {
677     /* try receiving body first */
678     recvd = cf_recv_body(cf, data, buf, len, err);
679     if(recvd < 0) {
680       if(*err != CURLE_AGAIN)
681         return -1;
682       recvd = 0;
683     }
684     if(recvd > 0) {
685       offset = recvd;
686     }
687   }
688 
689   if(offset < len && stream->pending) {
690     /* process any pending events for `data` first. if there are,
691      * return so the transfer can handle those. We do not want to
692      * progress ingress while events are pending here. */
693     recvd = h3_process_pending(cf, data, buf + offset, len - offset, err);
694     if(recvd < 0) {
695       if(*err != CURLE_AGAIN)
696         return -1;
697       recvd = 0;
698     }
699     if(recvd > 0) {
700       offset += recvd;
701     }
702   }
703 
704   if(offset) {
705     *err = CURLE_OK;
706     return offset;
707   }
708   *err = CURLE_AGAIN;
709   return 0;
710 }
711 
cf_quiche_recv(struct Curl_cfilter * cf,struct Curl_easy * data,char * buf,size_t len,CURLcode * err)712 static ssize_t cf_quiche_recv(struct Curl_cfilter *cf, struct Curl_easy *data,
713                               char *buf, size_t len, CURLcode *err)
714 {
715   struct HTTP *stream = data->req.p.http;
716   ssize_t recvd = -1;
717 
718   *err = CURLE_AGAIN;
719 
720   recvd = cf_recv_transfer_data(cf, data, buf, len, err);
721   if(recvd)
722     goto out;
723   if(stream->closed) {
724     recvd = recv_closed_stream(cf, data, err);
725     goto out;
726   }
727 
728   /* we did get nothing from the quiche buffers or pending events.
729    * Take in more data from the connection, any error is fatal */
730   if(cf_process_ingress(cf, data)) {
731     DEBUGF(LOG_CF(data, cf, "h3_stream_recv returns on ingress"));
732     *err = CURLE_RECV_ERROR;
733     recvd = -1;
734     goto out;
735   }
736   /* poll quiche and distribute the events to the transfers */
737   *err = cf_poll_events(cf, data);
738   if(*err) {
739     recvd = -1;
740     goto out;
741   }
742 
743   /* try to receive again for this transfer */
744   recvd = cf_recv_transfer_data(cf, data, buf, len, err);
745   if(recvd)
746     goto out;
747   if(stream->closed) {
748     recvd = recv_closed_stream(cf, data, err);
749     goto out;
750   }
751   recvd = -1;
752   *err = CURLE_AGAIN;
753   data->state.drain = 0;
754 
755 out:
756   if(cf_flush_egress(cf, data)) {
757     DEBUGF(LOG_CF(data, cf, "cf_recv, flush egress failed"));
758     *err = CURLE_SEND_ERROR;
759     return -1;
760   }
761   DEBUGF(LOG_CF(data, cf, "[h3sid=%"PRId64"] cf_recv -> %zd, err=%d",
762                 stream->stream3_id, recvd, *err));
763   if(recvd > 0)
764     notify_drain(cf, data);
765   return recvd;
766 }
767 
768 /* Index where :authority header field will appear in request header
769    field list. */
770 #define AUTHORITY_DST_IDX 3
771 
cf_http_request(struct Curl_cfilter * cf,struct Curl_easy * data,const void * mem,size_t len)772 static CURLcode cf_http_request(struct Curl_cfilter *cf,
773                                 struct Curl_easy *data,
774                                 const void *mem,
775                                 size_t len)
776 {
777   struct cf_quiche_ctx *ctx = cf->ctx;
778   struct HTTP *stream = data->req.p.http;
779   size_t nheader;
780   int64_t stream3_id;
781   quiche_h3_header *nva = NULL;
782   CURLcode result = CURLE_OK;
783   struct h2h3req *hreq = NULL;
784 
785   stream->h3req = TRUE; /* send off! */
786   stream->closed = FALSE;
787   stream->reset = FALSE;
788 
789   result = Curl_pseudo_headers(data, mem, len, NULL, &hreq);
790   if(result)
791     goto fail;
792   nheader = hreq->entries;
793 
794   nva = malloc(sizeof(quiche_h3_header) * nheader);
795   if(!nva) {
796     result = CURLE_OUT_OF_MEMORY;
797     goto fail;
798   }
799   else {
800     unsigned int i;
801     for(i = 0; i < nheader; i++) {
802       nva[i].name = (unsigned char *)hreq->header[i].name;
803       nva[i].name_len = hreq->header[i].namelen;
804       nva[i].value = (unsigned char *)hreq->header[i].value;
805       nva[i].value_len = hreq->header[i].valuelen;
806     }
807   }
808 
809   switch(data->state.httpreq) {
810   case HTTPREQ_POST:
811   case HTTPREQ_POST_FORM:
812   case HTTPREQ_POST_MIME:
813   case HTTPREQ_PUT:
814     if(data->state.infilesize != -1)
815       stream->upload_left = data->state.infilesize;
816     else
817       /* data sending without specifying the data amount up front */
818       stream->upload_left = -1; /* unknown, but not zero */
819 
820     stream->upload_done = !stream->upload_left;
821     stream3_id = quiche_h3_send_request(ctx->h3c, ctx->qconn, nva, nheader,
822                                         stream->upload_done);
823     break;
824   default:
825     stream->upload_left = 0;
826     stream->upload_done = TRUE;
827     stream3_id = quiche_h3_send_request(ctx->h3c, ctx->qconn, nva, nheader,
828                                         TRUE);
829     break;
830   }
831 
832   Curl_safefree(nva);
833 
834   if(stream3_id < 0) {
835     if(QUICHE_H3_ERR_STREAM_BLOCKED == stream3_id) {
836       DEBUGF(LOG_CF(data, cf, "send_request(%s, body_len=%ld) rejected "
837                     "with H3_ERR_STREAM_BLOCKED",
838                     data->state.url, (long)stream->upload_left));
839       result = CURLE_AGAIN;
840       goto fail;
841     }
842     else {
843       DEBUGF(LOG_CF(data, cf, "send_request(%s, body_len=%ld) -> %" PRId64,
844                     data->state.url, (long)stream->upload_left, stream3_id));
845     }
846     result = CURLE_SEND_ERROR;
847     goto fail;
848   }
849 
850   stream->stream3_id = stream3_id;
851   infof(data, "Using HTTP/3 Stream ID: %" PRId64 " (easy handle %p)",
852         stream3_id, (void *)data);
853   DEBUGF(LOG_CF(data, cf, "[h3sid=%" PRId64 "] opened for %s",
854                 stream3_id, data->state.url));
855 
856   Curl_pseudo_free(hreq);
857   return CURLE_OK;
858 
859 fail:
860   free(nva);
861   Curl_pseudo_free(hreq);
862   return result;
863 }
864 
cf_quiche_send(struct Curl_cfilter * cf,struct Curl_easy * data,const void * buf,size_t len,CURLcode * err)865 static ssize_t cf_quiche_send(struct Curl_cfilter *cf, struct Curl_easy *data,
866                               const void *buf, size_t len, CURLcode *err)
867 {
868   struct cf_quiche_ctx *ctx = cf->ctx;
869   struct HTTP *stream = data->req.p.http;
870   ssize_t nwritten;
871 
872   DEBUGF(LOG_CF(data, cf, "[h3sid=%" PRId64 "] cf_send(len=%zu) start",
873                 stream->h3req? stream->stream3_id : -1, len));
874   *err = cf_process_ingress(cf, data);
875   if(*err)
876     return -1;
877 
878   if(!stream->h3req) {
879     CURLcode result = cf_http_request(cf, data, buf, len);
880     if(result) {
881       *err = result;
882       return -1;
883     }
884     nwritten = len;
885   }
886   else {
887     nwritten = quiche_h3_send_body(ctx->h3c, ctx->qconn, stream->stream3_id,
888                                    (uint8_t *)buf, len, FALSE);
889     DEBUGF(LOG_CF(data, cf, "[h3sid=%" PRId64 "] send body(len=%zu) -> %zd",
890                   stream->stream3_id, len, nwritten));
891     if(nwritten == QUICHE_H3_ERR_DONE) {
892       /* no error, nothing to do (flow control?) */
893       *err = CURLE_AGAIN;
894       nwritten = -1;
895     }
896     else if(nwritten == QUICHE_H3_TRANSPORT_ERR_FINAL_SIZE) {
897       DEBUGF(LOG_CF(data, cf, "send_body(len=%zu) -> exceeds size", len));
898       *err = CURLE_SEND_ERROR;
899       nwritten = -1;
900     }
901     else if(nwritten < 0) {
902       DEBUGF(LOG_CF(data, cf, "send_body(len=%zu) -> SEND_ERROR", len));
903       *err = CURLE_SEND_ERROR;
904       nwritten = -1;
905     }
906     else {
907       *err = CURLE_OK;
908     }
909   }
910 
911   if(cf_flush_egress(cf, data)) {
912     *err = CURLE_SEND_ERROR;
913     return -1;
914   }
915 
916   return nwritten;
917 }
918 
stream_is_writeable(struct Curl_cfilter * cf,struct Curl_easy * data)919 static bool stream_is_writeable(struct Curl_cfilter *cf,
920                                 struct Curl_easy *data)
921 {
922   struct cf_quiche_ctx *ctx = cf->ctx;
923   struct HTTP *stream = data->req.p.http;
924 
925   /* surely, there must be a better way */
926   quiche_stream_iter *qiter = quiche_conn_writable(ctx->qconn);
927   if(qiter) {
928     uint64_t stream_id;
929     while(quiche_stream_iter_next(qiter, &stream_id)) {
930       if(stream_id == (uint64_t)stream->stream3_id)
931         return TRUE;
932     }
933     quiche_stream_iter_free(qiter);
934   }
935   return FALSE;
936 }
937 
cf_quiche_get_select_socks(struct Curl_cfilter * cf,struct Curl_easy * data,curl_socket_t * socks)938 static int cf_quiche_get_select_socks(struct Curl_cfilter *cf,
939                                       struct Curl_easy *data,
940                                       curl_socket_t *socks)
941 {
942   struct cf_quiche_ctx *ctx = cf->ctx;
943   struct SingleRequest *k = &data->req;
944   int rv = GETSOCK_BLANK;
945 
946   socks[0] = ctx->q.sockfd;
947 
948   /* in an HTTP/3 connection we can basically always get a frame so we should
949      always be ready for one */
950   rv |= GETSOCK_READSOCK(0);
951 
952   /* we're still uploading or the HTTP/3 layer wants to send data */
953   if(((k->keepon & KEEP_SENDBITS) == KEEP_SEND)
954      && stream_is_writeable(cf, data))
955     rv |= GETSOCK_WRITESOCK(0);
956 
957   return rv;
958 }
959 
960 /*
961  * Called from transfer.c:data_pending to know if we should keep looping
962  * to receive more data from the connection.
963  */
cf_quiche_data_pending(struct Curl_cfilter * cf,const struct Curl_easy * data)964 static bool cf_quiche_data_pending(struct Curl_cfilter *cf,
965                                    const struct Curl_easy *data)
966 {
967   struct HTTP *stream = data->req.p.http;
968 
969   if(stream->pending) {
970     DEBUGF(LOG_CF((struct Curl_easy *)data, cf,
971                    "[h3sid=%"PRId64"] has event pending", stream->stream3_id));
972     return TRUE;
973   }
974   if(stream->h3_recving_data) {
975     DEBUGF(LOG_CF((struct Curl_easy *)data, cf,
976                    "[h3sid=%"PRId64"] is receiving DATA", stream->stream3_id));
977     return TRUE;
978   }
979   if(data->state.drain) {
980     DEBUGF(LOG_CF((struct Curl_easy *)data, cf,
981                    "[h3sid=%"PRId64"] is draining", stream->stream3_id));
982     return TRUE;
983   }
984   return FALSE;
985 }
986 
cf_quiche_data_event(struct Curl_cfilter * cf,struct Curl_easy * data,int event,int arg1,void * arg2)987 static CURLcode cf_quiche_data_event(struct Curl_cfilter *cf,
988                                      struct Curl_easy *data,
989                                      int event, int arg1, void *arg2)
990 {
991   struct cf_quiche_ctx *ctx = cf->ctx;
992   CURLcode result = CURLE_OK;
993 
994   (void)arg1;
995   (void)arg2;
996   switch(event) {
997   case CF_CTRL_DATA_DONE: {
998     struct HTTP *stream = data->req.p.http;
999     DEBUGF(LOG_CF(data, cf, "[h3sid=%"PRId64"] easy handle is %s",
1000                   stream->stream3_id, arg1? "cancelled" : "done"));
1001     h3_clear_pending(data);
1002     break;
1003   }
1004   case CF_CTRL_DATA_DONE_SEND: {
1005     struct HTTP *stream = data->req.p.http;
1006     ssize_t sent;
1007     stream->upload_done = TRUE;
1008     sent = quiche_h3_send_body(ctx->h3c, ctx->qconn, stream->stream3_id,
1009                                NULL, 0, TRUE);
1010     DEBUGF(LOG_CF(data, cf, "[h3sid=%"PRId64"] send_body FINISHED",
1011                   stream->stream3_id));
1012     if(sent < 0)
1013       return CURLE_SEND_ERROR;
1014     break;
1015   }
1016   case CF_CTRL_DATA_IDLE:
1017     /* anything to do? */
1018     break;
1019   default:
1020     break;
1021   }
1022   return result;
1023 }
1024 
cf_verify_peer(struct Curl_cfilter * cf,struct Curl_easy * data)1025 static CURLcode cf_verify_peer(struct Curl_cfilter *cf,
1026                                struct Curl_easy *data)
1027 {
1028   struct cf_quiche_ctx *ctx = cf->ctx;
1029   CURLcode result = CURLE_OK;
1030 
1031   cf->conn->bits.multiplex = TRUE; /* at least potentially multiplexed */
1032   cf->conn->httpversion = 30;
1033   cf->conn->bundle->multiuse = BUNDLE_MULTIPLEX;
1034 
1035   if(cf->conn->ssl_config.verifyhost) {
1036     X509 *server_cert;
1037     server_cert = SSL_get_peer_certificate(ctx->ssl);
1038     if(!server_cert) {
1039       result = CURLE_PEER_FAILED_VERIFICATION;
1040       goto out;
1041     }
1042     result = Curl_ossl_verifyhost(data, cf->conn, server_cert);
1043     X509_free(server_cert);
1044     if(result)
1045       goto out;
1046   }
1047   else
1048     DEBUGF(LOG_CF(data, cf, "Skipped certificate verification"));
1049 
1050   ctx->h3config = quiche_h3_config_new();
1051   if(!ctx->h3config) {
1052     result = CURLE_OUT_OF_MEMORY;
1053     goto out;
1054   }
1055 
1056   /* Create a new HTTP/3 connection on the QUIC connection. */
1057   ctx->h3c = quiche_h3_conn_new_with_transport(ctx->qconn, ctx->h3config);
1058   if(!ctx->h3c) {
1059     result = CURLE_OUT_OF_MEMORY;
1060     goto out;
1061   }
1062   if(data->set.ssl.certinfo)
1063     /* asked to gather certificate info */
1064     (void)Curl_ossl_certchain(data, ctx->ssl);
1065 
1066 out:
1067   if(result) {
1068     if(ctx->h3config) {
1069       quiche_h3_config_free(ctx->h3config);
1070       ctx->h3config = NULL;
1071     }
1072     if(ctx->h3c) {
1073       quiche_h3_conn_free(ctx->h3c);
1074       ctx->h3c = NULL;
1075     }
1076   }
1077   return result;
1078 }
1079 
cf_connect_start(struct Curl_cfilter * cf,struct Curl_easy * data)1080 static CURLcode cf_connect_start(struct Curl_cfilter *cf,
1081                                  struct Curl_easy *data)
1082 {
1083   struct cf_quiche_ctx *ctx = cf->ctx;
1084   int rv;
1085   CURLcode result;
1086   const struct Curl_sockaddr_ex *sockaddr;
1087 
1088   DEBUGASSERT(ctx->q.sockfd != CURL_SOCKET_BAD);
1089 
1090 #ifdef DEBUG_QUICHE
1091   /* initialize debug log callback only once */
1092   static int debug_log_init = 0;
1093   if(!debug_log_init) {
1094     quiche_enable_debug_logging(quiche_debug_log, NULL);
1095     debug_log_init = 1;
1096   }
1097 #endif
1098 
1099   result = vquic_ctx_init(&ctx->q, MAX_UDP_PAYLOAD_SIZE * MAX_PKT_BURST);
1100   if(result)
1101     return result;
1102 
1103   ctx->cfg = quiche_config_new(QUICHE_PROTOCOL_VERSION);
1104   if(!ctx->cfg) {
1105     failf(data, "can't create quiche config");
1106     return CURLE_FAILED_INIT;
1107   }
1108   quiche_config_set_max_idle_timeout(ctx->cfg, QUIC_IDLE_TIMEOUT);
1109   quiche_config_set_initial_max_data(ctx->cfg, QUIC_MAX_DATA);
1110   quiche_config_set_initial_max_stream_data_bidi_local(
1111     ctx->cfg, QUIC_MAX_DATA);
1112   quiche_config_set_initial_max_stream_data_bidi_remote(
1113     ctx->cfg, QUIC_MAX_DATA);
1114   quiche_config_set_initial_max_stream_data_uni(ctx->cfg, QUIC_MAX_DATA);
1115   quiche_config_set_initial_max_streams_bidi(ctx->cfg, QUIC_MAX_STREAMS);
1116   quiche_config_set_initial_max_streams_uni(ctx->cfg, QUIC_MAX_STREAMS);
1117   quiche_config_set_application_protos(ctx->cfg,
1118                                        (uint8_t *)
1119                                        QUICHE_H3_APPLICATION_PROTOCOL,
1120                                        sizeof(QUICHE_H3_APPLICATION_PROTOCOL)
1121                                        - 1);
1122 
1123   DEBUGASSERT(!ctx->ssl);
1124   DEBUGASSERT(!ctx->sslctx);
1125   ctx->sslctx = quic_ssl_ctx(data);
1126   if(!ctx->sslctx)
1127     return CURLE_QUIC_CONNECT_ERROR;
1128   ctx->ssl = SSL_new(ctx->sslctx);
1129   if(!ctx->ssl)
1130     return CURLE_QUIC_CONNECT_ERROR;
1131 
1132   SSL_set_app_data(ctx->ssl, cf);
1133   SSL_set_tlsext_host_name(ctx->ssl, cf->conn->host.name);
1134 
1135   result = Curl_rand(data, ctx->scid, sizeof(ctx->scid));
1136   if(result)
1137     return result;
1138 
1139   Curl_cf_socket_peek(cf->next, data, &ctx->q.sockfd,
1140                       &sockaddr, NULL, NULL, NULL, NULL);
1141   ctx->q.local_addrlen = sizeof(ctx->q.local_addr);
1142   rv = getsockname(ctx->q.sockfd, (struct sockaddr *)&ctx->q.local_addr,
1143                    &ctx->q.local_addrlen);
1144   if(rv == -1)
1145     return CURLE_QUIC_CONNECT_ERROR;
1146 
1147   ctx->qconn = quiche_conn_new_with_tls((const uint8_t *)ctx->scid,
1148                                       sizeof(ctx->scid), NULL, 0,
1149                                       (struct sockaddr *)&ctx->q.local_addr,
1150                                       ctx->q.local_addrlen,
1151                                       &sockaddr->sa_addr, sockaddr->addrlen,
1152                                       ctx->cfg, ctx->ssl, false);
1153   if(!ctx->qconn) {
1154     failf(data, "can't create quiche connection");
1155     return CURLE_OUT_OF_MEMORY;
1156   }
1157 
1158   /* Known to not work on Windows */
1159 #if !defined(WIN32) && defined(HAVE_QUICHE_CONN_SET_QLOG_FD)
1160   {
1161     int qfd;
1162     (void)Curl_qlogdir(data, ctx->scid, sizeof(ctx->scid), &qfd);
1163     if(qfd != -1)
1164       quiche_conn_set_qlog_fd(ctx->qconn, qfd,
1165                               "qlog title", "curl qlog");
1166   }
1167 #endif
1168 
1169   result = cf_flush_egress(cf, data);
1170   if(result)
1171     return result;
1172 
1173   {
1174     unsigned char alpn_protocols[] = QUICHE_H3_APPLICATION_PROTOCOL;
1175     unsigned alpn_len, offset = 0;
1176 
1177     /* Replace each ALPN length prefix by a comma. */
1178     while(offset < sizeof(alpn_protocols) - 1) {
1179       alpn_len = alpn_protocols[offset];
1180       alpn_protocols[offset] = ',';
1181       offset += 1 + alpn_len;
1182     }
1183 
1184     DEBUGF(LOG_CF(data, cf, "Sent QUIC client Initial, ALPN: %s",
1185                    alpn_protocols + 1));
1186   }
1187 
1188   return CURLE_OK;
1189 }
1190 
cf_quiche_connect(struct Curl_cfilter * cf,struct Curl_easy * data,bool blocking,bool * done)1191 static CURLcode cf_quiche_connect(struct Curl_cfilter *cf,
1192                                   struct Curl_easy *data,
1193                                   bool blocking, bool *done)
1194 {
1195   struct cf_quiche_ctx *ctx = cf->ctx;
1196   CURLcode result = CURLE_OK;
1197   struct curltime now;
1198 
1199   if(cf->connected) {
1200     *done = TRUE;
1201     return CURLE_OK;
1202   }
1203 
1204   /* Connect the UDP filter first */
1205   if(!cf->next->connected) {
1206     result = Curl_conn_cf_connect(cf->next, data, blocking, done);
1207     if(result || !*done)
1208       return result;
1209   }
1210 
1211   *done = FALSE;
1212   now = Curl_now();
1213 
1214   if(ctx->reconnect_at.tv_sec && Curl_timediff(now, ctx->reconnect_at) < 0) {
1215     /* Not time yet to attempt the next connect */
1216     DEBUGF(LOG_CF(data, cf, "waiting for reconnect time"));
1217     goto out;
1218   }
1219 
1220   if(!ctx->qconn) {
1221     result = cf_connect_start(cf, data);
1222     if(result)
1223       goto out;
1224     ctx->started_at = now;
1225     result = cf_flush_egress(cf, data);
1226     /* we do not expect to be able to recv anything yet */
1227     goto out;
1228   }
1229 
1230   result = cf_process_ingress(cf, data);
1231   if(result)
1232     goto out;
1233 
1234   result = cf_flush_egress(cf, data);
1235   if(result)
1236     goto out;
1237 
1238   if(quiche_conn_is_established(ctx->qconn)) {
1239     DEBUGF(LOG_CF(data, cf, "handshake complete after %dms",
1240            (int)Curl_timediff(now, ctx->started_at)));
1241     ctx->handshake_at = now;
1242     result = cf_verify_peer(cf, data);
1243     if(!result) {
1244       DEBUGF(LOG_CF(data, cf, "peer verified"));
1245       cf->connected = TRUE;
1246       cf->conn->alpn = CURL_HTTP_VERSION_3;
1247       *done = TRUE;
1248       connkeep(cf->conn, "HTTP/3 default");
1249     }
1250   }
1251   else if(quiche_conn_is_draining(ctx->qconn)) {
1252     /* When a QUIC server instance is shutting down, it may send us a
1253      * CONNECTION_CLOSE right away. Our connection then enters the DRAINING
1254      * state.
1255      * This may be a stopping of the service or it may be that the server
1256      * is reloading and a new instance will start serving soon.
1257      * In any case, we tear down our socket and start over with a new one.
1258      * We re-open the underlying UDP cf right now, but do not start
1259      * connecting until called again.
1260      */
1261     int reconn_delay_ms = 200;
1262 
1263     DEBUGF(LOG_CF(data, cf, "connect, remote closed, reconnect after %dms",
1264                   reconn_delay_ms));
1265     Curl_conn_cf_close(cf->next, data);
1266     cf_quiche_ctx_clear(ctx);
1267     result = Curl_conn_cf_connect(cf->next, data, FALSE, done);
1268     if(!result && *done) {
1269       *done = FALSE;
1270       ctx->reconnect_at = Curl_now();
1271       ctx->reconnect_at.tv_usec += reconn_delay_ms * 1000;
1272       Curl_expire(data, reconn_delay_ms, EXPIRE_QUIC);
1273       result = CURLE_OK;
1274     }
1275   }
1276 
1277 out:
1278 #ifndef CURL_DISABLE_VERBOSE_STRINGS
1279   if(result && result != CURLE_AGAIN) {
1280     const char *r_ip;
1281     int r_port;
1282 
1283     Curl_cf_socket_peek(cf->next, data, NULL, NULL,
1284                         &r_ip, &r_port, NULL, NULL);
1285     infof(data, "connect to %s port %u failed: %s",
1286           r_ip, r_port, curl_easy_strerror(result));
1287   }
1288 #endif
1289   return result;
1290 }
1291 
cf_quiche_close(struct Curl_cfilter * cf,struct Curl_easy * data)1292 static void cf_quiche_close(struct Curl_cfilter *cf, struct Curl_easy *data)
1293 {
1294   struct cf_quiche_ctx *ctx = cf->ctx;
1295 
1296   (void)data;
1297   if(ctx) {
1298     if(ctx->qconn) {
1299       (void)quiche_conn_close(ctx->qconn, TRUE, 0, NULL, 0);
1300       /* flushing the egress is not a failsafe way to deliver all the
1301          outstanding packets, but we also don't want to get stuck here... */
1302       (void)cf_flush_egress(cf, data);
1303     }
1304     cf_quiche_ctx_clear(ctx);
1305   }
1306 }
1307 
cf_quiche_destroy(struct Curl_cfilter * cf,struct Curl_easy * data)1308 static void cf_quiche_destroy(struct Curl_cfilter *cf, struct Curl_easy *data)
1309 {
1310   struct cf_quiche_ctx *ctx = cf->ctx;
1311 
1312   (void)data;
1313   cf_quiche_ctx_clear(ctx);
1314   free(ctx);
1315   cf->ctx = NULL;
1316 }
1317 
cf_quiche_query(struct Curl_cfilter * cf,struct Curl_easy * data,int query,int * pres1,void * pres2)1318 static CURLcode cf_quiche_query(struct Curl_cfilter *cf,
1319                                 struct Curl_easy *data,
1320                                 int query, int *pres1, void *pres2)
1321 {
1322   struct cf_quiche_ctx *ctx = cf->ctx;
1323 
1324   switch(query) {
1325   case CF_QUERY_MAX_CONCURRENT: {
1326     uint64_t max_streams = CONN_INUSE(cf->conn);
1327     if(!ctx->goaway) {
1328       max_streams += quiche_conn_peer_streams_left_bidi(ctx->qconn);
1329     }
1330     *pres1 = (max_streams > INT_MAX)? INT_MAX : (int)max_streams;
1331     DEBUGF(LOG_CF(data, cf, "query: MAX_CONCURRENT -> %d", *pres1));
1332     return CURLE_OK;
1333   }
1334   case CF_QUERY_CONNECT_REPLY_MS:
1335     if(ctx->got_first_byte) {
1336       timediff_t ms = Curl_timediff(ctx->first_byte_at, ctx->started_at);
1337       *pres1 = (ms < INT_MAX)? (int)ms : INT_MAX;
1338     }
1339     else
1340       *pres1 = -1;
1341     return CURLE_OK;
1342   case CF_QUERY_TIMER_CONNECT: {
1343     struct curltime *when = pres2;
1344     if(ctx->got_first_byte)
1345       *when = ctx->first_byte_at;
1346     return CURLE_OK;
1347   }
1348   case CF_QUERY_TIMER_APPCONNECT: {
1349     struct curltime *when = pres2;
1350     if(cf->connected)
1351       *when = ctx->handshake_at;
1352     return CURLE_OK;
1353   }
1354   default:
1355     break;
1356   }
1357   return cf->next?
1358     cf->next->cft->query(cf->next, data, query, pres1, pres2) :
1359     CURLE_UNKNOWN_OPTION;
1360 }
1361 
cf_quiche_conn_is_alive(struct Curl_cfilter * cf,struct Curl_easy * data,bool * input_pending)1362 static bool cf_quiche_conn_is_alive(struct Curl_cfilter *cf,
1363                                     struct Curl_easy *data,
1364                                     bool *input_pending)
1365 {
1366   bool alive = TRUE;
1367 
1368   *input_pending = FALSE;
1369   if(!cf->next || !cf->next->cft->is_alive(cf->next, data, input_pending))
1370     return FALSE;
1371 
1372   if(*input_pending) {
1373     /* This happens before we've sent off a request and the connection is
1374        not in use by any other transfer, there shouldn't be any data here,
1375        only "protocol frames" */
1376     *input_pending = FALSE;
1377     Curl_attach_connection(data, cf->conn);
1378     if(cf_process_ingress(cf, data))
1379       alive = FALSE;
1380     else {
1381       alive = TRUE;
1382     }
1383     Curl_detach_connection(data);
1384   }
1385 
1386   return alive;
1387 }
1388 
1389 struct Curl_cftype Curl_cft_http3 = {
1390   "HTTP/3",
1391   CF_TYPE_IP_CONNECT | CF_TYPE_SSL | CF_TYPE_MULTIPLEX,
1392   0,
1393   cf_quiche_destroy,
1394   cf_quiche_connect,
1395   cf_quiche_close,
1396   Curl_cf_def_get_host,
1397   cf_quiche_get_select_socks,
1398   cf_quiche_data_pending,
1399   cf_quiche_send,
1400   cf_quiche_recv,
1401   cf_quiche_data_event,
1402   cf_quiche_conn_is_alive,
1403   Curl_cf_def_conn_keep_alive,
1404   cf_quiche_query,
1405 };
1406 
Curl_cf_quiche_create(struct Curl_cfilter ** pcf,struct Curl_easy * data,struct connectdata * conn,const struct Curl_addrinfo * ai)1407 CURLcode Curl_cf_quiche_create(struct Curl_cfilter **pcf,
1408                                struct Curl_easy *data,
1409                                struct connectdata *conn,
1410                                const struct Curl_addrinfo *ai)
1411 {
1412   struct cf_quiche_ctx *ctx = NULL;
1413   struct Curl_cfilter *cf = NULL, *udp_cf = NULL;
1414   CURLcode result;
1415 
1416   (void)data;
1417   (void)conn;
1418   ctx = calloc(sizeof(*ctx), 1);
1419   if(!ctx) {
1420     result = CURLE_OUT_OF_MEMORY;
1421     goto out;
1422   }
1423 
1424   result = Curl_cf_create(&cf, &Curl_cft_http3, ctx);
1425   if(result)
1426     goto out;
1427 
1428   result = Curl_cf_udp_create(&udp_cf, data, conn, ai, TRNSPRT_QUIC);
1429   if(result)
1430     goto out;
1431 
1432   udp_cf->conn = cf->conn;
1433   udp_cf->sockindex = cf->sockindex;
1434   cf->next = udp_cf;
1435 
1436 out:
1437   *pcf = (!result)? cf : NULL;
1438   if(result) {
1439     if(udp_cf)
1440       Curl_conn_cf_discard(udp_cf, data);
1441     Curl_safefree(cf);
1442     Curl_safefree(ctx);
1443   }
1444 
1445   return result;
1446 }
1447 
Curl_conn_is_quiche(const struct Curl_easy * data,const struct connectdata * conn,int sockindex)1448 bool Curl_conn_is_quiche(const struct Curl_easy *data,
1449                          const struct connectdata *conn,
1450                          int sockindex)
1451 {
1452   struct Curl_cfilter *cf = conn? conn->cfilter[sockindex] : NULL;
1453 
1454   (void)data;
1455   for(; cf; cf = cf->next) {
1456     if(cf->cft == &Curl_cft_http3)
1457       return TRUE;
1458     if(cf->cft->flags & CF_TYPE_IP_CONNECT)
1459       return FALSE;
1460   }
1461   return FALSE;
1462 }
1463 
1464 #endif
1465