diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-30 02:50:01 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-30 02:50:01 +0000 |
commit | 91275eb478ceb58083426099b6da3f4c7e189f19 (patch) | |
tree | 260f7d2fa77408b38c5cea96b320b9b0b6713ff2 /debian/vendor-h2o/lib/common/socket.c | |
parent | Merging upstream version 1.9.4. (diff) | |
download | dnsdist-91275eb478ceb58083426099b6da3f4c7e189f19.tar.xz dnsdist-91275eb478ceb58083426099b6da3f4c7e189f19.zip |
Merging debian version 1.9.4-1.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'debian/vendor-h2o/lib/common/socket.c')
-rw-r--r-- | debian/vendor-h2o/lib/common/socket.c | 1448 |
1 files changed, 0 insertions, 1448 deletions
diff --git a/debian/vendor-h2o/lib/common/socket.c b/debian/vendor-h2o/lib/common/socket.c deleted file mode 100644 index 2572233..0000000 --- a/debian/vendor-h2o/lib/common/socket.c +++ /dev/null @@ -1,1448 +0,0 @@ -/* - * Copyright (c) 2015 DeNA Co., Ltd., Kazuho Oku, Justin Zhu - * - * 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. - */ -#include <errno.h> -#include <fcntl.h> -#include <inttypes.h> -#include <limits.h> -#include <netdb.h> -#include <netinet/in.h> -#include <netinet/tcp.h> -#include <string.h> -#include <sys/un.h> -#include <unistd.h> -#include <openssl/err.h> -#if defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) -#include <sys/ioctl.h> -#endif -#if H2O_USE_PICOTLS -#include "picotls.h" -#endif -#include "h2o/socket.h" -#include "h2o/timeout.h" - -#if defined(__APPLE__) && defined(__clang__) -#pragma clang diagnostic ignored "-Wdeprecated-declarations" -#endif - -#ifndef IOV_MAX -#define IOV_MAX UIO_MAXIOV -#endif - -/* kernel-headers bundled with Ubuntu 14.04 does not have the constant defined in netinet/tcp.h */ -#if defined(__linux__) && !defined(TCP_NOTSENT_LOWAT) -#define TCP_NOTSENT_LOWAT 25 -#endif - -#define OPENSSL_HOSTNAME_VALIDATION_LINKAGE static -#include "../../deps/ssl-conservatory/openssl/openssl_hostname_validation.c" - -struct st_h2o_socket_ssl_t { - SSL_CTX *ssl_ctx; - SSL *ossl; -#if H2O_USE_PICOTLS - ptls_t *ptls; -#endif - int *did_write_in_read; /* used for detecting and closing the connection upon renegotiation (FIXME implement renegotiation) */ - size_t record_overhead; - struct { - h2o_socket_cb cb; - union { - struct { - struct { - enum { - ASYNC_RESUMPTION_STATE_COMPLETE = 0, /* just pass thru */ - ASYNC_RESUMPTION_STATE_RECORD, /* record first input, restore SSL state if it changes to REQUEST_SENT - */ - ASYNC_RESUMPTION_STATE_REQUEST_SENT /* async request has been sent, and is waiting for response */ - } state; - SSL_SESSION *session_data; - } async_resumption; - } server; - struct { - char *server_name; - h2o_cache_t *session_cache; - h2o_iovec_t session_cache_key; - h2o_cache_hashcode_t session_cache_key_hash; - } client; - }; - } handshake; - struct { - h2o_buffer_t *encrypted; - } input; - struct { - H2O_VECTOR(h2o_iovec_t) bufs; - h2o_mem_pool_t pool; /* placed at the last */ - } output; -}; - -struct st_h2o_ssl_context_t { - SSL_CTX *ctx; - const h2o_iovec_t *protocols; - h2o_iovec_t _npn_list_of_protocols; -}; - -/* backend functions */ -static void do_dispose_socket(h2o_socket_t *sock); -static void do_write(h2o_socket_t *sock, h2o_iovec_t *bufs, size_t bufcnt, h2o_socket_cb cb); -static void do_read_start(h2o_socket_t *sock); -static void do_read_stop(h2o_socket_t *sock); -static int do_export(h2o_socket_t *_sock, h2o_socket_export_t *info); -static h2o_socket_t *do_import(h2o_loop_t *loop, h2o_socket_export_t *info); -static socklen_t get_peername_uncached(h2o_socket_t *sock, struct sockaddr *sa); - -/* internal functions called from the backend */ -static const char *decode_ssl_input(h2o_socket_t *sock); -static void on_write_complete(h2o_socket_t *sock, const char *err); - -#if H2O_USE_LIBUV -#include "socket/uv-binding.c.h" -#else -#include "socket/evloop.c.h" -#endif - -h2o_buffer_mmap_settings_t h2o_socket_buffer_mmap_settings = { - 32 * 1024 * 1024, /* 32MB, should better be greater than max frame size of HTTP2 for performance reasons */ - "/tmp/h2o.b.XXXXXX"}; - -__thread h2o_buffer_prototype_t h2o_socket_buffer_prototype = { - {16}, /* keep 16 recently used chunks */ - {H2O_SOCKET_INITIAL_INPUT_BUFFER_SIZE * 2}, /* minimum initial capacity */ - &h2o_socket_buffer_mmap_settings}; - -const char *h2o_socket_error_out_of_memory = "out of memory"; -const char *h2o_socket_error_io = "I/O error"; -const char *h2o_socket_error_closed = "socket closed by peer"; -const char *h2o_socket_error_conn_fail = "connection failure"; -const char *h2o_socket_error_ssl_no_cert = "no certificate"; -const char *h2o_socket_error_ssl_cert_invalid = "invalid certificate"; -const char *h2o_socket_error_ssl_cert_name_mismatch = "certificate name mismatch"; -const char *h2o_socket_error_ssl_decode = "SSL decode error"; - -static void (*resumption_get_async)(h2o_socket_t *sock, h2o_iovec_t session_id); -static void (*resumption_new)(h2o_iovec_t session_id, h2o_iovec_t session_data); - -static int read_bio(BIO *b, char *out, int len) -{ - h2o_socket_t *sock = BIO_get_data(b); - - if (len == 0) - return 0; - - if (sock->ssl->input.encrypted->size == 0) { - BIO_set_retry_read(b); - return -1; - } - - if (sock->ssl->input.encrypted->size < len) { - len = (int)sock->ssl->input.encrypted->size; - } - memcpy(out, sock->ssl->input.encrypted->bytes, len); - h2o_buffer_consume(&sock->ssl->input.encrypted, len); - - return len; -} - -static void write_ssl_bytes(h2o_socket_t *sock, const void *in, size_t len) -{ - if (len != 0) { - void *bytes_alloced = h2o_mem_alloc_pool(&sock->ssl->output.pool, len); - memcpy(bytes_alloced, in, len); - h2o_vector_reserve(&sock->ssl->output.pool, &sock->ssl->output.bufs, sock->ssl->output.bufs.size + 1); - sock->ssl->output.bufs.entries[sock->ssl->output.bufs.size++] = h2o_iovec_init(bytes_alloced, len); - } -} - -static int write_bio(BIO *b, const char *in, int len) -{ - h2o_socket_t *sock = BIO_get_data(b); - - /* FIXME no support for SSL renegotiation (yet) */ - if (sock->ssl->did_write_in_read != NULL) { - *sock->ssl->did_write_in_read = 1; - return -1; - } - - write_ssl_bytes(sock, in, len); - return len; -} - -static int puts_bio(BIO *b, const char *str) -{ - return write_bio(b, str, (int)strlen(str)); -} - -static long ctrl_bio(BIO *b, int cmd, long num, void *ptr) -{ - switch (cmd) { - case BIO_CTRL_GET_CLOSE: - return BIO_get_shutdown(b); - case BIO_CTRL_SET_CLOSE: - BIO_set_shutdown(b, (int)num); - return 1; - case BIO_CTRL_FLUSH: - return 1; - default: - return 0; - } -} - -static void setup_bio(h2o_socket_t *sock) -{ - static BIO_METHOD *bio_methods = NULL; - if (bio_methods == NULL) { - static pthread_mutex_t init_lock = PTHREAD_MUTEX_INITIALIZER; - pthread_mutex_lock(&init_lock); - if (bio_methods == NULL) { - BIO_METHOD *biom = BIO_meth_new(BIO_TYPE_FD, "h2o_socket"); - BIO_meth_set_write(biom, write_bio); - BIO_meth_set_read(biom, read_bio); - BIO_meth_set_puts(biom, puts_bio); - BIO_meth_set_ctrl(biom, ctrl_bio); - __sync_synchronize(); - bio_methods = biom; - } - pthread_mutex_unlock(&init_lock); - } - - BIO *bio = BIO_new(bio_methods); - if (bio == NULL) - h2o_fatal("no memory"); - BIO_set_data(bio, sock); - BIO_set_init(bio, 1); - SSL_set_bio(sock->ssl->ossl, bio, bio); -} - -const char *decode_ssl_input(h2o_socket_t *sock) -{ - assert(sock->ssl != NULL); - assert(sock->ssl->handshake.cb == NULL); - -#if H2O_USE_PICOTLS - if (sock->ssl->ptls != NULL) { - if (sock->ssl->input.encrypted->size != 0) { - const char *src = sock->ssl->input.encrypted->bytes, *src_end = src + sock->ssl->input.encrypted->size; - h2o_iovec_t reserved; - ptls_buffer_t rbuf; - int ret; - if ((reserved = h2o_buffer_reserve(&sock->input, sock->ssl->input.encrypted->size)).base == NULL) - return h2o_socket_error_out_of_memory; - ptls_buffer_init(&rbuf, reserved.base, reserved.len); - do { - size_t consumed = src_end - src; - if ((ret = ptls_receive(sock->ssl->ptls, &rbuf, src, &consumed)) != 0) - break; - src += consumed; - } while (src != src_end); - h2o_buffer_consume(&sock->ssl->input.encrypted, sock->ssl->input.encrypted->size - (src_end - src)); - if (rbuf.is_allocated) { - if ((reserved = h2o_buffer_reserve(&sock->input, rbuf.off)).base == NULL) - return h2o_socket_error_out_of_memory; - memcpy(reserved.base, rbuf.base, rbuf.off); - sock->input->size += rbuf.off; - ptls_buffer_dispose(&rbuf); - } else { - sock->input->size += rbuf.off; - } - if (!(ret == 0 || ret == PTLS_ERROR_IN_PROGRESS)) - return h2o_socket_error_ssl_decode; - } - return NULL; - } -#endif - - while (sock->ssl->input.encrypted->size != 0 || SSL_pending(sock->ssl->ossl)) { - int rlen; - h2o_iovec_t buf = h2o_buffer_reserve(&sock->input, 4096); - if (buf.base == NULL) - return h2o_socket_error_out_of_memory; - { /* call SSL_read (while detecting SSL renegotiation and reporting it as error) */ - int did_write_in_read = 0; - sock->ssl->did_write_in_read = &did_write_in_read; - ERR_clear_error(); - rlen = SSL_read(sock->ssl->ossl, buf.base, (int)buf.len); - sock->ssl->did_write_in_read = NULL; - if (did_write_in_read) - return "ssl renegotiation not supported"; - } - if (rlen == -1) { - if (SSL_get_error(sock->ssl->ossl, rlen) != SSL_ERROR_WANT_READ) { - return h2o_socket_error_ssl_decode; - } - break; - } else if (rlen == 0) { - break; - } else { - sock->input->size += rlen; - } - } - - return 0; -} - -static void flush_pending_ssl(h2o_socket_t *sock, h2o_socket_cb cb) -{ - do_write(sock, sock->ssl->output.bufs.entries, sock->ssl->output.bufs.size, cb); -} - -static void clear_output_buffer(struct st_h2o_socket_ssl_t *ssl) -{ - memset(&ssl->output.bufs, 0, sizeof(ssl->output.bufs)); - h2o_mem_clear_pool(&ssl->output.pool); -} - -static void destroy_ssl(struct st_h2o_socket_ssl_t *ssl) -{ -#if H2O_USE_PICOTLS - if (ssl->ptls != NULL) { - ptls_free(ssl->ptls); - ssl->ptls = NULL; - } -#endif - if (ssl->ossl != NULL) { - if (!SSL_is_server(ssl->ossl)) { - free(ssl->handshake.client.server_name); - free(ssl->handshake.client.session_cache_key.base); - } - SSL_free(ssl->ossl); - ssl->ossl = NULL; - } - h2o_buffer_dispose(&ssl->input.encrypted); - clear_output_buffer(ssl); - free(ssl); -} - -static void dispose_socket(h2o_socket_t *sock, const char *err) -{ - void (*close_cb)(void *data); - void *close_cb_data; - - if (sock->ssl != NULL) { - destroy_ssl(sock->ssl); - sock->ssl = NULL; - } - h2o_buffer_dispose(&sock->input); - if (sock->_peername != NULL) { - free(sock->_peername); - sock->_peername = NULL; - } - - close_cb = sock->on_close.cb; - close_cb_data = sock->on_close.data; - - do_dispose_socket(sock); - - if (close_cb != NULL) - close_cb(close_cb_data); -} - -static void shutdown_ssl(h2o_socket_t *sock, const char *err) -{ - int ret; - - if (err != NULL) - goto Close; - - if (sock->_cb.write != NULL) { - /* note: libuv calls the write callback after the socket is closed by uv_close (with status set to 0 if the write succeeded) - */ - sock->_cb.write = NULL; - goto Close; - } - -#if H2O_USE_PICOTLS - if (sock->ssl->ptls != NULL) { - ptls_buffer_t wbuf; - uint8_t wbuf_small[32]; - ptls_buffer_init(&wbuf, wbuf_small, sizeof(wbuf_small)); - if ((ret = ptls_send_alert(sock->ssl->ptls, &wbuf, PTLS_ALERT_LEVEL_WARNING, PTLS_ALERT_CLOSE_NOTIFY)) != 0) - goto Close; - write_ssl_bytes(sock, wbuf.base, wbuf.off); - ptls_buffer_dispose(&wbuf); - ret = 1; /* close the socket after sending close_notify */ - } else -#endif - if (sock->ssl->ossl != NULL) { - ERR_clear_error(); - if ((ret = SSL_shutdown(sock->ssl->ossl)) == -1) - goto Close; - } else { - goto Close; - } - - if (sock->ssl->output.bufs.size != 0) { - h2o_socket_read_stop(sock); - flush_pending_ssl(sock, ret == 1 ? dispose_socket : shutdown_ssl); - } else if (ret == 2 && SSL_get_error(sock->ssl->ossl, ret) == SSL_ERROR_WANT_READ) { - h2o_socket_read_start(sock, shutdown_ssl); - } else { - goto Close; - } - - return; -Close: - dispose_socket(sock, err); -} - -void h2o_socket_dispose_export(h2o_socket_export_t *info) -{ - assert(info->fd != -1); - if (info->ssl != NULL) { - destroy_ssl(info->ssl); - info->ssl = NULL; - } - h2o_buffer_dispose(&info->input); - close(info->fd); - info->fd = -1; -} - -int h2o_socket_export(h2o_socket_t *sock, h2o_socket_export_t *info) -{ - static h2o_buffer_prototype_t nonpooling_prototype; - - assert(!h2o_socket_is_writing(sock)); - - if (do_export(sock, info) == -1) - return -1; - - if ((info->ssl = sock->ssl) != NULL) { - sock->ssl = NULL; - h2o_buffer_set_prototype(&info->ssl->input.encrypted, &nonpooling_prototype); - } - info->input = sock->input; - h2o_buffer_set_prototype(&info->input, &nonpooling_prototype); - h2o_buffer_init(&sock->input, &h2o_socket_buffer_prototype); - - h2o_socket_close(sock); - - return 0; -} - -h2o_socket_t *h2o_socket_import(h2o_loop_t *loop, h2o_socket_export_t *info) -{ - h2o_socket_t *sock; - - assert(info->fd != -1); - - sock = do_import(loop, info); - info->fd = -1; /* just in case */ - if ((sock->ssl = info->ssl) != NULL) { - setup_bio(sock); - h2o_buffer_set_prototype(&sock->ssl->input.encrypted, &h2o_socket_buffer_prototype); - } - sock->input = info->input; - h2o_buffer_set_prototype(&sock->input, &h2o_socket_buffer_prototype); - return sock; -} - -void h2o_socket_close(h2o_socket_t *sock) -{ - if (sock->ssl == NULL) { - dispose_socket(sock, 0); - } else { - shutdown_ssl(sock, 0); - } -} - -static uint16_t calc_suggested_tls_payload_size(h2o_socket_t *sock, uint16_t suggested_tls_record_size) -{ - uint16_t ps = suggested_tls_record_size; - if (sock->ssl != NULL && sock->ssl->record_overhead < ps) - ps -= sock->ssl->record_overhead; - return ps; -} - -static void disable_latency_optimized_write(h2o_socket_t *sock, int (*adjust_notsent_lowat)(h2o_socket_t *, unsigned)) -{ - if (sock->_latency_optimization.notsent_is_minimized) { - adjust_notsent_lowat(sock, 0); - sock->_latency_optimization.notsent_is_minimized = 0; - } - sock->_latency_optimization.state = H2O_SOCKET_LATENCY_OPTIMIZATION_STATE_DISABLED; - sock->_latency_optimization.suggested_tls_payload_size = 16384; - sock->_latency_optimization.suggested_write_size = SIZE_MAX; -} - -static inline void prepare_for_latency_optimized_write(h2o_socket_t *sock, - const h2o_socket_latency_optimization_conditions_t *conditions, uint32_t rtt, - uint32_t mss, uint32_t cwnd_size, uint32_t cwnd_avail, uint64_t loop_time, - int (*adjust_notsent_lowat)(h2o_socket_t *, unsigned)) -{ - /* check RTT */ - if (rtt < conditions->min_rtt * (uint64_t)1000) - goto Disable; - if (rtt * conditions->max_additional_delay < loop_time * 1000 * 100) - goto Disable; - - /* latency-optimization is enabled */ - sock->_latency_optimization.state = H2O_SOCKET_LATENCY_OPTIMIZATION_STATE_DETERMINED; - - /* no need to: - * 1) adjust the write size if single_write_size << cwnd_size - * 2) align TLS record boundary to TCP packet boundary if packet loss-rate is low and BW isn't small (implied by cwnd size) - */ - if (mss * cwnd_size < conditions->max_cwnd) { - if (!sock->_latency_optimization.notsent_is_minimized) { - if (adjust_notsent_lowat(sock, 1 /* cannot be set to zero on Linux */) != 0) - goto Disable; - sock->_latency_optimization.notsent_is_minimized = 1; - } - sock->_latency_optimization.suggested_tls_payload_size = calc_suggested_tls_payload_size(sock, mss); - sock->_latency_optimization.suggested_write_size = - cwnd_avail * (size_t)sock->_latency_optimization.suggested_tls_payload_size; - } else { - if (sock->_latency_optimization.notsent_is_minimized) { - if (adjust_notsent_lowat(sock, 0) != 0) - goto Disable; - sock->_latency_optimization.notsent_is_minimized = 0; - } - sock->_latency_optimization.suggested_tls_payload_size = 16384; - sock->_latency_optimization.suggested_write_size = SIZE_MAX; - } - return; - -Disable: - disable_latency_optimized_write(sock, adjust_notsent_lowat); -} - -/** - * Obtains RTT, MSS, size of CWND (in the number of packets). - * Also writes to cwnd_avail minimum number of packets (of MSS size) sufficient to shut up poll-for-write under the precondition - * that TCP_NOTSENT_LOWAT is set to 1. - */ -static int obtain_tcp_info(int fd, uint32_t *rtt, uint32_t *mss, uint32_t *cwnd_size, uint32_t *cwnd_avail) -{ -#define CALC_CWND_PAIR_FROM_BYTE_UNITS(cwnd_bytes, inflight_bytes) \ - do { \ - *cwnd_size = (cwnd_bytes + *mss / 2) / *mss; \ - *cwnd_avail = cwnd_bytes > inflight_bytes ? (cwnd_bytes - inflight_bytes) / *mss + 2 : 2; \ - } while (0) - -#if defined(__linux__) && defined(TCP_INFO) - - struct tcp_info tcpi; - socklen_t tcpisz = sizeof(tcpi); - if (getsockopt(fd, IPPROTO_TCP, TCP_INFO, &tcpi, &tcpisz) != 0) - return -1; - *rtt = tcpi.tcpi_rtt; - *mss = tcpi.tcpi_snd_mss; - *cwnd_size = tcpi.tcpi_snd_cwnd; - *cwnd_avail = tcpi.tcpi_snd_cwnd > tcpi.tcpi_unacked ? tcpi.tcpi_snd_cwnd - tcpi.tcpi_unacked + 2 : 2; - return 0; - -#elif defined(__FreeBSD__) && defined(TCP_INFO) && 0 /* disabled since we wouldn't use it anyways; OS lacks TCP_NOTSENT_LOWAT */ - - struct tcp_info tcpi; - socklen_t tcpisz = sizeof(tcpi); - int bytes_inflight; - if (getsockopt(fd, IPPROTO_TCP, TCP_INFO, &tcpi, &tcpisz) != 0 || ioctl(fd, FIONWRITE, &bytes_inflight) == -1) - return -1; - *rtt = tcpi.tcpi_rtt; - *mss = tcpi.tcpi_snd_mss; - CALC_CWND_PAIR_FROM_BYTE_UNITS(tcpi.tcpi_snd_cwnd, bytes_inflight); - return 0; - -#elif defined(__APPLE__) && defined(TCP_CONNECTION_INFO) - - struct tcp_connection_info tcpi; - socklen_t tcpisz = sizeof(tcpi); - if (getsockopt(fd, IPPROTO_TCP, TCP_CONNECTION_INFO, &tcpi, &tcpisz) != 0 || tcpi.tcpi_maxseg == 0) - return -1; - *rtt = tcpi.tcpi_srtt * 1000; - *mss = tcpi.tcpi_maxseg; - CALC_CWND_PAIR_FROM_BYTE_UNITS(tcpi.tcpi_snd_cwnd, tcpi.tcpi_snd_sbbytes); - return 0; - -#else - /* TODO add support for NetBSD; note that the OS returns the number of packets for tcpi_snd_cwnd; see - * http://twitter.com/n_soda/status/740719125878575105 - */ - return -1; -#endif - -#undef CALC_CWND_PAIR_FROM_BYTE_UNITS -} - -#ifdef TCP_NOTSENT_LOWAT -static int adjust_notsent_lowat(h2o_socket_t *sock, unsigned notsent_lowat) -{ - return setsockopt(h2o_socket_get_fd(sock), IPPROTO_TCP, TCP_NOTSENT_LOWAT, ¬sent_lowat, sizeof(notsent_lowat)); -} -#else -#define adjust_notsent_lowat NULL -#endif - -size_t h2o_socket_do_prepare_for_latency_optimized_write(h2o_socket_t *sock, - const h2o_socket_latency_optimization_conditions_t *conditions) -{ - uint32_t rtt = 0, mss = 0, cwnd_size = 0, cwnd_avail = 0; - uint64_t loop_time = UINT64_MAX; - int can_prepare = 1; - -#if !defined(TCP_NOTSENT_LOWAT) - /* the feature cannot be setup unless TCP_NOTSENT_LOWAT is available */ - can_prepare = 0; -#endif - -#if H2O_USE_LIBUV - /* poll-then-write is impossible with libuv */ - can_prepare = 0; -#else - if (can_prepare) - loop_time = h2o_evloop_get_execution_time(h2o_socket_get_loop(sock)); -#endif - - /* obtain TCP states */ - if (can_prepare && obtain_tcp_info(h2o_socket_get_fd(sock), &rtt, &mss, &cwnd_size, &cwnd_avail) != 0) - can_prepare = 0; - - /* determine suggested_write_size, suggested_tls_record_size and adjust TCP_NOTSENT_LOWAT based on the obtained information */ - if (can_prepare) { - prepare_for_latency_optimized_write(sock, conditions, rtt, mss, cwnd_size, cwnd_avail, loop_time, adjust_notsent_lowat); - } else { - disable_latency_optimized_write(sock, adjust_notsent_lowat); - } - - return sock->_latency_optimization.suggested_write_size; - -#undef CALC_CWND_PAIR_FROM_BYTE_UNITS -} - -void h2o_socket_write(h2o_socket_t *sock, h2o_iovec_t *bufs, size_t bufcnt, h2o_socket_cb cb) -{ - size_t i, prev_bytes_written = sock->bytes_written; - - for (i = 0; i != bufcnt; ++i) { - sock->bytes_written += bufs[i].len; -#if H2O_SOCKET_DUMP_WRITE - fprintf(stderr, "writing %zu bytes to fd:%d\n", bufs[i].len, h2o_socket_get_fd(sock)); - h2o_dump_memory(stderr, bufs[i].base, bufs[i].len); -#endif - } - - if (sock->ssl == NULL) { - do_write(sock, bufs, bufcnt, cb); - } else { - assert(sock->ssl->output.bufs.size == 0); - /* fill in the data */ - size_t ssl_record_size; - switch (sock->_latency_optimization.state) { - case H2O_SOCKET_LATENCY_OPTIMIZATION_STATE_TBD: - case H2O_SOCKET_LATENCY_OPTIMIZATION_STATE_DISABLED: - ssl_record_size = prev_bytes_written < 200 * 1024 ? calc_suggested_tls_payload_size(sock, 1400) : 16384; - break; - case H2O_SOCKET_LATENCY_OPTIMIZATION_STATE_DETERMINED: - sock->_latency_optimization.state = H2O_SOCKET_LATENCY_OPTIMIZATION_STATE_NEEDS_UPDATE; - /* fallthru */ - default: - ssl_record_size = sock->_latency_optimization.suggested_tls_payload_size; - break; - } - for (; bufcnt != 0; ++bufs, --bufcnt) { - size_t off = 0; - while (off != bufs[0].len) { - int ret; - size_t sz = bufs[0].len - off; - if (sz > ssl_record_size) - sz = ssl_record_size; -#if H2O_USE_PICOTLS - if (sock->ssl->ptls != NULL) { - size_t dst_size = sz + ptls_get_record_overhead(sock->ssl->ptls); - void *dst = h2o_mem_alloc_pool(&sock->ssl->output.pool, dst_size); - ptls_buffer_t wbuf; - ptls_buffer_init(&wbuf, dst, dst_size); - ret = ptls_send(sock->ssl->ptls, &wbuf, bufs[0].base + off, sz); - assert(ret == 0); - assert(!wbuf.is_allocated); - h2o_vector_reserve(&sock->ssl->output.pool, &sock->ssl->output.bufs, sock->ssl->output.bufs.size + 1); - sock->ssl->output.bufs.entries[sock->ssl->output.bufs.size++] = h2o_iovec_init(dst, wbuf.off); - } else -#endif - { - ret = SSL_write(sock->ssl->ossl, bufs[0].base + off, (int)sz); - if (ret != sz) { - /* The error happens if SSL_write is called after SSL_read returns a fatal error (e.g. due to corrupt TCP - * packet being received). We need to take care of this since some protocol implementations send data after - * the read-side of the connection gets closed (note that protocol implementations are (yet) incapable of - * distinguishing a normal shutdown and close due to an error using the `status` value of the read - * callback). - */ - clear_output_buffer(sock->ssl); - flush_pending_ssl(sock, cb); -#ifndef H2O_USE_LIBUV - ((struct st_h2o_evloop_socket_t *)sock)->_flags |= H2O_SOCKET_FLAG_IS_WRITE_ERROR; -#endif - return; - } - } - off += sz; - } - } - flush_pending_ssl(sock, cb); - } -} - -void on_write_complete(h2o_socket_t *sock, const char *err) -{ - h2o_socket_cb cb; - - if (sock->ssl != NULL) - clear_output_buffer(sock->ssl); - - cb = sock->_cb.write; - sock->_cb.write = NULL; - cb(sock, err); -} - -void h2o_socket_read_start(h2o_socket_t *sock, h2o_socket_cb cb) -{ - sock->_cb.read = cb; - do_read_start(sock); -} - -void h2o_socket_read_stop(h2o_socket_t *sock) -{ - sock->_cb.read = NULL; - do_read_stop(sock); -} - -void h2o_socket_setpeername(h2o_socket_t *sock, struct sockaddr *sa, socklen_t len) -{ - if (sock->_peername != NULL) - free(sock->_peername); - sock->_peername = h2o_mem_alloc(offsetof(struct st_h2o_socket_peername_t, addr) + len); - sock->_peername->len = len; - memcpy(&sock->_peername->addr, sa, len); -} - -socklen_t h2o_socket_getpeername(h2o_socket_t *sock, struct sockaddr *sa) -{ - /* return cached, if exists */ - if (sock->_peername != NULL) { - memcpy(sa, &sock->_peername->addr, sock->_peername->len); - return sock->_peername->len; - } - /* call, copy to cache, and return */ - socklen_t len = get_peername_uncached(sock, sa); - h2o_socket_setpeername(sock, sa, len); - return len; -} - -const char *h2o_socket_get_ssl_protocol_version(h2o_socket_t *sock) -{ - if (sock->ssl != NULL) { -#if H2O_USE_PICOTLS - if (sock->ssl->ptls != NULL) - return "TLSv1.3"; -#endif - if (sock->ssl->ossl != NULL) - return SSL_get_version(sock->ssl->ossl); - } - return NULL; -} - -int h2o_socket_get_ssl_session_reused(h2o_socket_t *sock) -{ - if (sock->ssl != NULL) { -#if H2O_USE_PICOTLS - if (sock->ssl->ptls != NULL) - return ptls_is_psk_handshake(sock->ssl->ptls); -#endif - if (sock->ssl->ossl != NULL) - return (int)SSL_session_reused(sock->ssl->ossl); - } - return -1; -} - -const char *h2o_socket_get_ssl_cipher(h2o_socket_t *sock) -{ - if (sock->ssl != NULL) { -#if H2O_USE_PICOTLS - if (sock->ssl->ptls != NULL) { - ptls_cipher_suite_t *cipher = ptls_get_cipher(sock->ssl->ptls); - if (cipher != NULL) - return cipher->aead->name; - } else -#endif - if (sock->ssl->ossl != NULL) - return SSL_get_cipher_name(sock->ssl->ossl); - } - return NULL; -} - -int h2o_socket_get_ssl_cipher_bits(h2o_socket_t *sock) -{ - if (sock->ssl != NULL) { -#if H2O_USE_PICOTLS - if (sock->ssl->ptls != NULL) { - ptls_cipher_suite_t *cipher = ptls_get_cipher(sock->ssl->ptls); - if (cipher == NULL) - return 0; - return (int)cipher->aead->key_size; - } else -#endif - if (sock->ssl->ossl != NULL) - return SSL_get_cipher_bits(sock->ssl->ossl, NULL); - } - return 0; -} - -h2o_iovec_t h2o_socket_get_ssl_session_id(h2o_socket_t *sock) -{ - if (sock->ssl != NULL) { -#if H2O_USE_PICOTLS - if (sock->ssl->ptls != NULL) { - /* FIXME */ - } else -#endif - if (sock->ssl->ossl != NULL) { - SSL_SESSION *session; - if (sock->ssl->handshake.server.async_resumption.state == ASYNC_RESUMPTION_STATE_COMPLETE && - (session = SSL_get_session(sock->ssl->ossl)) != NULL) { - unsigned id_len; - const unsigned char *id = SSL_SESSION_get_id(session, &id_len); - return h2o_iovec_init(id, id_len); - } - } - } - - return h2o_iovec_init(NULL, 0); -} - -const char *h2o_socket_get_ssl_server_name(const h2o_socket_t *sock) -{ - if (sock->ssl != NULL) { -#if H2O_USE_PICOTLS - if (sock->ssl->ptls != NULL) { - return ptls_get_server_name(sock->ssl->ptls); - } else -#endif - if (sock->ssl->ossl != NULL) { - return SSL_get_servername(sock->ssl->ossl, TLSEXT_NAMETYPE_host_name); - } - } - return NULL; -} - -h2o_iovec_t h2o_socket_log_ssl_session_id(h2o_socket_t *sock, h2o_mem_pool_t *pool) -{ - h2o_iovec_t base64id, rawid = h2o_socket_get_ssl_session_id(sock); - - if (rawid.base == NULL) - return h2o_iovec_init(NULL, 0); - - base64id.base = pool != NULL ? h2o_mem_alloc_pool(pool, h2o_base64_encode_capacity(rawid.len)) - : h2o_mem_alloc(h2o_base64_encode_capacity(rawid.len)); - base64id.len = h2o_base64_encode(base64id.base, rawid.base, rawid.len, 1); - return base64id; -} - -h2o_iovec_t h2o_socket_log_ssl_cipher_bits(h2o_socket_t *sock, h2o_mem_pool_t *pool) -{ - int bits = h2o_socket_get_ssl_cipher_bits(sock); - if (bits != 0) { - char *s = (char *)(pool != NULL ? h2o_mem_alloc_pool(pool, sizeof(H2O_INT16_LONGEST_STR)) - : h2o_mem_alloc(sizeof(H2O_INT16_LONGEST_STR))); - size_t len = sprintf(s, "%" PRId16, (int16_t)bits); - return h2o_iovec_init(s, len); - } else { - return h2o_iovec_init(NULL, 0); - } -} - -int h2o_socket_compare_address(struct sockaddr *x, struct sockaddr *y) -{ -#define CMP(a, b) \ - if (a != b) \ - return a < b ? -1 : 1 - - CMP(x->sa_family, y->sa_family); - - if (x->sa_family == AF_UNIX) { - struct sockaddr_un *xun = (void *)x, *yun = (void *)y; - int r = strcmp(xun->sun_path, yun->sun_path); - if (r != 0) - return r; - } else if (x->sa_family == AF_INET) { - struct sockaddr_in *xin = (void *)x, *yin = (void *)y; - CMP(ntohl(xin->sin_addr.s_addr), ntohl(yin->sin_addr.s_addr)); - CMP(ntohs(xin->sin_port), ntohs(yin->sin_port)); - } else if (x->sa_family == AF_INET6) { - struct sockaddr_in6 *xin6 = (void *)x, *yin6 = (void *)y; - int r = memcmp(xin6->sin6_addr.s6_addr, yin6->sin6_addr.s6_addr, sizeof(xin6->sin6_addr.s6_addr)); - if (r != 0) - return r; - CMP(ntohs(xin6->sin6_port), ntohs(yin6->sin6_port)); - CMP(xin6->sin6_flowinfo, yin6->sin6_flowinfo); - CMP(xin6->sin6_scope_id, yin6->sin6_scope_id); - } else { - assert(!"unknown sa_family"); - } - -#undef CMP - return 0; -} - -size_t h2o_socket_getnumerichost(struct sockaddr *sa, socklen_t salen, char *buf) -{ - if (sa->sa_family == AF_INET) { - /* fast path for IPv4 addresses */ - struct sockaddr_in *sin = (void *)sa; - uint32_t addr; - addr = htonl(sin->sin_addr.s_addr); - return sprintf(buf, "%d.%d.%d.%d", addr >> 24, (addr >> 16) & 255, (addr >> 8) & 255, addr & 255); - } - - if (getnameinfo(sa, salen, buf, NI_MAXHOST, NULL, 0, NI_NUMERICHOST) != 0) - return SIZE_MAX; - return strlen(buf); -} - -int32_t h2o_socket_getport(struct sockaddr *sa) -{ - switch (sa->sa_family) { - case AF_INET: - return htons(((struct sockaddr_in *)sa)->sin_port); - case AF_INET6: - return htons(((struct sockaddr_in6 *)sa)->sin6_port); - default: - return -1; - } -} - -static void create_ossl(h2o_socket_t *sock) -{ - sock->ssl->ossl = SSL_new(sock->ssl->ssl_ctx); - setup_bio(sock); -} - -static SSL_SESSION *on_async_resumption_get(SSL *ssl, -#if OPENSSL_VERSION_NUMBER >= 0x1010000fL && !defined(LIBRESSL_VERSION_NUMBER) - const -#endif - unsigned char *data, - int len, int *copy) -{ - h2o_socket_t *sock = BIO_get_data(SSL_get_rbio(ssl)); - - switch (sock->ssl->handshake.server.async_resumption.state) { - case ASYNC_RESUMPTION_STATE_RECORD: - sock->ssl->handshake.server.async_resumption.state = ASYNC_RESUMPTION_STATE_REQUEST_SENT; - resumption_get_async(sock, h2o_iovec_init(data, len)); - return NULL; - case ASYNC_RESUMPTION_STATE_COMPLETE: - *copy = 1; - return sock->ssl->handshake.server.async_resumption.session_data; - default: - assert(!"FIXME"); - return NULL; - } -} - -static int on_async_resumption_new(SSL *ssl, SSL_SESSION *session) -{ - h2o_iovec_t data; - const unsigned char *id; - unsigned id_len; - unsigned char *p; - - /* build data */ - data.len = i2d_SSL_SESSION(session, NULL); - data.base = alloca(data.len); - p = (void *)data.base; - i2d_SSL_SESSION(session, &p); - - id = SSL_SESSION_get_id(session, &id_len); - resumption_new(h2o_iovec_init(id, id_len), data); - return 0; -} - -static void on_handshake_complete(h2o_socket_t *sock, const char *err) -{ - if (err == NULL) { -#if H2O_USE_PICOTLS - if (sock->ssl->ptls != NULL) { - sock->ssl->record_overhead = ptls_get_record_overhead(sock->ssl->ptls); - } else -#endif - { - const SSL_CIPHER *cipher = SSL_get_current_cipher(sock->ssl->ossl); - switch (SSL_CIPHER_get_id(cipher)) { - case TLS1_CK_RSA_WITH_AES_128_GCM_SHA256: - case TLS1_CK_DHE_RSA_WITH_AES_128_GCM_SHA256: - case TLS1_CK_ECDHE_RSA_WITH_AES_128_GCM_SHA256: - case TLS1_CK_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256: - case TLS1_CK_RSA_WITH_AES_256_GCM_SHA384: - case TLS1_CK_DHE_RSA_WITH_AES_256_GCM_SHA384: - case TLS1_CK_ECDHE_RSA_WITH_AES_256_GCM_SHA384: - case TLS1_CK_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384: - sock->ssl->record_overhead = 5 /* header */ + 8 /* record_iv_length (RFC 5288 3) */ + 16 /* tag (RFC 5116 5.1) */; - break; -#if defined(TLS1_CK_DHE_RSA_CHACHA20_POLY1305) - case TLS1_CK_DHE_RSA_CHACHA20_POLY1305: - case TLS1_CK_ECDHE_RSA_CHACHA20_POLY1305: - case TLS1_CK_ECDHE_ECDSA_CHACHA20_POLY1305: - sock->ssl->record_overhead = 5 /* header */ + 16 /* tag */; - break; -#endif - default: - sock->ssl->record_overhead = 32; /* sufficiently large number that can hold most payloads */ - break; - } - } - } - - /* set ssl session into the cache */ - if (sock->ssl->ossl != NULL && !SSL_is_server(sock->ssl->ossl) && sock->ssl->handshake.client.session_cache != NULL) { - if (err == NULL || err == h2o_socket_error_ssl_cert_name_mismatch) { - SSL_SESSION *session = SSL_get1_session(sock->ssl->ossl); - h2o_cache_set(sock->ssl->handshake.client.session_cache, h2o_now(h2o_socket_get_loop(sock)), - sock->ssl->handshake.client.session_cache_key, sock->ssl->handshake.client.session_cache_key_hash, - h2o_iovec_init(session, 1)); - } - } - - h2o_socket_cb handshake_cb = sock->ssl->handshake.cb; - sock->_cb.write = NULL; - sock->ssl->handshake.cb = NULL; - if (err == NULL) - decode_ssl_input(sock); - handshake_cb(sock, err); -} - -static void proceed_handshake(h2o_socket_t *sock, const char *err) -{ - h2o_iovec_t first_input = {NULL}; - int ret = 0; - - sock->_cb.write = NULL; - - if (err != NULL) { - goto Complete; - } - - if (sock->ssl->ossl == NULL) { -#if H2O_USE_PICOTLS - /* prepare I/O */ - size_t consumed = sock->ssl->input.encrypted->size; - ptls_buffer_t wbuf; - ptls_buffer_init(&wbuf, "", 0); - - if (sock->ssl->ptls != NULL) { - /* picotls in action, proceed the handshake */ - ret = ptls_handshake(sock->ssl->ptls, &wbuf, sock->ssl->input.encrypted->bytes, &consumed, NULL); - } else { - /* start using picotls if the first packet contains TLS 1.3 CH */ - ptls_context_t *ptls_ctx = h2o_socket_ssl_get_picotls_context(sock->ssl->ssl_ctx); - if (ptls_ctx != NULL) { - ptls_t *ptls = ptls_new(ptls_ctx, 1); - if (ptls == NULL) - h2o_fatal("no memory"); - ret = ptls_handshake(ptls, &wbuf, sock->ssl->input.encrypted->bytes, &consumed, NULL); - if ((ret == 0 || ret == PTLS_ERROR_IN_PROGRESS) && wbuf.off != 0) { - sock->ssl->ptls = ptls; - sock->ssl->handshake.server.async_resumption.state = ASYNC_RESUMPTION_STATE_COMPLETE; - } else { - ptls_free(ptls); - } - } - } - - if (sock->ssl->ptls != NULL) { - /* complete I/O done by picotls */ - h2o_buffer_consume(&sock->ssl->input.encrypted, consumed); - switch (ret) { - case 0: - case PTLS_ERROR_IN_PROGRESS: - if (wbuf.off != 0) { - h2o_socket_read_stop(sock); - write_ssl_bytes(sock, wbuf.base, wbuf.off); - flush_pending_ssl(sock, ret == 0 ? on_handshake_complete : proceed_handshake); - } else { - h2o_socket_read_start(sock, proceed_handshake); - } - break; - default: - /* FIXME send alert in wbuf before calling the callback */ - on_handshake_complete(sock, "picotls handshake error"); - break; - } - ptls_buffer_dispose(&wbuf); - return; - } - ptls_buffer_dispose(&wbuf); -#endif - - /* fallback to openssl if the attempt failed */ - create_ossl(sock); - } - - if (sock->ssl->ossl != NULL && SSL_is_server(sock->ssl->ossl) && - sock->ssl->handshake.server.async_resumption.state == ASYNC_RESUMPTION_STATE_RECORD) { - if (sock->ssl->input.encrypted->size <= 1024) { - /* retain a copy of input if performing async resumption */ - first_input = h2o_iovec_init(alloca(sock->ssl->input.encrypted->size), sock->ssl->input.encrypted->size); - memcpy(first_input.base, sock->ssl->input.encrypted->bytes, first_input.len); - } else { - sock->ssl->handshake.server.async_resumption.state = ASYNC_RESUMPTION_STATE_COMPLETE; - } - } - -Redo: - ERR_clear_error(); - if (SSL_is_server(sock->ssl->ossl)) { - ret = SSL_accept(sock->ssl->ossl); - switch (sock->ssl->handshake.server.async_resumption.state) { - case ASYNC_RESUMPTION_STATE_COMPLETE: - break; - case ASYNC_RESUMPTION_STATE_RECORD: - /* async resumption has not been triggered; proceed the state to complete */ - sock->ssl->handshake.server.async_resumption.state = ASYNC_RESUMPTION_STATE_COMPLETE; - break; - case ASYNC_RESUMPTION_STATE_REQUEST_SENT: { - /* sent async request, reset the ssl state, and wait for async response */ - assert(ret < 0); - SSL_free(sock->ssl->ossl); - create_ossl(sock); - clear_output_buffer(sock->ssl); - h2o_buffer_consume(&sock->ssl->input.encrypted, sock->ssl->input.encrypted->size); - h2o_buffer_reserve(&sock->ssl->input.encrypted, first_input.len); - memcpy(sock->ssl->input.encrypted->bytes, first_input.base, first_input.len); - sock->ssl->input.encrypted->size = first_input.len; - h2o_socket_read_stop(sock); - return; - } - default: - h2o_fatal("unexpected async resumption state"); - break; - } - } else { - ret = SSL_connect(sock->ssl->ossl); - } - - if (ret == 0 || (ret < 0 && SSL_get_error(sock->ssl->ossl, ret) != SSL_ERROR_WANT_READ)) { - /* failed */ - long verify_result = SSL_get_verify_result(sock->ssl->ossl); - if (verify_result != X509_V_OK) { - err = X509_verify_cert_error_string(verify_result); - } else { - err = "ssl handshake failure"; - } - goto Complete; - } - - if (sock->ssl->output.bufs.size != 0) { - h2o_socket_read_stop(sock); - flush_pending_ssl(sock, ret == 1 ? on_handshake_complete : proceed_handshake); - } else { - if (ret == 1) { - if (!SSL_is_server(sock->ssl->ossl)) { - X509 *cert = SSL_get_peer_certificate(sock->ssl->ossl); - if (cert != NULL) { - switch (validate_hostname(sock->ssl->handshake.client.server_name, cert)) { - case MatchFound: - /* ok */ - break; - case MatchNotFound: - err = h2o_socket_error_ssl_cert_name_mismatch; - break; - default: - err = h2o_socket_error_ssl_cert_invalid; - break; - } - X509_free(cert); - } else { - err = h2o_socket_error_ssl_no_cert; - } - } - goto Complete; - } - if (sock->ssl->input.encrypted->size != 0) - goto Redo; - h2o_socket_read_start(sock, proceed_handshake); - } - return; - -Complete: - h2o_socket_read_stop(sock); - on_handshake_complete(sock, err); -} - -void h2o_socket_ssl_handshake(h2o_socket_t *sock, SSL_CTX *ssl_ctx, const char *server_name, h2o_socket_cb handshake_cb) -{ - sock->ssl = h2o_mem_alloc(sizeof(*sock->ssl)); - memset(sock->ssl, 0, offsetof(struct st_h2o_socket_ssl_t, output.pool)); - - sock->ssl->ssl_ctx = ssl_ctx; - - /* setup the buffers; sock->input should be empty, sock->ssl->input.encrypted should contain the initial input, if any */ - h2o_buffer_init(&sock->ssl->input.encrypted, &h2o_socket_buffer_prototype); - if (sock->input->size != 0) { - h2o_buffer_t *tmp = sock->input; - sock->input = sock->ssl->input.encrypted; - sock->ssl->input.encrypted = tmp; - } - - h2o_mem_init_pool(&sock->ssl->output.pool); - - sock->ssl->handshake.cb = handshake_cb; - if (server_name == NULL) { - /* is server */ - if (SSL_CTX_sess_get_get_cb(sock->ssl->ssl_ctx) != NULL) - sock->ssl->handshake.server.async_resumption.state = ASYNC_RESUMPTION_STATE_RECORD; - if (sock->ssl->input.encrypted->size != 0) - proceed_handshake(sock, 0); - else - h2o_socket_read_start(sock, proceed_handshake); - } else { - create_ossl(sock); - h2o_cache_t *session_cache = h2o_socket_ssl_get_session_cache(sock->ssl->ssl_ctx); - if (session_cache != NULL) { - struct sockaddr_storage sa; - int32_t port; - if (h2o_socket_getpeername(sock, (struct sockaddr *)&sa) != 0 && - (port = h2o_socket_getport((struct sockaddr *)&sa)) != -1) { - /* session cache is available */ - h2o_iovec_t session_cache_key; - session_cache_key.base = h2o_mem_alloc(strlen(server_name) + sizeof(":" H2O_UINT16_LONGEST_STR)); - session_cache_key.len = sprintf(session_cache_key.base, "%s:%" PRIu16, server_name, (uint16_t)port); - sock->ssl->handshake.client.session_cache = session_cache; - sock->ssl->handshake.client.session_cache_key = session_cache_key; - sock->ssl->handshake.client.session_cache_key_hash = - h2o_cache_calchash(session_cache_key.base, session_cache_key.len); - - /* fetch from session cache */ - h2o_cache_ref_t *cacheref = h2o_cache_fetch(session_cache, h2o_now(h2o_socket_get_loop(sock)), - sock->ssl->handshake.client.session_cache_key, - sock->ssl->handshake.client.session_cache_key_hash); - if (cacheref != NULL) { - SSL_set_session(sock->ssl->ossl, (SSL_SESSION *)cacheref->value.base); - h2o_cache_release(session_cache, cacheref); - } - } - } - sock->ssl->handshake.client.server_name = h2o_strdup(NULL, server_name, SIZE_MAX).base; - SSL_set_tlsext_host_name(sock->ssl->ossl, sock->ssl->handshake.client.server_name); - proceed_handshake(sock, 0); - } -} - -void h2o_socket_ssl_resume_server_handshake(h2o_socket_t *sock, h2o_iovec_t session_data) -{ - if (session_data.len != 0) { - const unsigned char *p = (void *)session_data.base; - sock->ssl->handshake.server.async_resumption.session_data = d2i_SSL_SESSION(NULL, &p, (long)session_data.len); - /* FIXME warn on failure */ - } - - sock->ssl->handshake.server.async_resumption.state = ASYNC_RESUMPTION_STATE_COMPLETE; - proceed_handshake(sock, 0); - - if (sock->ssl->handshake.server.async_resumption.session_data != NULL) { - SSL_SESSION_free(sock->ssl->handshake.server.async_resumption.session_data); - sock->ssl->handshake.server.async_resumption.session_data = NULL; - } -} - -void h2o_socket_ssl_async_resumption_init(h2o_socket_ssl_resumption_get_async_cb get_async_cb, - h2o_socket_ssl_resumption_new_cb new_cb) -{ - resumption_get_async = get_async_cb; - resumption_new = new_cb; -} - -void h2o_socket_ssl_async_resumption_setup_ctx(SSL_CTX *ctx) -{ - SSL_CTX_sess_set_get_cb(ctx, on_async_resumption_get); - SSL_CTX_sess_set_new_cb(ctx, on_async_resumption_new); - /* if necessary, it is the responsibility of the caller to disable the internal cache */ -} - -#if H2O_USE_PICOTLS - -static int get_ptls_index(void) -{ - static int index = -1; - - if (index == -1) { - static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; - pthread_mutex_lock(&mutex); - if (index == -1) { - index = SSL_CTX_get_ex_new_index(0, NULL, NULL, NULL, NULL); - assert(index != -1); - } - pthread_mutex_unlock(&mutex); - } - - return index; -} - -ptls_context_t *h2o_socket_ssl_get_picotls_context(SSL_CTX *ossl) -{ - return SSL_CTX_get_ex_data(ossl, get_ptls_index()); -} - -void h2o_socket_ssl_set_picotls_context(SSL_CTX *ossl, ptls_context_t *ptls) -{ - SSL_CTX_set_ex_data(ossl, get_ptls_index(), ptls); -} - -#endif - -static void on_dispose_ssl_ctx_session_cache(void *parent, void *ptr, CRYPTO_EX_DATA *ad, int idx, long argl, void *argp) -{ - h2o_cache_t *ssl_session_cache = (h2o_cache_t *)ptr; - if (ssl_session_cache != NULL) - h2o_cache_destroy(ssl_session_cache); -} - -static int get_ssl_session_cache_index(void) -{ - static int index = -1; - static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; - pthread_mutex_lock(&mutex); - if (index == -1) { - index = SSL_CTX_get_ex_new_index(0, NULL, NULL, NULL, on_dispose_ssl_ctx_session_cache); - assert(index != -1); - } - pthread_mutex_unlock(&mutex); - return index; -} - -h2o_cache_t *h2o_socket_ssl_get_session_cache(SSL_CTX *ctx) -{ - return (h2o_cache_t *)SSL_CTX_get_ex_data(ctx, get_ssl_session_cache_index()); -} - -void h2o_socket_ssl_set_session_cache(SSL_CTX *ctx, h2o_cache_t *cache) -{ - SSL_CTX_set_ex_data(ctx, get_ssl_session_cache_index(), cache); -} - -void h2o_socket_ssl_destroy_session_cache_entry(h2o_iovec_t value) -{ - SSL_SESSION *session = (SSL_SESSION *)value.base; - SSL_SESSION_free(session); -} - -h2o_iovec_t h2o_socket_ssl_get_selected_protocol(h2o_socket_t *sock) -{ - const unsigned char *data = NULL; - unsigned len = 0; - - assert(sock->ssl != NULL); - -#if H2O_USE_PICOTLS - if (sock->ssl->ptls != NULL) { - const char *proto = ptls_get_negotiated_protocol(sock->ssl->ptls); - return proto != NULL ? h2o_iovec_init(proto, strlen(proto)) : h2o_iovec_init(NULL, 0); - } -#endif - -#if H2O_USE_ALPN - if (len == 0) - SSL_get0_alpn_selected(sock->ssl->ossl, &data, &len); -#endif -#if H2O_USE_NPN - if (len == 0) - SSL_get0_next_proto_negotiated(sock->ssl->ossl, &data, &len); -#endif - - return h2o_iovec_init(data, len); -} - -static int on_alpn_select(SSL *ssl, const unsigned char **out, unsigned char *outlen, const unsigned char *_in, unsigned int inlen, - void *_protocols) -{ - const h2o_iovec_t *protocols = _protocols; - size_t i; - - for (i = 0; protocols[i].len != 0; ++i) { - const unsigned char *in = _in, *in_end = in + inlen; - while (in != in_end) { - size_t cand_len = *in++; - if (in_end - in < cand_len) { - /* broken request */ - return SSL_TLSEXT_ERR_NOACK; - } - if (cand_len == protocols[i].len && memcmp(in, protocols[i].base, cand_len) == 0) { - goto Found; - } - in += cand_len; - } - } - /* not found */ - return SSL_TLSEXT_ERR_NOACK; - -Found: - *out = (const unsigned char *)protocols[i].base; - *outlen = (unsigned char)protocols[i].len; - return SSL_TLSEXT_ERR_OK; -} - -#if H2O_USE_ALPN - -void h2o_ssl_register_alpn_protocols(SSL_CTX *ctx, const h2o_iovec_t *protocols) -{ - SSL_CTX_set_alpn_select_cb(ctx, on_alpn_select, (void *)protocols); -} - -#endif - -#if H2O_USE_NPN - -static int on_npn_advertise(SSL *ssl, const unsigned char **out, unsigned *outlen, void *protocols) -{ - *out = protocols; - *outlen = (unsigned)strlen(protocols); - return SSL_TLSEXT_ERR_OK; -} - -void h2o_ssl_register_npn_protocols(SSL_CTX *ctx, const char *protocols) -{ - SSL_CTX_set_next_protos_advertised_cb(ctx, on_npn_advertise, (void *)protocols); -} - -#endif - -void h2o_sliding_counter_stop(h2o_sliding_counter_t *counter, uint64_t now) -{ - uint64_t elapsed; - - assert(counter->cur.start_at != 0); - - /* calculate the time used, and reset cur */ - if (now <= counter->cur.start_at) - elapsed = 0; - else - elapsed = now - counter->cur.start_at; - counter->cur.start_at = 0; - - /* adjust prev */ - counter->prev.sum += elapsed; - counter->prev.sum -= counter->prev.slots[counter->prev.index]; - counter->prev.slots[counter->prev.index] = elapsed; - if (++counter->prev.index >= sizeof(counter->prev.slots) / sizeof(counter->prev.slots[0])) - counter->prev.index = 0; - - /* recalc average */ - counter->average = counter->prev.sum / (sizeof(counter->prev.slots) / sizeof(counter->prev.slots[0])); -} |