[nginx] Stream ssl_preread: $ssl_preread_alpn_protocols variable.

Roman Arutyunyan arut at nginx.com
Tue Mar 13 12:01:16 UTC 2018


details:   http://hg.nginx.org/nginx/rev/79eb4f7b6725
branches:  
changeset: 7227:79eb4f7b6725
user:      Roman Arutyunyan <arut at nginx.com>
date:      Mon Mar 12 16:03:08 2018 +0300
description:
Stream ssl_preread: $ssl_preread_alpn_protocols variable.

The variable keeps a comma-separated list of protocol names from ALPN TLS
extension defined by RFC 7301.

diffstat:

 src/stream/ngx_stream_ssl_preread_module.c |  128 +++++++++++++++++++++++++++-
 1 files changed, 122 insertions(+), 6 deletions(-)

diffs (248 lines):

diff -r 0b1eb40de6da -r 79eb4f7b6725 src/stream/ngx_stream_ssl_preread_module.c
--- a/src/stream/ngx_stream_ssl_preread_module.c	Wed Mar 07 18:28:12 2018 +0300
+++ b/src/stream/ngx_stream_ssl_preread_module.c	Mon Mar 12 16:03:08 2018 +0300
@@ -17,10 +17,12 @@ typedef struct {
 typedef struct {
     size_t          left;
     size_t          size;
+    size_t          ext;
     u_char         *pos;
     u_char         *dst;
     u_char          buf[4];
     ngx_str_t       host;
+    ngx_str_t       alpn;
     ngx_log_t      *log;
     ngx_pool_t     *pool;
     ngx_uint_t      state;
@@ -32,6 +34,8 @@ static ngx_int_t ngx_stream_ssl_preread_
     ngx_stream_ssl_preread_ctx_t *ctx, u_char *pos, u_char *last);
 static ngx_int_t ngx_stream_ssl_preread_server_name_variable(
     ngx_stream_session_t *s, ngx_stream_variable_value_t *v, uintptr_t data);
+static ngx_int_t ngx_stream_ssl_preread_alpn_protocols_variable(
+    ngx_stream_session_t *s, ngx_stream_variable_value_t *v, uintptr_t data);
 static ngx_int_t ngx_stream_ssl_preread_add_variables(ngx_conf_t *cf);
 static void *ngx_stream_ssl_preread_create_srv_conf(ngx_conf_t *cf);
 static char *ngx_stream_ssl_preread_merge_srv_conf(ngx_conf_t *cf, void *parent,
@@ -85,6 +89,9 @@ static ngx_stream_variable_t  ngx_stream
     { ngx_string("ssl_preread_server_name"), NULL,
       ngx_stream_ssl_preread_server_name_variable, 0, 0, 0 },
 
+    { ngx_string("ssl_preread_alpn_protocols"), NULL,
+      ngx_stream_ssl_preread_alpn_protocols_variable, 0, 0, 0 },
+
       ngx_stream_null_variable
 };
 
@@ -139,12 +146,14 @@ ngx_stream_ssl_preread_handler(ngx_strea
         if (p[0] != 0x16) {
             ngx_log_debug0(NGX_LOG_DEBUG_STREAM, ctx->log, 0,
                            "ssl preread: not a handshake");
+            ngx_stream_set_ctx(s, NULL, ngx_stream_ssl_preread_module);
             return NGX_DECLINED;
         }
 
         if (p[1] != 3) {
             ngx_log_debug0(NGX_LOG_DEBUG_STREAM, ctx->log, 0,
                            "ssl preread: unsupported SSL version");
+            ngx_stream_set_ctx(s, NULL, ngx_stream_ssl_preread_module);
             return NGX_DECLINED;
         }
 
@@ -158,6 +167,12 @@ ngx_stream_ssl_preread_handler(ngx_strea
         p += 5;
 
         rc = ngx_stream_ssl_preread_parse_record(ctx, p, p + len);
+
+        if (rc == NGX_DECLINED) {
+            ngx_stream_set_ctx(s, NULL, ngx_stream_ssl_preread_module);
+            return NGX_DECLINED;
+        }
+
         if (rc != NGX_AGAIN) {
             return rc;
         }
@@ -175,7 +190,7 @@ static ngx_int_t
 ngx_stream_ssl_preread_parse_record(ngx_stream_ssl_preread_ctx_t *ctx,
     u_char *pos, u_char *last)
 {
-    size_t   left, n, size;
+    size_t   left, n, size, ext;
     u_char  *dst, *p;
 
     enum {
@@ -192,7 +207,10 @@ ngx_stream_ssl_preread_parse_record(ngx_
         sw_ext_header,      /* extension_type, extension_data length */
         sw_sni_len,         /* SNI length */
         sw_sni_host_head,   /* SNI name_type, host_name length */
-        sw_sni_host         /* SNI host_name */
+        sw_sni_host,        /* SNI host_name */
+        sw_alpn_len,        /* ALPN length */
+        sw_alpn_proto_len,  /* ALPN protocol_name length */
+        sw_alpn_proto_data  /* ALPN protocol_name */
     } state;
 
     ngx_log_debug2(NGX_LOG_DEBUG_STREAM, ctx->log, 0,
@@ -201,6 +219,7 @@ ngx_stream_ssl_preread_parse_record(ngx_
     state = ctx->state;
     size = ctx->size;
     left = ctx->left;
+    ext = ctx->ext;
     dst = ctx->dst;
     p = ctx->buf;
 
@@ -299,10 +318,18 @@ ngx_stream_ssl_preread_parse_record(ngx_
             break;
 
         case sw_ext_header:
-            if (p[0] == 0 && p[1] == 0) {
+            if (p[0] == 0 && p[1] == 0 && ctx->host.data == NULL) {
                 /* SNI extension */
                 state = sw_sni_len;
-                dst = NULL;
+                dst = p;
+                size = 2;
+                break;
+            }
+
+            if (p[0] == 0 && p[1] == 16 && ctx->alpn.data == NULL) {
+                /* ALPN extension */
+                state = sw_alpn_len;
+                dst = p;
                 size = 2;
                 break;
             }
@@ -313,6 +340,7 @@ ngx_stream_ssl_preread_parse_record(ngx_
             break;
 
         case sw_sni_len:
+            ext = (p[0] << 8) + p[1];
             state = sw_sni_host_head;
             dst = p;
             size = 3;
@@ -325,14 +353,21 @@ ngx_stream_ssl_preread_parse_record(ngx_
                 return NGX_DECLINED;
             }
 
-            state = sw_sni_host;
             size = (p[1] << 8) + p[2];
 
+            if (ext < 3 + size) {
+                ngx_log_debug0(NGX_LOG_DEBUG_STREAM, ctx->log, 0,
+                               "ssl preread: SNI format error");
+                return NGX_DECLINED;
+            }
+            ext -= 3 + size;
+
             ctx->host.data = ngx_pnalloc(ctx->pool, size);
             if (ctx->host.data == NULL) {
                 return NGX_ERROR;
             }
 
+            state = sw_sni_host;
             dst = ctx->host.data;
             break;
 
@@ -341,7 +376,64 @@ ngx_stream_ssl_preread_parse_record(ngx_
 
             ngx_log_debug1(NGX_LOG_DEBUG_STREAM, ctx->log, 0,
                            "ssl preread: SNI hostname \"%V\"", &ctx->host);
-            return NGX_OK;
+
+            state = sw_ext;
+            dst = NULL;
+            size = ext;
+            break;
+
+        case sw_alpn_len:
+            ext = (p[0] << 8) + p[1];
+
+            ctx->alpn.data = ngx_pnalloc(ctx->pool, ext);
+            if (ctx->alpn.data == NULL) {
+                return NGX_ERROR;
+            }
+
+            state = sw_alpn_proto_len;
+            dst = p;
+            size = 1;
+            break;
+
+        case sw_alpn_proto_len:
+            size = p[0];
+
+            if (size == 0) {
+                ngx_log_debug0(NGX_LOG_DEBUG_STREAM, ctx->log, 0,
+                               "ssl preread: ALPN empty protocol");
+                return NGX_DECLINED;
+            }
+
+            if (ext < 1 + size) {
+                ngx_log_debug0(NGX_LOG_DEBUG_STREAM, ctx->log, 0,
+                               "ssl preread: ALPN format error");
+                return NGX_DECLINED;
+            }
+            ext -= 1 + size;
+
+            state = sw_alpn_proto_data;
+            dst = ctx->alpn.data + ctx->alpn.len;
+            break;
+
+        case sw_alpn_proto_data:
+            ctx->alpn.len += p[0];
+
+            ngx_log_debug1(NGX_LOG_DEBUG_STREAM, ctx->log, 0,
+                           "ssl preread: ALPN protocols \"%V\"", &ctx->alpn);
+
+            if (ext) {
+                ctx->alpn.data[ctx->alpn.len++] = ',';
+
+                state = sw_alpn_proto_len;
+                dst = p;
+                size = 1;
+                break;
+            }
+
+            state = sw_ext;
+            dst = NULL;
+            size = 0;
+            break;
         }
 
         if (left < size) {
@@ -354,6 +446,7 @@ ngx_stream_ssl_preread_parse_record(ngx_
     ctx->state = state;
     ctx->size = size;
     ctx->left = left;
+    ctx->ext = ext;
     ctx->dst = dst;
 
     return NGX_AGAIN;
@@ -384,6 +477,29 @@ ngx_stream_ssl_preread_server_name_varia
 
 
 static ngx_int_t
+ngx_stream_ssl_preread_alpn_protocols_variable(ngx_stream_session_t *s,
+    ngx_variable_value_t *v, uintptr_t data)
+{
+    ngx_stream_ssl_preread_ctx_t  *ctx;
+
+    ctx = ngx_stream_get_module_ctx(s, ngx_stream_ssl_preread_module);
+
+    if (ctx == NULL) {
+        v->not_found = 1;
+        return NGX_OK;
+    }
+
+    v->valid = 1;
+    v->no_cacheable = 0;
+    v->not_found = 0;
+    v->len = ctx->alpn.len;
+    v->data = ctx->alpn.data;
+
+    return NGX_OK;
+}
+
+
+static ngx_int_t
 ngx_stream_ssl_preread_add_variables(ngx_conf_t *cf)
 {
     ngx_stream_variable_t  *var, *v;


More information about the nginx-devel mailing list