mirror of
https://github.com/curl/curl.git
synced 2025-02-17 14:59:45 +08: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
167
configure.ac
167
configure.ac
@ -3337,6 +3337,163 @@ if test X"$want_h2" != Xno; then
|
||||
|
||||
fi
|
||||
|
||||
dnl **********************************************************************
|
||||
dnl Check for ngtcp2 (QUIC)
|
||||
dnl **********************************************************************
|
||||
|
||||
OPT_TCP2="yes"
|
||||
curl_h3_msg="disabled (--with-ngtcp2, --with-quiche)"
|
||||
|
||||
if test "x$disable_http" = "xyes"; then
|
||||
# without HTTP, ngtcp2 is no use
|
||||
OPT_TCP2="no"
|
||||
fi
|
||||
|
||||
AC_ARG_WITH(ngtcp2,
|
||||
AC_HELP_STRING([--with-ngtcp2=PATH],[Enable ngtcp2 usage])
|
||||
AC_HELP_STRING([--without-ngtcp2],[Disable ngtcp2 usage]),
|
||||
[OPT_TCP2=$withval])
|
||||
case "$OPT_TCP2" in
|
||||
no)
|
||||
dnl --without-ngtcp2 option used
|
||||
want_tcp2="no"
|
||||
;;
|
||||
yes)
|
||||
dnl --with-ngtcp2 option used without path
|
||||
want_tcp2="default"
|
||||
want_tcp2_path=""
|
||||
;;
|
||||
*)
|
||||
dnl --with-ngtcp2 option used with path
|
||||
want_tcp2="yes"
|
||||
want_tcp2_path="$withval/lib/pkgconfig"
|
||||
;;
|
||||
esac
|
||||
|
||||
curl_tcp2_msg="disabled (--with-ngtcp2)"
|
||||
if test X"$want_tcp2" != Xno; then
|
||||
dnl backup the pre-ngtcp2 variables
|
||||
CLEANLDFLAGS="$LDFLAGS"
|
||||
CLEANCPPFLAGS="$CPPFLAGS"
|
||||
CLEANLIBS="$LIBS"
|
||||
|
||||
CURL_CHECK_PKGCONFIG(libngtcp2, $want_tcp2_path)
|
||||
|
||||
if test "$PKGCONFIG" != "no" ; then
|
||||
LIB_TCP2=`CURL_EXPORT_PCDIR([$want_tcp2_path])
|
||||
$PKGCONFIG --libs-only-l libngtcp2`
|
||||
AC_MSG_NOTICE([-l is $LIB_TCP2])
|
||||
|
||||
CPP_TCP2=`CURL_EXPORT_PCDIR([$want_tcp2_path]) dnl
|
||||
$PKGCONFIG --cflags-only-I libngtcp2`
|
||||
AC_MSG_NOTICE([-I is $CPP_TCP2])
|
||||
|
||||
LD_TCP2=`CURL_EXPORT_PCDIR([$want_tcp2_path])
|
||||
$PKGCONFIG --libs-only-L libngtcp2`
|
||||
AC_MSG_NOTICE([-L is $LD_TCP2])
|
||||
|
||||
LDFLAGS="$LDFLAGS $LD_TCP2"
|
||||
CPPFLAGS="$CPPFLAGS $CPP_TCP2"
|
||||
LIBS="$LIB_TCP2 $LIBS"
|
||||
|
||||
if test "x$cross_compiling" != "xyes"; then
|
||||
DIR_TCP2=`echo $LD_TCP2 | $SED -e 's/-L//'`
|
||||
fi
|
||||
AC_CHECK_LIB(ngtcp2, ngtcp2_conn_client_new,
|
||||
[
|
||||
AC_CHECK_HEADERS(ngtcp2/ngtcp2.h,
|
||||
curl_h3_msg="enabled (ngtcp2)"
|
||||
NGTCP2_ENABLED=1
|
||||
AC_DEFINE(USE_NGTCP2, 1, [if ngtcp2 is in use])
|
||||
AC_SUBST(USE_NGTCP2, [1])
|
||||
CURL_LIBRARY_PATH="$CURL_LIBRARY_PATH:$DIR_TCP2"
|
||||
export CURL_LIBRARY_PATH
|
||||
AC_MSG_NOTICE([Added $DIR_TCP2 to CURL_LIBRARY_PATH])
|
||||
experimental="$experimental HTTP3"
|
||||
)
|
||||
],
|
||||
dnl not found, revert back to clean variables
|
||||
LDFLAGS=$CLEANLDFLAGS
|
||||
CPPFLAGS=$CLEANCPPFLAGS
|
||||
LIBS=$CLEANLIBS
|
||||
)
|
||||
|
||||
else
|
||||
dnl no ngtcp2 pkg-config found, deal with it
|
||||
if test X"$want_tcp2" != Xdefault; then
|
||||
dnl To avoid link errors, we do not allow --with-ngtcp2 without
|
||||
dnl a pkgconfig file
|
||||
AC_MSG_ERROR([--with-ngtcp2 was specified but could not find ngtcp2 pkg-config file.])
|
||||
fi
|
||||
fi
|
||||
|
||||
fi
|
||||
|
||||
dnl **********************************************************************
|
||||
dnl Check for quiche (QUIC)
|
||||
dnl **********************************************************************
|
||||
|
||||
OPT_QUICHE="yes"
|
||||
|
||||
if test "x$disable_http" = "xyes" -o "x$USE_NGTCP" = "x1"; then
|
||||
# without HTTP or with ngtcp2, quiche is no use
|
||||
OPT_QUICHE="no"
|
||||
fi
|
||||
|
||||
AC_ARG_WITH(quiche,
|
||||
AC_HELP_STRING([--with-quiche=PATH],[Enable quiche usage])
|
||||
AC_HELP_STRING([--without-quiche],[Disable quiche usage]),
|
||||
[OPT_QUICHE=$withval])
|
||||
case "$OPT_QUICHE" in
|
||||
*)
|
||||
dnl --with-quiche option used without path
|
||||
want_quiche="default"
|
||||
want_quiche_path=""
|
||||
;;
|
||||
no)
|
||||
dnl --without-quiche option used
|
||||
want_quiche="no"
|
||||
;;
|
||||
esac
|
||||
|
||||
if test X"$want_quiche" != Xno; then
|
||||
dnl backup the pre-quiche variables
|
||||
CLEANLDFLAGS="$LDFLAGS"
|
||||
CLEANCPPFLAGS="$CPPFLAGS"
|
||||
CLEANLIBS="$LIBS"
|
||||
|
||||
LIB_QUICHE="-l:libquiche.a -ldl -lpthread"
|
||||
CPP_QUICHE="-I$OPT_QUICHE/include"
|
||||
LD_QUICHE="-L$OPT_QUICHE/target/release"
|
||||
|
||||
LDFLAGS="$LDFLAGS $LD_QUICHE"
|
||||
CPPFLAGS="$CPPFLAGS $CPP_QUICHE"
|
||||
LIBS="$LIB_QUICHE $LIBS"
|
||||
|
||||
if test "x$cross_compiling" != "xyes"; then
|
||||
DIR_QUICHE=`echo $LD_QUICHE | $SED -e 's/-L//'`
|
||||
fi
|
||||
AC_CHECK_LIB(quiche, quiche_connect,
|
||||
[
|
||||
AC_CHECK_HEADERS(quiche.h,
|
||||
experimental="$experimental HTTP3"
|
||||
AC_MSG_NOTICE([HTTP3 support is experimental])
|
||||
curl_h3_msg="enabled (quiche)"
|
||||
QUICHE_ENABLED=1
|
||||
AC_DEFINE(USE_QUICHE, 1, [if quiche is in use])
|
||||
AC_SUBST(USE_QUICHE, [1])
|
||||
CURL_LIBRARY_PATH="$CURL_LIBRARY_PATH:$DIR_QUICHE"
|
||||
export CURL_LIBRARY_PATH
|
||||
AC_MSG_NOTICE([Added $DIR_QUICHE to CURL_LIBRARY_PATH]),
|
||||
)
|
||||
],
|
||||
dnl not found, revert back to clean variables
|
||||
LDFLAGS=$CLEANLDFLAGS
|
||||
CPPFLAGS=$CLEANCPPFLAGS
|
||||
LIBS=$CLEANLIBS
|
||||
)
|
||||
fi
|
||||
|
||||
dnl **********************************************************************
|
||||
dnl Check for zsh completion path
|
||||
dnl **********************************************************************
|
||||
@ -4162,7 +4319,6 @@ AC_HELP_STRING([--disable-alt-svc],[Disable alt-svc support]),
|
||||
*) AC_MSG_RESULT(yes)
|
||||
curl_altsvc_msg="enabled";
|
||||
enable_altsvc="yes"
|
||||
experimental="alt-svc"
|
||||
;;
|
||||
esac ],
|
||||
AC_MSG_RESULT(no)
|
||||
@ -4170,7 +4326,7 @@ AC_HELP_STRING([--disable-alt-svc],[Disable alt-svc support]),
|
||||
|
||||
if test "$enable_altsvc" = "yes"; then
|
||||
AC_DEFINE(USE_ALTSVC, 1, [to enable alt-svc])
|
||||
experimental="alt-svc"
|
||||
experimental="$experimental alt-svc"
|
||||
fi
|
||||
|
||||
dnl ************************************************************
|
||||
@ -4281,6 +4437,10 @@ if test "x$USE_NGHTTP2" = "x1"; then
|
||||
SUPPORT_FEATURES="$SUPPORT_FEATURES HTTP2"
|
||||
fi
|
||||
|
||||
if test "x$USE_NGTCP2" = "x1" -o "x$USE_QUICHE" = "x1"; then
|
||||
SUPPORT_FEATURES="$SUPPORT_FEATURES HTTP3"
|
||||
fi
|
||||
|
||||
if test "x$CURL_WITH_MULTI_SSL" = "x1"; then
|
||||
SUPPORT_FEATURES="$SUPPORT_FEATURES MultiSSL"
|
||||
fi
|
||||
@ -4472,11 +4632,12 @@ AC_MSG_NOTICE([Configured to build curl/libcurl:
|
||||
PSL: ${curl_psl_msg}
|
||||
Alt-svc: ${curl_altsvc_msg}
|
||||
HTTP2: ${curl_h2_msg}
|
||||
HTTP3: ${curl_h3_msg}
|
||||
Protocols: ${SUPPORT_PROTOCOLS}
|
||||
Features: ${SUPPORT_FEATURES}
|
||||
])
|
||||
if test -n "$experimental"; then
|
||||
cat >&2 << _EOF
|
||||
WARNING: $experimental is enabled but marked EXPERIMENTAL. Use with caution!
|
||||
WARNING: $experimental enabled but marked EXPERIMENTAL. Use with caution!
|
||||
_EOF
|
||||
fi
|
||||
|
85
docs/HTTP3.md
Normal file
85
docs/HTTP3.md
Normal file
@ -0,0 +1,85 @@
|
||||
# HTTP3 (and QUIC)
|
||||
|
||||
## Resources
|
||||
|
||||
[HTTP/3 Explained](https://daniel.haxx.se/http3-explained/) - the online free
|
||||
book describing the protocols involved.
|
||||
|
||||
[QUIC implementation](https://github.com/curl/curl/wiki/QUIC-implementation) -
|
||||
the wiki page describing the plan for how to support QUIC and HTTP/3 in curl
|
||||
and libcurl.
|
||||
|
||||
[quicwg.org](https://quicwg.org/) - home of the official protocol drafts
|
||||
|
||||
## QUIC libraries
|
||||
|
||||
QUIC libraries we're experiementing with:
|
||||
|
||||
[ngtcp2](https://github.com/ngtcp2/ngtcp2)
|
||||
|
||||
[quiche](https://github.com/cloudflare/quiche)
|
||||
|
||||
## Experimental!
|
||||
|
||||
HTTP/3 and QUIC support in curl is not yet working and this is early days.
|
||||
Consider all QUIC and HTTP/3 code to be **EXPERIMENTAL** until further notice.
|
||||
|
||||
curl does not have HTTP/3 support (yet).
|
||||
|
||||
The bleeding edge QUIC work is done in the dedicated
|
||||
[QUIC](https://github.com/curl/curl/tree/QUIC) branch, but the plan is to
|
||||
merge as often as possible from there to master. All QUIC related code will
|
||||
remain being build-time conditionally enabled.
|
||||
|
||||
# ngtcp2 version
|
||||
|
||||
## Build
|
||||
|
||||
1. clone ngtcp2 from git (the draft-17 branch)
|
||||
2. build and install ngtcp2's custom OpenSSL version (the quic-draft-17 branch)
|
||||
3. build and install ngtcp2 according to its instructions
|
||||
4. configure curl with ngtcp2 support: `./configure --with-ngtcp2=<install prefix>`
|
||||
5. build curl "normally"
|
||||
|
||||
## Running
|
||||
|
||||
Make sure the custom OpenSSL library is the one used at run-time, as otherwise
|
||||
you'll just get ld.so linker errors.
|
||||
|
||||
## Invoke from command line
|
||||
|
||||
curl --http3-direct https://nghttp2.org:8443/
|
||||
|
||||
# quiche version
|
||||
|
||||
## build
|
||||
|
||||
Build BoringSSL (it needs to be built manually so it can be reused with curl):
|
||||
|
||||
% mkdir -p quiche/deps/boringssl/build
|
||||
% cd quiche/deps/boringssl/build
|
||||
% cmake -DCMAKE_POSITION_INDEPENDENT_CODE=on ..
|
||||
% make -j`nproc`
|
||||
% cd ..
|
||||
% mkdir .openssl/lib -p
|
||||
% cp build/crypto/libcrypto.a build/ssl/libssl.a .openssl/lib
|
||||
% ln -s $PWD/include .openssl
|
||||
|
||||
Build quiche:
|
||||
|
||||
% cd ../..
|
||||
% QUICHE_BSSL_PATH=$PWD/deps/boringssl cargo build --release
|
||||
|
||||
Clone and build curl:
|
||||
|
||||
% cd ..
|
||||
% git clone https://github.com/curl/curl
|
||||
% ./buildconf
|
||||
% ./configure --with-ssl=$PWD/../quiche/deps/boringssl/.openssl --with-quiche=$PWD/../quiche --enable-debug
|
||||
% make -j`nproc`
|
||||
|
||||
## Running
|
||||
|
||||
Make an HTTP/1.1 request to a QUIC server:
|
||||
|
||||
% src/curl --http3-direct https://cloudflare-quic.com/
|
@ -60,6 +60,7 @@ EXTRA_DIST = \
|
||||
HISTORY.md \
|
||||
HTTP-COOKIES.md \
|
||||
HTTP2.md \
|
||||
HTTP3.md \
|
||||
INSTALL \
|
||||
INSTALL.cmake \
|
||||
INSTALL.md \
|
||||
|
@ -65,6 +65,7 @@ DPAGES = \
|
||||
http1.0.d \
|
||||
http1.1.d http2.d \
|
||||
http2-prior-knowledge.d \
|
||||
http3-direct.d \
|
||||
ignore-content-length.d \
|
||||
include.d \
|
||||
insecure.d \
|
||||
|
16
docs/cmdline-opts/http3-direct.d
Normal file
16
docs/cmdline-opts/http3-direct.d
Normal file
@ -0,0 +1,16 @@
|
||||
Long: http3-direct
|
||||
Tags: Versions
|
||||
Protocols: HTTP
|
||||
Added: 7.66.0
|
||||
Mutexed: http1.1 http1.0 http2 http2-prior-knowledge
|
||||
Requires: HTTP/3
|
||||
Help: Use HTTP v3
|
||||
---
|
||||
|
||||
WARNING: this option is experiemental. Do not use in production.
|
||||
|
||||
Tells curl to use HTTP version 3 directly to the host and port number used in
|
||||
the URL. A normal HTTP/3 transaction will be done to a host and then get
|
||||
redirected via Alt-SVc, but this option allows a user to circumvent that when
|
||||
you know that the target speaks HTTP/3 on the given host and port.
|
||||
|
@ -321,6 +321,8 @@ Enable and configure Alt-Svc: treatment. See \fICURLOPT_ALTSVC_CTRL(3)\fP
|
||||
Do an HTTP GET request. See \fICURLOPT_HTTPGET(3)\fP
|
||||
.IP CURLOPT_REQUEST_TARGET
|
||||
Set the request target. \fICURLOPT_REQUEST_TARGET(3)\fP
|
||||
.IP CURLOPT_H3
|
||||
Specify HTTP/3 behavior. \fICURLOPT_H3(3)\fP
|
||||
.IP CURLOPT_HTTP_VERSION
|
||||
HTTP version to use. \fICURLOPT_HTTP_VERSION(3)\fP
|
||||
.IP CURLOPT_HTTP09_ALLOWED
|
||||
|
61
docs/libcurl/opts/CURLOPT_H3.3
Normal file
61
docs/libcurl/opts/CURLOPT_H3.3
Normal file
@ -0,0 +1,61 @@
|
||||
.\" **************************************************************************
|
||||
.\" * _ _ ____ _
|
||||
.\" * 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.
|
||||
.\" *
|
||||
.\" **************************************************************************
|
||||
.\"
|
||||
.TH CURLOPT_H3 3 "27 Nov 2018" "libcurl 7.66.0" "curl_easy_setopt options"
|
||||
.SH NAME
|
||||
CURLOPT_H3 \- specify HTTP/3 protocol behavior
|
||||
.SH SYNOPSIS
|
||||
#include <curl/curl.h>
|
||||
|
||||
CURLcode curl_easy_setopt(CURL *handle, CURLOPT_H3, long bitmask);
|
||||
.SH EXPERIMENTAL
|
||||
Warning: this feature is early code and is marked as experimental. It can only
|
||||
be enabled by explicitly invoking configure with \fB--with-quiche\fP or
|
||||
\fB--with-ngtcp2\fP. You are advised to not ship this feature used in
|
||||
production before the experimental label is removed.
|
||||
.SH DESCRIPTION
|
||||
This function accepts a long \fIbitmask\fP with a set of flags set that
|
||||
controls the HTTP/3 behavior for this transfer.
|
||||
.IP "CURLH3_DIRECT"
|
||||
If this bit is set in \fIbitmask\fP, the host name and port number given in
|
||||
the URL will be used to connect to directly with QUIC and the port number then
|
||||
being a UDP port number.
|
||||
.SH DEFAULT
|
||||
0
|
||||
.SH PROTOCOLS
|
||||
HTTPS
|
||||
.SH EXAMPLE
|
||||
.nf
|
||||
CURL *curl = curl_easy_init();
|
||||
if(curl) {
|
||||
CURLcode ret;
|
||||
curl_easy_setopt(curl, CURLOPT_URL, "https://example.com/");
|
||||
curl_easy_setopt(curl, CURLOPT_H3, (long)CURLH3_DIRECT);
|
||||
ret = curl_easy_perform(curl);
|
||||
}
|
||||
.fi
|
||||
.SH AVAILABILITY
|
||||
Added in 7.66.0
|
||||
.SH RETURN VALUE
|
||||
Returns CURLE_OK if supported, an error otherwise.
|
||||
.SH "SEE ALSO"
|
||||
.BR CURLOPT_HTTP_VERSION "(3), "
|
@ -150,6 +150,7 @@ man_MANS = \
|
||||
CURLOPT_FTP_USE_EPSV.3 \
|
||||
CURLOPT_FTP_USE_PRET.3 \
|
||||
CURLOPT_GSSAPI_DELEGATION.3 \
|
||||
CURLOPT_H3.3 \
|
||||
CURLOPT_HAPPY_EYEBALLS_TIMEOUT_MS.3 \
|
||||
CURLOPT_HAPROXYPROTOCOL.3 \
|
||||
CURLOPT_HEADER.3 \
|
||||
|
@ -209,6 +209,7 @@ CURLFTP_CREATE_DIR_RETRY 7.19.4
|
||||
CURLGSSAPI_DELEGATION_FLAG 7.22.0
|
||||
CURLGSSAPI_DELEGATION_NONE 7.22.0
|
||||
CURLGSSAPI_DELEGATION_POLICY_FLAG 7.22.0
|
||||
CURLH3_DIRECT 7.66.0
|
||||
CURLHEADER_SEPARATE 7.37.0
|
||||
CURLHEADER_UNIFIED 7.37.0
|
||||
CURLINFO_ACTIVESOCKET 7.45.0
|
||||
@ -424,6 +425,7 @@ CURLOPT_FTP_USE_EPRT 7.10.5
|
||||
CURLOPT_FTP_USE_EPSV 7.9.2
|
||||
CURLOPT_FTP_USE_PRET 7.20.0
|
||||
CURLOPT_GSSAPI_DELEGATION 7.22.0
|
||||
CURLOPT_H3 7.66.0
|
||||
CURLOPT_HAPPY_EYEBALLS_TIMEOUT_MS 7.59.0
|
||||
CURLOPT_HAPROXYPROTOCOL 7.60.0
|
||||
CURLOPT_HEADER 7.1
|
||||
@ -925,6 +927,7 @@ CURL_VERSION_DEBUG 7.10.6
|
||||
CURL_VERSION_GSSAPI 7.38.0
|
||||
CURL_VERSION_GSSNEGOTIATE 7.10.6 7.38.0
|
||||
CURL_VERSION_HTTP2 7.33.0
|
||||
CURL_VERSION_HTTP3 7.66.0
|
||||
CURL_VERSION_HTTPS_PROXY 7.52.0
|
||||
CURL_VERSION_IDN 7.12.0
|
||||
CURL_VERSION_IPV6 7.10
|
||||
|
@ -923,6 +923,10 @@ typedef enum {
|
||||
#define CURLPROTO_SMBS (1<<27)
|
||||
#define CURLPROTO_ALL (~0) /* enable everything */
|
||||
|
||||
/* bitmask defines for CURLOPT_H3 */
|
||||
#define CURLH3_DIRECT (1<<0) /* go QUIC + HTTP/3 directly to the given host +
|
||||
port */
|
||||
|
||||
/* long may be 32 or 64 bits, but we should never depend on anything else
|
||||
but 32 */
|
||||
#define CURLOPTTYPE_LONG 0
|
||||
@ -1925,6 +1929,9 @@ typedef enum {
|
||||
/* maximum age of a connection to consider it for reuse (in seconds) */
|
||||
CINIT(MAXAGE_CONN, LONG, 288),
|
||||
|
||||
/* Bitmask to control HTTP/3 behavior. See CURLH3_* */
|
||||
CINIT(H3, LONG, 289),
|
||||
|
||||
CURLOPT_LASTENTRY /* the last unused */
|
||||
} CURLoption;
|
||||
|
||||
@ -2793,6 +2800,7 @@ typedef struct {
|
||||
#define CURL_VERSION_MULTI_SSL (1<<22) /* Multiple SSL backends available */
|
||||
#define CURL_VERSION_BROTLI (1<<23) /* Brotli features are present. */
|
||||
#define CURL_VERSION_ALTSVC (1<<24) /* Alt-Svc handling built-in */
|
||||
#define CURL_VERSION_HTTP3 (1<<25) /* HTTP3 support built-in */
|
||||
|
||||
/*
|
||||
* NAME curl_version_info()
|
||||
|
@ -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 */
|
@ -258,6 +258,7 @@ struct OperationConfig {
|
||||
0 is valid. default: CURL_HET_DEFAULT. */
|
||||
bool haproxy_protocol; /* whether to send HAProxy protocol v1 */
|
||||
bool disallow_username_in_url; /* disallow usernames in URLs */
|
||||
bool h3direct; /* go HTTP/3 directly */
|
||||
struct GlobalConfig *global;
|
||||
struct OperationConfig *prev;
|
||||
struct OperationConfig *next; /* Always last in the struct */
|
||||
|
@ -200,6 +200,7 @@ static const struct LongShort aliases[]= {
|
||||
{"01", "http1.1", ARG_NONE},
|
||||
{"02", "http2", ARG_NONE},
|
||||
{"03", "http2-prior-knowledge", ARG_NONE},
|
||||
{"04", "http3-direct", ARG_NONE},
|
||||
{"09", "http0.9", ARG_BOOL},
|
||||
{"1", "tlsv1", ARG_NONE},
|
||||
{"10", "tlsv1.0", ARG_NONE},
|
||||
@ -1189,10 +1190,14 @@ ParameterError getparameter(const char *flag, /* f or -long-flag */
|
||||
/* HTTP version 2.0 */
|
||||
config->httpversion = CURL_HTTP_VERSION_2_0;
|
||||
break;
|
||||
case '3':
|
||||
case '3': /* --http2-prior-knowledge */
|
||||
/* HTTP version 2.0 over clean TCP*/
|
||||
config->httpversion = CURL_HTTP_VERSION_2_PRIOR_KNOWLEDGE;
|
||||
break;
|
||||
case '4': /* --http3-direct */
|
||||
/* HTTP version 3 over QUIC - at once */
|
||||
config->h3direct = toggle;
|
||||
break;
|
||||
case '9':
|
||||
/* Allow HTTP/0.9 responses! */
|
||||
config->http09_allowed = toggle;
|
||||
|
@ -191,6 +191,8 @@ static const struct helptxt helptext[] = {
|
||||
"Use HTTP 2"},
|
||||
{" --http2-prior-knowledge",
|
||||
"Use HTTP 2 without HTTP/1.1 Upgrade"},
|
||||
{" --http3-direct",
|
||||
"Use HTTP v3"},
|
||||
{" --ignore-content-length",
|
||||
"Ignore the size of the remote resource"},
|
||||
{"-i, --include",
|
||||
@ -530,6 +532,7 @@ static const struct feat feats[] = {
|
||||
{"CharConv", CURL_VERSION_CONV},
|
||||
{"TLS-SRP", CURL_VERSION_TLSAUTH_SRP},
|
||||
{"HTTP2", CURL_VERSION_HTTP2},
|
||||
{"HTTP3", CURL_VERSION_HTTP3},
|
||||
{"UnixSockets", CURL_VERSION_UNIX_SOCKETS},
|
||||
{"HTTPS-proxy", CURL_VERSION_HTTPS_PROXY},
|
||||
{"MultiSSL", CURL_VERSION_MULTI_SSL},
|
||||
|
@ -1118,6 +1118,9 @@ static CURLcode create_transfers(struct GlobalConfig *global,
|
||||
if(config->tcp_fastopen)
|
||||
my_setopt(curl, CURLOPT_TCP_FASTOPEN, 1L);
|
||||
|
||||
if(config->h3direct)
|
||||
my_setopt(curl, CURLOPT_H3, CURLH3_DIRECT);
|
||||
|
||||
/* where to store */
|
||||
my_setopt(curl, CURLOPT_WRITEDATA, per);
|
||||
my_setopt(curl, CURLOPT_INTERLEAVEDATA, per);
|
||||
|
Loading…
Reference in New Issue
Block a user