From 4d8db595cab1222927a251a9b836ac4947b7d73d Mon Sep 17 00:00:00 2001 From: Fabian Frank Date: Tue, 4 Feb 2014 00:10:37 -0800 Subject: [PATCH] gtls: add ALPN support Add ALPN support when using GnuTLS >= 3.2.0. This allows libcurl to negotiate HTTP/2.0 for https connections when built with GnuTLS. See: http://www.gnutls.org/manual/gnutls.html#Application-Layer-Protocol-Negotiation-_0028ALPN_0029 http://tools.ietf.org/html/draft-ietf-tls-applayerprotoneg-04 --- lib/vtls/gtls.c | 45 +++++++++++++++++++++++++++++++++++++++++++++ lib/vtls/openssl.c | 7 ------- lib/vtls/vtls.h | 4 ++++ 3 files changed, 49 insertions(+), 7 deletions(-) diff --git a/lib/vtls/gtls.c b/lib/vtls/gtls.c index cd410551c..326af386f 100644 --- a/lib/vtls/gtls.c +++ b/lib/vtls/gtls.c @@ -89,6 +89,15 @@ static bool gtls_inited = FALSE; # if (GNUTLS_VERSION_NUMBER >= 0x020c03) # define GNUTLS_MAPS_WINSOCK_ERRORS 1 # endif + +# ifdef USE_NGHTTP2 +# undef HAS_ALPN +# if (GNUTLS_VERSION_NUMBER >= 0x030200) +# define HAS_ALPN +# else +# error http2 builds require GnuTLS >= 3.2.0 for ALPN support +# endif +# endif #endif /* @@ -374,6 +383,10 @@ gtls_connect_step1(struct connectdata *conn, const char* prioritylist; const char *err; #endif +#ifdef HAS_ALPN + int protocols_size = 2; + gnutls_datum_t protocols[2]; +#endif if(conn->ssl[sockindex].state == ssl_connection_complete) /* to make us tolerant against being called more than once for the @@ -556,6 +569,15 @@ gtls_connect_step1(struct connectdata *conn, rc = gnutls_priority_set_direct(session, prioritylist, &err); #endif +#ifdef HAS_ALPN + protocols[0].data = NGHTTP2_PROTO_VERSION_ID; + protocols[0].size = NGHTTP2_PROTO_VERSION_ID_LEN; + protocols[1].data = ALPN_HTTP_1_1; + protocols[1].size = ALPN_HTTP_1_1_LENGTH; + gnutls_alpn_set_protocols(session, protocols, protocols_size, 0); + infof(data, "ALPN, offering %s, %s\n", NGHTTP2_PROTO_VERSION_ID, + ALPN_HTTP_1_1); +#endif if(rc != GNUTLS_E_SUCCESS) { failf(data, "Did you pass a valid GnuTLS cipher list?"); @@ -637,6 +659,9 @@ gtls_connect_step3(struct connectdata *conn, int rc; int incache; void *ssl_sessionid; +#ifdef HAS_ALPN + gnutls_datum_t proto; +#endif CURLcode result = CURLE_OK; /* This function will return the peer's raw certificate (chain) as sent by @@ -841,6 +866,26 @@ gtls_connect_step3(struct connectdata *conn, ptr = gnutls_mac_get_name(gnutls_mac_get(session)); infof(data, "\t MAC: %s\n", ptr); +#ifdef HAS_ALPN + rc = gnutls_alpn_get_selected_protocol(session, &proto); + if(rc == 0) { + infof(data, "ALPN, server accepted to use %.*s\n", proto.size, proto.data); + + if(proto.size == NGHTTP2_PROTO_VERSION_ID_LEN && + memcmp(NGHTTP2_PROTO_VERSION_ID, proto.data, + NGHTTP2_PROTO_VERSION_ID_LEN) == 0) { + conn->negnpn = NPN_HTTP2_DRAFT09; + } + else if(proto.size == ALPN_HTTP_1_1_LENGTH && memcmp(ALPN_HTTP_1_1, + proto.data, ALPN_HTTP_1_1_LENGTH) == 0) { + conn->negnpn = NPN_HTTP1_1; + } + } + else { + infof(data, "ALPN, server did not agree to a protocol\n"); + } +#endif + conn->ssl[sockindex].state = ssl_connection_complete; conn->recv[sockindex] = gtls_recv; conn->send[sockindex] = gtls_send; diff --git a/lib/vtls/openssl.c b/lib/vtls/openssl.c index 063945922..2f9f8a0e5 100644 --- a/lib/vtls/openssl.c +++ b/lib/vtls/openssl.c @@ -1415,13 +1415,6 @@ static void ssl_tls_trace(int direction, int ssl_ver, int content_type, #endif -/* see http://tools.ietf.org/html/draft-ietf-tls-applayerprotoneg-04 */ -#ifdef HAS_ALPN -#define ALPN_HTTP_1_1_LENGTH 8 -#define ALPN_HTTP_1_1 "http/1.0" -#endif - - /* * in is a list of lenght prefixed strings. this function has to select * the protocol we want to use from the list and write its string into out. diff --git a/lib/vtls/vtls.h b/lib/vtls/vtls.h index 04ab60b18..823c041ed 100644 --- a/lib/vtls/vtls.h +++ b/lib/vtls/vtls.h @@ -27,6 +27,10 @@ #define MD5_DIGEST_LENGTH 16 /* fixed size */ #endif +/* see http://tools.ietf.org/html/draft-ietf-tls-applayerprotoneg-04 */ +#define ALPN_HTTP_1_1_LENGTH 8 +#define ALPN_HTTP_1_1 "http/1.0" + bool Curl_ssl_config_matches(struct ssl_config_data* data, struct ssl_config_data* needle); bool Curl_clone_ssl_config(struct ssl_config_data* source,