serf-dev mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From rhuij...@apache.org
Subject svn commit: r1708599 - in /serf/trunk: buckets/ssl_buckets.c serf_bucket_types.h
Date Wed, 14 Oct 2015 13:20:20 GMT
Author: rhuijben
Date: Wed Oct 14 13:20:20 2015
New Revision: 1708599

URL: http://svn.apache.org/viewvc?rev=1708599&view=rev
Log:
Add support for ALPN negotiation to the ssl_buckets. ALPN support will be
necessary if we want to support http/2 in a future serf version.

* serf-dev/dev/buckets/ssl_buckets.c
  (serf_ssl_context_t): Add some variables.
  (ssl_get_selected_protocol): New forward definition.
  (ssl_decrypt): Call protocol callback as soon as possible.
  (ssl_init_context): Update initialization.
  (serf_ssl_negotiate_protocol,
   ssl_get_selected_protocol): New functions.

* serf-dev/dev/serf_bucket_types.h
  (serf_ssl_protocol_result_cb_t): New typedef.
  (serf_ssl_negotiate_protocol): New function.

Modified:
    serf/trunk/buckets/ssl_buckets.c
    serf/trunk/serf_bucket_types.h

Modified: serf/trunk/buckets/ssl_buckets.c
URL: http://svn.apache.org/viewvc/serf/trunk/buckets/ssl_buckets.c?rev=1708599&r1=1708598&r2=1708599&view=diff
==============================================================================
--- serf/trunk/buckets/ssl_buckets.c (original)
+++ serf/trunk/buckets/ssl_buckets.c Wed Oct 14 13:20:20 2015
@@ -171,6 +171,12 @@ struct serf_ssl_context_t {
 
     /* Flag is set to 1 when a renegotiation is in progress. */
     int renegotiation;
+    int init_finished; /* True after SSL internal connection is 'connected' */
+
+    const char *selected_protocol; /* Cached protocol value once available */
+    /* Protocol callback */
+    serf_ssl_protocol_result_cb_t protocol_callback; 
+    void *protocol_userdata;
 
     serf_config_t *config;
 };
@@ -195,6 +201,8 @@ static void disable_compression(serf_ssl
 static char *
     pstrdup_escape_nul_bytes(const char *buf, int len, apr_pool_t *pool);
 
+static const char *ssl_get_selected_protocol(serf_ssl_context_t *context);
+
 #ifdef SERF_LOGGING_ENABLED
 /* Log all ssl alerts that we receive from the server. */
 static void
@@ -927,6 +935,34 @@ static apr_status_t ssl_decrypt(void *ba
                     "---\n%.*s\n-(%d)-\n", *len, buf, *len);
     }
  
+
+    if (!ctx->init_finished
+        && !SERF_BUCKET_READ_ERROR(status)) {
+
+        apr_status_t s = APR_SUCCESS;
+
+        ctx->init_finished = SSL_is_init_finished(ctx->ssl);
+
+        /* Call the protocol callback as soon as possible as this triggers
+           pipelining data for the selected protocol. */
+        if (ctx->protocol_callback) {
+            const char *protocol = ssl_get_selected_protocol(ctx);
+
+            /* When ctx->init_finished is TRUE protocol will never be NULL,
+               reporting the final result if not already handled */
+            if (protocol) {
+                s = ctx->protocol_callback(ctx->protocol_userdata, protocol);
+                ctx->protocol_callback = NULL;
+            }
+        }
+
+        if (SERF_BUCKET_READ_ERROR(s)) {
+            serf__log(LOGLVL_ERROR, LOGCOMP_SSL, __FILE__, ctx->config,
+                      "ssl_decrypt: negotiation reported: %d\n", status);
+            status = s;
+        }
+    }
+
     serf__log(LOGLVL_DEBUG, LOGCOMP_SSL, __FILE__, ctx->config,
               "ssl_decrypt: %d %d\n", status, *len);
 
@@ -1483,6 +1519,11 @@ static serf_ssl_context_t *ssl_init_cont
     ssl_ctx->server_cert_callback = NULL;
     ssl_ctx->server_cert_chain_callback = NULL;
 
+    ssl_ctx->selected_protocol = "";
+    ssl_ctx->init_finished = FALSE;
+    ssl_ctx->protocol_callback = NULL;
+    ssl_ctx->protocol_userdata = NULL;
+
     SSL_CTX_set_verify(ssl_ctx->ctx, SSL_VERIFY_PEER,
                        validate_server_certificate);
     SSL_CTX_set_options(ssl_ctx->ctx, SSL_OP_ALL);
@@ -1569,6 +1610,97 @@ apr_status_t serf_ssl_set_hostname(serf_
     return APR_ENOTIMPL;
 }
 
+apr_status_t serf_ssl_negotiate_protocol(serf_ssl_context_t *context,
+                                         const char *protocols,
+                                         serf_ssl_protocol_result_cb_t callback,
+                                         void *callback_data)
+{
+    apr_pool_t *subpool;
+    char *raw_header;
+    char *at;
+    const char *next;
+    apr_size_t raw_len = strlen(protocols)+1;
+    apr_size_t len;
+
+    if (raw_len >= 65536)
+        return APR_EINVAL;
+
+    /* The OpenSSL api wants the value in network format.
+       A length byte followed by the value for all items. */
+
+    apr_pool_create(&subpool, context->pool);
+
+    at = raw_header = apr_palloc(subpool, raw_len);
+
+    while (next = strchr(protocols, ',')) {
+        len = (next - protocols);
+
+        if (len > 255) {
+            apr_pool_destroy(subpool);
+            return APR_EINVAL;
+        }
+
+        *at = len;
+        at++;
+        memcpy(at, protocols, len);
+        at += len;
+
+        protocols = next + 1;
+    }
+
+    len = strlen(protocols);
+    if (len > 255) {
+      apr_pool_destroy(subpool);
+      return APR_EINVAL;
+    }
+
+    *at = len;
+    at++;
+    memcpy(at, protocols, len);
+    at += len;
+
+#if OPENSSL_VERSION_NUMBER >= 0x10002000L /* >= 1.0.2 */
+    if (SSL_set_alpn_protos(context->ssl, raw_header, raw_len)) {
+        ERR_clear_error();
+    }
+    apr_pool_destroy(subpool);
+
+    context->protocol_callback = callback;
+    context->protocol_userdata = callback_data;
+    context->selected_protocol = NULL;
+    return APR_SUCCESS;
+#else
+    apr_pool_destroy(subpool);
+    return APR_ENOTIMPL;
+#endif
+}
+
+/* Gets the protocol selected by the server via ALPN. Returns NULL if
+ * ALPN and "" if no selection is made.
+ *
+ * ### Should we make this public as serf_ssl_get_selected_protocol(),
+ *     or is the callback the only relevant scenario?
+ */
+static const char *ssl_get_selected_protocol(serf_ssl_context_t *context)
+{
+    if (! context->selected_protocol) {
+#if OPENSSL_VERSION_NUMBER >= 0x10002000L /* >= 1.0.2 */
+        const unsigned char *data = NULL;
+        unsigned len = 0;
+
+        SSL_get0_alpn_selected(context->ssl, &data, &len);
+
+        if (data && len)
+            context->selected_protocol = apr_pstrmemdup(context->pool,
+                                                        data, len);
+        else if (context->init_finished)
+            context->selected_protocol = "";
+#endif
+    }
+    return context->selected_protocol;
+}
+
+
 apr_status_t serf_ssl_use_default_certificates(serf_ssl_context_t *ssl_ctx)
 {
     X509_STORE *store = SSL_CTX_get_cert_store(ssl_ctx->ctx);

Modified: serf/trunk/serf_bucket_types.h
URL: http://svn.apache.org/viewvc/serf/trunk/serf_bucket_types.h?rev=1708599&r1=1708598&r2=1708599&view=diff
==============================================================================
--- serf/trunk/serf_bucket_types.h (original)
+++ serf/trunk/serf_bucket_types.h Wed Oct 14 13:20:20 2015
@@ -585,6 +585,31 @@ apr_status_t serf_ssl_use_default_certif
 apr_status_t serf_ssl_set_hostname(
     serf_ssl_context_t *context, const char *hostname);
 
+
+typedef apr_status_t (*serf_ssl_protocol_result_cb_t)(
+    void *data,
+    const char *protocol);
+
+/**
+ * Enables ALPN negotiation with the server. Setups that the supported protocols
+ * will be sent to the server, and enables handling the response via a callback.
+ *
+ * SUPPORTED_PROTOCOLS is a comma separated list of protocols. No whitespace
+ * should be added as the values are used literally. E.g. "h2,http/1.1,h2-16"
+ *
+ * Returns APR_ENOTIMPL when the ssl library doesn't implement ALPN.
+ *
+ * If successfull CALLBACK will be called as soon as the protocol is negotiated
+ * or directly after the secured stream is connected.
+ *
+ * @since New in 1.4.
+ */
+apr_status_t serf_ssl_negotiate_protocol(
+    serf_ssl_context_t *context,
+    const char *protocols,
+    serf_ssl_protocol_result_cb_t callback,
+    void *callback_data);
+
 /**
  * Return the depth of the certificate.
  */



Mime
View raw message