static http2_session_data* create_http2_session_data(app_context *app_ctx, int fd, struct sockaddr *addr, int addrlen) { int rv; http2_session_data *session_data; SSL *ssl; char host[NI_MAXHOST]; int val = 1; ssl = create_ssl(app_ctx->ssl_ctx); session_data = malloc(sizeof(http2_session_data)); memset(session_data, 0, sizeof(http2_session_data)); session_data->app_ctx = app_ctx; setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, (char *)&val, sizeof(val)); session_data->bev = bufferevent_openssl_socket_new (app_ctx->evbase, fd, ssl, BUFFEREVENT_SSL_ACCEPTING, BEV_OPT_CLOSE_ON_FREE | BEV_OPT_DEFER_CALLBACKS); session_data->handshake_leftlen = NGHTTP2_CLIENT_CONNECTION_HEADER_LEN; rv = getnameinfo(addr, addrlen, host, sizeof(host), NULL, 0, NI_NUMERICHOST); if(rv != 0) { session_data->client_addr = strdup("(unknown)"); } else { session_data->client_addr = strdup(host); } return session_data; }
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)); /* 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); create_ssl(sock, ssl_ctx); sock->ssl->handshake.cb = handshake_cb; if (server_name == NULL) { /* is server */ if (SSL_CTX_sess_get_get_cb(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 { sock->ssl->handshake.client.server_name = h2o_strdup(NULL, server_name, SIZE_MAX).base; SSL_set_tlsext_host_name(sock->ssl->ssl, sock->ssl->handshake.client.server_name); proceed_handshake(sock, 0); } }
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)); /* 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); create_ssl(sock, ssl_ctx); sock->ssl->handshake.cb = handshake_cb; if (server_name == NULL) { /* is server */ if (SSL_CTX_sess_get_get_cb(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 { h2o_cache_t *session_cache = h2o_socket_ssl_get_session_cache(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->ssl, (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->ssl, sock->ssl->handshake.client.server_name); proceed_handshake(sock, 0); } }
/* Start connecting to the remote peer |host:port| */ static void initiate_connection(struct event_base *evbase, SSL_CTX *ssl_ctx, const char *host, uint16_t port, http2_session_data *session_data) { int rv; struct bufferevent *bev; SSL *ssl; ssl = create_ssl(ssl_ctx); bev = bufferevent_openssl_socket_new( evbase, -1, ssl, BUFFEREVENT_SSL_CONNECTING, BEV_OPT_DEFER_CALLBACKS | BEV_OPT_CLOSE_ON_FREE); bufferevent_enable(bev, EV_READ | EV_WRITE); bufferevent_setcb(bev, readcb, writecb, eventcb, session_data); rv = bufferevent_socket_connect_hostname(bev, session_data->dnsbase, AF_UNSPEC, host, port); if (rv != 0) { errx(1, "Could not connect to the remote host %s", host); } session_data->bev = bev; }
static void proceed_handshake(h2o_socket_t *sock, const char *err) { h2o_iovec_t first_input = {NULL}; int ret; sock->_cb.write = NULL; if (err != NULL) { goto Complete; } if (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: if (SSL_is_server(sock->ssl->ssl)) { ret = SSL_accept(sock->ssl->ssl); } else { ret = SSL_connect(sock->ssl->ssl); } switch (sock->ssl->handshake.server.async_resumption.state) { 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_CTX *ssl_ctx = SSL_get_SSL_CTX(sock->ssl->ssl); SSL_free(sock->ssl->ssl); create_ssl(sock, ssl_ctx); 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: break; } if (ret == 0 || (ret < 0 && SSL_get_error(sock->ssl->ssl, ret) != SSL_ERROR_WANT_READ)) { /* failed */ long verify_result = SSL_get_verify_result(sock->ssl->ssl); 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->ssl)) { X509 *cert = SSL_get_peer_certificate(sock->ssl->ssl); 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); }