mirror of
https://github.com/curl/curl.git
synced 2026-08-25 12:13:37 +03:00
HTTP3: initial (experimental) support
USe configure --with-ngtcp2 or --with-quiche Using either option will enable a HTTP3 build. Co-authored-by: Alessandro Ghedini <alessandro@ghedini.me> Closes #3500
This commit is contained in:
parent
7644abf8e8
commit
3af0e76d1e
33 changed files with 2520 additions and 23 deletions
|
|
@ -37,6 +37,10 @@ LIB_VTLS_HFILES = vtls/openssl.h vtls/vtls.h vtls/gtls.h \
|
|||
vtls/wolfssl.h vtls/schannel.h vtls/sectransp.h vtls/gskit.h \
|
||||
vtls/mbedtls.h vtls/mesalink.h
|
||||
|
||||
LIB_VQUIC_CFILES = vquic/ngtcp2.c vquic/ngtcp2-crypto.c vquic/quiche.c
|
||||
|
||||
LIB_VQUIC_HFILES = vquic/ngtcp2.h vquic/ngtcp2-crypto.h vquic/quiche.h
|
||||
|
||||
LIB_CFILES = file.c timeval.c base64.c hostip.c progress.c formdata.c \
|
||||
cookie.c http.c sendf.c ftp.c url.c dict.c if2ip.c speedcheck.c \
|
||||
ldap.c version.c getenv.c escape.c mprintf.c telnet.c netrc.c \
|
||||
|
|
@ -55,7 +59,7 @@ LIB_CFILES = file.c timeval.c base64.c hostip.c progress.c formdata.c \
|
|||
curl_multibyte.c hostcheck.c conncache.c dotdot.c \
|
||||
x509asn1.c http2.c smb.c curl_endian.c curl_des.c system_win32.c \
|
||||
mime.c sha256.c setopt.c curl_path.c curl_ctype.c curl_range.c psl.c \
|
||||
doh.c urlapi.c curl_get_line.c altsvc.c
|
||||
doh.c urlapi.c curl_get_line.c altsvc.c quic.c
|
||||
|
||||
LIB_HFILES = arpa_telnet.h netrc.h file.h timeval.h hostip.h progress.h \
|
||||
formdata.h cookie.h http.h sendf.h ftp.h url.h dict.h if2ip.h \
|
||||
|
|
@ -76,9 +80,11 @@ LIB_HFILES = arpa_telnet.h netrc.h file.h timeval.h hostip.h progress.h \
|
|||
x509asn1.h http2.h sigpipe.h smb.h curl_endian.h curl_des.h \
|
||||
curl_printf.h system_win32.h rand.h mime.h curl_sha256.h setopt.h \
|
||||
curl_path.h curl_ctype.h curl_range.h psl.h doh.h urlapi-int.h \
|
||||
curl_get_line.h altsvc.h
|
||||
curl_get_line.h altsvc.h quic.h
|
||||
|
||||
LIB_RCFILES = libcurl.rc
|
||||
|
||||
CSOURCES = $(LIB_CFILES) $(LIB_VAUTH_CFILES) $(LIB_VTLS_CFILES)
|
||||
HHEADERS = $(LIB_HFILES) $(LIB_VAUTH_HFILES) $(LIB_VTLS_HFILES)
|
||||
CSOURCES = $(LIB_CFILES) $(LIB_VAUTH_CFILES) $(LIB_VTLS_CFILES) \
|
||||
$(LIB_VQUIC_CFILES)
|
||||
HHEADERS = $(LIB_HFILES) $(LIB_VAUTH_HFILES) $(LIB_VTLS_HFILES) \
|
||||
$(LIB_VQUIC_HFILES)
|
||||
|
|
|
|||
|
|
@ -706,7 +706,8 @@ Curl_addrinfo *Curl_resolver_getaddrinfo(struct connectdata *conn,
|
|||
|
||||
memset(&hints, 0, sizeof(hints));
|
||||
hints.ai_family = pf;
|
||||
hints.ai_socktype = conn->socktype;
|
||||
hints.ai_socktype = (conn->transport == TRNSPRT_TCP)?
|
||||
SOCK_STREAM : SOCK_DGRAM;
|
||||
|
||||
msnprintf(sbuf, sizeof(sbuf), "%d", port);
|
||||
|
||||
|
|
|
|||
|
|
@ -75,6 +75,7 @@
|
|||
#include "conncache.h"
|
||||
#include "multihandle.h"
|
||||
#include "system_win32.h"
|
||||
#include "quic.h"
|
||||
|
||||
/* The last 3 #include files should be in this order */
|
||||
#include "curl_printf.h"
|
||||
|
|
@ -683,8 +684,8 @@ UNITTEST bool getaddressinfo(struct sockaddr *sa, char *addr,
|
|||
connection */
|
||||
void Curl_updateconninfo(struct connectdata *conn, curl_socket_t sockfd)
|
||||
{
|
||||
if(conn->socktype == SOCK_DGRAM)
|
||||
/* there's no connection! */
|
||||
if(conn->transport != TRNSPRT_TCP)
|
||||
/* there's no TCP connection! */
|
||||
return;
|
||||
|
||||
#if defined(HAVE_GETPEERNAME) || defined(HAVE_GETSOCKNAME)
|
||||
|
|
@ -1099,8 +1100,8 @@ static CURLcode singleipconnect(struct connectdata *conn,
|
|||
if(conn->num_addr > 1)
|
||||
Curl_expire(data, conn->timeoutms_per_addr, EXPIRE_DNS_PER_NAME);
|
||||
|
||||
/* Connect TCP sockets, bind UDP */
|
||||
if(!isconnected && (conn->socktype == SOCK_STREAM)) {
|
||||
/* Connect TCP and QUIC sockets */
|
||||
if(!isconnected && (conn->transport != TRNSPRT_UDP)) {
|
||||
if(conn->bits.tcp_fastopen) {
|
||||
#if defined(CONNECT_DATA_IDEMPOTENT) /* Darwin */
|
||||
# if defined(HAVE_BUILTIN_AVAILABLE)
|
||||
|
|
@ -1152,6 +1153,15 @@ static CURLcode singleipconnect(struct connectdata *conn,
|
|||
return CURLE_OK;
|
||||
}
|
||||
|
||||
#ifdef ENABLE_QUIC
|
||||
if(!isconnected && (conn->transport == TRNSPRT_QUIC)) {
|
||||
result = Curl_quic_connect(conn, sockfd, &addr.sa_addr, addr.addrlen);
|
||||
if(result)
|
||||
return result;
|
||||
rc = 0; /* connect success */
|
||||
}
|
||||
#endif
|
||||
|
||||
if(-1 == rc) {
|
||||
switch(error) {
|
||||
case EINPROGRESS:
|
||||
|
|
@ -1386,8 +1396,9 @@ CURLcode Curl_socket(struct connectdata *conn,
|
|||
*/
|
||||
|
||||
addr->family = ai->ai_family;
|
||||
addr->socktype = conn->socktype;
|
||||
addr->protocol = conn->socktype == SOCK_DGRAM?IPPROTO_UDP:ai->ai_protocol;
|
||||
addr->socktype = (conn->transport == TRNSPRT_TCP) ? SOCK_STREAM : SOCK_DGRAM;
|
||||
addr->protocol = conn->transport != TRNSPRT_TCP ? IPPROTO_UDP :
|
||||
ai->ai_protocol;
|
||||
addr->addrlen = ai->ai_addrlen;
|
||||
|
||||
if(addr->addrlen > sizeof(struct Curl_sockaddr_storage))
|
||||
|
|
|
|||
|
|
@ -827,4 +827,8 @@ int getpwuid_r(uid_t uid, struct passwd *pwd, char *buf,
|
|||
#define UNITTEST static
|
||||
#endif
|
||||
|
||||
#if defined(USE_NGTCP2) || defined(USE_QUICHE)
|
||||
#define ENABLE_QUIC
|
||||
#endif
|
||||
|
||||
#endif /* HEADER_CURL_SETUP_H */
|
||||
|
|
|
|||
|
|
@ -165,7 +165,8 @@ Curl_addrinfo *Curl_getaddrinfo(struct connectdata *conn,
|
|||
|
||||
memset(&hints, 0, sizeof(hints));
|
||||
hints.ai_family = pf;
|
||||
hints.ai_socktype = conn->socktype;
|
||||
hints.ai_socktype = (conn->transport == TRNSPRT_TCP) ?
|
||||
SOCK_STREAM : SOCK_DGRAM;
|
||||
|
||||
#ifndef USE_RESOLVE_ON_IPS
|
||||
/*
|
||||
|
|
|
|||
29
lib/http.c
29
lib/http.c
|
|
@ -171,10 +171,22 @@ static CURLcode http_setup_conn(struct connectdata *conn)
|
|||
Curl_mime_initpart(&http->form, conn->data);
|
||||
data->req.protop = http;
|
||||
|
||||
if(!CONN_INUSE(conn))
|
||||
/* if not already multi-using, setup connection details */
|
||||
Curl_http2_setup_conn(conn);
|
||||
Curl_http2_setup_req(data);
|
||||
if(data->set.h3opts & CURLH3_DIRECT) {
|
||||
if(conn->handler->flags & PROTOPT_SSL)
|
||||
/* Only go h3-direct on HTTPS URLs. It needs a UDP socket and does the
|
||||
QUIC dance. */
|
||||
conn->transport = TRNSPRT_QUIC;
|
||||
else {
|
||||
failf(data, "HTTP/3 requested for non-HTTPS URL");
|
||||
return CURLE_URL_MALFORMAT;
|
||||
}
|
||||
}
|
||||
else {
|
||||
if(!CONN_INUSE(conn))
|
||||
/* if not already multi-using, setup connection details */
|
||||
Curl_http2_setup_conn(conn);
|
||||
Curl_http2_setup_req(data);
|
||||
}
|
||||
return CURLE_OK;
|
||||
}
|
||||
|
||||
|
|
@ -1555,6 +1567,15 @@ static CURLcode https_connecting(struct connectdata *conn, bool *done)
|
|||
CURLcode result;
|
||||
DEBUGASSERT((conn) && (conn->handler->flags & PROTOPT_SSL));
|
||||
|
||||
#ifdef ENABLE_QUIC
|
||||
if(conn->transport == TRNSPRT_QUIC) {
|
||||
result = Curl_quic_is_connected(conn, FIRSTSOCKET, done);
|
||||
if(result)
|
||||
connclose(conn, "Failed HTTPS connection (over QUIC)");
|
||||
return result;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* perform SSL initialization for this socket */
|
||||
result = Curl_ssl_connect_nonblocking(conn, FIRSTSOCKET, done);
|
||||
if(result)
|
||||
|
|
|
|||
38
lib/quic.c
Normal file
38
lib/quic.c
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
/***************************************************************************
|
||||
* _ _ ____ _
|
||||
* Project ___| | | | _ \| |
|
||||
* / __| | | | |_) | |
|
||||
* | (__| |_| | _ <| |___
|
||||
* \___|\___/|_| \_\_____|
|
||||
*
|
||||
* Copyright (C) 1998 - 2019, Daniel Stenberg, <daniel@haxx.se>, et al.
|
||||
*
|
||||
* This software is licensed as described in the file COPYING, which
|
||||
* you should have received as part of this distribution. The terms
|
||||
* are also available at https://curl.haxx.se/docs/copyright.html.
|
||||
*
|
||||
* You may opt to use, copy, modify, merge, publish, distribute and/or sell
|
||||
* copies of the Software, and permit persons to whom the Software is
|
||||
* furnished to do so, under the terms of the COPYING file.
|
||||
*
|
||||
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
|
||||
* KIND, either express or implied.
|
||||
*
|
||||
***************************************************************************/
|
||||
|
||||
#include "curl_setup.h"
|
||||
|
||||
#ifdef ENABLE_QUIC
|
||||
#include "quic.h"
|
||||
/* backend-independent QUIC functionality */
|
||||
const char *Curl_quic_backend(void)
|
||||
{
|
||||
#ifdef USE_NGTCP2
|
||||
return "ngtcp2";
|
||||
#endif
|
||||
#ifdef USE_QUICHE
|
||||
return "quiche";
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
54
lib/quic.h
Normal file
54
lib/quic.h
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
#ifndef HEADER_CURL_QUIC_H
|
||||
#define HEADER_CURL_QUIC_H
|
||||
/***************************************************************************
|
||||
* _ _ ____ _
|
||||
* Project ___| | | | _ \| |
|
||||
* / __| | | | |_) | |
|
||||
* | (__| |_| | _ <| |___
|
||||
* \___|\___/|_| \_\_____|
|
||||
*
|
||||
* Copyright (C) 1998 - 2019, Daniel Stenberg, <daniel@haxx.se>, et al.
|
||||
*
|
||||
* This software is licensed as described in the file COPYING, which
|
||||
* you should have received as part of this distribution. The terms
|
||||
* are also available at https://curl.haxx.se/docs/copyright.html.
|
||||
*
|
||||
* You may opt to use, copy, modify, merge, publish, distribute and/or sell
|
||||
* copies of the Software, and permit persons to whom the Software is
|
||||
* furnished to do so, under the terms of the COPYING file.
|
||||
*
|
||||
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
|
||||
* KIND, either express or implied.
|
||||
*
|
||||
***************************************************************************/
|
||||
|
||||
#include "curl_setup.h"
|
||||
|
||||
#ifdef ENABLE_QUIC
|
||||
#ifdef USE_NGTCP2
|
||||
#include "vquic/ngtcp2.h"
|
||||
#endif
|
||||
#ifdef USE_QUICHE
|
||||
#include "vquic/quiche.h"
|
||||
#endif
|
||||
|
||||
#include "urldata.h"
|
||||
|
||||
/* generic */
|
||||
const char *Curl_quic_backend(void);
|
||||
|
||||
/* functions provided by the specific backends */
|
||||
CURLcode Curl_quic_connect(struct connectdata *conn,
|
||||
curl_socket_t sockfd,
|
||||
const struct sockaddr *addr,
|
||||
socklen_t addrlen);
|
||||
CURLcode Curl_quic_is_connected(struct connectdata *conn, int sockindex,
|
||||
bool *done);
|
||||
int Curl_quic_ver(char *p, size_t len);
|
||||
|
||||
#else
|
||||
/* no QUIC */
|
||||
#define Curl_quic_backend() ""
|
||||
#endif
|
||||
|
||||
#endif /* HEADER_CURL_QUIC_H */
|
||||
|
|
@ -2744,6 +2744,14 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param)
|
|||
return result;
|
||||
break;
|
||||
#endif
|
||||
case CURLOPT_H3:
|
||||
#ifdef ENABLE_QUIC
|
||||
arg = va_arg(param, long);
|
||||
data->set.h3opts = arg;
|
||||
#else
|
||||
return CURLE_NOT_BUILT_IN;
|
||||
#endif
|
||||
break;
|
||||
default:
|
||||
/* unknown tag and its companion, just ignore: */
|
||||
result = CURLE_UNKNOWN_OPTION;
|
||||
|
|
|
|||
|
|
@ -1376,7 +1376,7 @@ static CURLcode tftp_setup_connection(struct connectdata * conn)
|
|||
struct Curl_easy *data = conn->data;
|
||||
char *type;
|
||||
|
||||
conn->socktype = SOCK_DGRAM; /* UDP datagram based */
|
||||
conn->transport = TRNSPRT_UDP;
|
||||
|
||||
/* TFTP URLs support an extension like ";mode=<typecode>" that
|
||||
* we'll try to get now! */
|
||||
|
|
|
|||
|
|
@ -2110,7 +2110,7 @@ static CURLcode setup_connection_internals(struct connectdata *conn)
|
|||
{
|
||||
const struct Curl_handler * p;
|
||||
CURLcode result;
|
||||
conn->socktype = SOCK_STREAM; /* most of them are TCP streams */
|
||||
conn->transport = TRNSPRT_TCP; /* most of them are TCP streams */
|
||||
|
||||
/* Perform setup complement if some. */
|
||||
p = conn->handler;
|
||||
|
|
|
|||
|
|
@ -129,6 +129,7 @@ typedef ssize_t (Curl_recv)(struct connectdata *conn, /* connection data */
|
|||
#include "smb.h"
|
||||
#include "wildcard.h"
|
||||
#include "multihandle.h"
|
||||
#include "quic.h"
|
||||
|
||||
#ifdef HAVE_GSSAPI
|
||||
# ifdef HAVE_GSSGNU
|
||||
|
|
@ -831,7 +832,15 @@ struct connectdata {
|
|||
|
||||
unsigned int scope_id; /* Scope id for IPv6 */
|
||||
|
||||
int socktype; /* SOCK_STREAM or SOCK_DGRAM */
|
||||
enum {
|
||||
TRNSPRT_TCP = 3,
|
||||
TRNSPRT_UDP = 4,
|
||||
TRNSPRT_QUIC = 5
|
||||
} transport;
|
||||
|
||||
#ifdef ENABLE_QUIC
|
||||
struct quicsocket quic;
|
||||
#endif
|
||||
|
||||
struct hostname host;
|
||||
char *hostname_resolve; /* host name to resolve to address, allocated */
|
||||
|
|
@ -1672,6 +1681,7 @@ struct UserDefined {
|
|||
CURLU *uh; /* URL handle for the current parsed URL */
|
||||
void *trailer_data; /* pointer to pass to trailer data callback */
|
||||
curl_trailer_callback trailer_callback; /* trailing data callback */
|
||||
long h3opts; /* the CURLOPT_H3 bitmask */
|
||||
bit is_fread_set:1; /* has read callback been set to non-NULL? */
|
||||
bit is_fwrite_set:1; /* has write callback been set to non-NULL? */
|
||||
bit free_referer:1; /* set TRUE if 'referer' points to a string we
|
||||
|
|
|
|||
|
|
@ -27,6 +27,7 @@
|
|||
#include "vtls/vtls.h"
|
||||
#include "http2.h"
|
||||
#include "ssh.h"
|
||||
#include "quic.h"
|
||||
#include "curl_printf.h"
|
||||
|
||||
#ifdef USE_ARES
|
||||
|
|
@ -187,6 +188,11 @@ char *curl_version(void)
|
|||
left -= len;
|
||||
ptr += len;
|
||||
#endif
|
||||
#ifdef ENABLE_QUIC
|
||||
len = Curl_quic_ver(ptr, left);
|
||||
left -= len;
|
||||
ptr += len;
|
||||
#endif
|
||||
#ifdef USE_LIBRTMP
|
||||
{
|
||||
char suff[2];
|
||||
|
|
@ -358,6 +364,9 @@ static curl_version_info_data version_info = {
|
|||
#if defined(USE_NGHTTP2)
|
||||
| CURL_VERSION_HTTP2
|
||||
#endif
|
||||
#if defined(ENABLE_QUIC)
|
||||
| CURL_VERSION_HTTP3
|
||||
#endif
|
||||
#if defined(USE_UNIX_SOCKETS)
|
||||
| CURL_VERSION_UNIX_SOCKETS
|
||||
#endif
|
||||
|
|
|
|||
520
lib/vquic/ngtcp2-crypto.c
Normal file
520
lib/vquic/ngtcp2-crypto.c
Normal file
|
|
@ -0,0 +1,520 @@
|
|||
/***************************************************************************
|
||||
* _ _ ____ _
|
||||
* Project ___| | | | _ \| |
|
||||
* / __| | | | |_) | |
|
||||
* | (__| |_| | _ <| |___
|
||||
* \___|\___/|_| \_\_____|
|
||||
*
|
||||
* Copyright (C) 1998 - 2019, Daniel Stenberg, <daniel@haxx.se>, et al.
|
||||
*
|
||||
* This software is licensed as described in the file COPYING, which
|
||||
* you should have received as part of this distribution. The terms
|
||||
* are also available at https://curl.haxx.se/docs/copyright.html.
|
||||
*
|
||||
* You may opt to use, copy, modify, merge, publish, distribute and/or sell
|
||||
* copies of the Software, and permit persons to whom the Software is
|
||||
* furnished to do so, under the terms of the COPYING file.
|
||||
*
|
||||
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
|
||||
* KIND, either express or implied.
|
||||
*
|
||||
***************************************************************************/
|
||||
|
||||
#include "curl_setup.h"
|
||||
|
||||
#ifdef USE_NGTCP2
|
||||
#include <ngtcp2/ngtcp2.h>
|
||||
#include <openssl/ssl.h>
|
||||
#include <openssl/evp.h>
|
||||
#include <openssl/kdf.h>
|
||||
#include "ngtcp2-crypto.h"
|
||||
/* The last 3 #include files should be in this order */
|
||||
#include "curl_printf.h"
|
||||
#include "curl_memory.h"
|
||||
#include "memdebug.h"
|
||||
|
||||
void Curl_qc_prf_sha256(struct Context *ctx)
|
||||
{
|
||||
ctx->prf = EVP_sha256();
|
||||
}
|
||||
|
||||
void Curl_qc_aead_aes_128_gcm(struct Context *ctx)
|
||||
{
|
||||
ctx->aead = EVP_aes_128_gcm();
|
||||
ctx->hp = EVP_aes_128_ctr();
|
||||
}
|
||||
|
||||
size_t Curl_qc_aead_nonce_length(const struct Context *ctx)
|
||||
{
|
||||
return EVP_CIPHER_iv_length(ctx->aead);
|
||||
}
|
||||
|
||||
|
||||
int Curl_qc_negotiated_prf(struct Context *ctx, SSL *ssl)
|
||||
{
|
||||
switch(SSL_CIPHER_get_id(SSL_get_current_cipher(ssl))) {
|
||||
case 0x03001301u: /* TLS_AES_128_GCM_SHA256 */
|
||||
case 0x03001303u: /* TLS_CHACHA20_POLY1305_SHA256 */
|
||||
ctx->prf = EVP_sha256();
|
||||
return 0;
|
||||
case 0x03001302u: /* TLS_AES_256_GCM_SHA384 */
|
||||
ctx->prf = EVP_sha384();
|
||||
return 0;
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
int Curl_qc_negotiated_aead(struct Context *ctx, SSL *ssl)
|
||||
{
|
||||
switch(SSL_CIPHER_get_id(SSL_get_current_cipher(ssl))) {
|
||||
case 0x03001301u: /* TLS_AES_128_GCM_SHA256 */
|
||||
ctx->aead = EVP_aes_128_gcm();
|
||||
ctx->hp = EVP_aes_128_ctr();
|
||||
return 0;
|
||||
case 0x03001302u: /* TLS_AES_256_GCM_SHA384 */
|
||||
ctx->aead = EVP_aes_256_gcm();
|
||||
ctx->hp = EVP_aes_256_ctr();
|
||||
return 0;
|
||||
case 0x03001303u: /* TLS_CHACHA20_POLY1305_SHA256 */
|
||||
ctx->aead = EVP_chacha20_poly1305();
|
||||
ctx->hp = EVP_chacha20();
|
||||
return 0;
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
ssize_t Curl_qc_encrypt_pn(uint8_t *dest, size_t destlen,
|
||||
const uint8_t *plaintext, size_t plaintextlen,
|
||||
const struct Context *ctx,
|
||||
const uint8_t *key, size_t keylen,
|
||||
const uint8_t *nonce, size_t noncelen)
|
||||
{
|
||||
EVP_CIPHER_CTX *actx = EVP_CIPHER_CTX_new();
|
||||
size_t outlen = 0;
|
||||
int len;
|
||||
(void)destlen;
|
||||
(void)keylen;
|
||||
(void)noncelen;
|
||||
|
||||
if(!actx)
|
||||
return -1;
|
||||
|
||||
if(EVP_EncryptInit_ex(actx, ctx->hp, NULL, key, nonce) != 1)
|
||||
goto error;
|
||||
|
||||
if(EVP_EncryptUpdate(actx, dest, &len, plaintext, (int)plaintextlen) != 1)
|
||||
goto error;
|
||||
|
||||
assert(len > 0);
|
||||
|
||||
outlen = len;
|
||||
|
||||
if(EVP_EncryptFinal_ex(actx, dest + outlen, &len) != 1)
|
||||
goto error;
|
||||
|
||||
assert(len == 0);
|
||||
/* outlen += len; */
|
||||
|
||||
EVP_CIPHER_CTX_free(actx);
|
||||
return outlen;
|
||||
|
||||
error:
|
||||
EVP_CIPHER_CTX_free(actx);
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int hkdf_expand(uint8_t *dest, size_t destlen, const uint8_t *secret,
|
||||
size_t secretlen, const uint8_t *info, size_t infolen,
|
||||
const struct Context *ctx)
|
||||
{
|
||||
void *pctx = EVP_PKEY_CTX_new_id(EVP_PKEY_HKDF, NULL);
|
||||
if(!pctx)
|
||||
return -1;
|
||||
|
||||
if(EVP_PKEY_derive_init(pctx) != 1)
|
||||
goto err;
|
||||
|
||||
if(EVP_PKEY_CTX_hkdf_mode(pctx, EVP_PKEY_HKDEF_MODE_EXPAND_ONLY) != 1)
|
||||
goto err;
|
||||
|
||||
if(EVP_PKEY_CTX_set_hkdf_md(pctx, ctx->prf) != 1)
|
||||
goto err;
|
||||
|
||||
if(EVP_PKEY_CTX_set1_hkdf_salt(pctx, "", 0) != 1)
|
||||
goto err;
|
||||
|
||||
if(EVP_PKEY_CTX_set1_hkdf_key(pctx, secret, (int)secretlen) != 1)
|
||||
goto err;
|
||||
|
||||
if(EVP_PKEY_CTX_add1_hkdf_info(pctx, info, (int)infolen) != 1)
|
||||
goto err;
|
||||
|
||||
if(EVP_PKEY_derive(pctx, dest, &destlen) != 1)
|
||||
goto err;
|
||||
|
||||
return 0;
|
||||
err:
|
||||
EVP_PKEY_CTX_free(pctx);
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int hkdf_extract(uint8_t *dest, size_t destlen,
|
||||
const uint8_t *secret, size_t secretlen,
|
||||
const uint8_t *salt, size_t saltlen,
|
||||
const struct Context *ctx)
|
||||
{
|
||||
void *pctx = EVP_PKEY_CTX_new_id(EVP_PKEY_HKDF, NULL);
|
||||
if(!pctx)
|
||||
return -1;
|
||||
|
||||
if(EVP_PKEY_derive_init(pctx) != 1)
|
||||
goto err;
|
||||
|
||||
if(EVP_PKEY_CTX_hkdf_mode(pctx, EVP_PKEY_HKDEF_MODE_EXTRACT_ONLY) != 1) {
|
||||
goto err;
|
||||
}
|
||||
|
||||
if(EVP_PKEY_CTX_set_hkdf_md(pctx, ctx->prf) != 1) {
|
||||
goto err;
|
||||
}
|
||||
|
||||
if(EVP_PKEY_CTX_set1_hkdf_salt(pctx, salt, (int)saltlen) != 1) {
|
||||
goto err;
|
||||
}
|
||||
|
||||
if(EVP_PKEY_CTX_set1_hkdf_key(pctx, secret, (int)secretlen) != 1) {
|
||||
goto err;
|
||||
}
|
||||
|
||||
if(EVP_PKEY_derive(pctx, dest, &destlen) != 1) {
|
||||
goto err;
|
||||
}
|
||||
|
||||
EVP_PKEY_CTX_free(pctx);
|
||||
return 0;
|
||||
err:
|
||||
EVP_PKEY_CTX_free(pctx);
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int qhkdf_expand(uint8_t *dest, size_t destlen,
|
||||
const uint8_t *secret, size_t secretlen,
|
||||
const uint8_t *qlabel, size_t qlabellen,
|
||||
const struct Context *ctx)
|
||||
{
|
||||
uint8_t info[256];
|
||||
static const char LABEL[] = "quic ";
|
||||
|
||||
uint8_t *p = &info[0];
|
||||
*p++ = (destlen / 256) & 0xff;
|
||||
*p++ = destlen % 256;
|
||||
*p++ = (strlen(LABEL) + qlabellen) & 0xff;
|
||||
memcpy(p, LABEL, strlen(LABEL));
|
||||
p += strlen(LABEL);
|
||||
memcpy(p, qlabel, qlabellen);
|
||||
p += qlabellen;
|
||||
*p++ = 0;
|
||||
|
||||
return hkdf_expand(dest, destlen, secret, secretlen, &info[0],
|
||||
p - &info[0], ctx);
|
||||
}
|
||||
|
||||
static size_t aead_key_length(const struct Context *ctx)
|
||||
{
|
||||
return EVP_CIPHER_key_length(ctx->aead);
|
||||
}
|
||||
|
||||
static size_t aead_tag_length(const struct Context *ctx)
|
||||
{
|
||||
if(ctx->aead == EVP_aes_128_gcm() || ctx->aead == EVP_aes_256_gcm()) {
|
||||
return EVP_GCM_TLS_TAG_LEN;
|
||||
}
|
||||
if(ctx->aead == EVP_chacha20_poly1305()) {
|
||||
return EVP_CHACHAPOLY_TLS_TAG_LEN;
|
||||
}
|
||||
assert(0);
|
||||
}
|
||||
|
||||
size_t Curl_qc_aead_max_overhead(const struct Context *ctx)
|
||||
{
|
||||
return aead_tag_length(ctx);
|
||||
}
|
||||
|
||||
ssize_t Curl_qc_encrypt(uint8_t *dest, size_t destlen,
|
||||
const uint8_t *plaintext, size_t plaintextlen,
|
||||
const struct Context *ctx,
|
||||
const uint8_t *key, size_t keylen,
|
||||
const uint8_t *nonce, size_t noncelen,
|
||||
const uint8_t *ad, size_t adlen)
|
||||
{
|
||||
size_t taglen = aead_tag_length(ctx);
|
||||
EVP_CIPHER_CTX *actx;
|
||||
size_t outlen = 0;
|
||||
int len;
|
||||
(void)keylen;
|
||||
|
||||
if(destlen < plaintextlen + taglen) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
actx = EVP_CIPHER_CTX_new();
|
||||
if(!actx)
|
||||
return -1;
|
||||
|
||||
if(EVP_EncryptInit_ex(actx, ctx->aead, NULL, NULL, NULL) != 1)
|
||||
goto error;
|
||||
|
||||
if(EVP_CIPHER_CTX_ctrl(actx, EVP_CTRL_AEAD_SET_IVLEN,
|
||||
(int)noncelen, NULL) != 1)
|
||||
goto error;
|
||||
|
||||
if(EVP_EncryptInit_ex(actx, NULL, NULL, key, nonce) != 1)
|
||||
goto error;
|
||||
|
||||
if(EVP_EncryptUpdate(actx, NULL, &len, ad, (int)adlen) != 1)
|
||||
goto error;
|
||||
|
||||
if(EVP_EncryptUpdate(actx, dest, &len, plaintext, (int)plaintextlen) != 1)
|
||||
goto error;
|
||||
|
||||
outlen = len;
|
||||
if(EVP_EncryptFinal_ex(actx, dest + outlen, &len) != 1)
|
||||
goto error;
|
||||
|
||||
outlen += len;
|
||||
assert(outlen + taglen <= destlen);
|
||||
|
||||
if(EVP_CIPHER_CTX_ctrl(actx, EVP_CTRL_AEAD_GET_TAG,
|
||||
(int)taglen, dest + outlen) != 1)
|
||||
goto error;
|
||||
|
||||
outlen += taglen;
|
||||
|
||||
EVP_CIPHER_CTX_free(actx);
|
||||
return outlen;
|
||||
|
||||
error:
|
||||
EVP_CIPHER_CTX_free(actx);
|
||||
return -1;
|
||||
}
|
||||
|
||||
ssize_t Curl_qc_decrypt(uint8_t *dest, size_t destlen,
|
||||
const uint8_t *ciphertext, size_t ciphertextlen,
|
||||
const struct Context *ctx,
|
||||
const uint8_t *key, size_t keylen,
|
||||
const uint8_t *nonce, size_t noncelen,
|
||||
const uint8_t *ad, size_t adlen)
|
||||
{
|
||||
size_t taglen = aead_tag_length(ctx);
|
||||
const uint8_t *tag;
|
||||
EVP_CIPHER_CTX *actx;
|
||||
size_t outlen;
|
||||
int len;
|
||||
(void)keylen;
|
||||
|
||||
if(taglen > ciphertextlen || destlen + taglen < ciphertextlen) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
ciphertextlen -= taglen;
|
||||
tag = ciphertext + ciphertextlen;
|
||||
|
||||
actx = EVP_CIPHER_CTX_new();
|
||||
if(!actx)
|
||||
return -1;
|
||||
|
||||
if(EVP_DecryptInit_ex(actx, ctx->aead, NULL, NULL, NULL) != 1)
|
||||
goto error;
|
||||
|
||||
if(EVP_CIPHER_CTX_ctrl(actx, EVP_CTRL_AEAD_SET_IVLEN, (int)noncelen, NULL) !=
|
||||
1)
|
||||
goto error;
|
||||
|
||||
if(EVP_DecryptInit_ex(actx, NULL, NULL, key, nonce) != 1)
|
||||
goto error;
|
||||
|
||||
if(EVP_DecryptUpdate(actx, NULL, &len, ad, (int)adlen) != 1)
|
||||
goto error;
|
||||
|
||||
if(EVP_DecryptUpdate(actx, dest, &len, ciphertext, (int)ciphertextlen) != 1)
|
||||
goto error;
|
||||
|
||||
outlen = len;
|
||||
if(EVP_CIPHER_CTX_ctrl(actx, EVP_CTRL_AEAD_SET_TAG,
|
||||
(int)taglen, (char *)tag) != 1)
|
||||
goto error;
|
||||
|
||||
if(EVP_DecryptFinal_ex(actx, dest + outlen, &len) != 1)
|
||||
goto error;
|
||||
|
||||
outlen += len;
|
||||
|
||||
EVP_CIPHER_CTX_free(actx);
|
||||
return outlen;
|
||||
error:
|
||||
EVP_CIPHER_CTX_free(actx);
|
||||
return -1;
|
||||
}
|
||||
|
||||
int Curl_qc_derive_initial_secret(uint8_t *dest, size_t destlen,
|
||||
const ngtcp2_cid *secret,
|
||||
const uint8_t *salt,
|
||||
size_t saltlen)
|
||||
{
|
||||
struct Context ctx;
|
||||
Curl_qc_prf_sha256(&ctx);
|
||||
return hkdf_extract(dest, destlen, secret->data, secret->datalen, salt,
|
||||
saltlen, &ctx);
|
||||
}
|
||||
|
||||
int Curl_qc_derive_client_initial_secret(uint8_t *dest,
|
||||
size_t destlen,
|
||||
const uint8_t *secret,
|
||||
size_t secretlen)
|
||||
{
|
||||
static uint8_t LABEL[] = "client in";
|
||||
struct Context ctx;
|
||||
Curl_qc_prf_sha256(&ctx);
|
||||
return qhkdf_expand(dest, destlen, secret, secretlen, LABEL,
|
||||
strlen((char *)LABEL), &ctx);
|
||||
}
|
||||
|
||||
ssize_t Curl_qc_derive_packet_protection_key(uint8_t *dest, size_t destlen,
|
||||
const uint8_t *secret,
|
||||
size_t secretlen,
|
||||
const struct Context *ctx)
|
||||
{
|
||||
int rv;
|
||||
static uint8_t LABEL_KEY[] = "key";
|
||||
size_t keylen = aead_key_length(ctx);
|
||||
if(keylen > destlen) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
rv = qhkdf_expand(dest, keylen, secret, secretlen, LABEL_KEY,
|
||||
strlen((char *)LABEL_KEY), ctx);
|
||||
if(rv) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return keylen;
|
||||
}
|
||||
|
||||
ssize_t Curl_qc_derive_packet_protection_iv(uint8_t *dest, size_t destlen,
|
||||
const uint8_t *secret,
|
||||
size_t secretlen,
|
||||
const struct Context *ctx)
|
||||
{
|
||||
int rv;
|
||||
static uint8_t LABEL_IV[] = "iv";
|
||||
|
||||
size_t ivlen = CURLMAX(8, Curl_qc_aead_nonce_length(ctx));
|
||||
if(ivlen > destlen) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
rv = qhkdf_expand(dest, ivlen, secret, secretlen, LABEL_IV,
|
||||
strlen((char *)LABEL_IV), ctx);
|
||||
if(rv) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return ivlen;
|
||||
}
|
||||
|
||||
int Curl_qc_derive_server_initial_secret(uint8_t *dest, size_t destlen,
|
||||
const uint8_t *secret,
|
||||
size_t secretlen)
|
||||
{
|
||||
static uint8_t LABEL[] = "server in";
|
||||
struct Context ctx;
|
||||
Curl_qc_prf_sha256(&ctx);
|
||||
return qhkdf_expand(dest, destlen, secret, secretlen, LABEL,
|
||||
strlen((char *)LABEL), &ctx);
|
||||
}
|
||||
|
||||
static int
|
||||
hkdf_expand_label(uint8_t *dest, size_t destlen, const uint8_t *secret,
|
||||
size_t secretlen, const uint8_t *label, size_t labellen,
|
||||
const struct Context *ctx)
|
||||
{
|
||||
uint8_t info[256];
|
||||
static const uint8_t LABEL[] = "tls13 ";
|
||||
|
||||
uint8_t *p = &info[0];
|
||||
*p++ = (destlen / 256)&0xff;
|
||||
*p++ = destlen % 256;
|
||||
*p++ = (strlen((char *)LABEL) + labellen)&0xff;
|
||||
memcpy(p, LABEL, strlen((char *)LABEL));
|
||||
p += strlen((char *)LABEL);
|
||||
memcpy(p, label, labellen);
|
||||
p += labellen;
|
||||
*p++ = 0;
|
||||
|
||||
return hkdf_expand(dest, destlen, secret, secretlen, &info[0],
|
||||
p - &info[0], ctx);
|
||||
}
|
||||
|
||||
ssize_t
|
||||
Curl_qc_derive_header_protection_key(uint8_t *dest, size_t destlen,
|
||||
const uint8_t *secret, size_t secretlen,
|
||||
const struct Context *ctx)
|
||||
{
|
||||
int rv;
|
||||
static uint8_t LABEL[] = "quic hp";
|
||||
|
||||
size_t keylen = aead_key_length(ctx);
|
||||
if(keylen > destlen)
|
||||
return -1;
|
||||
|
||||
rv = hkdf_expand_label(dest, keylen, secret, secretlen, LABEL,
|
||||
strlen((char *)LABEL), ctx);
|
||||
|
||||
if(rv)
|
||||
return -1;
|
||||
|
||||
return keylen;
|
||||
}
|
||||
|
||||
ssize_t Curl_qc_hp_mask(uint8_t *dest, size_t destlen,
|
||||
const struct Context *ctx,
|
||||
const uint8_t *key, size_t keylen,
|
||||
const uint8_t *sample, size_t samplelen)
|
||||
{
|
||||
static uint8_t PLAINTEXT[] = "\x00\x00\x00\x00\x00";
|
||||
EVP_CIPHER_CTX *actx;
|
||||
size_t outlen = 0;
|
||||
int len;
|
||||
(void)destlen; /* TODO: make use of these! */
|
||||
(void)keylen;
|
||||
(void)samplelen;
|
||||
|
||||
actx = EVP_CIPHER_CTX_new();
|
||||
if(!actx)
|
||||
return -1;
|
||||
|
||||
if(EVP_EncryptInit_ex(actx, ctx->hp, NULL, key, sample) != 1)
|
||||
goto error;
|
||||
if(EVP_EncryptUpdate(actx, dest, &len, PLAINTEXT,
|
||||
(int)strlen((char *)PLAINTEXT)) != 1)
|
||||
goto error;
|
||||
|
||||
DEBUGASSERT(len == 5);
|
||||
|
||||
outlen = len;
|
||||
|
||||
if(EVP_EncryptFinal_ex(actx, dest + outlen, &len) != 1)
|
||||
goto error;
|
||||
|
||||
DEBUGASSERT(len == 0);
|
||||
|
||||
return outlen;
|
||||
error:
|
||||
EVP_CIPHER_CTX_free(actx);
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
#endif
|
||||
93
lib/vquic/ngtcp2-crypto.h
Normal file
93
lib/vquic/ngtcp2-crypto.h
Normal file
|
|
@ -0,0 +1,93 @@
|
|||
#ifndef HEADER_CURL_VQUIC_NGTCP2_CRYPTO_H
|
||||
#define HEADER_CURL_VQUIC_NGTCP2_CRYPTO_H
|
||||
/***************************************************************************
|
||||
* _ _ ____ _
|
||||
* Project ___| | | | _ \| |
|
||||
* / __| | | | |_) | |
|
||||
* | (__| |_| | _ <| |___
|
||||
* \___|\___/|_| \_\_____|
|
||||
*
|
||||
* Copyright (C) 1998 - 2019, Daniel Stenberg, <daniel@haxx.se>, et al.
|
||||
*
|
||||
* This software is licensed as described in the file COPYING, which
|
||||
* you should have received as part of this distribution. The terms
|
||||
* are also available at https://curl.haxx.se/docs/copyright.html.
|
||||
*
|
||||
* You may opt to use, copy, modify, merge, publish, distribute and/or sell
|
||||
* copies of the Software, and permit persons to whom the Software is
|
||||
* furnished to do so, under the terms of the COPYING file.
|
||||
*
|
||||
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
|
||||
* KIND, either express or implied.
|
||||
*
|
||||
***************************************************************************/
|
||||
|
||||
#include "curl_setup.h"
|
||||
|
||||
#ifdef USE_NGTCP2
|
||||
struct Context {
|
||||
#if defined(OPENSSL_IS_BORINGSSL)
|
||||
const EVP_AEAD *aead;
|
||||
#else /* !OPENSSL_IS_BORINGSSL */
|
||||
const EVP_CIPHER *aead;
|
||||
#endif /* !OPENSSL_IS_BORINGSSL */
|
||||
const EVP_CIPHER *hp;
|
||||
const EVP_MD *prf;
|
||||
uint8_t tx_secret[64];
|
||||
uint8_t rx_secret[64];
|
||||
size_t secretlen;
|
||||
};
|
||||
|
||||
void Curl_qc_prf_sha256(struct Context *ctx);
|
||||
void Curl_qc_aead_aes_128_gcm(struct Context *ctx);
|
||||
size_t Curl_qc_aead_nonce_length(const struct Context *ctx);
|
||||
int Curl_qc_negotiated_prf(struct Context *ctx, SSL *ssl);
|
||||
int Curl_qc_negotiated_aead(struct Context *ctx, SSL *ssl);
|
||||
size_t Curl_qc_aead_max_overhead(const struct Context *ctx);
|
||||
ssize_t Curl_qc_encrypt(uint8_t *dest, size_t destlen,
|
||||
const uint8_t *plaintext, size_t plaintextlen,
|
||||
const struct Context *ctx,
|
||||
const uint8_t *key, size_t keylen,
|
||||
const uint8_t *nonce, size_t noncelen,
|
||||
const uint8_t *ad, size_t adlen);
|
||||
ssize_t Curl_qc_decrypt(uint8_t *dest, size_t destlen,
|
||||
const uint8_t *ciphertext, size_t ciphertextlen,
|
||||
const struct Context *ctx,
|
||||
const uint8_t *key, size_t keylen,
|
||||
const uint8_t *nonce, size_t noncelen,
|
||||
const uint8_t *ad, size_t adlen);
|
||||
ssize_t Curl_qc_encrypt_pn(uint8_t *dest, size_t destlen,
|
||||
const uint8_t *plaintext, size_t plaintextlen,
|
||||
const struct Context *ctx,
|
||||
const uint8_t *key, size_t keylen,
|
||||
const uint8_t *nonce, size_t noncelen);
|
||||
int Curl_qc_derive_initial_secret(uint8_t *dest, size_t destlen,
|
||||
const ngtcp2_cid *secret,
|
||||
const uint8_t *salt,
|
||||
size_t saltlen);
|
||||
int Curl_qc_derive_client_initial_secret(uint8_t *dest,
|
||||
size_t destlen,
|
||||
const uint8_t *secret,
|
||||
size_t secretlen);
|
||||
ssize_t Curl_qc_derive_packet_protection_key(uint8_t *dest, size_t destlen,
|
||||
const uint8_t *secret,
|
||||
size_t secretlen,
|
||||
const struct Context *ctx);
|
||||
ssize_t Curl_qc_derive_packet_protection_iv(uint8_t *dest, size_t destlen,
|
||||
const uint8_t *secret,
|
||||
size_t secretlen,
|
||||
const struct Context *ctx);
|
||||
int Curl_qc_derive_server_initial_secret(uint8_t *dest, size_t destlen,
|
||||
const uint8_t *secret,
|
||||
size_t secretlen);
|
||||
ssize_t
|
||||
Curl_qc_derive_header_protection_key(uint8_t *dest, size_t destlen,
|
||||
const uint8_t *secret, size_t secretlen,
|
||||
const struct Context *ctx);
|
||||
|
||||
ssize_t Curl_qc_hp_mask(uint8_t *dest, size_t destlen,
|
||||
const struct Context *ctx,
|
||||
const uint8_t *key, size_t keylen,
|
||||
const uint8_t *sample, size_t samplelen);
|
||||
#endif /* USE_NGTCP2 */
|
||||
#endif /* HEADER_CURL_VQUIC_NGTCP2_CRYPTO_H */
|
||||
1029
lib/vquic/ngtcp2.c
Normal file
1029
lib/vquic/ngtcp2.c
Normal file
File diff suppressed because it is too large
Load diff
65
lib/vquic/ngtcp2.h
Normal file
65
lib/vquic/ngtcp2.h
Normal file
|
|
@ -0,0 +1,65 @@
|
|||
#ifndef HEADER_CURL_VQUIC_NGTCP2_H
|
||||
#define HEADER_CURL_VQUIC_NGTCP2_H
|
||||
/***************************************************************************
|
||||
* _ _ ____ _
|
||||
* Project ___| | | | _ \| |
|
||||
* / __| | | | |_) | |
|
||||
* | (__| |_| | _ <| |___
|
||||
* \___|\___/|_| \_\_____|
|
||||
*
|
||||
* Copyright (C) 1998 - 2019, Daniel Stenberg, <daniel@haxx.se>, et al.
|
||||
*
|
||||
* This software is licensed as described in the file COPYING, which
|
||||
* you should have received as part of this distribution. The terms
|
||||
* are also available at https://curl.haxx.se/docs/copyright.html.
|
||||
*
|
||||
* You may opt to use, copy, modify, merge, publish, distribute and/or sell
|
||||
* copies of the Software, and permit persons to whom the Software is
|
||||
* furnished to do so, under the terms of the COPYING file.
|
||||
*
|
||||
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
|
||||
* KIND, either express or implied.
|
||||
*
|
||||
***************************************************************************/
|
||||
|
||||
#include "curl_setup.h"
|
||||
|
||||
#ifdef USE_NGTCP2
|
||||
|
||||
#include <ngtcp2/ngtcp2.h>
|
||||
#include <openssl/ssl.h>
|
||||
#include "ngtcp2-crypto.h"
|
||||
|
||||
struct quic_handshake {
|
||||
char *buf; /* pointer to the buffer */
|
||||
size_t alloclen; /* size of allocation */
|
||||
size_t len; /* size of content in buffer */
|
||||
size_t nread; /* how many bytes have been read */
|
||||
};
|
||||
|
||||
struct quicsocket {
|
||||
ngtcp2_conn *conn;
|
||||
ngtcp2_cid dcid;
|
||||
ngtcp2_cid scid;
|
||||
uint32_t version;
|
||||
ngtcp2_conn_callbacks callbacks;
|
||||
ngtcp2_settings settings;
|
||||
SSL_CTX *sslctx;
|
||||
SSL *ssl;
|
||||
struct Context crypto_ctx;
|
||||
struct Context hs_crypto_ctx;
|
||||
struct quic_handshake handshake;
|
||||
/* the last TLS alert description generated by the local endpoint */
|
||||
uint8_t tls_alert;
|
||||
};
|
||||
|
||||
#include "urldata.h"
|
||||
|
||||
CURLcode Curl_quic_connect(struct connectdata *conn,
|
||||
curl_socket_t sockfd,
|
||||
const struct sockaddr *addr,
|
||||
socklen_t addrlen);
|
||||
int Curl_quic_ver(char *p, size_t len);
|
||||
#endif
|
||||
|
||||
#endif /* HEADER_CURL_VQUIC_NGTCP2_H */
|
||||
229
lib/vquic/quiche.c
Normal file
229
lib/vquic/quiche.c
Normal file
|
|
@ -0,0 +1,229 @@
|
|||
/***************************************************************************
|
||||
* _ _ ____ _
|
||||
* Project ___| | | | _ \| |
|
||||
* / __| | | | |_) | |
|
||||
* | (__| |_| | _ <| |___
|
||||
* \___|\___/|_| \_\_____|
|
||||
*
|
||||
* Copyright (C) 1998 - 2019, Daniel Stenberg, <daniel@haxx.se>, et al.
|
||||
*
|
||||
* This software is licensed as described in the file COPYING, which
|
||||
* you should have received as part of this distribution. The terms
|
||||
* are also available at https://curl.haxx.se/docs/copyright.html.
|
||||
*
|
||||
* You may opt to use, copy, modify, merge, publish, distribute and/or sell
|
||||
* copies of the Software, and permit persons to whom the Software is
|
||||
* furnished to do so, under the terms of the COPYING file.
|
||||
*
|
||||
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
|
||||
* KIND, either express or implied.
|
||||
*
|
||||
***************************************************************************/
|
||||
|
||||
#include "curl_setup.h"
|
||||
|
||||
#ifdef USE_QUICHE
|
||||
#include <quiche.h>
|
||||
#include <openssl/err.h>
|
||||
#include "urldata.h"
|
||||
#include "sendf.h"
|
||||
#include "strdup.h"
|
||||
#include "rand.h"
|
||||
#include "quic.h"
|
||||
|
||||
/* The last 3 #include files should be in this order */
|
||||
#include "curl_printf.h"
|
||||
#include "curl_memory.h"
|
||||
#include "memdebug.h"
|
||||
|
||||
#define QUIC_MAX_STREAMS (256*1024)
|
||||
#define QUIC_MAX_DATA (1*1024*1024)
|
||||
#define QUIC_IDLE_TIMEOUT 60 * 1000 /* milliseconds */
|
||||
|
||||
static CURLcode process_ingress(struct connectdata *conn,
|
||||
curl_socket_t sockfd);
|
||||
|
||||
static CURLcode flush_egress(struct connectdata *conn, curl_socket_t sockfd);
|
||||
|
||||
static Curl_recv quic_stream_recv;
|
||||
static Curl_send quic_stream_send;
|
||||
|
||||
|
||||
CURLcode Curl_quic_connect(struct connectdata *conn, curl_socket_t sockfd,
|
||||
const struct sockaddr *addr, socklen_t addrlen)
|
||||
{
|
||||
CURLcode result;
|
||||
struct quicsocket *qs = &conn->quic;
|
||||
(void)addr;
|
||||
(void)addrlen;
|
||||
|
||||
infof(conn->data, "Connecting socket %d over QUIC\n", sockfd);
|
||||
|
||||
qs->cfg = quiche_config_new(QUICHE_PROTOCOL_VERSION);
|
||||
if(!qs->cfg)
|
||||
return CURLE_FAILED_INIT; /* TODO: better return code */
|
||||
|
||||
quiche_config_set_idle_timeout(qs->cfg, QUIC_IDLE_TIMEOUT);
|
||||
quiche_config_set_initial_max_data(qs->cfg, QUIC_MAX_DATA);
|
||||
quiche_config_set_initial_max_stream_data_bidi_local(qs->cfg, QUIC_MAX_DATA);
|
||||
quiche_config_set_initial_max_stream_data_bidi_remote(qs->cfg, QUIC_MAX_DATA);
|
||||
quiche_config_set_initial_max_stream_data_uni(qs->cfg, QUIC_MAX_DATA);
|
||||
quiche_config_set_initial_max_streams_bidi(qs->cfg, QUIC_MAX_STREAMS);
|
||||
quiche_config_set_initial_max_streams_uni(qs->cfg, QUIC_MAX_STREAMS);
|
||||
quiche_config_set_application_protos(qs->cfg, (uint8_t *) "\x05hq-20", 6);
|
||||
|
||||
result = Curl_rand(conn->data, qs->scid, sizeof(qs->scid));
|
||||
if(result)
|
||||
return result;
|
||||
|
||||
qs->conn = quiche_connect(conn->host.name, (const uint8_t *) qs->scid,
|
||||
sizeof(qs->scid), qs->cfg);
|
||||
if(!qs->conn)
|
||||
return CURLE_FAILED_INIT; /* TODO: better return code */
|
||||
|
||||
result = flush_egress(conn, sockfd);
|
||||
if(result)
|
||||
return CURLE_FAILED_INIT; /* TODO: better return code */
|
||||
|
||||
infof(conn->data, "Sent QUIC client Initial\n");
|
||||
|
||||
return CURLE_OK;
|
||||
}
|
||||
|
||||
CURLcode Curl_quic_is_connected(struct connectdata *conn, int sockindex,
|
||||
bool *done)
|
||||
{
|
||||
CURLcode result;
|
||||
struct quicsocket *qs = &conn->quic;
|
||||
curl_socket_t sockfd = conn->sock[sockindex];
|
||||
|
||||
result = process_ingress(conn, sockfd);
|
||||
if(result)
|
||||
return result;
|
||||
|
||||
result = flush_egress(conn, sockfd);
|
||||
if(result)
|
||||
return result;
|
||||
|
||||
if(quiche_conn_is_established(qs->conn)) {
|
||||
conn->recv[sockindex] = quic_stream_recv;
|
||||
conn->send[sockindex] = quic_stream_send;
|
||||
*done = TRUE;
|
||||
}
|
||||
|
||||
return CURLE_OK;
|
||||
}
|
||||
|
||||
static CURLcode process_ingress(struct connectdata *conn, int sockfd)
|
||||
{
|
||||
ssize_t recvd;
|
||||
struct quicsocket *qs = &conn->quic;
|
||||
static uint8_t buf[65535];
|
||||
|
||||
do {
|
||||
recvd = recv(sockfd, buf, sizeof(buf), 0);
|
||||
if((recvd < 0) && ((errno == EAGAIN) || (errno == EWOULDBLOCK)))
|
||||
break;
|
||||
|
||||
if(recvd < 0)
|
||||
return CURLE_RECV_ERROR;
|
||||
|
||||
recvd = quiche_conn_recv(qs->conn, buf, recvd);
|
||||
if(recvd == QUICHE_ERR_DONE)
|
||||
break;
|
||||
|
||||
if(recvd < 0)
|
||||
return CURLE_RECV_ERROR;
|
||||
} while(1);
|
||||
|
||||
return CURLE_OK;
|
||||
}
|
||||
|
||||
static CURLcode flush_egress(struct connectdata *conn, int sockfd)
|
||||
{
|
||||
ssize_t sent;
|
||||
struct quicsocket *qs = &conn->quic;
|
||||
static uint8_t out[1200];
|
||||
|
||||
do {
|
||||
sent = quiche_conn_send(qs->conn, out, sizeof(out));
|
||||
if(sent == QUICHE_ERR_DONE)
|
||||
break;
|
||||
|
||||
if(sent < 0)
|
||||
return CURLE_SEND_ERROR;
|
||||
|
||||
sent = send(sockfd, out, sent, 0);
|
||||
if(sent < 0)
|
||||
return CURLE_SEND_ERROR;
|
||||
} while(1);
|
||||
|
||||
return CURLE_OK;
|
||||
}
|
||||
|
||||
static ssize_t quic_stream_recv(struct connectdata *conn,
|
||||
int sockindex,
|
||||
char *buf,
|
||||
size_t buffersize,
|
||||
CURLcode *curlcode)
|
||||
{
|
||||
bool fin;
|
||||
ssize_t recvd;
|
||||
struct quicsocket *qs = &conn->quic;
|
||||
curl_socket_t sockfd = conn->sock[sockindex];
|
||||
|
||||
if(process_ingress(conn, sockfd)) {
|
||||
*curlcode = CURLE_RECV_ERROR;
|
||||
return -1;
|
||||
}
|
||||
|
||||
recvd = quiche_conn_stream_recv(qs->conn, 0, (uint8_t *) buf, buffersize, &fin);
|
||||
if(recvd == QUICHE_ERR_DONE) {
|
||||
*curlcode = CURLE_AGAIN;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if(recvd < 0) {
|
||||
*curlcode = CURLE_RECV_ERROR;
|
||||
return -1;
|
||||
}
|
||||
|
||||
*curlcode = CURLE_OK;
|
||||
return recvd;
|
||||
}
|
||||
|
||||
static ssize_t quic_stream_send(struct connectdata *conn,
|
||||
int sockindex,
|
||||
const void *mem,
|
||||
size_t len,
|
||||
CURLcode *curlcode)
|
||||
{
|
||||
ssize_t sent;
|
||||
struct quicsocket *qs = &conn->quic;
|
||||
curl_socket_t sockfd = conn->sock[sockindex];
|
||||
|
||||
sent = quiche_conn_stream_send(qs->conn, 0, mem, len, true);
|
||||
if(sent < 0) {
|
||||
*curlcode = CURLE_SEND_ERROR;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if(flush_egress(conn, sockfd)) {
|
||||
*curlcode = CURLE_SEND_ERROR;
|
||||
return -1;
|
||||
}
|
||||
|
||||
*curlcode = CURLE_OK;
|
||||
return sent;
|
||||
}
|
||||
|
||||
/*
|
||||
* Store quiche version info in this buffer, Prefix with a space. Return total
|
||||
* length written.
|
||||
*/
|
||||
int Curl_quic_ver(char *p, size_t len)
|
||||
{
|
||||
return msnprintf(p, len, " quiche");
|
||||
}
|
||||
|
||||
#endif
|
||||
47
lib/vquic/quiche.h
Normal file
47
lib/vquic/quiche.h
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
#ifndef HEADER_CURL_VQUIC_QUICHE_H
|
||||
#define HEADER_CURL_VQUIC_QUICHE_H
|
||||
/***************************************************************************
|
||||
* _ _ ____ _
|
||||
* Project ___| | | | _ \| |
|
||||
* / __| | | | |_) | |
|
||||
* | (__| |_| | _ <| |___
|
||||
* \___|\___/|_| \_\_____|
|
||||
*
|
||||
* Copyright (C) 1998 - 2019, Daniel Stenberg, <daniel@haxx.se>, et al.
|
||||
*
|
||||
* This software is licensed as described in the file COPYING, which
|
||||
* you should have received as part of this distribution. The terms
|
||||
* are also available at https://curl.haxx.se/docs/copyright.html.
|
||||
*
|
||||
* You may opt to use, copy, modify, merge, publish, distribute and/or sell
|
||||
* copies of the Software, and permit persons to whom the Software is
|
||||
* furnished to do so, under the terms of the COPYING file.
|
||||
*
|
||||
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
|
||||
* KIND, either express or implied.
|
||||
*
|
||||
***************************************************************************/
|
||||
|
||||
#include "curl_setup.h"
|
||||
|
||||
#ifdef USE_QUICHE
|
||||
|
||||
#include <quiche.h>
|
||||
|
||||
struct quic_handshake {
|
||||
char *buf; /* pointer to the buffer */
|
||||
size_t alloclen; /* size of allocation */
|
||||
size_t len; /* size of content in buffer */
|
||||
size_t nread; /* how many bytes have been read */
|
||||
};
|
||||
|
||||
struct quicsocket {
|
||||
quiche_config *cfg;
|
||||
quiche_conn *conn;
|
||||
uint8_t scid[QUICHE_MAX_CONN_ID_LEN];
|
||||
uint32_t version;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
#endif /* HEADER_CURL_VQUIC_QUICHE_H */
|
||||
Loading…
Add table
Add a link
Reference in a new issue