Commit c24c7ffa authored by Tatsuhiro Tsujikawa's avatar Tatsuhiro Tsujikawa

[WIP] Add QUIC to h2load

parent 29cbf8b8
......@@ -454,6 +454,20 @@ if test "x${request_libcares}" = "xyes" &&
AC_MSG_ERROR([libcares was requested (--with-libcares) but not found])
fi
# ngtcp2 (for src)
PKG_CHECK_MODULES([LIBNGTCP2], [libngtcp2 >= 0.0.0], [have_libngtcp2=yes],
[have_libngtcp2=no])
if test "x${have_libngtcp2}" = "xno"; then
AC_MSG_NOTICE($LIBNGTCP2_PKG_ERRORS)
fi
# nghttp3 (for src)
PKG_CHECK_MODULES([LIBNGHTTP3], [libnghttp3 >= 0.0.0], [have_libnghttp3=yes],
[have_libnghttp3=no])
if test "x${have_libnghttp3}" = "xno"; then
AC_MSG_NOTICE($LIBNGHTTP3_PKT_ERRORS)
fi
# libevent_openssl (for examples)
# 2.0.8 is required because we use evconnlistener_set_error_cb()
have_libevent_openssl=no
......@@ -1011,6 +1025,8 @@ AC_MSG_NOTICE([summary of build options:
Libxml2: ${have_libxml2} (CFLAGS='${LIBXML2_CFLAGS}' LIBS='${LIBXML2_LIBS}')
Libev: ${have_libev} (CFLAGS='${LIBEV_CFLAGS}' LIBS='${LIBEV_LIBS}')
Libc-ares: ${have_libcares} (CFLAGS='${LIBCARES_CFLAGS}' LIBS='${LIBCARES_LIBS}')
libngtcp2: ${have_libngtcp2} (CFLAGS='${LIBNGTCP2_CFLAGS}' LIBS='${LIBNGTCP2_LIBS}')
libnghttp3: ${have_libnghttp3} (CFLAGS='${LIBNGHTTP3_CFLAGS}' LIBS='${LIBNGHTTP3_LIBS}')
Libevent(SSL): ${have_libevent_openssl} (CFLAGS='${LIBEVENT_OPENSSL_CFLAGS}' LIBS='${LIBEVENT_OPENSSL_LIBS}')
Jansson: ${have_jansson} (CFLAGS='${JANSSON_CFLAGS}' LIBS='${JANSSON_LIBS}')
Jemalloc: ${have_jemalloc} (CFLAGS='${JEMALLOC_CFLAGS}' LIBS='${JEMALLOC_LIBS}')
......
......@@ -46,6 +46,8 @@ AM_CPPFLAGS = \
@LIBEV_CFLAGS@ \
@OPENSSL_CFLAGS@ \
@LIBCARES_CFLAGS@ \
@LIBNGHTTP3_CFLAGS@ \
@LIBNGTCP2_CFLAGS@ \
@JANSSON_CFLAGS@ \
@ZLIB_CFLAGS@ \
@DEFS@
......@@ -59,6 +61,8 @@ LDADD = $(top_builddir)/lib/libnghttp2.la \
@LIBEV_LIBS@ \
@OPENSSL_LIBS@ \
@LIBCARES_LIBS@ \
@LIBNGHTTP3_LIBS@ \
@LIBNGTCP2_LIBS@ \
@SYSTEMD_LIBS@ \
@JANSSON_LIBS@ \
@ZLIB_LIBS@ \
......@@ -97,7 +101,10 @@ h2load_SOURCES = util.cc util.h \
tls.cc tls.h \
h2load_session.h \
h2load_http2_session.cc h2load_http2_session.h \
h2load_http1_session.cc h2load_http1_session.h
h2load_http1_session.cc h2load_http1_session.h \
h2load_http3_session.cc h2load_http3_session.h \
h2load_quic.cc h2load_quic.h \
quic.cc quic.h
NGHTTPX_SRCS = \
util.cc util.h http2.cc http2.h timegm.c timegm.h base64.h \
......
......@@ -48,10 +48,14 @@
#include <openssl/err.h>
#include <ngtcp2/ngtcp2.h>
#include "url-parser/url_parser.h"
#include "h2load_http1_session.h"
#include "h2load_http2_session.h"
#include "h2load_http3_session.h"
#include "h2load_quic.h"
#include "tls.h"
#include "http2.h"
#include "util.h"
......@@ -119,6 +123,9 @@ bool Config::is_rate_mode() const { return (this->rate != 0); }
bool Config::is_timing_based_mode() const { return (this->duration > 0); }
bool Config::has_base_uri() const { return (!this->base_uri.empty()); }
bool Config::rps_enabled() const { return this->rps > 0.0; }
bool Config::is_quic() const {
return !npn_list.empty() && npn_list[0] == NGTCP2_ALPN_H3;
}
Config config;
namespace {
......@@ -409,6 +416,7 @@ Client::Client(uint32_t id, Worker *worker, size_t req_todo)
cstat{},
worker(worker),
ssl(nullptr),
quic{},
next_addr(config.addrs),
current_addr(nullptr),
reqidx(0),
......@@ -420,6 +428,7 @@ Client::Client(uint32_t id, Worker *worker, size_t req_todo)
req_done(0),
id(id),
fd(-1),
local_addr{},
new_connection_requested(false),
final(false),
rps_duration_started(0),
......@@ -449,11 +458,18 @@ Client::Client(uint32_t id, Worker *worker, size_t req_todo)
ev_timer_init(&rps_watcher, rps_cb, 0., 0.);
rps_watcher.data = this;
ev_timer_init(&quic.pkt_timer, quic_pkt_timeout_cb, 0., 0.);
quic.pkt_timer.data = this;
}
Client::~Client() {
disconnect();
if (config.is_quic()) {
quic_free();
}
if (ssl) {
SSL_free(ssl);
}
......@@ -466,6 +482,37 @@ int Client::do_read() { return readfn(*this); }
int Client::do_write() { return writefn(*this); }
int Client::make_socket(addrinfo *addr) {
int rv;
if (config.is_quic()) {
fd = util::create_nonblock_udp_socket(addr->ai_family);
if (fd == -1) {
return -1;
}
rv = util::bind_any_addr_udp(fd, addr->ai_family);
if (rv != 0) {
close(fd);
fd = -1;
return -1;
}
socklen_t addrlen = sizeof(local_addr.su.storage);
rv = getsockname(fd, &local_addr.su.sa, &addrlen);
if (rv == -1) {
return -1;
}
local_addr.len = addrlen;
if (quic_init(&local_addr.su.sa, local_addr.len, addr->ai_addr,
addr->ai_addrlen) != 0) {
std::cerr << "quic_init failed" << std::endl;
return -1;
}
return 0;
}
fd = util::create_nonblock_socket(addr->ai_family);
if (fd == -1) {
return -1;
......@@ -475,17 +522,15 @@ int Client::make_socket(addrinfo *addr) {
ssl = SSL_new(worker->ssl_ctx);
}
auto config = worker->config;
if (!util::numeric_host(config->host.c_str())) {
SSL_set_tlsext_host_name(ssl, config->host.c_str());
}
SSL_set_fd(ssl, fd);
SSL_set_connect_state(ssl);
}
auto rv = ::connect(fd, addr->ai_addr, addr->ai_addrlen);
if (ssl && !util::numeric_host(config.host.c_str())) {
SSL_set_tlsext_host_name(ssl, config.host.c_str());
}
rv = ::connect(fd, addr->ai_addr, addr->ai_addrlen);
if (rv != 0 && errno != EINPROGRESS) {
if (ssl) {
SSL_free(ssl);
......@@ -542,13 +587,20 @@ int Client::connect() {
current_addr = addr;
}
writefn = &Client::connected;
ev_io_set(&rev, fd, EV_READ);
ev_io_set(&wev, fd, EV_WRITE);
ev_io_start(worker->loop, &wev);
if (config.is_quic()) {
ev_io_start(worker->loop, &rev);
readfn = &Client::read_quic;
writefn = &Client::write_quic;
} else {
writefn = &Client::connected;
}
return 0;
}
......@@ -603,6 +655,11 @@ void Client::fail() {
void Client::disconnect() {
record_client_end_time();
if (config.is_quic()) {
quic_close_connection();
}
ev_timer_stop(worker->loop, &quic.pkt_timer);
ev_timer_stop(worker->loop, &conn_inactivity_watcher);
ev_timer_stop(worker->loop, &conn_active_watcher);
ev_timer_stop(worker->loop, &rps_watcher);
......@@ -765,6 +822,9 @@ void Client::report_app_info() {
}
void Client::terminate_session() {
if (config.is_quic()) {
quic.close_requested = true;
}
session->terminate();
// http1 session needs writecb to tear down session.
signal_write();
......@@ -963,7 +1023,15 @@ int Client::connection_made() {
if (next_proto) {
auto proto = StringRef{next_proto, next_proto_len};
if (util::check_h2_is_selected(proto)) {
if (config.is_quic()) {
if (util::streq(StringRef{&NGTCP2_ALPN_H3[1]}, proto)) {
auto s = std::make_unique<Http3Session>(this);
if (s->init_conn() == -1) {
return -1;
}
session = std::move(s);
}
} else if (util::check_h2_is_selected(proto)) {
session = std::make_unique<Http2Session>(this);
} else if (util::streq(NGHTTP2_H1_1, proto)) {
session = std::make_unique<Http1Session>(this);
......@@ -1285,6 +1353,15 @@ int Client::write_tls() {
return 0;
}
int Client::write_udp(const sockaddr *addr, socklen_t addrlen,
const uint8_t *data, size_t datalen) {
auto nwrite = sendto(fd, data, datalen, MSG_DONTWAIT, addr, addrlen);
if (nwrite < 0) {
std::cerr << "sendto: errno=" << errno << std::endl;
}
return 0;
}
void Client::record_request_time(RequestStat *req_stat) {
req_stat->request_time = std::chrono::steady_clock::now();
req_stat->request_wall_time = std::chrono::system_clock::now();
......@@ -1698,6 +1775,79 @@ int client_select_next_proto_cb(SSL *ssl, unsigned char **out,
} // namespace
#endif // !OPENSSL_NO_NEXTPROTONEG
namespace {
int quic_transport_params_add_cb(SSL *ssl, unsigned int ext_type,
unsigned int content,
const unsigned char **out, size_t *outlen,
X509 *x, size_t chainidx, int *al,
void *add_arg) {
auto c = static_cast<Client *>(SSL_get_app_data(ssl));
auto conn = c->quic.conn;
ngtcp2_transport_params params;
ngtcp2_conn_get_local_transport_params(conn, &params);
constexpr size_t bufsize = 128;
auto buf = std::make_unique<uint8_t[]>(bufsize);
auto nwrite = ngtcp2_encode_transport_params(
buf.get(), bufsize, NGTCP2_TRANSPORT_PARAMS_TYPE_CLIENT_HELLO, &params);
if (nwrite < 0) {
std::cerr << "ngtcp2_encode_transport_params: " << ngtcp2_strerror(nwrite)
<< std::endl;
*al = SSL_AD_INTERNAL_ERROR;
return -1;
}
*out = buf.release();
*outlen = static_cast<size_t>(nwrite);
return 1;
}
} // namespace
namespace {
void quic_transport_params_free_cb(SSL *ssl, unsigned int ext_type,
unsigned int context,
const unsigned char *out, void *add_arg) {
delete[] const_cast<unsigned char *>(out);
}
} // namespace
namespace {
int quic_transport_params_parse_cb(SSL *ssl, unsigned int ext_type,
unsigned int context,
const unsigned char *in, size_t inlen,
X509 *x, size_t chainidx, int *al,
void *parse_arg) {
auto c = static_cast<Client *>(SSL_get_app_data(ssl));
auto conn = c->quic.conn;
int rv;
ngtcp2_transport_params params;
rv = ngtcp2_decode_transport_params(
&params, NGTCP2_TRANSPORT_PARAMS_TYPE_ENCRYPTED_EXTENSIONS, in, inlen);
if (rv != 0) {
std::cerr << "ngtcp2_decode_transport_params: " << ngtcp2_strerror(rv)
<< std::endl;
*al = SSL_AD_ILLEGAL_PARAMETER;
return -1;
}
rv = ngtcp2_conn_set_remote_transport_params(conn, &params);
if (rv != 0) {
std::cerr << "ngtcp2_conn_set_remote_transport_params: "
<< ngtcp2_strerror(rv) << std::endl;
*al = SSL_AD_ILLEGAL_PARAMETER;
return -1;
}
return 1;
}
} // namespace
namespace {
constexpr char UNIX_PATH_PREFIX[] = "unix:";
} // namespace
......@@ -2576,9 +2726,26 @@ int main(int argc, char **argv) {
SSL_CTX_set_mode(ssl_ctx, SSL_MODE_AUTO_RETRY);
SSL_CTX_set_mode(ssl_ctx, SSL_MODE_RELEASE_BUFFERS);
if (nghttp2::tls::ssl_ctx_set_proto_versions(
ssl_ctx, nghttp2::tls::NGHTTP2_TLS_MIN_VERSION,
nghttp2::tls::NGHTTP2_TLS_MAX_VERSION) != 0) {
if (config.is_quic()) {
SSL_CTX_set_min_proto_version(ssl_ctx, TLS1_3_VERSION);
SSL_CTX_set_max_proto_version(ssl_ctx, TLS1_3_VERSION);
SSL_CTX_clear_options(ssl_ctx, SSL_OP_ENABLE_MIDDLEBOX_COMPAT);
SSL_CTX_set_mode(ssl_ctx, SSL_MODE_QUIC_HACK);
if (SSL_CTX_add_custom_ext(
ssl_ctx, NGTCP2_TLSEXT_QUIC_TRANSPORT_PARAMETERS,
SSL_EXT_CLIENT_HELLO | SSL_EXT_TLS1_3_ENCRYPTED_EXTENSIONS,
quic_transport_params_add_cb, quic_transport_params_free_cb,
nullptr, quic_transport_params_parse_cb, nullptr) != 1) {
std::cerr << "SSL_CTX_add_custom_ext(NGTCP2_TLSEXT_QUIC_TRANSPORT_"
"PARAMETERS) failed: "
<< ERR_error_string(ERR_get_error(), nullptr) << std::endl;
exit(EXIT_FAILURE);
}
} else if (nghttp2::tls::ssl_ctx_set_proto_versions(
ssl_ctx, nghttp2::tls::NGHTTP2_TLS_MIN_VERSION,
nghttp2::tls::NGHTTP2_TLS_MAX_VERSION) != 0) {
std::cerr << "Could not set TLS versions" << std::endl;
exit(EXIT_FAILURE);
}
......@@ -2590,6 +2757,9 @@ int main(int argc, char **argv) {
exit(EXIT_FAILURE);
}
// TODO Use SSL_CTX_set_ciphersuites to set TLSv1.3 cipher list
// TODO Use SSL_CTX_set1_groups_list to set key share
#ifndef OPENSSL_NO_NEXTPROTONEG
SSL_CTX_set_next_proto_select_cb(ssl_ctx, client_select_next_proto_cb,
nullptr);
......
......@@ -45,11 +45,14 @@
#include <nghttp2/nghttp2.h>
#include <ngtcp2/ngtcp2.h>
#include <ev.h>
#include <openssl/ssl.h>
#include "http2.h"
#include "quic.h"
#include "memchunk.h"
#include "template.h"
......@@ -124,6 +127,7 @@ struct Config {
bool is_timing_based_mode() const;
bool has_base_uri() const;
bool rps_enabled() const;
bool is_quic() const;
};
struct RequestStat {
......@@ -299,6 +303,13 @@ struct Stream {
Stream();
};
struct Crypto {
Crypto() : datalen(0), acked_offset(0) {}
std::array<uint8_t, 1024> data;
size_t datalen;
size_t acked_offset;
};
struct Client {
DefaultMemchunks wb;
std::unordered_map<int32_t, Stream> streams;
......@@ -309,6 +320,19 @@ struct Client {
std::function<int(Client &)> readfn, writefn;
Worker *worker;
SSL *ssl;
struct {
ev_timer pkt_timer;
ngtcp2_conn *conn;
quic::Error last_error;
ngtcp2_crypto_level tx_crypto_level;
ngtcp2_crypto_level rx_crypto_level;
std::vector<uint8_t> server_handshake;
size_t server_handshake_nread;
// Client never send CRYPTO in Short packet.
std::array<Crypto, 2> crypto;
size_t max_pktlen;
bool close_requested;
} quic;
ev_timer request_timeout_watcher;
addrinfo *next_addr;
// Address for the current address. When try_new_connection() is
......@@ -332,6 +356,7 @@ struct Client {
// The client id per worker
uint32_t id;
int fd;
Address local_addr;
ev_timer conn_active_watcher;
ev_timer conn_inactivity_watcher;
std::string selected_proto;
......@@ -419,6 +444,62 @@ struct Client {
void record_client_end_time();
void signal_write();
// QUIC
int quic_init(const sockaddr *local_addr, socklen_t local_addrlen,
const sockaddr *remote_addr, socklen_t remote_addrlen);
void quic_free();
int read_quic();
int write_quic();
int write_udp(const sockaddr *addr, socklen_t addrlen, const uint8_t *data,
size_t datalen);
void quic_close_connection();
int quic_setup_initial_crypto();
int quic_client_initial();
int quic_recv_crypto_data(ngtcp2_crypto_level crypto_level,
const uint8_t *data, size_t datalen);
int quic_handshake_completed();
int quic_in_encrypt(uint8_t *dest, size_t destlen, const uint8_t *plaintext,
size_t plaintextlen, const uint8_t *key, size_t keylen,
const uint8_t *nonce, size_t noncelen, const uint8_t *ad,
size_t adlen);
int quic_in_decrypt(uint8_t *dest, size_t destlen, const uint8_t *ciphertext,
size_t ciphertextlen, const uint8_t *key, size_t keylen,
const uint8_t *nonce, size_t noncelen, const uint8_t *ad,
size_t adlen);
int quic_encrypt(uint8_t *dest, size_t destlen, const uint8_t *plaintext,
size_t plaintextlen, const uint8_t *key, size_t keylen,
const uint8_t *nonce, size_t noncelen, const uint8_t *ad,
size_t adlen);
int quic_decrypt(uint8_t *dest, size_t destlen, const uint8_t *ciphertext,
size_t ciphertextlen, const uint8_t *key, size_t keylen,
const uint8_t *nonce, size_t noncelen, const uint8_t *ad,
size_t adlen);
int quic_in_hp_mask(uint8_t *dest, size_t destlen, const uint8_t *key,
size_t keylen, const uint8_t *sample, size_t samplelen);
int quic_hp_mask(uint8_t *dest, size_t destlen, const uint8_t *key,
size_t keylen, const uint8_t *sample, size_t samplelen);
int quic_recv_stream_data(int64_t stream_id, int fin, const uint8_t *data,
size_t datalen);
int quic_stream_close(int64_t stream_id, uint16_t app_error_code);
int quic_stream_reset(int64_t stream_id, uint16_t app_error_code);
int quic_extend_max_local_streams();
int quic_tls_handshake(bool initial = false);
int quic_read_tls();
int quic_on_key(int name, const uint8_t *secret, size_t secretlen);
void quic_set_tls_alert(uint8_t alert);
size_t quic_read_server_handshake(uint8_t *buf, size_t buflen);
int quic_write_server_handshake(ngtcp2_crypto_level crypto_level,
const uint8_t *data, size_t datalen);
void quic_write_client_handshake(const uint8_t *data, size_t datalen);
void quic_write_client_handshake(Crypto &crypto, const uint8_t *data,
size_t datalen);
int quic_pkt_timeout();
void quic_restart_pkt_timer();
};
} // namespace h2load
......
This diff is collapsed.
/*
* nghttp2 - HTTP/2 C Library
*
* Copyright (c) 2019 nghttp2 contributors
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#ifndef H2LOAD_HTTP3_SESSION_H
#define H2LOAD_HTTP3_SESSION_H
#include "h2load_session.h"
#include <nghttp3/nghttp3.h>
namespace h2load {
struct Client;
class Http3Session : public Session {
public:
Http3Session(Client *client);
virtual ~Http3Session();
virtual void on_connect();
virtual int submit_request();
virtual int on_read(const uint8_t *data, size_t len);
virtual int on_write();
virtual void terminate();
virtual size_t max_concurrent_streams();
int init_conn();
int stream_close(int64_t stream_id, uint16_t error_code);
void recv_data(int64_t stream_id, const uint8_t *data, size_t datalen);
void consume(int64_t stream_id, size_t nconsumed);
void begin_headers(int64_t stream_id);
void recv_header(int64_t stream_id, const nghttp3_vec *name,
const nghttp3_vec *value);
int send_stop_sending(int64_t stream_id);
int close_stream(int64_t stream_id, uint16_t error_code);
int reset_stream(int64_t stream_id);
int extend_max_local_streams();
int64_t submit_request_internal();
ssize_t read_stream(int64_t stream_id, const uint8_t *data, size_t datalen,
int fin);
ssize_t write_stream(int64_t &stream_id, int &fin, nghttp3_vec *vec,
size_t veccnt);
int block_stream(int64_t stream_id);
int add_write_offset(int64_t stream_id, size_t ndatalen);
private:
Client *client_;
nghttp3_conn *conn_;
size_t npending_request_;
size_t reqidx_;
};
} // namespace h2load
#endif // H2LOAD_HTTP3_SESSION_H
This diff is collapsed.
/*
* nghttp2 - HTTP/2 C Library
*
* Copyright (c) 2019 nghttp2 contributors
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#ifndef H2LOAD_QUIC_H
#define H2LOAD_QUIC_H
#include "nghttp2_config.h"
#include <ev.h>
#include "h2load.h"
namespace h2load {
void quic_pkt_timeout_cb(struct ev_loop *loop, ev_timer *w, int revents);
} // namespace h2load
#endif // H2LOAD_QUIC_H
This diff is collapsed.
/*
* nghttp2 - HTTP/2 C Library
*
* Copyright (c) 2019 nghttp2 contributors
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#ifndef QUIC_H
#define QUIC_H
#include "nghttp2_config.h"
#include <openssl/ssl.h>
namespace quic {
const EVP_CIPHER *aead(SSL *ssl);
const EVP_CIPHER *hp(SSL *ssl);
const EVP_MD *prf(SSL *ssl);
size_t aead_max_overhead(const EVP_CIPHER *aead);
int hkdf_extract(uint8_t *dest, size_t destlen, const uint8_t *secret,
size_t secretlen, const uint8_t *salt, size_t saltlen,
const EVP_MD *prf);
int hkdf_expand(uint8_t *dest, size_t destlen, const uint8_t *secret,
size_t secretlen, const uint8_t *info, size_t infolen,
const EVP_MD *prf);
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 EVP_MD *prf);
int derive_initial_secret(uint8_t *dest, size_t destlen, const uint8_t *secret,
size_t secretlen, const uint8_t *salt,
size_t saltlen);
int derive_client_initial_secret(uint8_t *dest, size_t destlen,
const uint8_t *secret, size_t secretlen);
int derive_server_initial_secret(uint8_t *dest, size_t destlen,
const uint8_t *secret, size_t secretlen);
int derive_packet_protection_key(uint8_t *key, size_t &keylen, uint8_t *iv,
size_t &ivlen, const uint8_t *secret,
size_t secretlen, const EVP_CIPHER *aead,
const EVP_MD *prf);
int derive_header_protection_key(uint8_t *key, size_t &keylen,
const uint8_t *secret, size_t secretlen,
const EVP_CIPHER *aead, const EVP_MD *prf);
ssize_t encrypt(uint8_t *dest, size_t destlen, const uint8_t *plaintext,
size_t plaintextlen, const uint8_t *key, size_t keylen,
const uint8_t *nonce, size_t noncelen, const uint8_t *ad,
size_t adlen, const EVP_CIPHER *aead);
ssize_t decrypt(uint8_t *dest, size_t destlen, const uint8_t *ciphertext,
size_t ciphertextlen, const uint8_t *key, size_t keylen,
const uint8_t *nonce, size_t noncelen, const uint8_t *ad,
size_t adlen, const EVP_CIPHER *aead);
ssize_t hp_mask(uint8_t *dest, size_t destlen, const uint8_t *key,
size_t keylen, const uint8_t *sample, size_t samplelen,
const EVP_CIPHER *cipher);
enum class ErrorType {
Transport,
TransportVersionNegotiation,
Application,
};
struct Error {
Error(ErrorType type, uint16_t code) : type(type), code(code) {}
Error() : type(ErrorType::Transport), code(0) {}
ErrorType type;
uint16_t code;
};
Error err_transport(int liberr);
Error err_transport_tls(int alert);
Error err_application(int liberr);
} // namespace quic
#endif // QUIC_H
......@@ -945,6 +945,56 @@ int create_nonblock_socket(int family) {
return fd;
}
int create_nonblock_udp_socket(int family) {
#ifdef SOCK_NONBLOCK
auto fd = socket(family, SOCK_DGRAM | SOCK_NONBLOCK | SOCK_CLOEXEC, 0);
if (fd == -1) {
return -1;
}
#else // !SOCK_NONBLOCK
auto fd = socket(family, SOCK_STREAM, 0);
if (fd == -1) {
return -1;
}
make_socket_nonblocking(fd);
make_socket_closeonexec(fd);
#endif // !SOCK_NONBLOCK
return fd;
}
int bind_any_addr_udp(int fd, int family) {
addrinfo hints{};
addrinfo *res, *rp;
int rv;
hints.ai_family = family;
hints.ai_socktype = SOCK_DGRAM;
hints.ai_flags = AI_PASSIVE;
rv = getaddrinfo(nullptr, "0", &hints, &res);
if (rv != 0) {
return -1;
}
for (rp = res; rp; rp = rp->ai_next) {
if (bind(fd, rp->ai_addr, rp->ai_addrlen) != -1) {
break;
}
}
freeaddrinfo(res);
if (!rp) {
return -1;
}
return 0;
}
bool check_socket_connected(int fd) {
int error;
socklen_t len = sizeof(error);
......
......@@ -626,6 +626,9 @@ int make_socket_nonblocking(int fd);
int make_socket_nodelay(int fd);
int create_nonblock_socket(int family);
int create_nonblock_udp_socket(int family);
int bind_any_addr_udp(int fd, int family);
bool check_socket_connected(int fd);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment