113498266Sopenharmony_ci/*************************************************************************** 213498266Sopenharmony_ci * _ _ ____ _ 313498266Sopenharmony_ci * Project ___| | | | _ \| | 413498266Sopenharmony_ci * / __| | | | |_) | | 513498266Sopenharmony_ci * | (__| |_| | _ <| |___ 613498266Sopenharmony_ci * \___|\___/|_| \_\_____| 713498266Sopenharmony_ci * 813498266Sopenharmony_ci * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. 913498266Sopenharmony_ci * 1013498266Sopenharmony_ci * This software is licensed as described in the file COPYING, which 1113498266Sopenharmony_ci * you should have received as part of this distribution. The terms 1213498266Sopenharmony_ci * are also available at https://curl.se/docs/copyright.html. 1313498266Sopenharmony_ci * 1413498266Sopenharmony_ci * You may opt to use, copy, modify, merge, publish, distribute and/or sell 1513498266Sopenharmony_ci * copies of the Software, and permit persons to whom the Software is 1613498266Sopenharmony_ci * furnished to do so, under the terms of the COPYING file. 1713498266Sopenharmony_ci * 1813498266Sopenharmony_ci * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY 1913498266Sopenharmony_ci * KIND, either express or implied. 2013498266Sopenharmony_ci * 2113498266Sopenharmony_ci * SPDX-License-Identifier: curl 2213498266Sopenharmony_ci * 2313498266Sopenharmony_ci ***************************************************************************/ 2413498266Sopenharmony_ci 2513498266Sopenharmony_ci#include "curl_setup.h" 2613498266Sopenharmony_ci 2713498266Sopenharmony_ci#ifdef USE_MSH3 2813498266Sopenharmony_ci 2913498266Sopenharmony_ci#include "urldata.h" 3013498266Sopenharmony_ci#include "timeval.h" 3113498266Sopenharmony_ci#include "multiif.h" 3213498266Sopenharmony_ci#include "sendf.h" 3313498266Sopenharmony_ci#include "curl_trc.h" 3413498266Sopenharmony_ci#include "cfilters.h" 3513498266Sopenharmony_ci#include "cf-socket.h" 3613498266Sopenharmony_ci#include "connect.h" 3713498266Sopenharmony_ci#include "progress.h" 3813498266Sopenharmony_ci#include "http1.h" 3913498266Sopenharmony_ci#include "curl_msh3.h" 4013498266Sopenharmony_ci#include "socketpair.h" 4113498266Sopenharmony_ci#include "vtls/vtls.h" 4213498266Sopenharmony_ci#include "vquic/vquic.h" 4313498266Sopenharmony_ci 4413498266Sopenharmony_ci/* The last 3 #include files should be in this order */ 4513498266Sopenharmony_ci#include "curl_printf.h" 4613498266Sopenharmony_ci#include "curl_memory.h" 4713498266Sopenharmony_ci#include "memdebug.h" 4813498266Sopenharmony_ci 4913498266Sopenharmony_ci#ifdef CURL_DISABLE_SOCKETPAIR 5013498266Sopenharmony_ci#error "MSH3 cannot be build with CURL_DISABLE_SOCKETPAIR set" 5113498266Sopenharmony_ci#endif 5213498266Sopenharmony_ci 5313498266Sopenharmony_ci#define H3_STREAM_WINDOW_SIZE (128 * 1024) 5413498266Sopenharmony_ci#define H3_STREAM_CHUNK_SIZE (16 * 1024) 5513498266Sopenharmony_ci#define H3_STREAM_RECV_CHUNKS \ 5613498266Sopenharmony_ci (H3_STREAM_WINDOW_SIZE / H3_STREAM_CHUNK_SIZE) 5713498266Sopenharmony_ci 5813498266Sopenharmony_ci#ifdef _WIN32 5913498266Sopenharmony_ci#define msh3_lock CRITICAL_SECTION 6013498266Sopenharmony_ci#define msh3_lock_initialize(lock) InitializeCriticalSection(lock) 6113498266Sopenharmony_ci#define msh3_lock_uninitialize(lock) DeleteCriticalSection(lock) 6213498266Sopenharmony_ci#define msh3_lock_acquire(lock) EnterCriticalSection(lock) 6313498266Sopenharmony_ci#define msh3_lock_release(lock) LeaveCriticalSection(lock) 6413498266Sopenharmony_ci#else /* !_WIN32 */ 6513498266Sopenharmony_ci#include <pthread.h> 6613498266Sopenharmony_ci#define msh3_lock pthread_mutex_t 6713498266Sopenharmony_ci#define msh3_lock_initialize(lock) do { \ 6813498266Sopenharmony_ci pthread_mutexattr_t attr; \ 6913498266Sopenharmony_ci pthread_mutexattr_init(&attr); \ 7013498266Sopenharmony_ci pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); \ 7113498266Sopenharmony_ci pthread_mutex_init(lock, &attr); \ 7213498266Sopenharmony_ci pthread_mutexattr_destroy(&attr); \ 7313498266Sopenharmony_ci}while(0) 7413498266Sopenharmony_ci#define msh3_lock_uninitialize(lock) pthread_mutex_destroy(lock) 7513498266Sopenharmony_ci#define msh3_lock_acquire(lock) pthread_mutex_lock(lock) 7613498266Sopenharmony_ci#define msh3_lock_release(lock) pthread_mutex_unlock(lock) 7713498266Sopenharmony_ci#endif /* _WIN32 */ 7813498266Sopenharmony_ci 7913498266Sopenharmony_ci 8013498266Sopenharmony_cistatic void MSH3_CALL msh3_conn_connected(MSH3_CONNECTION *Connection, 8113498266Sopenharmony_ci void *IfContext); 8213498266Sopenharmony_cistatic void MSH3_CALL msh3_conn_shutdown_complete(MSH3_CONNECTION *Connection, 8313498266Sopenharmony_ci void *IfContext); 8413498266Sopenharmony_cistatic void MSH3_CALL msh3_conn_new_request(MSH3_CONNECTION *Connection, 8513498266Sopenharmony_ci void *IfContext, 8613498266Sopenharmony_ci MSH3_REQUEST *Request); 8713498266Sopenharmony_cistatic void MSH3_CALL msh3_header_received(MSH3_REQUEST *Request, 8813498266Sopenharmony_ci void *IfContext, 8913498266Sopenharmony_ci const MSH3_HEADER *Header); 9013498266Sopenharmony_cistatic bool MSH3_CALL msh3_data_received(MSH3_REQUEST *Request, 9113498266Sopenharmony_ci void *IfContext, uint32_t *Length, 9213498266Sopenharmony_ci const uint8_t *Data); 9313498266Sopenharmony_cistatic void MSH3_CALL msh3_complete(MSH3_REQUEST *Request, void *IfContext, 9413498266Sopenharmony_ci bool Aborted, uint64_t AbortError); 9513498266Sopenharmony_cistatic void MSH3_CALL msh3_shutdown_complete(MSH3_REQUEST *Request, 9613498266Sopenharmony_ci void *IfContext); 9713498266Sopenharmony_cistatic void MSH3_CALL msh3_data_sent(MSH3_REQUEST *Request, 9813498266Sopenharmony_ci void *IfContext, void *SendContext); 9913498266Sopenharmony_ci 10013498266Sopenharmony_ci 10113498266Sopenharmony_civoid Curl_msh3_ver(char *p, size_t len) 10213498266Sopenharmony_ci{ 10313498266Sopenharmony_ci uint32_t v[4]; 10413498266Sopenharmony_ci MsH3Version(v); 10513498266Sopenharmony_ci (void)msnprintf(p, len, "msh3/%d.%d.%d.%d", v[0], v[1], v[2], v[3]); 10613498266Sopenharmony_ci} 10713498266Sopenharmony_ci 10813498266Sopenharmony_ci#define SP_LOCAL 0 10913498266Sopenharmony_ci#define SP_REMOTE 1 11013498266Sopenharmony_ci 11113498266Sopenharmony_cistruct cf_msh3_ctx { 11213498266Sopenharmony_ci MSH3_API *api; 11313498266Sopenharmony_ci MSH3_CONNECTION *qconn; 11413498266Sopenharmony_ci struct Curl_sockaddr_ex addr; 11513498266Sopenharmony_ci curl_socket_t sock[2]; /* fake socket pair until we get support in msh3 */ 11613498266Sopenharmony_ci char l_ip[MAX_IPADR_LEN]; /* local IP as string */ 11713498266Sopenharmony_ci int l_port; /* local port number */ 11813498266Sopenharmony_ci struct cf_call_data call_data; 11913498266Sopenharmony_ci struct curltime connect_started; /* time the current attempt started */ 12013498266Sopenharmony_ci struct curltime handshake_at; /* time connect handshake finished */ 12113498266Sopenharmony_ci /* Flags written by msh3/msquic thread */ 12213498266Sopenharmony_ci bool handshake_complete; 12313498266Sopenharmony_ci bool handshake_succeeded; 12413498266Sopenharmony_ci bool connected; 12513498266Sopenharmony_ci /* Flags written by curl thread */ 12613498266Sopenharmony_ci BIT(verbose); 12713498266Sopenharmony_ci BIT(active); 12813498266Sopenharmony_ci}; 12913498266Sopenharmony_ci 13013498266Sopenharmony_ci/* How to access `call_data` from a cf_msh3 filter */ 13113498266Sopenharmony_ci#undef CF_CTX_CALL_DATA 13213498266Sopenharmony_ci#define CF_CTX_CALL_DATA(cf) \ 13313498266Sopenharmony_ci ((struct cf_msh3_ctx *)(cf)->ctx)->call_data 13413498266Sopenharmony_ci 13513498266Sopenharmony_ci/** 13613498266Sopenharmony_ci * All about the H3 internals of a stream 13713498266Sopenharmony_ci */ 13813498266Sopenharmony_cistruct stream_ctx { 13913498266Sopenharmony_ci struct MSH3_REQUEST *req; 14013498266Sopenharmony_ci struct bufq recvbuf; /* h3 response */ 14113498266Sopenharmony_ci#ifdef _WIN32 14213498266Sopenharmony_ci CRITICAL_SECTION recv_lock; 14313498266Sopenharmony_ci#else /* !_WIN32 */ 14413498266Sopenharmony_ci pthread_mutex_t recv_lock; 14513498266Sopenharmony_ci#endif /* _WIN32 */ 14613498266Sopenharmony_ci uint64_t error3; /* HTTP/3 stream error code */ 14713498266Sopenharmony_ci int status_code; /* HTTP status code */ 14813498266Sopenharmony_ci CURLcode recv_error; 14913498266Sopenharmony_ci bool closed; 15013498266Sopenharmony_ci bool reset; 15113498266Sopenharmony_ci bool upload_done; 15213498266Sopenharmony_ci bool firstheader; /* FALSE until headers arrive */ 15313498266Sopenharmony_ci bool recv_header_complete; 15413498266Sopenharmony_ci}; 15513498266Sopenharmony_ci 15613498266Sopenharmony_ci#define H3_STREAM_CTX(d) ((struct stream_ctx *)(((d) && (d)->req.p.http)? \ 15713498266Sopenharmony_ci ((struct HTTP *)(d)->req.p.http)->h3_ctx \ 15813498266Sopenharmony_ci : NULL)) 15913498266Sopenharmony_ci#define H3_STREAM_LCTX(d) ((struct HTTP *)(d)->req.p.http)->h3_ctx 16013498266Sopenharmony_ci#define H3_STREAM_ID(d) (H3_STREAM_CTX(d)? \ 16113498266Sopenharmony_ci H3_STREAM_CTX(d)->id : -2) 16213498266Sopenharmony_ci 16313498266Sopenharmony_ci 16413498266Sopenharmony_cistatic CURLcode h3_data_setup(struct Curl_cfilter *cf, 16513498266Sopenharmony_ci struct Curl_easy *data) 16613498266Sopenharmony_ci{ 16713498266Sopenharmony_ci struct stream_ctx *stream = H3_STREAM_CTX(data); 16813498266Sopenharmony_ci 16913498266Sopenharmony_ci if(stream) 17013498266Sopenharmony_ci return CURLE_OK; 17113498266Sopenharmony_ci 17213498266Sopenharmony_ci stream = calloc(1, sizeof(*stream)); 17313498266Sopenharmony_ci if(!stream) 17413498266Sopenharmony_ci return CURLE_OUT_OF_MEMORY; 17513498266Sopenharmony_ci 17613498266Sopenharmony_ci H3_STREAM_LCTX(data) = stream; 17713498266Sopenharmony_ci stream->req = ZERO_NULL; 17813498266Sopenharmony_ci msh3_lock_initialize(&stream->recv_lock); 17913498266Sopenharmony_ci Curl_bufq_init2(&stream->recvbuf, H3_STREAM_CHUNK_SIZE, 18013498266Sopenharmony_ci H3_STREAM_RECV_CHUNKS, BUFQ_OPT_SOFT_LIMIT); 18113498266Sopenharmony_ci CURL_TRC_CF(data, cf, "data setup"); 18213498266Sopenharmony_ci return CURLE_OK; 18313498266Sopenharmony_ci} 18413498266Sopenharmony_ci 18513498266Sopenharmony_cistatic void h3_data_done(struct Curl_cfilter *cf, struct Curl_easy *data) 18613498266Sopenharmony_ci{ 18713498266Sopenharmony_ci struct stream_ctx *stream = H3_STREAM_CTX(data); 18813498266Sopenharmony_ci 18913498266Sopenharmony_ci (void)cf; 19013498266Sopenharmony_ci if(stream) { 19113498266Sopenharmony_ci CURL_TRC_CF(data, cf, "easy handle is done"); 19213498266Sopenharmony_ci Curl_bufq_free(&stream->recvbuf); 19313498266Sopenharmony_ci free(stream); 19413498266Sopenharmony_ci H3_STREAM_LCTX(data) = NULL; 19513498266Sopenharmony_ci } 19613498266Sopenharmony_ci} 19713498266Sopenharmony_ci 19813498266Sopenharmony_cistatic void drain_stream_from_other_thread(struct Curl_easy *data, 19913498266Sopenharmony_ci struct stream_ctx *stream) 20013498266Sopenharmony_ci{ 20113498266Sopenharmony_ci unsigned char bits; 20213498266Sopenharmony_ci 20313498266Sopenharmony_ci /* risky */ 20413498266Sopenharmony_ci bits = CURL_CSELECT_IN; 20513498266Sopenharmony_ci if(stream && !stream->upload_done) 20613498266Sopenharmony_ci bits |= CURL_CSELECT_OUT; 20713498266Sopenharmony_ci if(data->state.select_bits != bits) { 20813498266Sopenharmony_ci data->state.select_bits = bits; 20913498266Sopenharmony_ci /* cannot expire from other thread */ 21013498266Sopenharmony_ci } 21113498266Sopenharmony_ci} 21213498266Sopenharmony_ci 21313498266Sopenharmony_cistatic void drain_stream(struct Curl_cfilter *cf, 21413498266Sopenharmony_ci struct Curl_easy *data) 21513498266Sopenharmony_ci{ 21613498266Sopenharmony_ci struct stream_ctx *stream = H3_STREAM_CTX(data); 21713498266Sopenharmony_ci unsigned char bits; 21813498266Sopenharmony_ci 21913498266Sopenharmony_ci (void)cf; 22013498266Sopenharmony_ci bits = CURL_CSELECT_IN; 22113498266Sopenharmony_ci if(stream && !stream->upload_done) 22213498266Sopenharmony_ci bits |= CURL_CSELECT_OUT; 22313498266Sopenharmony_ci if(data->state.select_bits != bits) { 22413498266Sopenharmony_ci data->state.select_bits = bits; 22513498266Sopenharmony_ci Curl_expire(data, 0, EXPIRE_RUN_NOW); 22613498266Sopenharmony_ci } 22713498266Sopenharmony_ci} 22813498266Sopenharmony_ci 22913498266Sopenharmony_cistatic const MSH3_CONNECTION_IF msh3_conn_if = { 23013498266Sopenharmony_ci msh3_conn_connected, 23113498266Sopenharmony_ci msh3_conn_shutdown_complete, 23213498266Sopenharmony_ci msh3_conn_new_request 23313498266Sopenharmony_ci}; 23413498266Sopenharmony_ci 23513498266Sopenharmony_cistatic void MSH3_CALL msh3_conn_connected(MSH3_CONNECTION *Connection, 23613498266Sopenharmony_ci void *IfContext) 23713498266Sopenharmony_ci{ 23813498266Sopenharmony_ci struct Curl_cfilter *cf = IfContext; 23913498266Sopenharmony_ci struct cf_msh3_ctx *ctx = cf->ctx; 24013498266Sopenharmony_ci struct Curl_easy *data = CF_DATA_CURRENT(cf); 24113498266Sopenharmony_ci (void)Connection; 24213498266Sopenharmony_ci 24313498266Sopenharmony_ci CURL_TRC_CF(data, cf, "[MSH3] connected"); 24413498266Sopenharmony_ci ctx->handshake_succeeded = true; 24513498266Sopenharmony_ci ctx->connected = true; 24613498266Sopenharmony_ci ctx->handshake_complete = true; 24713498266Sopenharmony_ci} 24813498266Sopenharmony_ci 24913498266Sopenharmony_cistatic void MSH3_CALL msh3_conn_shutdown_complete(MSH3_CONNECTION *Connection, 25013498266Sopenharmony_ci void *IfContext) 25113498266Sopenharmony_ci{ 25213498266Sopenharmony_ci struct Curl_cfilter *cf = IfContext; 25313498266Sopenharmony_ci struct cf_msh3_ctx *ctx = cf->ctx; 25413498266Sopenharmony_ci struct Curl_easy *data = CF_DATA_CURRENT(cf); 25513498266Sopenharmony_ci 25613498266Sopenharmony_ci (void)Connection; 25713498266Sopenharmony_ci CURL_TRC_CF(data, cf, "[MSH3] shutdown complete"); 25813498266Sopenharmony_ci ctx->connected = false; 25913498266Sopenharmony_ci ctx->handshake_complete = true; 26013498266Sopenharmony_ci} 26113498266Sopenharmony_ci 26213498266Sopenharmony_cistatic void MSH3_CALL msh3_conn_new_request(MSH3_CONNECTION *Connection, 26313498266Sopenharmony_ci void *IfContext, 26413498266Sopenharmony_ci MSH3_REQUEST *Request) 26513498266Sopenharmony_ci{ 26613498266Sopenharmony_ci (void)Connection; 26713498266Sopenharmony_ci (void)IfContext; 26813498266Sopenharmony_ci (void)Request; 26913498266Sopenharmony_ci} 27013498266Sopenharmony_ci 27113498266Sopenharmony_cistatic const MSH3_REQUEST_IF msh3_request_if = { 27213498266Sopenharmony_ci msh3_header_received, 27313498266Sopenharmony_ci msh3_data_received, 27413498266Sopenharmony_ci msh3_complete, 27513498266Sopenharmony_ci msh3_shutdown_complete, 27613498266Sopenharmony_ci msh3_data_sent 27713498266Sopenharmony_ci}; 27813498266Sopenharmony_ci 27913498266Sopenharmony_ci/* Decode HTTP status code. Returns -1 if no valid status code was 28013498266Sopenharmony_ci decoded. (duplicate from http2.c) */ 28113498266Sopenharmony_cistatic int decode_status_code(const char *value, size_t len) 28213498266Sopenharmony_ci{ 28313498266Sopenharmony_ci int i; 28413498266Sopenharmony_ci int res; 28513498266Sopenharmony_ci 28613498266Sopenharmony_ci if(len != 3) { 28713498266Sopenharmony_ci return -1; 28813498266Sopenharmony_ci } 28913498266Sopenharmony_ci 29013498266Sopenharmony_ci res = 0; 29113498266Sopenharmony_ci 29213498266Sopenharmony_ci for(i = 0; i < 3; ++i) { 29313498266Sopenharmony_ci char c = value[i]; 29413498266Sopenharmony_ci 29513498266Sopenharmony_ci if(c < '0' || c > '9') { 29613498266Sopenharmony_ci return -1; 29713498266Sopenharmony_ci } 29813498266Sopenharmony_ci 29913498266Sopenharmony_ci res *= 10; 30013498266Sopenharmony_ci res += c - '0'; 30113498266Sopenharmony_ci } 30213498266Sopenharmony_ci 30313498266Sopenharmony_ci return res; 30413498266Sopenharmony_ci} 30513498266Sopenharmony_ci 30613498266Sopenharmony_ci/* 30713498266Sopenharmony_ci * write_resp_raw() copies response data in raw format to the `data`'s 30813498266Sopenharmony_ci * receive buffer. If not enough space is available, it appends to the 30913498266Sopenharmony_ci * `data`'s overflow buffer. 31013498266Sopenharmony_ci */ 31113498266Sopenharmony_cistatic CURLcode write_resp_raw(struct Curl_easy *data, 31213498266Sopenharmony_ci const void *mem, size_t memlen) 31313498266Sopenharmony_ci{ 31413498266Sopenharmony_ci struct stream_ctx *stream = H3_STREAM_CTX(data); 31513498266Sopenharmony_ci CURLcode result = CURLE_OK; 31613498266Sopenharmony_ci ssize_t nwritten; 31713498266Sopenharmony_ci 31813498266Sopenharmony_ci if(!stream) 31913498266Sopenharmony_ci return CURLE_RECV_ERROR; 32013498266Sopenharmony_ci 32113498266Sopenharmony_ci nwritten = Curl_bufq_write(&stream->recvbuf, mem, memlen, &result); 32213498266Sopenharmony_ci if(nwritten < 0) { 32313498266Sopenharmony_ci return result; 32413498266Sopenharmony_ci } 32513498266Sopenharmony_ci 32613498266Sopenharmony_ci if((size_t)nwritten < memlen) { 32713498266Sopenharmony_ci /* This MUST not happen. Our recbuf is dimensioned to hold the 32813498266Sopenharmony_ci * full max_stream_window and then some for this very reason. */ 32913498266Sopenharmony_ci DEBUGASSERT(0); 33013498266Sopenharmony_ci return CURLE_RECV_ERROR; 33113498266Sopenharmony_ci } 33213498266Sopenharmony_ci return result; 33313498266Sopenharmony_ci} 33413498266Sopenharmony_ci 33513498266Sopenharmony_cistatic void MSH3_CALL msh3_header_received(MSH3_REQUEST *Request, 33613498266Sopenharmony_ci void *userp, 33713498266Sopenharmony_ci const MSH3_HEADER *hd) 33813498266Sopenharmony_ci{ 33913498266Sopenharmony_ci struct Curl_easy *data = userp; 34013498266Sopenharmony_ci struct stream_ctx *stream = H3_STREAM_CTX(data); 34113498266Sopenharmony_ci CURLcode result; 34213498266Sopenharmony_ci (void)Request; 34313498266Sopenharmony_ci 34413498266Sopenharmony_ci if(!stream || stream->recv_header_complete) { 34513498266Sopenharmony_ci return; 34613498266Sopenharmony_ci } 34713498266Sopenharmony_ci 34813498266Sopenharmony_ci msh3_lock_acquire(&stream->recv_lock); 34913498266Sopenharmony_ci 35013498266Sopenharmony_ci if((hd->NameLength == 7) && 35113498266Sopenharmony_ci !strncmp(HTTP_PSEUDO_STATUS, (char *)hd->Name, 7)) { 35213498266Sopenharmony_ci char line[14]; /* status line is always 13 characters long */ 35313498266Sopenharmony_ci size_t ncopy; 35413498266Sopenharmony_ci 35513498266Sopenharmony_ci DEBUGASSERT(!stream->firstheader); 35613498266Sopenharmony_ci stream->status_code = decode_status_code(hd->Value, hd->ValueLength); 35713498266Sopenharmony_ci DEBUGASSERT(stream->status_code != -1); 35813498266Sopenharmony_ci ncopy = msnprintf(line, sizeof(line), "HTTP/3 %03d \r\n", 35913498266Sopenharmony_ci stream->status_code); 36013498266Sopenharmony_ci result = write_resp_raw(data, line, ncopy); 36113498266Sopenharmony_ci if(result) 36213498266Sopenharmony_ci stream->recv_error = result; 36313498266Sopenharmony_ci stream->firstheader = TRUE; 36413498266Sopenharmony_ci } 36513498266Sopenharmony_ci else { 36613498266Sopenharmony_ci /* store as an HTTP1-style header */ 36713498266Sopenharmony_ci DEBUGASSERT(stream->firstheader); 36813498266Sopenharmony_ci result = write_resp_raw(data, hd->Name, hd->NameLength); 36913498266Sopenharmony_ci if(!result) 37013498266Sopenharmony_ci result = write_resp_raw(data, ": ", 2); 37113498266Sopenharmony_ci if(!result) 37213498266Sopenharmony_ci result = write_resp_raw(data, hd->Value, hd->ValueLength); 37313498266Sopenharmony_ci if(!result) 37413498266Sopenharmony_ci result = write_resp_raw(data, "\r\n", 2); 37513498266Sopenharmony_ci if(result) { 37613498266Sopenharmony_ci stream->recv_error = result; 37713498266Sopenharmony_ci } 37813498266Sopenharmony_ci } 37913498266Sopenharmony_ci 38013498266Sopenharmony_ci drain_stream_from_other_thread(data, stream); 38113498266Sopenharmony_ci msh3_lock_release(&stream->recv_lock); 38213498266Sopenharmony_ci} 38313498266Sopenharmony_ci 38413498266Sopenharmony_cistatic bool MSH3_CALL msh3_data_received(MSH3_REQUEST *Request, 38513498266Sopenharmony_ci void *IfContext, uint32_t *buflen, 38613498266Sopenharmony_ci const uint8_t *buf) 38713498266Sopenharmony_ci{ 38813498266Sopenharmony_ci struct Curl_easy *data = IfContext; 38913498266Sopenharmony_ci struct stream_ctx *stream = H3_STREAM_CTX(data); 39013498266Sopenharmony_ci CURLcode result; 39113498266Sopenharmony_ci bool rv = FALSE; 39213498266Sopenharmony_ci 39313498266Sopenharmony_ci /* TODO: we would like to limit the amount of data we are buffer here. 39413498266Sopenharmony_ci * There seems to be no mechanism in msh3 to adjust flow control and 39513498266Sopenharmony_ci * it is undocumented what happens if we return FALSE here or less 39613498266Sopenharmony_ci * length (buflen is an inout parameter). 39713498266Sopenharmony_ci */ 39813498266Sopenharmony_ci (void)Request; 39913498266Sopenharmony_ci if(!stream) 40013498266Sopenharmony_ci return FALSE; 40113498266Sopenharmony_ci 40213498266Sopenharmony_ci msh3_lock_acquire(&stream->recv_lock); 40313498266Sopenharmony_ci 40413498266Sopenharmony_ci if(!stream->recv_header_complete) { 40513498266Sopenharmony_ci result = write_resp_raw(data, "\r\n", 2); 40613498266Sopenharmony_ci if(result) { 40713498266Sopenharmony_ci stream->recv_error = result; 40813498266Sopenharmony_ci goto out; 40913498266Sopenharmony_ci } 41013498266Sopenharmony_ci stream->recv_header_complete = true; 41113498266Sopenharmony_ci } 41213498266Sopenharmony_ci 41313498266Sopenharmony_ci result = write_resp_raw(data, buf, *buflen); 41413498266Sopenharmony_ci if(result) { 41513498266Sopenharmony_ci stream->recv_error = result; 41613498266Sopenharmony_ci } 41713498266Sopenharmony_ci rv = TRUE; 41813498266Sopenharmony_ci 41913498266Sopenharmony_ciout: 42013498266Sopenharmony_ci msh3_lock_release(&stream->recv_lock); 42113498266Sopenharmony_ci return rv; 42213498266Sopenharmony_ci} 42313498266Sopenharmony_ci 42413498266Sopenharmony_cistatic void MSH3_CALL msh3_complete(MSH3_REQUEST *Request, void *IfContext, 42513498266Sopenharmony_ci bool aborted, uint64_t error) 42613498266Sopenharmony_ci{ 42713498266Sopenharmony_ci struct Curl_easy *data = IfContext; 42813498266Sopenharmony_ci struct stream_ctx *stream = H3_STREAM_CTX(data); 42913498266Sopenharmony_ci 43013498266Sopenharmony_ci (void)Request; 43113498266Sopenharmony_ci if(!stream) 43213498266Sopenharmony_ci return; 43313498266Sopenharmony_ci msh3_lock_acquire(&stream->recv_lock); 43413498266Sopenharmony_ci stream->closed = TRUE; 43513498266Sopenharmony_ci stream->recv_header_complete = true; 43613498266Sopenharmony_ci if(error) 43713498266Sopenharmony_ci stream->error3 = error; 43813498266Sopenharmony_ci if(aborted) 43913498266Sopenharmony_ci stream->reset = TRUE; 44013498266Sopenharmony_ci msh3_lock_release(&stream->recv_lock); 44113498266Sopenharmony_ci} 44213498266Sopenharmony_ci 44313498266Sopenharmony_cistatic void MSH3_CALL msh3_shutdown_complete(MSH3_REQUEST *Request, 44413498266Sopenharmony_ci void *IfContext) 44513498266Sopenharmony_ci{ 44613498266Sopenharmony_ci struct Curl_easy *data = IfContext; 44713498266Sopenharmony_ci struct stream_ctx *stream = H3_STREAM_CTX(data); 44813498266Sopenharmony_ci 44913498266Sopenharmony_ci if(!stream) 45013498266Sopenharmony_ci return; 45113498266Sopenharmony_ci (void)Request; 45213498266Sopenharmony_ci (void)stream; 45313498266Sopenharmony_ci} 45413498266Sopenharmony_ci 45513498266Sopenharmony_cistatic void MSH3_CALL msh3_data_sent(MSH3_REQUEST *Request, 45613498266Sopenharmony_ci void *IfContext, void *SendContext) 45713498266Sopenharmony_ci{ 45813498266Sopenharmony_ci struct Curl_easy *data = IfContext; 45913498266Sopenharmony_ci struct stream_ctx *stream = H3_STREAM_CTX(data); 46013498266Sopenharmony_ci if(!stream) 46113498266Sopenharmony_ci return; 46213498266Sopenharmony_ci (void)Request; 46313498266Sopenharmony_ci (void)stream; 46413498266Sopenharmony_ci (void)SendContext; 46513498266Sopenharmony_ci} 46613498266Sopenharmony_ci 46713498266Sopenharmony_cistatic ssize_t recv_closed_stream(struct Curl_cfilter *cf, 46813498266Sopenharmony_ci struct Curl_easy *data, 46913498266Sopenharmony_ci CURLcode *err) 47013498266Sopenharmony_ci{ 47113498266Sopenharmony_ci struct stream_ctx *stream = H3_STREAM_CTX(data); 47213498266Sopenharmony_ci ssize_t nread = -1; 47313498266Sopenharmony_ci 47413498266Sopenharmony_ci if(!stream) { 47513498266Sopenharmony_ci *err = CURLE_RECV_ERROR; 47613498266Sopenharmony_ci return -1; 47713498266Sopenharmony_ci } 47813498266Sopenharmony_ci (void)cf; 47913498266Sopenharmony_ci if(stream->reset) { 48013498266Sopenharmony_ci failf(data, "HTTP/3 stream reset by server"); 48113498266Sopenharmony_ci *err = CURLE_PARTIAL_FILE; 48213498266Sopenharmony_ci CURL_TRC_CF(data, cf, "cf_recv, was reset -> %d", *err); 48313498266Sopenharmony_ci goto out; 48413498266Sopenharmony_ci } 48513498266Sopenharmony_ci else if(stream->error3) { 48613498266Sopenharmony_ci failf(data, "HTTP/3 stream was not closed cleanly: (error %zd)", 48713498266Sopenharmony_ci (ssize_t)stream->error3); 48813498266Sopenharmony_ci *err = CURLE_HTTP3; 48913498266Sopenharmony_ci CURL_TRC_CF(data, cf, "cf_recv, closed uncleanly -> %d", *err); 49013498266Sopenharmony_ci goto out; 49113498266Sopenharmony_ci } 49213498266Sopenharmony_ci else { 49313498266Sopenharmony_ci CURL_TRC_CF(data, cf, "cf_recv, closed ok -> %d", *err); 49413498266Sopenharmony_ci } 49513498266Sopenharmony_ci *err = CURLE_OK; 49613498266Sopenharmony_ci nread = 0; 49713498266Sopenharmony_ci 49813498266Sopenharmony_ciout: 49913498266Sopenharmony_ci return nread; 50013498266Sopenharmony_ci} 50113498266Sopenharmony_ci 50213498266Sopenharmony_cistatic void set_quic_expire(struct Curl_cfilter *cf, struct Curl_easy *data) 50313498266Sopenharmony_ci{ 50413498266Sopenharmony_ci struct stream_ctx *stream = H3_STREAM_CTX(data); 50513498266Sopenharmony_ci 50613498266Sopenharmony_ci /* we have no indication from msh3 when it would be a good time 50713498266Sopenharmony_ci * to juggle the connection again. So, we compromise by calling 50813498266Sopenharmony_ci * us again every some milliseconds. */ 50913498266Sopenharmony_ci (void)cf; 51013498266Sopenharmony_ci if(stream && stream->req && !stream->closed) { 51113498266Sopenharmony_ci Curl_expire(data, 10, EXPIRE_QUIC); 51213498266Sopenharmony_ci } 51313498266Sopenharmony_ci else { 51413498266Sopenharmony_ci Curl_expire(data, 50, EXPIRE_QUIC); 51513498266Sopenharmony_ci } 51613498266Sopenharmony_ci} 51713498266Sopenharmony_ci 51813498266Sopenharmony_cistatic ssize_t cf_msh3_recv(struct Curl_cfilter *cf, struct Curl_easy *data, 51913498266Sopenharmony_ci char *buf, size_t len, CURLcode *err) 52013498266Sopenharmony_ci{ 52113498266Sopenharmony_ci struct stream_ctx *stream = H3_STREAM_CTX(data); 52213498266Sopenharmony_ci ssize_t nread = -1; 52313498266Sopenharmony_ci struct cf_call_data save; 52413498266Sopenharmony_ci 52513498266Sopenharmony_ci (void)cf; 52613498266Sopenharmony_ci if(!stream) { 52713498266Sopenharmony_ci *err = CURLE_RECV_ERROR; 52813498266Sopenharmony_ci return -1; 52913498266Sopenharmony_ci } 53013498266Sopenharmony_ci CF_DATA_SAVE(save, cf, data); 53113498266Sopenharmony_ci CURL_TRC_CF(data, cf, "req: recv with %zu byte buffer", len); 53213498266Sopenharmony_ci 53313498266Sopenharmony_ci msh3_lock_acquire(&stream->recv_lock); 53413498266Sopenharmony_ci 53513498266Sopenharmony_ci if(stream->recv_error) { 53613498266Sopenharmony_ci failf(data, "request aborted"); 53713498266Sopenharmony_ci *err = stream->recv_error; 53813498266Sopenharmony_ci goto out; 53913498266Sopenharmony_ci } 54013498266Sopenharmony_ci 54113498266Sopenharmony_ci *err = CURLE_OK; 54213498266Sopenharmony_ci 54313498266Sopenharmony_ci if(!Curl_bufq_is_empty(&stream->recvbuf)) { 54413498266Sopenharmony_ci nread = Curl_bufq_read(&stream->recvbuf, 54513498266Sopenharmony_ci (unsigned char *)buf, len, err); 54613498266Sopenharmony_ci CURL_TRC_CF(data, cf, "read recvbuf(len=%zu) -> %zd, %d", 54713498266Sopenharmony_ci len, nread, *err); 54813498266Sopenharmony_ci if(nread < 0) 54913498266Sopenharmony_ci goto out; 55013498266Sopenharmony_ci if(stream->closed) 55113498266Sopenharmony_ci drain_stream(cf, data); 55213498266Sopenharmony_ci } 55313498266Sopenharmony_ci else if(stream->closed) { 55413498266Sopenharmony_ci nread = recv_closed_stream(cf, data, err); 55513498266Sopenharmony_ci goto out; 55613498266Sopenharmony_ci } 55713498266Sopenharmony_ci else { 55813498266Sopenharmony_ci CURL_TRC_CF(data, cf, "req: nothing here, call again"); 55913498266Sopenharmony_ci *err = CURLE_AGAIN; 56013498266Sopenharmony_ci } 56113498266Sopenharmony_ci 56213498266Sopenharmony_ciout: 56313498266Sopenharmony_ci msh3_lock_release(&stream->recv_lock); 56413498266Sopenharmony_ci set_quic_expire(cf, data); 56513498266Sopenharmony_ci CF_DATA_RESTORE(cf, save); 56613498266Sopenharmony_ci return nread; 56713498266Sopenharmony_ci} 56813498266Sopenharmony_ci 56913498266Sopenharmony_cistatic ssize_t cf_msh3_send(struct Curl_cfilter *cf, struct Curl_easy *data, 57013498266Sopenharmony_ci const void *buf, size_t len, CURLcode *err) 57113498266Sopenharmony_ci{ 57213498266Sopenharmony_ci struct cf_msh3_ctx *ctx = cf->ctx; 57313498266Sopenharmony_ci struct stream_ctx *stream = H3_STREAM_CTX(data); 57413498266Sopenharmony_ci struct h1_req_parser h1; 57513498266Sopenharmony_ci struct dynhds h2_headers; 57613498266Sopenharmony_ci MSH3_HEADER *nva = NULL; 57713498266Sopenharmony_ci size_t nheader, i; 57813498266Sopenharmony_ci ssize_t nwritten = -1; 57913498266Sopenharmony_ci struct cf_call_data save; 58013498266Sopenharmony_ci bool eos; 58113498266Sopenharmony_ci 58213498266Sopenharmony_ci CF_DATA_SAVE(save, cf, data); 58313498266Sopenharmony_ci 58413498266Sopenharmony_ci Curl_h1_req_parse_init(&h1, H1_PARSE_DEFAULT_MAX_LINE_LEN); 58513498266Sopenharmony_ci Curl_dynhds_init(&h2_headers, 0, DYN_HTTP_REQUEST); 58613498266Sopenharmony_ci 58713498266Sopenharmony_ci /* Sizes must match for cast below to work" */ 58813498266Sopenharmony_ci DEBUGASSERT(stream); 58913498266Sopenharmony_ci CURL_TRC_CF(data, cf, "req: send %zu bytes", len); 59013498266Sopenharmony_ci 59113498266Sopenharmony_ci if(!stream->req) { 59213498266Sopenharmony_ci /* The first send on the request contains the headers and possibly some 59313498266Sopenharmony_ci data. Parse out the headers and create the request, then if there is 59413498266Sopenharmony_ci any data left over go ahead and send it too. */ 59513498266Sopenharmony_ci nwritten = Curl_h1_req_parse_read(&h1, buf, len, NULL, 0, err); 59613498266Sopenharmony_ci if(nwritten < 0) 59713498266Sopenharmony_ci goto out; 59813498266Sopenharmony_ci DEBUGASSERT(h1.done); 59913498266Sopenharmony_ci DEBUGASSERT(h1.req); 60013498266Sopenharmony_ci 60113498266Sopenharmony_ci *err = Curl_http_req_to_h2(&h2_headers, h1.req, data); 60213498266Sopenharmony_ci if(*err) { 60313498266Sopenharmony_ci nwritten = -1; 60413498266Sopenharmony_ci goto out; 60513498266Sopenharmony_ci } 60613498266Sopenharmony_ci 60713498266Sopenharmony_ci nheader = Curl_dynhds_count(&h2_headers); 60813498266Sopenharmony_ci nva = malloc(sizeof(MSH3_HEADER) * nheader); 60913498266Sopenharmony_ci if(!nva) { 61013498266Sopenharmony_ci *err = CURLE_OUT_OF_MEMORY; 61113498266Sopenharmony_ci nwritten = -1; 61213498266Sopenharmony_ci goto out; 61313498266Sopenharmony_ci } 61413498266Sopenharmony_ci 61513498266Sopenharmony_ci for(i = 0; i < nheader; ++i) { 61613498266Sopenharmony_ci struct dynhds_entry *e = Curl_dynhds_getn(&h2_headers, i); 61713498266Sopenharmony_ci nva[i].Name = e->name; 61813498266Sopenharmony_ci nva[i].NameLength = e->namelen; 61913498266Sopenharmony_ci nva[i].Value = e->value; 62013498266Sopenharmony_ci nva[i].ValueLength = e->valuelen; 62113498266Sopenharmony_ci } 62213498266Sopenharmony_ci 62313498266Sopenharmony_ci switch(data->state.httpreq) { 62413498266Sopenharmony_ci case HTTPREQ_POST: 62513498266Sopenharmony_ci case HTTPREQ_POST_FORM: 62613498266Sopenharmony_ci case HTTPREQ_POST_MIME: 62713498266Sopenharmony_ci case HTTPREQ_PUT: 62813498266Sopenharmony_ci /* known request body size or -1 */ 62913498266Sopenharmony_ci eos = FALSE; 63013498266Sopenharmony_ci break; 63113498266Sopenharmony_ci default: 63213498266Sopenharmony_ci /* there is not request body */ 63313498266Sopenharmony_ci eos = TRUE; 63413498266Sopenharmony_ci stream->upload_done = TRUE; 63513498266Sopenharmony_ci break; 63613498266Sopenharmony_ci } 63713498266Sopenharmony_ci 63813498266Sopenharmony_ci CURL_TRC_CF(data, cf, "req: send %zu headers", nheader); 63913498266Sopenharmony_ci stream->req = MsH3RequestOpen(ctx->qconn, &msh3_request_if, data, 64013498266Sopenharmony_ci nva, nheader, 64113498266Sopenharmony_ci eos ? MSH3_REQUEST_FLAG_FIN : 64213498266Sopenharmony_ci MSH3_REQUEST_FLAG_NONE); 64313498266Sopenharmony_ci if(!stream->req) { 64413498266Sopenharmony_ci failf(data, "request open failed"); 64513498266Sopenharmony_ci *err = CURLE_SEND_ERROR; 64613498266Sopenharmony_ci goto out; 64713498266Sopenharmony_ci } 64813498266Sopenharmony_ci *err = CURLE_OK; 64913498266Sopenharmony_ci nwritten = len; 65013498266Sopenharmony_ci goto out; 65113498266Sopenharmony_ci } 65213498266Sopenharmony_ci else { 65313498266Sopenharmony_ci /* request is open */ 65413498266Sopenharmony_ci CURL_TRC_CF(data, cf, "req: send %zu body bytes", len); 65513498266Sopenharmony_ci if(len > 0xFFFFFFFF) { 65613498266Sopenharmony_ci len = 0xFFFFFFFF; 65713498266Sopenharmony_ci } 65813498266Sopenharmony_ci 65913498266Sopenharmony_ci if(!MsH3RequestSend(stream->req, MSH3_REQUEST_FLAG_NONE, buf, 66013498266Sopenharmony_ci (uint32_t)len, stream)) { 66113498266Sopenharmony_ci *err = CURLE_SEND_ERROR; 66213498266Sopenharmony_ci goto out; 66313498266Sopenharmony_ci } 66413498266Sopenharmony_ci 66513498266Sopenharmony_ci /* TODO - msh3/msquic will hold onto this memory until the send complete 66613498266Sopenharmony_ci event. How do we make sure curl doesn't free it until then? */ 66713498266Sopenharmony_ci *err = CURLE_OK; 66813498266Sopenharmony_ci nwritten = len; 66913498266Sopenharmony_ci } 67013498266Sopenharmony_ci 67113498266Sopenharmony_ciout: 67213498266Sopenharmony_ci set_quic_expire(cf, data); 67313498266Sopenharmony_ci free(nva); 67413498266Sopenharmony_ci Curl_h1_req_parse_free(&h1); 67513498266Sopenharmony_ci Curl_dynhds_free(&h2_headers); 67613498266Sopenharmony_ci CF_DATA_RESTORE(cf, save); 67713498266Sopenharmony_ci return nwritten; 67813498266Sopenharmony_ci} 67913498266Sopenharmony_ci 68013498266Sopenharmony_cistatic void cf_msh3_adjust_pollset(struct Curl_cfilter *cf, 68113498266Sopenharmony_ci struct Curl_easy *data, 68213498266Sopenharmony_ci struct easy_pollset *ps) 68313498266Sopenharmony_ci{ 68413498266Sopenharmony_ci struct cf_msh3_ctx *ctx = cf->ctx; 68513498266Sopenharmony_ci struct stream_ctx *stream = H3_STREAM_CTX(data); 68613498266Sopenharmony_ci struct cf_call_data save; 68713498266Sopenharmony_ci 68813498266Sopenharmony_ci CF_DATA_SAVE(save, cf, data); 68913498266Sopenharmony_ci if(stream && ctx->sock[SP_LOCAL] != CURL_SOCKET_BAD) { 69013498266Sopenharmony_ci if(stream->recv_error) { 69113498266Sopenharmony_ci Curl_pollset_add_in(data, ps, ctx->sock[SP_LOCAL]); 69213498266Sopenharmony_ci drain_stream(cf, data); 69313498266Sopenharmony_ci } 69413498266Sopenharmony_ci else if(stream->req) { 69513498266Sopenharmony_ci Curl_pollset_add_out(data, ps, ctx->sock[SP_LOCAL]); 69613498266Sopenharmony_ci drain_stream(cf, data); 69713498266Sopenharmony_ci } 69813498266Sopenharmony_ci } 69913498266Sopenharmony_ci} 70013498266Sopenharmony_ci 70113498266Sopenharmony_cistatic bool cf_msh3_data_pending(struct Curl_cfilter *cf, 70213498266Sopenharmony_ci const struct Curl_easy *data) 70313498266Sopenharmony_ci{ 70413498266Sopenharmony_ci struct stream_ctx *stream = H3_STREAM_CTX(data); 70513498266Sopenharmony_ci struct cf_call_data save; 70613498266Sopenharmony_ci bool pending = FALSE; 70713498266Sopenharmony_ci 70813498266Sopenharmony_ci CF_DATA_SAVE(save, cf, data); 70913498266Sopenharmony_ci 71013498266Sopenharmony_ci (void)cf; 71113498266Sopenharmony_ci if(stream && stream->req) { 71213498266Sopenharmony_ci msh3_lock_acquire(&stream->recv_lock); 71313498266Sopenharmony_ci CURL_TRC_CF((struct Curl_easy *)data, cf, "data pending = %zu", 71413498266Sopenharmony_ci Curl_bufq_len(&stream->recvbuf)); 71513498266Sopenharmony_ci pending = !Curl_bufq_is_empty(&stream->recvbuf); 71613498266Sopenharmony_ci msh3_lock_release(&stream->recv_lock); 71713498266Sopenharmony_ci if(pending) 71813498266Sopenharmony_ci drain_stream(cf, (struct Curl_easy *)data); 71913498266Sopenharmony_ci } 72013498266Sopenharmony_ci 72113498266Sopenharmony_ci CF_DATA_RESTORE(cf, save); 72213498266Sopenharmony_ci return pending; 72313498266Sopenharmony_ci} 72413498266Sopenharmony_ci 72513498266Sopenharmony_cistatic void cf_msh3_active(struct Curl_cfilter *cf, struct Curl_easy *data) 72613498266Sopenharmony_ci{ 72713498266Sopenharmony_ci struct cf_msh3_ctx *ctx = cf->ctx; 72813498266Sopenharmony_ci 72913498266Sopenharmony_ci /* use this socket from now on */ 73013498266Sopenharmony_ci cf->conn->sock[cf->sockindex] = ctx->sock[SP_LOCAL]; 73113498266Sopenharmony_ci /* the first socket info gets set at conn and data */ 73213498266Sopenharmony_ci if(cf->sockindex == FIRSTSOCKET) { 73313498266Sopenharmony_ci cf->conn->remote_addr = &ctx->addr; 73413498266Sopenharmony_ci #ifdef ENABLE_IPV6 73513498266Sopenharmony_ci cf->conn->bits.ipv6 = (ctx->addr.family == AF_INET6)? TRUE : FALSE; 73613498266Sopenharmony_ci #endif 73713498266Sopenharmony_ci Curl_persistconninfo(data, cf->conn, ctx->l_ip, ctx->l_port); 73813498266Sopenharmony_ci } 73913498266Sopenharmony_ci ctx->active = TRUE; 74013498266Sopenharmony_ci} 74113498266Sopenharmony_ci 74213498266Sopenharmony_cistatic CURLcode h3_data_pause(struct Curl_cfilter *cf, 74313498266Sopenharmony_ci struct Curl_easy *data, 74413498266Sopenharmony_ci bool pause) 74513498266Sopenharmony_ci{ 74613498266Sopenharmony_ci if(!pause) { 74713498266Sopenharmony_ci drain_stream(cf, data); 74813498266Sopenharmony_ci Curl_expire(data, 0, EXPIRE_RUN_NOW); 74913498266Sopenharmony_ci } 75013498266Sopenharmony_ci return CURLE_OK; 75113498266Sopenharmony_ci} 75213498266Sopenharmony_ci 75313498266Sopenharmony_cistatic CURLcode cf_msh3_data_event(struct Curl_cfilter *cf, 75413498266Sopenharmony_ci struct Curl_easy *data, 75513498266Sopenharmony_ci int event, int arg1, void *arg2) 75613498266Sopenharmony_ci{ 75713498266Sopenharmony_ci struct stream_ctx *stream = H3_STREAM_CTX(data); 75813498266Sopenharmony_ci struct cf_call_data save; 75913498266Sopenharmony_ci CURLcode result = CURLE_OK; 76013498266Sopenharmony_ci 76113498266Sopenharmony_ci CF_DATA_SAVE(save, cf, data); 76213498266Sopenharmony_ci 76313498266Sopenharmony_ci (void)arg1; 76413498266Sopenharmony_ci (void)arg2; 76513498266Sopenharmony_ci switch(event) { 76613498266Sopenharmony_ci case CF_CTRL_DATA_SETUP: 76713498266Sopenharmony_ci result = h3_data_setup(cf, data); 76813498266Sopenharmony_ci break; 76913498266Sopenharmony_ci case CF_CTRL_DATA_PAUSE: 77013498266Sopenharmony_ci result = h3_data_pause(cf, data, (arg1 != 0)); 77113498266Sopenharmony_ci break; 77213498266Sopenharmony_ci case CF_CTRL_DATA_DONE: 77313498266Sopenharmony_ci h3_data_done(cf, data); 77413498266Sopenharmony_ci break; 77513498266Sopenharmony_ci case CF_CTRL_DATA_DONE_SEND: 77613498266Sopenharmony_ci CURL_TRC_CF(data, cf, "req: send done"); 77713498266Sopenharmony_ci if(stream) { 77813498266Sopenharmony_ci stream->upload_done = TRUE; 77913498266Sopenharmony_ci if(stream->req) { 78013498266Sopenharmony_ci char buf[1]; 78113498266Sopenharmony_ci if(!MsH3RequestSend(stream->req, MSH3_REQUEST_FLAG_FIN, 78213498266Sopenharmony_ci buf, 0, data)) { 78313498266Sopenharmony_ci result = CURLE_SEND_ERROR; 78413498266Sopenharmony_ci } 78513498266Sopenharmony_ci } 78613498266Sopenharmony_ci } 78713498266Sopenharmony_ci break; 78813498266Sopenharmony_ci case CF_CTRL_CONN_INFO_UPDATE: 78913498266Sopenharmony_ci CURL_TRC_CF(data, cf, "req: update info"); 79013498266Sopenharmony_ci cf_msh3_active(cf, data); 79113498266Sopenharmony_ci break; 79213498266Sopenharmony_ci default: 79313498266Sopenharmony_ci break; 79413498266Sopenharmony_ci } 79513498266Sopenharmony_ci 79613498266Sopenharmony_ci CF_DATA_RESTORE(cf, save); 79713498266Sopenharmony_ci return result; 79813498266Sopenharmony_ci} 79913498266Sopenharmony_ci 80013498266Sopenharmony_cistatic CURLcode cf_connect_start(struct Curl_cfilter *cf, 80113498266Sopenharmony_ci struct Curl_easy *data) 80213498266Sopenharmony_ci{ 80313498266Sopenharmony_ci struct cf_msh3_ctx *ctx = cf->ctx; 80413498266Sopenharmony_ci struct ssl_primary_config *conn_config; 80513498266Sopenharmony_ci MSH3_ADDR addr = {0}; 80613498266Sopenharmony_ci CURLcode result; 80713498266Sopenharmony_ci bool verify; 80813498266Sopenharmony_ci 80913498266Sopenharmony_ci conn_config = Curl_ssl_cf_get_primary_config(cf); 81013498266Sopenharmony_ci if(!conn_config) 81113498266Sopenharmony_ci return CURLE_FAILED_INIT; 81213498266Sopenharmony_ci verify = !!conn_config->verifypeer; 81313498266Sopenharmony_ci 81413498266Sopenharmony_ci memcpy(&addr, &ctx->addr.sa_addr, ctx->addr.addrlen); 81513498266Sopenharmony_ci MSH3_SET_PORT(&addr, (uint16_t)cf->conn->remote_port); 81613498266Sopenharmony_ci 81713498266Sopenharmony_ci if(verify && (conn_config->CAfile || conn_config->CApath)) { 81813498266Sopenharmony_ci /* TODO: need a way to provide trust anchors to MSH3 */ 81913498266Sopenharmony_ci#ifdef DEBUGBUILD 82013498266Sopenharmony_ci /* we need this for our test cases to run */ 82113498266Sopenharmony_ci CURL_TRC_CF(data, cf, "non-standard CA not supported, " 82213498266Sopenharmony_ci "switching off verifypeer in DEBUG mode"); 82313498266Sopenharmony_ci verify = 0; 82413498266Sopenharmony_ci#else 82513498266Sopenharmony_ci CURL_TRC_CF(data, cf, "non-standard CA not supported, " 82613498266Sopenharmony_ci "attempting with built-in verification"); 82713498266Sopenharmony_ci#endif 82813498266Sopenharmony_ci } 82913498266Sopenharmony_ci 83013498266Sopenharmony_ci CURL_TRC_CF(data, cf, "connecting to %s:%d (verify=%d)", 83113498266Sopenharmony_ci cf->conn->host.name, (int)cf->conn->remote_port, verify); 83213498266Sopenharmony_ci 83313498266Sopenharmony_ci ctx->api = MsH3ApiOpen(); 83413498266Sopenharmony_ci if(!ctx->api) { 83513498266Sopenharmony_ci failf(data, "can't create msh3 api"); 83613498266Sopenharmony_ci return CURLE_FAILED_INIT; 83713498266Sopenharmony_ci } 83813498266Sopenharmony_ci 83913498266Sopenharmony_ci ctx->qconn = MsH3ConnectionOpen(ctx->api, 84013498266Sopenharmony_ci &msh3_conn_if, 84113498266Sopenharmony_ci cf, 84213498266Sopenharmony_ci cf->conn->host.name, 84313498266Sopenharmony_ci &addr, 84413498266Sopenharmony_ci !verify); 84513498266Sopenharmony_ci if(!ctx->qconn) { 84613498266Sopenharmony_ci failf(data, "can't create msh3 connection"); 84713498266Sopenharmony_ci if(ctx->api) { 84813498266Sopenharmony_ci MsH3ApiClose(ctx->api); 84913498266Sopenharmony_ci ctx->api = NULL; 85013498266Sopenharmony_ci } 85113498266Sopenharmony_ci return CURLE_FAILED_INIT; 85213498266Sopenharmony_ci } 85313498266Sopenharmony_ci 85413498266Sopenharmony_ci result = h3_data_setup(cf, data); 85513498266Sopenharmony_ci if(result) 85613498266Sopenharmony_ci return result; 85713498266Sopenharmony_ci 85813498266Sopenharmony_ci return CURLE_OK; 85913498266Sopenharmony_ci} 86013498266Sopenharmony_ci 86113498266Sopenharmony_cistatic CURLcode cf_msh3_connect(struct Curl_cfilter *cf, 86213498266Sopenharmony_ci struct Curl_easy *data, 86313498266Sopenharmony_ci bool blocking, bool *done) 86413498266Sopenharmony_ci{ 86513498266Sopenharmony_ci struct cf_msh3_ctx *ctx = cf->ctx; 86613498266Sopenharmony_ci struct cf_call_data save; 86713498266Sopenharmony_ci CURLcode result = CURLE_OK; 86813498266Sopenharmony_ci 86913498266Sopenharmony_ci (void)blocking; 87013498266Sopenharmony_ci if(cf->connected) { 87113498266Sopenharmony_ci *done = TRUE; 87213498266Sopenharmony_ci return CURLE_OK; 87313498266Sopenharmony_ci } 87413498266Sopenharmony_ci 87513498266Sopenharmony_ci CF_DATA_SAVE(save, cf, data); 87613498266Sopenharmony_ci 87713498266Sopenharmony_ci if(ctx->sock[SP_LOCAL] == CURL_SOCKET_BAD) { 87813498266Sopenharmony_ci if(Curl_socketpair(AF_UNIX, SOCK_STREAM, 0, &ctx->sock[0]) < 0) { 87913498266Sopenharmony_ci ctx->sock[SP_LOCAL] = CURL_SOCKET_BAD; 88013498266Sopenharmony_ci ctx->sock[SP_REMOTE] = CURL_SOCKET_BAD; 88113498266Sopenharmony_ci return CURLE_COULDNT_CONNECT; 88213498266Sopenharmony_ci } 88313498266Sopenharmony_ci } 88413498266Sopenharmony_ci 88513498266Sopenharmony_ci *done = FALSE; 88613498266Sopenharmony_ci if(!ctx->qconn) { 88713498266Sopenharmony_ci ctx->connect_started = Curl_now(); 88813498266Sopenharmony_ci result = cf_connect_start(cf, data); 88913498266Sopenharmony_ci if(result) 89013498266Sopenharmony_ci goto out; 89113498266Sopenharmony_ci } 89213498266Sopenharmony_ci 89313498266Sopenharmony_ci if(ctx->handshake_complete) { 89413498266Sopenharmony_ci ctx->handshake_at = Curl_now(); 89513498266Sopenharmony_ci if(ctx->handshake_succeeded) { 89613498266Sopenharmony_ci CURL_TRC_CF(data, cf, "handshake succeeded"); 89713498266Sopenharmony_ci cf->conn->bits.multiplex = TRUE; /* at least potentially multiplexed */ 89813498266Sopenharmony_ci cf->conn->httpversion = 30; 89913498266Sopenharmony_ci cf->conn->bundle->multiuse = BUNDLE_MULTIPLEX; 90013498266Sopenharmony_ci cf->connected = TRUE; 90113498266Sopenharmony_ci cf->conn->alpn = CURL_HTTP_VERSION_3; 90213498266Sopenharmony_ci *done = TRUE; 90313498266Sopenharmony_ci connkeep(cf->conn, "HTTP/3 default"); 90413498266Sopenharmony_ci Curl_pgrsTime(data, TIMER_APPCONNECT); 90513498266Sopenharmony_ci } 90613498266Sopenharmony_ci else { 90713498266Sopenharmony_ci failf(data, "failed to connect, handshake failed"); 90813498266Sopenharmony_ci result = CURLE_COULDNT_CONNECT; 90913498266Sopenharmony_ci } 91013498266Sopenharmony_ci } 91113498266Sopenharmony_ci 91213498266Sopenharmony_ciout: 91313498266Sopenharmony_ci CF_DATA_RESTORE(cf, save); 91413498266Sopenharmony_ci return result; 91513498266Sopenharmony_ci} 91613498266Sopenharmony_ci 91713498266Sopenharmony_cistatic void cf_msh3_close(struct Curl_cfilter *cf, struct Curl_easy *data) 91813498266Sopenharmony_ci{ 91913498266Sopenharmony_ci struct cf_msh3_ctx *ctx = cf->ctx; 92013498266Sopenharmony_ci struct cf_call_data save; 92113498266Sopenharmony_ci 92213498266Sopenharmony_ci (void)data; 92313498266Sopenharmony_ci CF_DATA_SAVE(save, cf, data); 92413498266Sopenharmony_ci 92513498266Sopenharmony_ci if(ctx) { 92613498266Sopenharmony_ci CURL_TRC_CF(data, cf, "destroying"); 92713498266Sopenharmony_ci if(ctx->qconn) { 92813498266Sopenharmony_ci MsH3ConnectionClose(ctx->qconn); 92913498266Sopenharmony_ci ctx->qconn = NULL; 93013498266Sopenharmony_ci } 93113498266Sopenharmony_ci if(ctx->api) { 93213498266Sopenharmony_ci MsH3ApiClose(ctx->api); 93313498266Sopenharmony_ci ctx->api = NULL; 93413498266Sopenharmony_ci } 93513498266Sopenharmony_ci 93613498266Sopenharmony_ci if(ctx->active) { 93713498266Sopenharmony_ci /* We share our socket at cf->conn->sock[cf->sockindex] when active. 93813498266Sopenharmony_ci * If it is no longer there, someone has stolen (and hopefully 93913498266Sopenharmony_ci * closed it) and we just forget about it. 94013498266Sopenharmony_ci */ 94113498266Sopenharmony_ci ctx->active = FALSE; 94213498266Sopenharmony_ci if(ctx->sock[SP_LOCAL] == cf->conn->sock[cf->sockindex]) { 94313498266Sopenharmony_ci CURL_TRC_CF(data, cf, "cf_msh3_close(%d) active", 94413498266Sopenharmony_ci (int)ctx->sock[SP_LOCAL]); 94513498266Sopenharmony_ci cf->conn->sock[cf->sockindex] = CURL_SOCKET_BAD; 94613498266Sopenharmony_ci } 94713498266Sopenharmony_ci else { 94813498266Sopenharmony_ci CURL_TRC_CF(data, cf, "cf_socket_close(%d) no longer at " 94913498266Sopenharmony_ci "conn->sock[], discarding", (int)ctx->sock[SP_LOCAL]); 95013498266Sopenharmony_ci ctx->sock[SP_LOCAL] = CURL_SOCKET_BAD; 95113498266Sopenharmony_ci } 95213498266Sopenharmony_ci if(cf->sockindex == FIRSTSOCKET) 95313498266Sopenharmony_ci cf->conn->remote_addr = NULL; 95413498266Sopenharmony_ci } 95513498266Sopenharmony_ci if(ctx->sock[SP_LOCAL] != CURL_SOCKET_BAD) { 95613498266Sopenharmony_ci sclose(ctx->sock[SP_LOCAL]); 95713498266Sopenharmony_ci } 95813498266Sopenharmony_ci if(ctx->sock[SP_REMOTE] != CURL_SOCKET_BAD) { 95913498266Sopenharmony_ci sclose(ctx->sock[SP_REMOTE]); 96013498266Sopenharmony_ci } 96113498266Sopenharmony_ci ctx->sock[SP_LOCAL] = CURL_SOCKET_BAD; 96213498266Sopenharmony_ci ctx->sock[SP_REMOTE] = CURL_SOCKET_BAD; 96313498266Sopenharmony_ci } 96413498266Sopenharmony_ci CF_DATA_RESTORE(cf, save); 96513498266Sopenharmony_ci} 96613498266Sopenharmony_ci 96713498266Sopenharmony_cistatic void cf_msh3_destroy(struct Curl_cfilter *cf, struct Curl_easy *data) 96813498266Sopenharmony_ci{ 96913498266Sopenharmony_ci struct cf_call_data save; 97013498266Sopenharmony_ci 97113498266Sopenharmony_ci CF_DATA_SAVE(save, cf, data); 97213498266Sopenharmony_ci cf_msh3_close(cf, data); 97313498266Sopenharmony_ci free(cf->ctx); 97413498266Sopenharmony_ci cf->ctx = NULL; 97513498266Sopenharmony_ci /* no CF_DATA_RESTORE(cf, save); its gone */ 97613498266Sopenharmony_ci 97713498266Sopenharmony_ci} 97813498266Sopenharmony_ci 97913498266Sopenharmony_cistatic CURLcode cf_msh3_query(struct Curl_cfilter *cf, 98013498266Sopenharmony_ci struct Curl_easy *data, 98113498266Sopenharmony_ci int query, int *pres1, void *pres2) 98213498266Sopenharmony_ci{ 98313498266Sopenharmony_ci struct cf_msh3_ctx *ctx = cf->ctx; 98413498266Sopenharmony_ci 98513498266Sopenharmony_ci switch(query) { 98613498266Sopenharmony_ci case CF_QUERY_MAX_CONCURRENT: { 98713498266Sopenharmony_ci /* TODO: we do not have access to this so far, fake it */ 98813498266Sopenharmony_ci (void)ctx; 98913498266Sopenharmony_ci *pres1 = 100; 99013498266Sopenharmony_ci return CURLE_OK; 99113498266Sopenharmony_ci } 99213498266Sopenharmony_ci case CF_QUERY_TIMER_CONNECT: { 99313498266Sopenharmony_ci struct curltime *when = pres2; 99413498266Sopenharmony_ci /* we do not know when the first byte arrived */ 99513498266Sopenharmony_ci if(cf->connected) 99613498266Sopenharmony_ci *when = ctx->handshake_at; 99713498266Sopenharmony_ci return CURLE_OK; 99813498266Sopenharmony_ci } 99913498266Sopenharmony_ci case CF_QUERY_TIMER_APPCONNECT: { 100013498266Sopenharmony_ci struct curltime *when = pres2; 100113498266Sopenharmony_ci if(cf->connected) 100213498266Sopenharmony_ci *when = ctx->handshake_at; 100313498266Sopenharmony_ci return CURLE_OK; 100413498266Sopenharmony_ci } 100513498266Sopenharmony_ci default: 100613498266Sopenharmony_ci break; 100713498266Sopenharmony_ci } 100813498266Sopenharmony_ci return cf->next? 100913498266Sopenharmony_ci cf->next->cft->query(cf->next, data, query, pres1, pres2) : 101013498266Sopenharmony_ci CURLE_UNKNOWN_OPTION; 101113498266Sopenharmony_ci} 101213498266Sopenharmony_ci 101313498266Sopenharmony_cistatic bool cf_msh3_conn_is_alive(struct Curl_cfilter *cf, 101413498266Sopenharmony_ci struct Curl_easy *data, 101513498266Sopenharmony_ci bool *input_pending) 101613498266Sopenharmony_ci{ 101713498266Sopenharmony_ci struct cf_msh3_ctx *ctx = cf->ctx; 101813498266Sopenharmony_ci 101913498266Sopenharmony_ci (void)data; 102013498266Sopenharmony_ci *input_pending = FALSE; 102113498266Sopenharmony_ci return ctx && ctx->sock[SP_LOCAL] != CURL_SOCKET_BAD && ctx->qconn && 102213498266Sopenharmony_ci ctx->connected; 102313498266Sopenharmony_ci} 102413498266Sopenharmony_ci 102513498266Sopenharmony_cistruct Curl_cftype Curl_cft_http3 = { 102613498266Sopenharmony_ci "HTTP/3", 102713498266Sopenharmony_ci CF_TYPE_IP_CONNECT | CF_TYPE_SSL | CF_TYPE_MULTIPLEX, 102813498266Sopenharmony_ci 0, 102913498266Sopenharmony_ci cf_msh3_destroy, 103013498266Sopenharmony_ci cf_msh3_connect, 103113498266Sopenharmony_ci cf_msh3_close, 103213498266Sopenharmony_ci Curl_cf_def_get_host, 103313498266Sopenharmony_ci cf_msh3_adjust_pollset, 103413498266Sopenharmony_ci cf_msh3_data_pending, 103513498266Sopenharmony_ci cf_msh3_send, 103613498266Sopenharmony_ci cf_msh3_recv, 103713498266Sopenharmony_ci cf_msh3_data_event, 103813498266Sopenharmony_ci cf_msh3_conn_is_alive, 103913498266Sopenharmony_ci Curl_cf_def_conn_keep_alive, 104013498266Sopenharmony_ci cf_msh3_query, 104113498266Sopenharmony_ci}; 104213498266Sopenharmony_ci 104313498266Sopenharmony_ciCURLcode Curl_cf_msh3_create(struct Curl_cfilter **pcf, 104413498266Sopenharmony_ci struct Curl_easy *data, 104513498266Sopenharmony_ci struct connectdata *conn, 104613498266Sopenharmony_ci const struct Curl_addrinfo *ai) 104713498266Sopenharmony_ci{ 104813498266Sopenharmony_ci struct cf_msh3_ctx *ctx = NULL; 104913498266Sopenharmony_ci struct Curl_cfilter *cf = NULL; 105013498266Sopenharmony_ci CURLcode result; 105113498266Sopenharmony_ci 105213498266Sopenharmony_ci (void)data; 105313498266Sopenharmony_ci (void)conn; 105413498266Sopenharmony_ci (void)ai; /* TODO: msh3 resolves itself? */ 105513498266Sopenharmony_ci ctx = calloc(1, sizeof(*ctx)); 105613498266Sopenharmony_ci if(!ctx) { 105713498266Sopenharmony_ci result = CURLE_OUT_OF_MEMORY; 105813498266Sopenharmony_ci goto out; 105913498266Sopenharmony_ci } 106013498266Sopenharmony_ci Curl_sock_assign_addr(&ctx->addr, ai, TRNSPRT_QUIC); 106113498266Sopenharmony_ci ctx->sock[SP_LOCAL] = CURL_SOCKET_BAD; 106213498266Sopenharmony_ci ctx->sock[SP_REMOTE] = CURL_SOCKET_BAD; 106313498266Sopenharmony_ci 106413498266Sopenharmony_ci result = Curl_cf_create(&cf, &Curl_cft_http3, ctx); 106513498266Sopenharmony_ci 106613498266Sopenharmony_ciout: 106713498266Sopenharmony_ci *pcf = (!result)? cf : NULL; 106813498266Sopenharmony_ci if(result) { 106913498266Sopenharmony_ci Curl_safefree(cf); 107013498266Sopenharmony_ci Curl_safefree(ctx); 107113498266Sopenharmony_ci } 107213498266Sopenharmony_ci 107313498266Sopenharmony_ci return result; 107413498266Sopenharmony_ci} 107513498266Sopenharmony_ci 107613498266Sopenharmony_cibool Curl_conn_is_msh3(const struct Curl_easy *data, 107713498266Sopenharmony_ci const struct connectdata *conn, 107813498266Sopenharmony_ci int sockindex) 107913498266Sopenharmony_ci{ 108013498266Sopenharmony_ci struct Curl_cfilter *cf = conn? conn->cfilter[sockindex] : NULL; 108113498266Sopenharmony_ci 108213498266Sopenharmony_ci (void)data; 108313498266Sopenharmony_ci for(; cf; cf = cf->next) { 108413498266Sopenharmony_ci if(cf->cft == &Curl_cft_http3) 108513498266Sopenharmony_ci return TRUE; 108613498266Sopenharmony_ci if(cf->cft->flags & CF_TYPE_IP_CONNECT) 108713498266Sopenharmony_ci return FALSE; 108813498266Sopenharmony_ci } 108913498266Sopenharmony_ci return FALSE; 109013498266Sopenharmony_ci} 109113498266Sopenharmony_ci 109213498266Sopenharmony_ci#endif /* USE_MSH3 */ 1093