• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /***************************************************************************
2  *                                  _   _ ____  _
3  *  Project                     ___| | | |  _ \| |
4  *                             / __| | | | |_) | |
5  *                            | (__| |_| |  _ <| |___
6  *                             \___|\___/|_| \_\_____|
7  *
8  * Copyright (C) 1998 - 2019, 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.haxx.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_NGTCP2
26 #include <ngtcp2/ngtcp2.h>
27 #include <ngtcp2/ngtcp2_crypto.h>
28 #include <nghttp3/nghttp3.h>
29 #include <openssl/err.h>
30 #include "urldata.h"
31 #include "sendf.h"
32 #include "strdup.h"
33 #include "rand.h"
34 #include "ngtcp2.h"
35 #include "multiif.h"
36 #include "strcase.h"
37 #include "connect.h"
38 #include "strerror.h"
39 
40 /* The last 3 #include files should be in this order */
41 #include "curl_printf.h"
42 #include "curl_memory.h"
43 #include "memdebug.h"
44 
45 /* #define DEBUG_NGTCP2 */
46 #ifdef CURLDEBUG
47 #define DEBUG_HTTP3
48 #endif
49 #ifdef DEBUG_HTTP3
50 #define H3BUGF(x) x
51 #else
52 #define H3BUGF(x) do { } WHILE_FALSE
53 #endif
54 
55 /*
56  * This holds outgoing HTTP/3 stream data that is used by nghttp3 until acked.
57  * It is used as a circular buffer. Add new bytes at the end until it reaches
58  * the far end, then start over at index 0 again.
59  */
60 
61 #define H3_SEND_SIZE (20*1024)
62 struct h3out {
63   uint8_t buf[H3_SEND_SIZE];
64   size_t used;   /* number of bytes used in the buffer */
65   size_t windex; /* index in the buffer where to start writing the next
66                     data block */
67 };
68 
69 #define QUIC_MAX_STREAMS (256*1024)
70 #define QUIC_MAX_DATA (1*1024*1024)
71 #define QUIC_IDLE_TIMEOUT 60000 /* milliseconds */
72 #define QUIC_CIPHERS                                                          \
73   "TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384:TLS_CHACHA20_"               \
74   "POLY1305_SHA256:TLS_AES_128_CCM_SHA256"
75 #define QUIC_GROUPS "P-256:X25519:P-384:P-521"
76 
77 static CURLcode ng_process_ingress(struct connectdata *conn,
78                                    curl_socket_t sockfd,
79                                    struct quicsocket *qs);
80 static CURLcode ng_flush_egress(struct connectdata *conn, int sockfd,
81                                 struct quicsocket *qs);
82 static int cb_h3_acked_stream_data(nghttp3_conn *conn, int64_t stream_id,
83                                    size_t datalen, void *user_data,
84                                    void *stream_user_data);
85 
timestamp(void)86 static ngtcp2_tstamp timestamp(void)
87 {
88   struct curltime ct = Curl_now();
89   return ct.tv_sec * NGTCP2_SECONDS + ct.tv_usec * NGTCP2_MICROSECONDS;
90 }
91 
92 #ifdef DEBUG_NGTCP2
quic_printf(void * user_data,const char * fmt,...)93 static void quic_printf(void *user_data, const char *fmt, ...)
94 {
95   va_list ap;
96   (void)user_data; /* TODO, use this to do infof() instead long-term */
97   va_start(ap, fmt);
98   vfprintf(stderr, fmt, ap);
99   va_end(ap);
100   fprintf(stderr, "\n");
101 }
102 #endif
103 
104 static ngtcp2_crypto_level
quic_from_ossl_level(OSSL_ENCRYPTION_LEVEL ossl_level)105 quic_from_ossl_level(OSSL_ENCRYPTION_LEVEL ossl_level)
106 {
107   switch(ossl_level) {
108   case ssl_encryption_initial:
109     return NGTCP2_CRYPTO_LEVEL_INITIAL;
110   case ssl_encryption_early_data:
111     return NGTCP2_CRYPTO_LEVEL_EARLY;
112   case ssl_encryption_handshake:
113     return NGTCP2_CRYPTO_LEVEL_HANDSHAKE;
114   case ssl_encryption_application:
115     return NGTCP2_CRYPTO_LEVEL_APP;
116   default:
117     assert(0);
118   }
119 }
120 
setup_initial_crypto_context(struct quicsocket * qs)121 static int setup_initial_crypto_context(struct quicsocket *qs)
122 {
123   const ngtcp2_cid *dcid = ngtcp2_conn_get_dcid(qs->qconn);
124 
125   if(ngtcp2_crypto_derive_and_install_initial_key(
126          qs->qconn, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, dcid,
127          NGTCP2_CRYPTO_SIDE_CLIENT) != 0)
128     return -1;
129 
130   return 0;
131 }
132 
quic_settings(ngtcp2_settings * s,uint64_t stream_buffer_size)133 static void quic_settings(ngtcp2_settings *s,
134                           uint64_t stream_buffer_size)
135 {
136   ngtcp2_settings_default(s);
137 #ifdef DEBUG_NGTCP2
138   s->log_printf = quic_printf;
139 #else
140   s->log_printf = NULL;
141 #endif
142   s->initial_ts = timestamp();
143   s->transport_params.initial_max_stream_data_bidi_local = stream_buffer_size;
144   s->transport_params.initial_max_stream_data_bidi_remote = QUIC_MAX_STREAMS;
145   s->transport_params.initial_max_stream_data_uni = QUIC_MAX_STREAMS;
146   s->transport_params.initial_max_data = QUIC_MAX_DATA;
147   s->transport_params.initial_max_streams_bidi = 1;
148   s->transport_params.initial_max_streams_uni = 3;
149   s->transport_params.idle_timeout = QUIC_IDLE_TIMEOUT;
150 }
151 
152 static FILE *keylog_file; /* not thread-safe */
keylog_callback(const SSL * ssl,const char * line)153 static void keylog_callback(const SSL *ssl, const char *line)
154 {
155   (void)ssl;
156   fputs(line, keylog_file);
157   fputc('\n', keylog_file);
158   fflush(keylog_file);
159 }
160 
161 static int init_ngh3_conn(struct quicsocket *qs);
162 
quic_set_encryption_secrets(SSL * ssl,OSSL_ENCRYPTION_LEVEL ossl_level,const uint8_t * rx_secret,const uint8_t * tx_secret,size_t secretlen)163 static int quic_set_encryption_secrets(SSL *ssl,
164                                        OSSL_ENCRYPTION_LEVEL ossl_level,
165                                        const uint8_t *rx_secret,
166                                        const uint8_t *tx_secret,
167                                        size_t secretlen)
168 {
169   struct quicsocket *qs = (struct quicsocket *)SSL_get_app_data(ssl);
170   int level = quic_from_ossl_level(ossl_level);
171 
172   if(ngtcp2_crypto_derive_and_install_key(
173          qs->qconn, ssl, NULL, NULL, NULL, NULL, NULL, NULL, level, rx_secret,
174          tx_secret, secretlen, NGTCP2_CRYPTO_SIDE_CLIENT) != 0)
175     return 0;
176 
177   if(level == NGTCP2_CRYPTO_LEVEL_APP && init_ngh3_conn(qs) != CURLE_OK)
178     return 0;
179 
180   return 1;
181 }
182 
quic_add_handshake_data(SSL * ssl,OSSL_ENCRYPTION_LEVEL ossl_level,const uint8_t * data,size_t len)183 static int quic_add_handshake_data(SSL *ssl, OSSL_ENCRYPTION_LEVEL ossl_level,
184                                    const uint8_t *data, size_t len)
185 {
186   struct quicsocket *qs = (struct quicsocket *)SSL_get_app_data(ssl);
187   struct quic_handshake *crypto_data;
188   ngtcp2_crypto_level level = quic_from_ossl_level(ossl_level);
189   int rv;
190 
191   crypto_data = &qs->client_crypto_data[level];
192   if(crypto_data->buf == NULL) {
193     crypto_data->buf = malloc(4096);
194     crypto_data->alloclen = 4096;
195     /* TODO Explode if malloc failed */
196   }
197 
198   /* TODO Just pretend that handshake does not grow more than 4KiB for
199      now */
200   assert(crypto_data->len + len <= crypto_data->alloclen);
201 
202   memcpy(&crypto_data->buf[crypto_data->len], data, len);
203   crypto_data->len += len;
204 
205   rv = ngtcp2_conn_submit_crypto_data(
206       qs->qconn, level, (uint8_t *)(&crypto_data->buf[crypto_data->len] - len),
207       len);
208   if(rv) {
209     H3BUGF(fprintf(stderr, "write_client_handshake failed\n"));
210   }
211   assert(0 == rv);
212 
213   return 1;
214 }
215 
quic_flush_flight(SSL * ssl)216 static int quic_flush_flight(SSL *ssl)
217 {
218   (void)ssl;
219   return 1;
220 }
221 
quic_send_alert(SSL * ssl,enum ssl_encryption_level_t level,uint8_t alert)222 static int quic_send_alert(SSL *ssl, enum ssl_encryption_level_t level,
223                            uint8_t alert)
224 {
225   struct quicsocket *qs = (struct quicsocket *)SSL_get_app_data(ssl);
226   (void)level;
227 
228   qs->tls_alert = alert;
229   return 1;
230 }
231 
232 static SSL_QUIC_METHOD quic_method = {quic_set_encryption_secrets,
233                                       quic_add_handshake_data,
234                                       quic_flush_flight, quic_send_alert};
235 
quic_ssl_ctx(struct Curl_easy * data)236 static SSL_CTX *quic_ssl_ctx(struct Curl_easy *data)
237 {
238   SSL_CTX *ssl_ctx = SSL_CTX_new(TLS_method());
239   const char *keylog_filename;
240 
241   SSL_CTX_set_min_proto_version(ssl_ctx, TLS1_3_VERSION);
242   SSL_CTX_set_max_proto_version(ssl_ctx, TLS1_3_VERSION);
243 
244   SSL_CTX_set_default_verify_paths(ssl_ctx);
245 
246   if(SSL_CTX_set_ciphersuites(ssl_ctx, QUIC_CIPHERS) != 1) {
247     failf(data, "SSL_CTX_set_ciphersuites: %s",
248           ERR_error_string(ERR_get_error(), NULL));
249     return NULL;
250   }
251 
252   if(SSL_CTX_set1_groups_list(ssl_ctx, QUIC_GROUPS) != 1) {
253     failf(data, "SSL_CTX_set1_groups_list failed");
254     return NULL;
255   }
256 
257   SSL_CTX_set_quic_method(ssl_ctx, &quic_method);
258 
259   keylog_filename = getenv("SSLKEYLOGFILE");
260   if(keylog_filename) {
261     keylog_file = fopen(keylog_filename, "wb");
262     if(keylog_file) {
263       SSL_CTX_set_keylog_callback(ssl_ctx, keylog_callback);
264     }
265   }
266 
267   return ssl_ctx;
268 }
269 
270 /** SSL callbacks ***/
271 
quic_init_ssl(struct quicsocket * qs)272 static int quic_init_ssl(struct quicsocket *qs)
273 {
274   const uint8_t *alpn = NULL;
275   size_t alpnlen = 0;
276   /* this will need some attention when HTTPS proxy over QUIC get fixed */
277   const char * const hostname = qs->conn->host.name;
278 
279   if(qs->ssl)
280     SSL_free(qs->ssl);
281 
282   qs->ssl = SSL_new(qs->sslctx);
283 
284   SSL_set_app_data(qs->ssl, qs);
285   SSL_set_connect_state(qs->ssl);
286 
287   switch(qs->version) {
288 #ifdef NGTCP2_PROTO_VER
289   case NGTCP2_PROTO_VER:
290     alpn = (const uint8_t *)NGTCP2_ALPN_H3;
291     alpnlen = sizeof(NGTCP2_ALPN_H3) - 1;
292     break;
293 #endif
294   }
295   if(alpn)
296     SSL_set_alpn_protos(qs->ssl, alpn, (int)alpnlen);
297 
298   /* set SNI */
299   SSL_set_tlsext_host_name(qs->ssl, hostname);
300   return 0;
301 }
302 
cb_initial(ngtcp2_conn * quic,void * user_data)303 static int cb_initial(ngtcp2_conn *quic, void *user_data)
304 {
305   struct quicsocket *qs = (struct quicsocket *)user_data;
306 
307   if(ngtcp2_crypto_read_write_crypto_data(
308          quic, qs->ssl, NGTCP2_CRYPTO_LEVEL_INITIAL, NULL, 0) != 0)
309     return NGTCP2_ERR_CALLBACK_FAILURE;
310 
311   return 0;
312 }
313 
314 static int
cb_recv_crypto_data(ngtcp2_conn * tconn,ngtcp2_crypto_level crypto_level,uint64_t offset,const uint8_t * data,size_t datalen,void * user_data)315 cb_recv_crypto_data(ngtcp2_conn *tconn, ngtcp2_crypto_level crypto_level,
316                     uint64_t offset,
317                     const uint8_t *data, size_t datalen,
318                     void *user_data)
319 {
320   struct quicsocket *qs = (struct quicsocket *)user_data;
321   (void)offset;
322 
323   if(ngtcp2_crypto_read_write_crypto_data(tconn, qs->ssl, crypto_level, data,
324                                           datalen) != 0)
325     return NGTCP2_ERR_CRYPTO;
326 
327   return 0;
328 }
329 
cb_handshake_completed(ngtcp2_conn * tconn,void * user_data)330 static int cb_handshake_completed(ngtcp2_conn *tconn, void *user_data)
331 {
332   struct quicsocket *qs = (struct quicsocket *)user_data;
333   (void)tconn;
334   infof(qs->conn->data, "QUIC handshake is completed\n");
335 
336   return 0;
337 }
338 
cb_recv_stream_data(ngtcp2_conn * tconn,int64_t stream_id,int fin,uint64_t offset,const uint8_t * buf,size_t buflen,void * user_data,void * stream_user_data)339 static int cb_recv_stream_data(ngtcp2_conn *tconn, int64_t stream_id,
340                                int fin, uint64_t offset,
341                                const uint8_t *buf, size_t buflen,
342                                void *user_data, void *stream_user_data)
343 {
344   struct quicsocket *qs = (struct quicsocket *)user_data;
345   ssize_t nconsumed;
346   (void)offset;
347   (void)stream_user_data;
348 
349   infof(qs->conn->data, "Received %ld bytes data on stream %u\n",
350         buflen, stream_id);
351 
352   nconsumed =
353     nghttp3_conn_read_stream(qs->h3conn, stream_id, buf, buflen, fin);
354   if(nconsumed < 0) {
355     failf(qs->conn->data, "nghttp3_conn_read_stream returned error: %s\n",
356           nghttp3_strerror((int)nconsumed));
357     return NGTCP2_ERR_CALLBACK_FAILURE;
358   }
359 
360   ngtcp2_conn_extend_max_stream_offset(tconn, stream_id, nconsumed);
361   ngtcp2_conn_extend_max_offset(tconn, nconsumed);
362 
363   return 0;
364 }
365 
366 static int
cb_acked_stream_data_offset(ngtcp2_conn * tconn,int64_t stream_id,uint64_t offset,size_t datalen,void * user_data,void * stream_user_data)367 cb_acked_stream_data_offset(ngtcp2_conn *tconn, int64_t stream_id,
368                             uint64_t offset, size_t datalen, void *user_data,
369                             void *stream_user_data)
370 {
371   struct quicsocket *qs = (struct quicsocket *)user_data;
372   int rv;
373   (void)stream_id;
374   (void)tconn;
375   (void)offset;
376   (void)datalen;
377   (void)stream_user_data;
378 
379   rv = nghttp3_conn_add_ack_offset(qs->h3conn, stream_id, datalen);
380   if(rv != 0) {
381     failf(qs->conn->data, "nghttp3_conn_add_ack_offset returned error: %s\n",
382           nghttp3_strerror(rv));
383     return NGTCP2_ERR_CALLBACK_FAILURE;
384   }
385 
386   return 0;
387 }
388 
cb_stream_close(ngtcp2_conn * tconn,int64_t stream_id,uint64_t app_error_code,void * user_data,void * stream_user_data)389 static int cb_stream_close(ngtcp2_conn *tconn, int64_t stream_id,
390                            uint64_t app_error_code,
391                            void *user_data, void *stream_user_data)
392 {
393   struct quicsocket *qs = (struct quicsocket *)user_data;
394   int rv;
395   (void)tconn;
396   (void)stream_user_data;
397   /* stream is closed... */
398 
399   rv = nghttp3_conn_close_stream(qs->h3conn, stream_id,
400                                  app_error_code);
401   if(rv != 0) {
402     failf(qs->conn->data, "nghttp3_conn_close_stream returned error: %s\n",
403           nghttp3_strerror(rv));
404     return NGTCP2_ERR_CALLBACK_FAILURE;
405   }
406 
407   return 0;
408 }
409 
cb_stream_reset(ngtcp2_conn * tconn,int64_t stream_id,uint64_t final_size,uint64_t app_error_code,void * user_data,void * stream_user_data)410 static int cb_stream_reset(ngtcp2_conn *tconn, int64_t stream_id,
411                            uint64_t final_size, uint64_t app_error_code,
412                            void *user_data, void *stream_user_data)
413 {
414   struct quicsocket *qs = (struct quicsocket *)user_data;
415   int rv;
416   (void)tconn;
417   (void)final_size;
418   (void)app_error_code;
419   (void)stream_user_data;
420 
421   rv = nghttp3_conn_reset_stream(qs->h3conn, stream_id);
422   if(rv != 0) {
423     failf(qs->conn->data, "nghttp3_conn_reset_stream returned error: %s\n",
424           nghttp3_strerror(rv));
425     return NGTCP2_ERR_CALLBACK_FAILURE;
426   }
427 
428   return 0;
429 }
430 
cb_recv_retry(ngtcp2_conn * tconn,const ngtcp2_pkt_hd * hd,const ngtcp2_pkt_retry * retry,void * user_data)431 static int cb_recv_retry(ngtcp2_conn *tconn, const ngtcp2_pkt_hd *hd,
432                          const ngtcp2_pkt_retry *retry, void *user_data)
433 {
434   /* Re-generate handshake secrets here because connection ID might change. */
435   struct quicsocket *qs = (struct quicsocket *)user_data;
436   (void)tconn;
437   (void)hd;
438   (void)retry;
439 
440   setup_initial_crypto_context(qs);
441 
442   return 0;
443 }
444 
cb_extend_max_local_streams_bidi(ngtcp2_conn * tconn,uint64_t max_streams,void * user_data)445 static int cb_extend_max_local_streams_bidi(ngtcp2_conn *tconn,
446                                             uint64_t max_streams,
447                                             void *user_data)
448 {
449   (void)tconn;
450   (void)max_streams;
451   (void)user_data;
452 
453   return 0;
454 }
455 
cb_extend_max_stream_data(ngtcp2_conn * tconn,int64_t stream_id,uint64_t max_data,void * user_data,void * stream_user_data)456 static int cb_extend_max_stream_data(ngtcp2_conn *tconn, int64_t stream_id,
457                                      uint64_t max_data, void *user_data,
458                                      void *stream_user_data)
459 {
460   struct quicsocket *qs = (struct quicsocket *)user_data;
461   int rv;
462   (void)tconn;
463   (void)max_data;
464   (void)stream_user_data;
465 
466   rv = nghttp3_conn_unblock_stream(qs->h3conn, stream_id);
467   if(rv != 0) {
468     failf(qs->conn->data, "nghttp3_conn_unblock_stream returned error: %s\n",
469           nghttp3_strerror(rv));
470     return NGTCP2_ERR_CALLBACK_FAILURE;
471   }
472 
473   return 0;
474 }
475 
cb_get_new_connection_id(ngtcp2_conn * tconn,ngtcp2_cid * cid,uint8_t * token,size_t cidlen,void * user_data)476 static int cb_get_new_connection_id(ngtcp2_conn *tconn, ngtcp2_cid *cid,
477                                     uint8_t *token, size_t cidlen,
478                                     void *user_data)
479 {
480   struct quicsocket *qs = (struct quicsocket *)user_data;
481   CURLcode result;
482   (void)tconn;
483 
484   result = Curl_rand(qs->conn->data, cid->data, cidlen);
485   if(result)
486     return NGTCP2_ERR_CALLBACK_FAILURE;
487   cid->datalen = cidlen;
488 
489   result = Curl_rand(qs->conn->data, token, NGTCP2_STATELESS_RESET_TOKENLEN);
490   if(result)
491     return NGTCP2_ERR_CALLBACK_FAILURE;
492 
493   return 0;
494 }
495 
496 static ngtcp2_conn_callbacks ng_callbacks = {
497   cb_initial,
498   NULL, /* recv_client_initial */
499   cb_recv_crypto_data,
500   cb_handshake_completed,
501   NULL, /* recv_version_negotiation */
502   ngtcp2_crypto_encrypt_cb,
503   ngtcp2_crypto_decrypt_cb,
504   ngtcp2_crypto_hp_mask_cb,
505   cb_recv_stream_data,
506   NULL, /* acked_crypto_offset */
507   cb_acked_stream_data_offset,
508   NULL, /* stream_open */
509   cb_stream_close,
510   NULL, /* recv_stateless_reset */
511   cb_recv_retry,
512   cb_extend_max_local_streams_bidi,
513   NULL, /* extend_max_local_streams_uni */
514   NULL, /* rand  */
515   cb_get_new_connection_id,
516   NULL, /* remove_connection_id */
517   NULL, /* update_key */
518   NULL, /* path_validation */
519   NULL, /* select_preferred_addr */
520   cb_stream_reset,
521   NULL, /* extend_max_remote_streams_bidi */
522   NULL, /* extend_max_remote_streams_uni */
523   cb_extend_max_stream_data,
524 };
525 
526 /*
527  * Might be called twice for happy eyeballs.
528  */
Curl_quic_connect(struct connectdata * conn,curl_socket_t sockfd,int sockindex,const struct sockaddr * addr,socklen_t addrlen)529 CURLcode Curl_quic_connect(struct connectdata *conn,
530                            curl_socket_t sockfd,
531                            int sockindex,
532                            const struct sockaddr *addr,
533                            socklen_t addrlen)
534 {
535   int rc;
536   int rv;
537   CURLcode result;
538   ngtcp2_path path; /* TODO: this must be initialized properly */
539   struct Curl_easy *data = conn->data;
540   struct quicsocket *qs = &conn->hequic[sockindex];
541   char ipbuf[40];
542   long port;
543   uint8_t paramsbuf[64];
544   ngtcp2_transport_params params;
545   ssize_t nwrite;
546 
547   qs->conn = conn;
548 
549   /* extract the used address as a string */
550   if(!Curl_addr2string((struct sockaddr*)addr, addrlen, ipbuf, &port)) {
551     char buffer[STRERROR_LEN];
552     failf(data, "ssrem inet_ntop() failed with errno %d: %s",
553           SOCKERRNO, Curl_strerror(SOCKERRNO, buffer, sizeof(buffer)));
554     return CURLE_BAD_FUNCTION_ARGUMENT;
555   }
556 
557   infof(data, "Connect socket %d over QUIC to %s:%ld\n",
558         sockfd, ipbuf, port);
559 
560   qs->version = NGTCP2_PROTO_VER;
561   qs->sslctx = quic_ssl_ctx(data);
562   if(!qs->sslctx)
563     return CURLE_FAILED_INIT; /* TODO: better return code */
564 
565   if(quic_init_ssl(qs))
566     return CURLE_FAILED_INIT; /* TODO: better return code */
567 
568   qs->dcid.datalen = NGTCP2_MAX_CIDLEN;
569   result = Curl_rand(data, qs->dcid.data, NGTCP2_MAX_CIDLEN);
570   if(result)
571     return result;
572 
573   qs->scid.datalen = NGTCP2_MAX_CIDLEN;
574   result = Curl_rand(data, qs->scid.data, NGTCP2_MAX_CIDLEN);
575   if(result)
576     return result;
577 
578   quic_settings(&qs->settings, data->set.buffer_size);
579 
580   qs->local_addrlen = sizeof(qs->local_addr);
581   rv = getsockname(sockfd, (struct sockaddr *)&qs->local_addr,
582                    &qs->local_addrlen);
583   if(rv == -1)
584     return CURLE_FAILED_INIT;
585 
586   ngtcp2_addr_init(&path.local, (uint8_t *)&qs->local_addr, qs->local_addrlen,
587                    NULL);
588   ngtcp2_addr_init(&path.remote, (uint8_t*)addr, addrlen, NULL);
589 
590 #ifdef NGTCP2_PROTO_VER
591 #define QUICVER NGTCP2_PROTO_VER
592 #else
593 #error "unsupported ngtcp2 version"
594 #endif
595   rc = ngtcp2_conn_client_new(&qs->qconn, &qs->dcid, &qs->scid, &path, QUICVER,
596                               &ng_callbacks, &qs->settings, NULL, qs);
597   if(rc)
598     return CURLE_FAILED_INIT; /* TODO: create a QUIC error code */
599 
600   ngtcp2_conn_get_local_transport_params(qs->qconn, &params);
601   nwrite = ngtcp2_encode_transport_params(
602     paramsbuf, sizeof(paramsbuf), NGTCP2_TRANSPORT_PARAMS_TYPE_CLIENT_HELLO,
603     &params);
604   if(nwrite < 0) {
605     failf(data, "ngtcp2_encode_transport_params: %s\n",
606           ngtcp2_strerror((int)nwrite));
607     return CURLE_FAILED_INIT;
608   }
609 
610   if(!SSL_set_quic_transport_params(qs->ssl, paramsbuf, nwrite))
611     return CURLE_FAILED_INIT;
612 
613   rc = setup_initial_crypto_context(qs);
614   if(rc)
615     return CURLE_FAILED_INIT; /* TODO: better return code */
616 
617   return CURLE_OK;
618 }
619 
620 /*
621  * Store ngtp2 version info in this buffer, Prefix with a space.  Return total
622  * length written.
623  */
Curl_quic_ver(char * p,size_t len)624 int Curl_quic_ver(char *p, size_t len)
625 {
626   ngtcp2_info *ng2 = ngtcp2_version(0);
627   nghttp3_info *ht3 = nghttp3_version(0);
628   return msnprintf(p, len, " ngtcp2/%s nghttp3/%s",
629                    ng2->version_str, ht3->version_str);
630 }
631 
ng_getsock(struct connectdata * conn,curl_socket_t * socks)632 static int ng_getsock(struct connectdata *conn, curl_socket_t *socks)
633 {
634   struct SingleRequest *k = &conn->data->req;
635   int bitmap = GETSOCK_BLANK;
636 
637   socks[0] = conn->sock[FIRSTSOCKET];
638 
639   /* in a HTTP/2 connection we can basically always get a frame so we should
640      always be ready for one */
641   bitmap |= GETSOCK_READSOCK(FIRSTSOCKET);
642 
643   /* we're still uploading or the HTTP/2 layer wants to send data */
644   if((k->keepon & (KEEP_SEND|KEEP_SEND_PAUSE)) == KEEP_SEND)
645     bitmap |= GETSOCK_WRITESOCK(FIRSTSOCKET);
646 
647   return bitmap;
648 }
649 
ng_perform_getsock(const struct connectdata * conn,curl_socket_t * socks)650 static int ng_perform_getsock(const struct connectdata *conn,
651                               curl_socket_t *socks)
652 {
653   return ng_getsock((struct connectdata *)conn, socks);
654 }
655 
ng_disconnect(struct connectdata * conn,bool dead_connection)656 static CURLcode ng_disconnect(struct connectdata *conn,
657                               bool dead_connection)
658 {
659   (void)conn;
660   (void)dead_connection;
661   return CURLE_OK;
662 }
663 
ng_conncheck(struct connectdata * conn,unsigned int checks_to_perform)664 static unsigned int ng_conncheck(struct connectdata *conn,
665                                  unsigned int checks_to_perform)
666 {
667   (void)conn;
668   (void)checks_to_perform;
669   return CONNRESULT_NONE;
670 }
671 
672 static const struct Curl_handler Curl_handler_http3 = {
673   "HTTPS",                              /* scheme */
674   ZERO_NULL,                            /* setup_connection */
675   Curl_http,                            /* do_it */
676   Curl_http_done,                       /* done */
677   ZERO_NULL,                            /* do_more */
678   ZERO_NULL,                            /* connect_it */
679   ZERO_NULL,                            /* connecting */
680   ZERO_NULL,                            /* doing */
681   ng_getsock,                           /* proto_getsock */
682   ng_getsock,                           /* doing_getsock */
683   ZERO_NULL,                            /* domore_getsock */
684   ng_perform_getsock,                   /* perform_getsock */
685   ng_disconnect,                        /* disconnect */
686   ZERO_NULL,                            /* readwrite */
687   ng_conncheck,                         /* connection_check */
688   PORT_HTTP,                            /* defport */
689   CURLPROTO_HTTPS,                      /* protocol */
690   PROTOPT_SSL | PROTOPT_STREAM          /* flags */
691 };
692 
cb_h3_stream_close(nghttp3_conn * conn,int64_t stream_id,uint64_t app_error_code,void * user_data,void * stream_user_data)693 static int cb_h3_stream_close(nghttp3_conn *conn, int64_t stream_id,
694                               uint64_t app_error_code, void *user_data,
695                               void *stream_user_data)
696 {
697   struct Curl_easy *data = stream_user_data;
698   struct HTTP *stream = data->req.protop;
699   (void)conn;
700   (void)stream_id;
701   (void)app_error_code;
702   (void)user_data;
703   H3BUGF(infof(data, "cb_h3_stream_close CALLED\n"));
704 
705   stream->closed = TRUE;
706   Curl_expire(data, 0, EXPIRE_QUIC);
707   return 0;
708 }
709 
cb_h3_recv_data(nghttp3_conn * conn,int64_t stream_id,const uint8_t * buf,size_t buflen,void * user_data,void * stream_user_data)710 static int cb_h3_recv_data(nghttp3_conn *conn, int64_t stream_id,
711                            const uint8_t *buf, size_t buflen,
712                            void *user_data, void *stream_user_data)
713 {
714   struct quicsocket *qs = user_data;
715   size_t ncopy;
716   struct Curl_easy *data = stream_user_data;
717   struct HTTP *stream = data->req.protop;
718   (void)conn;
719   H3BUGF(infof(data, "cb_h3_recv_data CALLED with %d bytes\n", buflen));
720 
721   /* TODO: this needs to be handled properly */
722   DEBUGASSERT(buflen <= stream->len);
723 
724   ncopy = CURLMIN(stream->len, buflen);
725   memcpy(stream->mem, buf, ncopy);
726   stream->len -= ncopy;
727   stream->memlen += ncopy;
728 #if 0 /* extra debugging of incoming h3 data */
729   fprintf(stderr, "!! Copies %zd bytes to %p (total %zd)\n",
730           ncopy, stream->mem, stream->memlen);
731   {
732     size_t i;
733     for(i = 0; i < ncopy; i++) {
734       fprintf(stderr, "!! data[%d]: %02x '%c'\n", i, buf[i], buf[i]);
735     }
736   }
737 #endif
738   stream->mem += ncopy;
739 
740   ngtcp2_conn_extend_max_stream_offset(qs->qconn, stream_id, buflen);
741   ngtcp2_conn_extend_max_offset(qs->qconn, buflen);
742 
743   return 0;
744 }
745 
cb_h3_deferred_consume(nghttp3_conn * conn,int64_t stream_id,size_t consumed,void * user_data,void * stream_user_data)746 static int cb_h3_deferred_consume(nghttp3_conn *conn, int64_t stream_id,
747                                   size_t consumed, void *user_data,
748                                   void *stream_user_data)
749 {
750   struct quicsocket *qs = user_data;
751   (void)conn;
752   (void)stream_user_data;
753 
754   ngtcp2_conn_extend_max_stream_offset(qs->qconn, stream_id, consumed);
755   ngtcp2_conn_extend_max_offset(qs->qconn, consumed);
756 
757   return 0;
758 }
759 
760 /* Decode HTTP status code.  Returns -1 if no valid status code was
761    decoded. (duplicate from http2.c) */
decode_status_code(const uint8_t * value,size_t len)762 static int decode_status_code(const uint8_t *value, size_t len)
763 {
764   int i;
765   int res;
766 
767   if(len != 3) {
768     return -1;
769   }
770 
771   res = 0;
772 
773   for(i = 0; i < 3; ++i) {
774     char c = value[i];
775 
776     if(c < '0' || c > '9') {
777       return -1;
778     }
779 
780     res *= 10;
781     res += c - '0';
782   }
783 
784   return res;
785 }
786 
cb_h3_end_headers(nghttp3_conn * conn,int64_t stream_id,void * user_data,void * stream_user_data)787 static int cb_h3_end_headers(nghttp3_conn *conn, int64_t stream_id,
788                              void *user_data, void *stream_user_data)
789 {
790   struct Curl_easy *data = stream_user_data;
791   struct HTTP *stream = data->req.protop;
792   (void)conn;
793   (void)stream_id;
794   (void)user_data;
795 
796   if(stream->memlen >= 2) {
797     memcpy(stream->mem, "\r\n", 2);
798     stream->len -= 2;
799     stream->memlen += 2;
800     stream->mem += 2;
801   }
802   return 0;
803 }
804 
cb_h3_recv_header(nghttp3_conn * conn,int64_t stream_id,int32_t token,nghttp3_rcbuf * name,nghttp3_rcbuf * value,uint8_t flags,void * user_data,void * stream_user_data)805 static int cb_h3_recv_header(nghttp3_conn *conn, int64_t stream_id,
806                              int32_t token, nghttp3_rcbuf *name,
807                              nghttp3_rcbuf *value, uint8_t flags,
808                              void *user_data, void *stream_user_data)
809 {
810   nghttp3_vec h3name = nghttp3_rcbuf_get_buf(name);
811   nghttp3_vec h3val = nghttp3_rcbuf_get_buf(value);
812   struct Curl_easy *data = stream_user_data;
813   struct HTTP *stream = data->req.protop;
814   size_t ncopy;
815   (void)conn;
816   (void)stream_id;
817   (void)token;
818   (void)flags;
819   (void)user_data;
820 
821   if(h3name.len == sizeof(":status") - 1 &&
822      !memcmp(":status", h3name.base, h3name.len)) {
823     int status = decode_status_code(h3val.base, h3val.len);
824     DEBUGASSERT(status != -1);
825     msnprintf(stream->mem, stream->len, "HTTP/3 %03d \r\n", status);
826   }
827   else {
828     /* store as a HTTP1-style header */
829     msnprintf(stream->mem, stream->len, "%.*s: %.*s\n",
830               h3name.len, h3name.base, h3val.len, h3val.base);
831   }
832 
833   ncopy = strlen(stream->mem);
834   stream->len -= ncopy;
835   stream->memlen += ncopy;
836   stream->mem += ncopy;
837   return 0;
838 }
839 
cb_h3_send_stop_sending(nghttp3_conn * conn,int64_t stream_id,uint64_t app_error_code,void * user_data,void * stream_user_data)840 static int cb_h3_send_stop_sending(nghttp3_conn *conn, int64_t stream_id,
841                                    uint64_t app_error_code,
842                                    void *user_data,
843                                    void *stream_user_data)
844 {
845   (void)conn;
846   (void)stream_id;
847   (void)app_error_code;
848   (void)user_data;
849   (void)stream_user_data;
850   return 0;
851 }
852 
853 static nghttp3_conn_callbacks ngh3_callbacks = {
854   cb_h3_acked_stream_data, /* acked_stream_data */
855   cb_h3_stream_close,
856   cb_h3_recv_data,
857   cb_h3_deferred_consume,
858   NULL, /* begin_headers */
859   cb_h3_recv_header,
860   cb_h3_end_headers,
861   NULL, /* begin_trailers */
862   cb_h3_recv_header,
863   NULL, /* end_trailers */
864   NULL, /* http_begin_push_promise */
865   NULL, /* http_recv_push_promise */
866   NULL, /* http_end_push_promise */
867   NULL, /* http_cancel_push */
868   cb_h3_send_stop_sending,
869   NULL, /* push_stream */
870   NULL, /* end_stream */
871 };
872 
init_ngh3_conn(struct quicsocket * qs)873 static int init_ngh3_conn(struct quicsocket *qs)
874 {
875   CURLcode result;
876   int rc;
877   int64_t ctrl_stream_id, qpack_enc_stream_id, qpack_dec_stream_id;
878 
879   if(ngtcp2_conn_get_max_local_streams_uni(qs->qconn) < 3) {
880     failf(qs->conn->data, "too few available QUIC streams");
881     return CURLE_FAILED_INIT;
882   }
883 
884   nghttp3_conn_settings_default(&qs->h3settings);
885 
886   rc = nghttp3_conn_client_new(&qs->h3conn,
887                                &ngh3_callbacks,
888                                &qs->h3settings,
889                                nghttp3_mem_default(),
890                                qs);
891   if(rc) {
892     result = CURLE_OUT_OF_MEMORY;
893     goto fail;
894   }
895 
896   rc = ngtcp2_conn_open_uni_stream(qs->qconn, &ctrl_stream_id, NULL);
897   if(rc) {
898     result = CURLE_FAILED_INIT;
899     goto fail;
900   }
901 
902   rc = nghttp3_conn_bind_control_stream(qs->h3conn, ctrl_stream_id);
903   if(rc) {
904     result = CURLE_FAILED_INIT;
905     goto fail;
906   }
907 
908   rc = ngtcp2_conn_open_uni_stream(qs->qconn, &qpack_enc_stream_id, NULL);
909   if(rc) {
910     result = CURLE_FAILED_INIT;
911     goto fail;
912   }
913 
914   rc = ngtcp2_conn_open_uni_stream(qs->qconn, &qpack_dec_stream_id, NULL);
915   if(rc) {
916     result = CURLE_FAILED_INIT;
917     goto fail;
918   }
919 
920   rc = nghttp3_conn_bind_qpack_streams(qs->h3conn, qpack_enc_stream_id,
921                                        qpack_dec_stream_id);
922   if(rc) {
923     result = CURLE_FAILED_INIT;
924     goto fail;
925   }
926 
927   return CURLE_OK;
928   fail:
929 
930   return result;
931 }
932 
933 static Curl_recv ngh3_stream_recv;
934 static Curl_send ngh3_stream_send;
935 
936 /* incoming data frames on the h3 stream */
ngh3_stream_recv(struct connectdata * conn,int sockindex,char * buf,size_t buffersize,CURLcode * curlcode)937 static ssize_t ngh3_stream_recv(struct connectdata *conn,
938                                 int sockindex,
939                                 char *buf,
940                                 size_t buffersize,
941                                 CURLcode *curlcode)
942 {
943   curl_socket_t sockfd = conn->sock[sockindex];
944   struct HTTP *stream = conn->data->req.protop;
945   struct quicsocket *qs = conn->quic;
946 
947   if(!stream->memlen) {
948     /* remember where to store incoming data for this stream and how big the
949        buffer is */
950     stream->mem = buf;
951     stream->len = buffersize;
952   }
953   /* else, there's data in the buffer already */
954 
955   if(ng_process_ingress(conn, sockfd, qs)) {
956     *curlcode = CURLE_RECV_ERROR;
957     return -1;
958   }
959   if(ng_flush_egress(conn, sockfd, qs)) {
960     *curlcode = CURLE_SEND_ERROR;
961     return -1;
962   }
963 
964   if(stream->memlen) {
965     ssize_t memlen = stream->memlen;
966     /* data arrived */
967     *curlcode = CURLE_OK;
968     /* reset to allow more data to come */
969     stream->memlen = 0;
970     stream->mem = buf;
971     stream->len = buffersize;
972     H3BUGF(infof(conn->data, "!! ngh3_stream_recv returns %zd bytes at %p\n",
973                  memlen, buf));
974     return memlen;
975   }
976 
977   if(stream->closed) {
978     *curlcode = CURLE_OK;
979     return 0;
980   }
981 
982   infof(conn->data, "ngh3_stream_recv returns 0 bytes and EAGAIN\n");
983   *curlcode = CURLE_AGAIN;
984   return -1;
985 }
986 
987 /* this amount of data has now been acked on this stream */
cb_h3_acked_stream_data(nghttp3_conn * conn,int64_t stream_id,size_t datalen,void * user_data,void * stream_user_data)988 static int cb_h3_acked_stream_data(nghttp3_conn *conn, int64_t stream_id,
989                                    size_t datalen, void *user_data,
990                                    void *stream_user_data)
991 {
992   struct Curl_easy *data = stream_user_data;
993   struct HTTP *stream = data->req.protop;
994   (void)conn;
995   (void)stream_id;
996   (void)user_data;
997 
998   if(!data->set.postfields) {
999     stream->h3out->used -= datalen;
1000     H3BUGF(infof(data,
1001                  "cb_h3_acked_stream_data, %zd bytes, %zd left unacked\n",
1002                  datalen, stream->h3out->used));
1003     DEBUGASSERT(stream->h3out->used < H3_SEND_SIZE);
1004   }
1005   return 0;
1006 }
1007 
cb_h3_readfunction(nghttp3_conn * conn,int64_t stream_id,nghttp3_vec * vec,size_t veccnt,uint32_t * pflags,void * user_data,void * stream_user_data)1008 static ssize_t cb_h3_readfunction(nghttp3_conn *conn, int64_t stream_id,
1009                                   nghttp3_vec *vec, size_t veccnt,
1010                                   uint32_t *pflags, void *user_data,
1011                                   void *stream_user_data)
1012 {
1013   struct Curl_easy *data = stream_user_data;
1014   size_t nread;
1015   struct HTTP *stream = data->req.protop;
1016   (void)conn;
1017   (void)stream_id;
1018   (void)user_data;
1019   (void)veccnt;
1020 
1021   if(data->set.postfields) {
1022     vec[0].base = data->set.postfields;
1023     vec[0].len = data->state.infilesize;
1024     *pflags = NGHTTP3_DATA_FLAG_EOF;
1025     return 1;
1026   }
1027 
1028   nread = CURLMIN(stream->upload_len, H3_SEND_SIZE - stream->h3out->used);
1029   if(nread > 0) {
1030     /* nghttp3 wants us to hold on to the data until it tells us it is okay to
1031        delete it. Append the data at the end of the h3out buffer. Since we can
1032        only return consecutive data, copy the amount that fits and the next
1033        part comes in next invoke. */
1034     struct h3out *out = stream->h3out;
1035     if(nread + out->windex > H3_SEND_SIZE)
1036       nread = H3_SEND_SIZE - out->windex;
1037 
1038     memcpy(&out->buf[out->windex], stream->upload_mem, nread);
1039     out->windex += nread;
1040     out->used += nread;
1041 
1042     /* that's the chunk we return to nghttp3 */
1043     vec[0].base = &out->buf[out->windex];
1044     vec[0].len = nread;
1045 
1046     if(out->windex == H3_SEND_SIZE)
1047       out->windex = 0; /* wrap */
1048     stream->upload_mem += nread;
1049     stream->upload_len -= nread;
1050     if(data->state.infilesize != -1) {
1051       stream->upload_left -= nread;
1052       if(!stream->upload_left)
1053         *pflags = NGHTTP3_DATA_FLAG_EOF;
1054     }
1055     H3BUGF(infof(data, "cb_h3_readfunction %zd bytes%s (at %zd unacked)\n",
1056                  nread, *pflags == NGHTTP3_DATA_FLAG_EOF?" EOF":"",
1057                  out->used));
1058   }
1059   if(stream->upload_done && !stream->upload_len &&
1060      (stream->upload_left <= 0)) {
1061     H3BUGF(infof(data, "!!!!!!!!! cb_h3_readfunction sets EOF\n"));
1062     *pflags = NGHTTP3_DATA_FLAG_EOF;
1063     return 0;
1064   }
1065   else if(!nread) {
1066     return NGHTTP3_ERR_WOULDBLOCK;
1067   }
1068   return 1;
1069 }
1070 
1071 /* Index where :authority header field will appear in request header
1072    field list. */
1073 #define AUTHORITY_DST_IDX 3
1074 
http_request(struct connectdata * conn,const void * mem,size_t len)1075 static CURLcode http_request(struct connectdata *conn, const void *mem,
1076                              size_t len)
1077 {
1078   struct HTTP *stream = conn->data->req.protop;
1079   size_t nheader;
1080   size_t i;
1081   size_t authority_idx;
1082   char *hdbuf = (char *)mem;
1083   char *end, *line_end;
1084   struct quicsocket *qs = conn->quic;
1085   CURLcode result = CURLE_OK;
1086   struct Curl_easy *data = conn->data;
1087   nghttp3_nv *nva = NULL;
1088   int64_t stream3_id;
1089   int rc;
1090   struct h3out *h3out = NULL;
1091 
1092   rc = ngtcp2_conn_open_bidi_stream(qs->qconn, &stream3_id, NULL);
1093   if(rc) {
1094     failf(conn->data, "can get bidi streams");
1095     result = CURLE_SEND_ERROR;
1096     goto fail;
1097   }
1098 
1099   stream->stream3_id = stream3_id;
1100   stream->h3req = TRUE; /* senf off! */
1101 
1102   /* Calculate number of headers contained in [mem, mem + len). Assumes a
1103      correctly generated HTTP header field block. */
1104   nheader = 0;
1105   for(i = 1; i < len; ++i) {
1106     if(hdbuf[i] == '\n' && hdbuf[i - 1] == '\r') {
1107       ++nheader;
1108       ++i;
1109     }
1110   }
1111   if(nheader < 2)
1112     goto fail;
1113 
1114   /* We counted additional 2 \r\n in the first and last line. We need 3
1115      new headers: :method, :path and :scheme. Therefore we need one
1116      more space. */
1117   nheader += 1;
1118   nva = malloc(sizeof(nghttp3_nv) * nheader);
1119   if(!nva) {
1120     result = CURLE_OUT_OF_MEMORY;
1121     goto fail;
1122   }
1123 
1124   /* Extract :method, :path from request line
1125      We do line endings with CRLF so checking for CR is enough */
1126   line_end = memchr(hdbuf, '\r', len);
1127   if(!line_end) {
1128     result = CURLE_BAD_FUNCTION_ARGUMENT; /* internal error */
1129     goto fail;
1130   }
1131 
1132   /* Method does not contain spaces */
1133   end = memchr(hdbuf, ' ', line_end - hdbuf);
1134   if(!end || end == hdbuf)
1135     goto fail;
1136   nva[0].name = (unsigned char *)":method";
1137   nva[0].namelen = strlen((char *)nva[0].name);
1138   nva[0].value = (unsigned char *)hdbuf;
1139   nva[0].valuelen = (size_t)(end - hdbuf);
1140   nva[0].flags = NGHTTP3_NV_FLAG_NONE;
1141 
1142   hdbuf = end + 1;
1143 
1144   /* Path may contain spaces so scan backwards */
1145   end = NULL;
1146   for(i = (size_t)(line_end - hdbuf); i; --i) {
1147     if(hdbuf[i - 1] == ' ') {
1148       end = &hdbuf[i - 1];
1149       break;
1150     }
1151   }
1152   if(!end || end == hdbuf)
1153     goto fail;
1154   nva[1].name = (unsigned char *)":path";
1155   nva[1].namelen = strlen((char *)nva[1].name);
1156   nva[1].value = (unsigned char *)hdbuf;
1157   nva[1].valuelen = (size_t)(end - hdbuf);
1158   nva[1].flags = NGHTTP3_NV_FLAG_NONE;
1159 
1160   nva[2].name = (unsigned char *)":scheme";
1161   nva[2].namelen = strlen((char *)nva[2].name);
1162   if(conn->handler->flags & PROTOPT_SSL)
1163     nva[2].value = (unsigned char *)"https";
1164   else
1165     nva[2].value = (unsigned char *)"http";
1166   nva[2].valuelen = strlen((char *)nva[2].value);
1167   nva[2].flags = NGHTTP3_NV_FLAG_NONE;
1168 
1169 
1170   authority_idx = 0;
1171   i = 3;
1172   while(i < nheader) {
1173     size_t hlen;
1174 
1175     hdbuf = line_end + 2;
1176 
1177     /* check for next CR, but only within the piece of data left in the given
1178        buffer */
1179     line_end = memchr(hdbuf, '\r', len - (hdbuf - (char *)mem));
1180     if(!line_end || (line_end == hdbuf))
1181       goto fail;
1182 
1183     /* header continuation lines are not supported */
1184     if(*hdbuf == ' ' || *hdbuf == '\t')
1185       goto fail;
1186 
1187     for(end = hdbuf; end < line_end && *end != ':'; ++end)
1188       ;
1189     if(end == hdbuf || end == line_end)
1190       goto fail;
1191     hlen = end - hdbuf;
1192 
1193     if(hlen == 4 && strncasecompare("host", hdbuf, 4)) {
1194       authority_idx = i;
1195       nva[i].name = (unsigned char *)":authority";
1196       nva[i].namelen = strlen((char *)nva[i].name);
1197     }
1198     else {
1199       nva[i].namelen = (size_t)(end - hdbuf);
1200       /* Lower case the header name for HTTP/3 */
1201       Curl_strntolower((char *)hdbuf, hdbuf, nva[i].namelen);
1202       nva[i].name = (unsigned char *)hdbuf;
1203     }
1204     nva[i].flags = NGHTTP3_NV_FLAG_NONE;
1205     hdbuf = end + 1;
1206     while(*hdbuf == ' ' || *hdbuf == '\t')
1207       ++hdbuf;
1208     end = line_end;
1209 
1210 #if 0 /* This should probably go in more or less like this */
1211     switch(inspect_header((const char *)nva[i].name, nva[i].namelen, hdbuf,
1212                           end - hdbuf)) {
1213     case HEADERINST_IGNORE:
1214       /* skip header fields prohibited by HTTP/2 specification. */
1215       --nheader;
1216       continue;
1217     case HEADERINST_TE_TRAILERS:
1218       nva[i].value = (uint8_t*)"trailers";
1219       nva[i].value_len = sizeof("trailers") - 1;
1220       break;
1221     default:
1222       nva[i].value = (unsigned char *)hdbuf;
1223       nva[i].value_len = (size_t)(end - hdbuf);
1224     }
1225 #endif
1226     nva[i].value = (unsigned char *)hdbuf;
1227     nva[i].valuelen = (size_t)(end - hdbuf);
1228     nva[i].flags = NGHTTP3_NV_FLAG_NONE;
1229 
1230     ++i;
1231   }
1232 
1233   /* :authority must come before non-pseudo header fields */
1234   if(authority_idx != 0 && authority_idx != AUTHORITY_DST_IDX) {
1235     nghttp3_nv authority = nva[authority_idx];
1236     for(i = authority_idx; i > AUTHORITY_DST_IDX; --i) {
1237       nva[i] = nva[i - 1];
1238     }
1239     nva[i] = authority;
1240   }
1241 
1242   /* Warn stream may be rejected if cumulative length of headers is too
1243      large. */
1244 #define MAX_ACC 60000  /* <64KB to account for some overhead */
1245   {
1246     size_t acc = 0;
1247     for(i = 0; i < nheader; ++i)
1248       acc += nva[i].namelen + nva[i].valuelen;
1249 
1250     if(acc > MAX_ACC) {
1251       infof(data, "http_request: Warning: The cumulative length of all "
1252             "headers exceeds %zu bytes and that could cause the "
1253             "stream to be rejected.\n", MAX_ACC);
1254     }
1255   }
1256 
1257   switch(data->set.httpreq) {
1258   case HTTPREQ_POST:
1259   case HTTPREQ_POST_FORM:
1260   case HTTPREQ_POST_MIME:
1261   case HTTPREQ_PUT: {
1262     nghttp3_data_reader data_reader;
1263     if(data->state.infilesize != -1)
1264       stream->upload_left = data->state.infilesize;
1265     else
1266       /* data sending without specifying the data amount up front */
1267       stream->upload_left = -1; /* unknown, but not zero */
1268 
1269     data_reader.read_data = cb_h3_readfunction;
1270 
1271     h3out = calloc(sizeof(struct h3out), 1);
1272     if(!h3out) {
1273       result = CURLE_OUT_OF_MEMORY;
1274       goto fail;
1275     }
1276     stream->h3out = h3out;
1277 
1278     rc = nghttp3_conn_submit_request(qs->h3conn, stream->stream3_id,
1279                                      nva, nheader, &data_reader,
1280                                      conn->data);
1281     if(rc) {
1282       result = CURLE_SEND_ERROR;
1283       goto fail;
1284     }
1285     break;
1286   }
1287   default:
1288     stream->upload_left = 0; /* nothing left to send */
1289     rc = nghttp3_conn_submit_request(qs->h3conn, stream->stream3_id,
1290                                      nva, nheader,
1291                                      NULL, /* no body! */
1292                                      conn->data);
1293     if(rc) {
1294       result = CURLE_SEND_ERROR;
1295       goto fail;
1296     }
1297     break;
1298   }
1299 
1300   Curl_safefree(nva);
1301 
1302   infof(data, "Using HTTP/3 Stream ID: %x (easy handle %p)\n",
1303         stream3_id, (void *)data);
1304 
1305   return CURLE_OK;
1306 
1307 fail:
1308   free(nva);
1309   return result;
1310 }
ngh3_stream_send(struct connectdata * conn,int sockindex,const void * mem,size_t len,CURLcode * curlcode)1311 static ssize_t ngh3_stream_send(struct connectdata *conn,
1312                                 int sockindex,
1313                                 const void *mem,
1314                                 size_t len,
1315                                 CURLcode *curlcode)
1316 {
1317   ssize_t sent;
1318   struct quicsocket *qs = conn->quic;
1319   curl_socket_t sockfd = conn->sock[sockindex];
1320   struct HTTP *stream = conn->data->req.protop;
1321 
1322   if(!stream->h3req) {
1323     CURLcode result = http_request(conn, mem, len);
1324     if(result) {
1325       *curlcode = CURLE_SEND_ERROR;
1326       return -1;
1327     }
1328     sent = len;
1329   }
1330   else {
1331     H3BUGF(infof(conn->data, "ngh3_stream_send() wants to send %zd bytes\n",
1332                  len));
1333     if(!stream->upload_len) {
1334       stream->upload_mem = mem;
1335       stream->upload_len = len;
1336       (void)nghttp3_conn_resume_stream(qs->h3conn, stream->stream3_id);
1337       sent = len;
1338     }
1339     else {
1340       *curlcode = CURLE_AGAIN;
1341       return -1;
1342     }
1343   }
1344 
1345   if(ng_flush_egress(conn, sockfd, qs)) {
1346     *curlcode = CURLE_SEND_ERROR;
1347     return -1;
1348   }
1349 
1350   *curlcode = CURLE_OK;
1351   return sent;
1352 }
1353 
ng_has_connected(struct connectdata * conn,int tempindex)1354 static void ng_has_connected(struct connectdata *conn, int tempindex)
1355 {
1356   conn->recv[FIRSTSOCKET] = ngh3_stream_recv;
1357   conn->send[FIRSTSOCKET] = ngh3_stream_send;
1358   conn->handler = &Curl_handler_http3;
1359   conn->bits.multiplex = TRUE; /* at least potentially multiplexed */
1360   conn->httpversion = 30;
1361   conn->bundle->multiuse = BUNDLE_MULTIPLEX;
1362   conn->quic = &conn->hequic[tempindex];
1363   DEBUGF(infof(conn->data, "ngtcp2 established connection!\n"));
1364 }
1365 
1366 /*
1367  * There can be multiple connection attempts going on in parallel.
1368  */
Curl_quic_is_connected(struct connectdata * conn,int sockindex,bool * done)1369 CURLcode Curl_quic_is_connected(struct connectdata *conn,
1370                                 int sockindex,
1371                                 bool *done)
1372 {
1373   CURLcode result;
1374   struct quicsocket *qs = &conn->hequic[sockindex];
1375   curl_socket_t sockfd = conn->tempsock[sockindex];
1376 
1377   result = ng_process_ingress(conn, sockfd, qs);
1378   if(result)
1379     return result;
1380 
1381   result = ng_flush_egress(conn, sockfd, qs);
1382   if(result)
1383     return result;
1384 
1385   if(ngtcp2_conn_get_handshake_completed(qs->qconn)) {
1386     *done = TRUE;
1387     ng_has_connected(conn, sockindex);
1388   }
1389 
1390   return result;
1391 }
1392 
ng_process_ingress(struct connectdata * conn,int sockfd,struct quicsocket * qs)1393 static CURLcode ng_process_ingress(struct connectdata *conn, int sockfd,
1394                                    struct quicsocket *qs)
1395 {
1396   ssize_t recvd;
1397   int rv;
1398   uint8_t buf[65536];
1399   size_t bufsize = sizeof(buf);
1400   struct sockaddr_storage remote_addr;
1401   socklen_t remote_addrlen;
1402   ngtcp2_path path;
1403   ngtcp2_tstamp ts = timestamp();
1404 
1405   for(;;) {
1406     remote_addrlen = sizeof(remote_addr);
1407     while((recvd = recvfrom(sockfd, buf, bufsize, 0,
1408                             (struct sockaddr *)&remote_addr,
1409                             &remote_addrlen)) == -1 &&
1410           SOCKERRNO == EINTR)
1411       ;
1412     if(recvd == -1) {
1413       if(SOCKERRNO == EAGAIN || SOCKERRNO == EWOULDBLOCK)
1414         break;
1415 
1416       failf(conn->data, "ngtcp2: recvfrom() unexpectedly returned %d", recvd);
1417       return CURLE_RECV_ERROR;
1418     }
1419 
1420     ngtcp2_addr_init(&path.local, (uint8_t *)&qs->local_addr,
1421                      qs->local_addrlen, NULL);
1422     ngtcp2_addr_init(&path.remote, (uint8_t *)&remote_addr, remote_addrlen,
1423                      NULL);
1424 
1425     rv = ngtcp2_conn_read_pkt(qs->qconn, &path, buf, recvd, ts);
1426     if(rv != 0) {
1427       /* TODO Send CONNECTION_CLOSE if possible */
1428       return CURLE_RECV_ERROR;
1429     }
1430   }
1431 
1432   return CURLE_OK;
1433 }
1434 
ng_flush_egress(struct connectdata * conn,int sockfd,struct quicsocket * qs)1435 static CURLcode ng_flush_egress(struct connectdata *conn, int sockfd,
1436                                 struct quicsocket *qs)
1437 {
1438   int rv;
1439   ssize_t sent;
1440   ssize_t outlen;
1441   uint8_t out[NGTCP2_MAX_PKTLEN_IPV4];
1442   size_t pktlen;
1443   ngtcp2_path_storage ps;
1444   ngtcp2_tstamp ts = timestamp();
1445   struct sockaddr_storage remote_addr;
1446   ngtcp2_tstamp expiry;
1447   ngtcp2_duration timeout;
1448   int64_t stream_id;
1449   ssize_t veccnt;
1450   int fin;
1451   nghttp3_vec vec[16];
1452   ssize_t ndatalen;
1453 
1454   switch(qs->local_addr.ss_family) {
1455   case AF_INET:
1456     pktlen = NGTCP2_MAX_PKTLEN_IPV4;
1457     break;
1458   case AF_INET6:
1459     pktlen = NGTCP2_MAX_PKTLEN_IPV6;
1460     break;
1461   default:
1462     assert(0);
1463   }
1464 
1465   rv = ngtcp2_conn_handle_expiry(qs->qconn, ts);
1466   if(rv != 0) {
1467     failf(conn->data, "ngtcp2_conn_handle_expiry returned error: %s\n",
1468           ngtcp2_strerror(rv));
1469     return CURLE_SEND_ERROR;
1470   }
1471 
1472   ngtcp2_path_storage_zero(&ps);
1473 
1474   for(;;) {
1475     outlen = -1;
1476     if(qs->h3conn && ngtcp2_conn_get_max_data_left(qs->qconn)) {
1477       veccnt = nghttp3_conn_writev_stream(qs->h3conn, &stream_id, &fin, vec,
1478                                           sizeof(vec) / sizeof(vec[0]));
1479       if(veccnt < 0) {
1480         failf(conn->data, "nghttp3_conn_writev_stream returned error: %s\n",
1481               nghttp3_strerror((int)veccnt));
1482         return CURLE_SEND_ERROR;
1483       }
1484       else if(veccnt > 0) {
1485         outlen =
1486           ngtcp2_conn_writev_stream(qs->qconn, &ps.path,
1487                                     out, pktlen, &ndatalen,
1488                                     NGTCP2_WRITE_STREAM_FLAG_MORE,
1489                                     stream_id, fin,
1490                                     (const ngtcp2_vec *)vec, veccnt, ts);
1491         if(outlen == 0) {
1492           break;
1493         }
1494         if(outlen < 0) {
1495           if(outlen == NGTCP2_ERR_STREAM_DATA_BLOCKED ||
1496              outlen == NGTCP2_ERR_STREAM_SHUT_WR) {
1497             rv = nghttp3_conn_block_stream(qs->h3conn, stream_id);
1498             if(rv != 0) {
1499               failf(conn->data,
1500                     "nghttp3_conn_block_stream returned error: %s\n",
1501                     nghttp3_strerror(rv));
1502               return CURLE_SEND_ERROR;
1503             }
1504             continue;
1505           }
1506           else if(outlen == NGTCP2_ERR_WRITE_STREAM_MORE) {
1507             assert(ndatalen > 0);
1508             rv = nghttp3_conn_add_write_offset(qs->h3conn, stream_id,
1509                                                ndatalen);
1510             if(rv != 0) {
1511               failf(conn->data,
1512                     "nghttp3_conn_add_write_offset returned error: %s\n",
1513                     nghttp3_strerror(rv));
1514               return CURLE_SEND_ERROR;
1515             }
1516             continue;
1517           }
1518           else {
1519             failf(conn->data, "ngtcp2_conn_writev_stream returned error: %s\n",
1520                   ngtcp2_strerror((int)outlen));
1521             return CURLE_SEND_ERROR;
1522           }
1523         }
1524         else if(ndatalen >= 0) {
1525           rv = nghttp3_conn_add_write_offset(qs->h3conn, stream_id, ndatalen);
1526           if(rv != 0) {
1527             failf(conn->data,
1528                   "nghttp3_conn_add_write_offset returned error: %s\n",
1529                   nghttp3_strerror(rv));
1530             return CURLE_SEND_ERROR;
1531           }
1532         }
1533       }
1534     }
1535     if(outlen < 0) {
1536       outlen = ngtcp2_conn_write_pkt(qs->qconn, &ps.path, out, pktlen, ts);
1537       if(outlen < 0) {
1538         failf(conn->data, "ngtcp2_conn_write_pkt returned error: %s\n",
1539               ngtcp2_strerror((int)outlen));
1540         return CURLE_SEND_ERROR;
1541       }
1542       if(outlen == 0)
1543         break;
1544     }
1545 
1546     memcpy(&remote_addr, ps.path.remote.addr, ps.path.remote.addrlen);
1547     while((sent = send(sockfd, out, outlen, 0)) == -1 &&
1548           SOCKERRNO == EINTR)
1549       ;
1550 
1551     if(sent == -1) {
1552       if(SOCKERRNO == EAGAIN || SOCKERRNO == EWOULDBLOCK) {
1553         /* TODO Cache packet */
1554         break;
1555       }
1556       else {
1557         failf(conn->data, "send() returned %zd (errno %d)\n", sent,
1558               SOCKERRNO);
1559         return CURLE_SEND_ERROR;
1560       }
1561     }
1562   }
1563 
1564   expiry = ngtcp2_conn_get_expiry(qs->qconn);
1565   if(expiry != UINT64_MAX) {
1566     if(expiry <= ts) {
1567       timeout = NGTCP2_MILLISECONDS;
1568     }
1569     else {
1570       timeout = expiry - ts;
1571     }
1572     Curl_expire(conn->data, timeout / NGTCP2_MILLISECONDS, EXPIRE_QUIC);
1573   }
1574 
1575   return CURLE_OK;
1576 }
1577 
1578 /*
1579  * Called from transfer.c:done_sending when we stop HTTP/3 uploading.
1580  */
Curl_quic_done_sending(struct connectdata * conn)1581 CURLcode Curl_quic_done_sending(struct connectdata *conn)
1582 {
1583   if(conn->handler == &Curl_handler_http3) {
1584     /* only for HTTP/3 transfers */
1585     struct HTTP *stream = conn->data->req.protop;
1586     struct quicsocket *qs = conn->quic;
1587     stream->upload_done = TRUE;
1588     (void)nghttp3_conn_resume_stream(qs->h3conn, stream->stream3_id);
1589   }
1590 
1591   return CURLE_OK;
1592 }
1593 #endif
1594