diff src/core/ngx_proxy_protocol.c @ 8072:cca4c8a715de

PROXY protocol v2 TLV variables. The variables have prefix $proxy_protocol_tlv_ and are accessible by name and by type. Examples are: $proxy_protocol_tlv_0x01, $proxy_protocol_tlv_alpn.
author Roman Arutyunyan <arut@nginx.com>
date Wed, 12 Oct 2022 16:58:16 +0400
parents 017fd847f4f7
children aa663cc2a77d
line wrap: on
line diff
--- a/src/core/ngx_proxy_protocol.c
+++ b/src/core/ngx_proxy_protocol.c
@@ -15,6 +15,12 @@
 
 #define ngx_proxy_protocol_parse_uint16(p)  ((p)[0] << 8 | (p)[1])
 
+#define ngx_proxy_protocol_parse_uint32(p)                                    \
+    ( ((uint32_t) (p)[0] << 24)                                               \
+    + (           (p)[1] << 16)                                               \
+    + (           (p)[2] << 8)                                                \
+    + (           (p)[3]) )
+
 
 typedef struct {
     u_char                                  signature[12];
@@ -40,12 +46,52 @@ typedef struct {
 } ngx_proxy_protocol_inet6_addrs_t;
 
 
+typedef struct {
+    u_char                                  type;
+    u_char                                  len[2];
+} ngx_proxy_protocol_tlv_t;
+
+
+typedef struct {
+    u_char                                  client;
+    u_char                                  verify[4];
+} ngx_proxy_protocol_tlv_ssl_t;
+
+
+typedef struct {
+    ngx_str_t                               name;
+    ngx_uint_t                              type;
+} ngx_proxy_protocol_tlv_entry_t;
+
+
 static u_char *ngx_proxy_protocol_read_addr(ngx_connection_t *c, u_char *p,
     u_char *last, ngx_str_t *addr);
 static u_char *ngx_proxy_protocol_read_port(u_char *p, u_char *last,
     in_port_t *port, u_char sep);
 static u_char *ngx_proxy_protocol_v2_read(ngx_connection_t *c, u_char *buf,
     u_char *last);
+static ngx_int_t ngx_proxy_protocol_lookup_tlv(ngx_connection_t *c,
+    ngx_str_t *tlvs, ngx_uint_t type, ngx_str_t *value);
+
+
+static ngx_proxy_protocol_tlv_entry_t  ngx_proxy_protocol_tlv_entries[] = {
+    { ngx_string("alpn"),       0x01 },
+    { ngx_string("authority"),  0x02 },
+    { ngx_string("unique_id"),  0x05 },
+    { ngx_string("ssl"),        0x20 },
+    { ngx_string("netns"),      0x30 },
+    { ngx_null_string,          0x00 }
+};
+
+
+static ngx_proxy_protocol_tlv_entry_t  ngx_proxy_protocol_tlv_ssl_entries[] = {
+    { ngx_string("version"),    0x21 },
+    { ngx_string("cn"),         0x22 },
+    { ngx_string("cipher"),     0x23 },
+    { ngx_string("sig_alg"),    0x24 },
+    { ngx_string("key_alg"),    0x25 },
+    { ngx_null_string,          0x00 }
+};
 
 
 u_char *
@@ -418,11 +464,147 @@ ngx_proxy_protocol_v2_read(ngx_connectio
                    &pp->src_addr, pp->src_port, &pp->dst_addr, pp->dst_port);
 
     if (buf < end) {
-        ngx_log_debug1(NGX_LOG_DEBUG_CORE, c->log, 0,
-                       "PROXY protocol v2 %z bytes of tlv ignored", end - buf);
+        pp->tlvs.data = ngx_pnalloc(c->pool, end - buf);
+        if (pp->tlvs.data == NULL) {
+            return NULL;
+        }
+
+        ngx_memcpy(pp->tlvs.data, buf, end - buf);
+        pp->tlvs.len = end - buf;
     }
 
     c->proxy_protocol = pp;
 
     return end;
 }
+
+
+ngx_int_t
+ngx_proxy_protocol_get_tlv(ngx_connection_t *c, ngx_str_t *name,
+    ngx_str_t *value)
+{
+    u_char                          *p;
+    size_t                           n;
+    uint32_t                         verify;
+    ngx_str_t                        ssl, *tlvs;
+    ngx_int_t                        rc, type;
+    ngx_proxy_protocol_tlv_ssl_t    *tlv_ssl;
+    ngx_proxy_protocol_tlv_entry_t  *te;
+
+    if (c->proxy_protocol == NULL) {
+        return NGX_DECLINED;
+    }
+
+    ngx_log_debug1(NGX_LOG_DEBUG_CORE, c->log, 0,
+                   "PROXY protocol v2 get tlv \"%V\"", name);
+
+    te = ngx_proxy_protocol_tlv_entries;
+    tlvs = &c->proxy_protocol->tlvs;
+
+    p = name->data;
+    n = name->len;
+
+    if (n >= 4 && p[0] == 's' && p[1] == 's' && p[2] == 'l' && p[3] == '_') {
+
+        rc = ngx_proxy_protocol_lookup_tlv(c, tlvs, 0x20, &ssl);
+        if (rc != NGX_OK) {
+            return rc;
+        }
+
+        if (ssl.len < sizeof(ngx_proxy_protocol_tlv_ssl_t)) {
+            return NGX_ERROR;
+        }
+
+        p += 4;
+        n -= 4;
+
+        if (n == 6 && ngx_strncmp(p, "verify", 6) == 0) {
+
+            tlv_ssl = (ngx_proxy_protocol_tlv_ssl_t *) ssl.data;
+            verify = ngx_proxy_protocol_parse_uint32(tlv_ssl->verify);
+
+            value->data = ngx_pnalloc(c->pool, NGX_INT32_LEN);
+            if (value->data == NULL) {
+                return NGX_ERROR;
+            }
+
+            value->len = ngx_sprintf(value->data, "%uD", verify)
+                         - value->data;
+            return NGX_OK;
+        }
+
+        ssl.data += sizeof(ngx_proxy_protocol_tlv_ssl_t);
+        ssl.len -= sizeof(ngx_proxy_protocol_tlv_ssl_t);
+
+        te = ngx_proxy_protocol_tlv_ssl_entries;
+        tlvs = &ssl;
+    }
+
+    if (n >= 2 && p[0] == '0' && p[1] == 'x') {
+
+        type = ngx_hextoi(p + 2, n - 2);
+        if (type == NGX_ERROR) {
+            ngx_log_error(NGX_LOG_ERR, c->log, 0,
+                          "invalid PROXY protocol TLV \"%V\"", name);
+            return NGX_ERROR;
+        }
+
+        return ngx_proxy_protocol_lookup_tlv(c, tlvs, type, value);
+    }
+
+    for ( /* void */ ; te->type; te++) {
+        if (te->name.len == n && ngx_strncmp(te->name.data, p, n) == 0) {
+            return ngx_proxy_protocol_lookup_tlv(c, tlvs, te->type, value);
+        }
+    }
+
+    ngx_log_error(NGX_LOG_ERR, c->log, 0,
+                  "unknown PROXY protocol TLV \"%V\"", name);
+
+    return NGX_DECLINED;
+}
+
+
+static ngx_int_t
+ngx_proxy_protocol_lookup_tlv(ngx_connection_t *c, ngx_str_t *tlvs,
+    ngx_uint_t type, ngx_str_t *value)
+{
+    u_char                    *p;
+    size_t                     n, len;
+    ngx_proxy_protocol_tlv_t  *tlv;
+
+    ngx_log_debug1(NGX_LOG_DEBUG_CORE, c->log, 0,
+                   "PROXY protocol v2 lookup tlv:%02xi", type);
+
+    p = tlvs->data;
+    n = tlvs->len;
+
+    while (n) {
+        if (n < sizeof(ngx_proxy_protocol_tlv_t)) {
+            ngx_log_error(NGX_LOG_ERR, c->log, 0, "broken PROXY protocol TLV");
+            return NGX_ERROR;
+        }
+
+        tlv = (ngx_proxy_protocol_tlv_t *) p;
+        len = ngx_proxy_protocol_parse_uint16(tlv->len);
+
+        p += sizeof(ngx_proxy_protocol_tlv_t);
+        n -= sizeof(ngx_proxy_protocol_tlv_t);
+
+        if (n < len) {
+            ngx_log_error(NGX_LOG_ERR, c->log, 0, "broken PROXY protocol TLV");
+            return NGX_ERROR;
+        }
+
+        if (tlv->type == type) {
+            value->data = p;
+            value->len = len;
+            return NGX_OK;
+        }
+
+        p += len;
+        n -= len;
+    }
+
+    return NGX_DECLINED;
+}