From cdd72bad77393da9323ab13ebea63611d7b1e3ad Mon Sep 17 00:00:00 2001 From: Tatsuhiro Tsujikawa Date: Sun, 19 Jun 2016 22:32:47 +0900 Subject: [PATCH] examples: Add ALPN support to tutorial client/server This commit adds ALPN support to tutorial client/server. It also adds a code to check h2 was negotiated, if not, drop connection. For tutorial server, now it sends connection preface just after TLS handshake was made without waiting for the client connection preface. --- examples/libevent-client.c | 25 ++++++++++++++++++++++ examples/libevent-server.c | 43 +++++++++++++++++++++++++++++++++++++- 2 files changed, 67 insertions(+), 1 deletion(-) diff --git a/examples/libevent-client.c b/examples/libevent-client.c index 7061e78b..8fbef879 100644 --- a/examples/libevent-client.c +++ b/examples/libevent-client.c @@ -322,6 +322,11 @@ static SSL_CTX *create_ssl_ctx(void) { SSL_OP_NO_COMPRESSION | SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION); SSL_CTX_set_next_proto_select_cb(ssl_ctx, select_next_proto_cb, NULL); + +#if OPENSSL_VERSION_NUMBER >= 0x10002000L + SSL_CTX_set_alpn_protos(ssl_ctx, (const unsigned char *)"\x02h2", 3); +#endif // OPENSSL_VERSION_NUMBER >= 0x10002000L + return ssl_ctx; } @@ -475,7 +480,27 @@ static void eventcb(struct bufferevent *bev, short events, void *ptr) { if (events & BEV_EVENT_CONNECTED) { int fd = bufferevent_getfd(bev); int val = 1; + const unsigned char *alpn = NULL; + unsigned int alpnlen = 0; + SSL *ssl; + fprintf(stderr, "Connected\n"); + + ssl = bufferevent_openssl_get_ssl(session_data->bev); + + SSL_get0_next_proto_negotiated(ssl, &alpn, &alpnlen); +#if OPENSSL_VERSION_NUMBER >= 0x10002000L + if (alpn == NULL) { + SSL_get0_alpn_selected(ssl, &alpn, &alpnlen); + } +#endif // OPENSSL_VERSION_NUMBER >= 0x10002000L + + if (alpn == NULL || alpnlen != 2 || memcmp("h2", alpn, 2) != 0) { + fprintf(stderr, "h2 is not negotiated\n"); + delete_http2_session_data(session_data); + return; + } + setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, (char *)&val, sizeof(val)); initialize_nghttp2_session(session_data); send_client_connection_header(session_data); diff --git a/examples/libevent-server.c b/examples/libevent-server.c index 3a9ddfbc..09f52f99 100644 --- a/examples/libevent-server.c +++ b/examples/libevent-server.c @@ -116,6 +116,22 @@ static int next_proto_cb(SSL *s _U_, const unsigned char **data, return SSL_TLSEXT_ERR_OK; } +#if OPENSSL_VERSION_NUMBER >= 0x10002000L +static int alpn_select_proto_cb(SSL *ssl _U_, const unsigned char **out, + unsigned char *outlen, const unsigned char *in, + unsigned int inlen, void *arg _U_) { + int rv; + + rv = nghttp2_select_next_protocol((unsigned char **)out, outlen, in, inlen); + + if (rv != 1) { + return SSL_TLSEXT_ERR_NOACK; + } + + return SSL_TLSEXT_ERR_OK; +} +#endif // OPENSSL_VERSION_NUMBER >= 0x10002000L + /* Create SSL_CTX. */ static SSL_CTX *create_ssl_ctx(const char *key_file, const char *cert_file) { SSL_CTX *ssl_ctx; @@ -152,6 +168,11 @@ static SSL_CTX *create_ssl_ctx(const char *key_file, const char *cert_file) { next_proto_list_len = 1 + NGHTTP2_PROTO_VERSION_ID_LEN; SSL_CTX_set_next_protos_advertised_cb(ssl_ctx, next_proto_cb, NULL); + +#if OPENSSL_VERSION_NUMBER >= 0x10002000L + SSL_CTX_set_alpn_select_cb(ssl_ctx, alpn_select_proto_cb, NULL); +#endif // OPENSSL_VERSION_NUMBER >= 0x10002000L + return ssl_ctx; } @@ -640,11 +661,31 @@ static void writecb(struct bufferevent *bev, void *ptr) { static void eventcb(struct bufferevent *bev _U_, short events, void *ptr) { http2_session_data *session_data = (http2_session_data *)ptr; if (events & BEV_EVENT_CONNECTED) { + const unsigned char *alpn = NULL; + unsigned int alpnlen = 0; + SSL *ssl; + fprintf(stderr, "%s connected\n", session_data->client_addr); + ssl = bufferevent_openssl_get_ssl(session_data->bev); + + SSL_get0_next_proto_negotiated(ssl, &alpn, &alpnlen); +#if OPENSSL_VERSION_NUMBER >= 0x10002000L + if (alpn == NULL) { + SSL_get0_alpn_selected(ssl, &alpn, &alpnlen); + } +#endif // OPENSSL_VERSION_NUMBER >= 0x10002000L + + if (alpn == NULL || alpnlen != 2 || memcmp("h2", alpn, 2) != 0) { + fprintf(stderr, "%s h2 is not negotiated\n", session_data->client_addr); + delete_http2_session_data(session_data); + return; + } + initialize_nghttp2_session(session_data); - if (send_server_connection_header(session_data) != 0) { + if (send_server_connection_header(session_data) != 0 || + session_send(session_data) != 0) { delete_http2_session_data(session_data); return; }