]> granicus.if.org Git - curl/commitdiff
gtls: add ALPN support
authorFabian Frank <fabian@pagefault.de>
Tue, 4 Feb 2014 08:10:37 +0000 (00:10 -0800)
committerDaniel Stenberg <daniel@haxx.se>
Tue, 4 Feb 2014 08:48:27 +0000 (09:48 +0100)
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
lib/vtls/openssl.c
lib/vtls/vtls.h

index cd410551c8975d2a542ddcc542b5ddd84f93e9a0..326af386fc8ca685dfe0236d692104b0ea6b92e9 100644 (file)
@@ -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;
index 0639459222be1465cfaecf48bed11e78fa973970..2f9f8a0e5741f5bffd8f6d944f1e621458c26bc6 100644 (file)
@@ -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.
index 04ab60b1868e120c931f94018c31c54b7cf48dde..823c041eda5538096093dcdd4a030676e1ed4c61 100644 (file)
 #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,