"Fossies" - the Fresh Open Source Software Archive

Member "curl-7.66.0/lib/vquic/ngtcp2.c" (9 Sep 2019, 46362 Bytes) of package /linux/www/curl-7.66.0.tar.xz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "ngtcp2.c" see the Fossies "Dox" file reference documentation.

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