<html><head><meta http-equiv="Content-Type" content="text/html; charset=us-ascii"></head><body style="word-wrap: break-word; -webkit-nbsp-mode: space; line-break: after-white-space;" class="">Hi,<div class=""><br class=""></div><div class=""><div><blockquote type="cite" class=""><div class="">On 31 Aug 2022, at 19:52, Roman Arutyunyan <<a href="mailto:arut@nginx.com" class="">arut@nginx.com</a>> wrote:</div><br class="Apple-interchange-newline"><div class=""><div class=""># HG changeset patch<br class=""># User Roman Arutyunyan <<a href="mailto:arut@nginx.com" class="">arut@nginx.com</a>><br class=""># Date 1661436099 -14400<br class="">#      Thu Aug 25 18:01:39 2022 +0400<br class=""># Node ID 4b856f1dff939e4eb9c131e17af061cf2c38cfac<br class=""># Parent  069a4813e8d6d7ec662d282a10f5f7062ebd817f<br class="">Core: support for reading PROXY protocol v2 TLVs.<br class=""><br class="">The TLV values are available in HTTP and Stream variables<br class="">$proxy_protocol_tlv_0xN, where N is a hexadecimal TLV type number with no<br class="">leading zeroes.<br class=""></div></div></blockquote><div><br class=""></div><div class="">A small update, thanks to Eran Kornblau.</div><div class=""><br class=""></div><div class="">diff --git a/src/core/ngx_proxy_protocol.c b/src/core/ngx_proxy_protocol.c<br class="">--- a/src/core/ngx_proxy_protocol.c<br class="">+++ b/src/core/ngx_proxy_protocol.c<br class="">@@ -446,7 +446,7 @@ ngx_proxy_protocol_v2_read(ngx_connectio<br class="">             }<br class=""> <br class="">             ngx_log_debug2(NGX_LOG_DEBUG_CORE, c->log, 0,<br class="">-                           "PROXY protocol v2 TLV type:0x%0xd len:%uz",<br class="">+                           "PROXY protocol v2 TLV type:0x%xd len:%uz",<br class="">                            tlv->type, len);<br class=""> <br class="">             t = ngx_list_push(pp_tlv);<br class="">@@ -462,7 +462,7 @@ ngx_proxy_protocol_v2_read(ngx_connectio<br class="">                 return NULL;<br class="">             }<br class=""> <br class="">-            t->key.len = ngx_sprintf(t->key.data, "%0xd", tlv->type)<br class="">+            t->key.len = ngx_sprintf(t->key.data, "%xd", tlv->type)<br class="">                          - t->key.data;<br class=""> <br class="">             t->value.data = ngx_pnalloc(c->pool, len);</div><div class=""><br class=""></div><div class="">Another possible update is support for arbitrary number of leading zeroes.</div><div class="">Not sure we need this right now though.</div><div class=""></div><br class=""><blockquote type="cite" class=""><div class=""><div class="">diff --git a/src/core/ngx_proxy_protocol.c b/src/core/ngx_proxy_protocol.c<br class="">--- a/src/core/ngx_proxy_protocol.c<br class="">+++ b/src/core/ngx_proxy_protocol.c<br class="">@@ -40,6 +40,12 @@ typedef struct {<br class=""> } ngx_proxy_protocol_inet6_addrs_t;<br class=""><br class=""><br class="">+typedef struct {<br class="">+    u_char                                  type;<br class="">+    u_char                                  len[2];<br class="">+} ngx_proxy_protocol_tlv_t;<br class="">+<br class="">+<br class=""> static u_char *ngx_proxy_protocol_read_addr(ngx_connection_t *c, u_char *p,<br class="">     u_char *last, ngx_str_t *addr);<br class=""> static u_char *ngx_proxy_protocol_read_port(u_char *p, u_char *last,<br class="">@@ -273,8 +279,11 @@ ngx_proxy_protocol_v2_read(ngx_connectio<br class="">     size_t                              len;<br class="">     socklen_t                           socklen;<br class="">     ngx_uint_t                          version, command, family, transport;<br class="">+    ngx_list_t                         *pp_tlv;<br class="">     ngx_sockaddr_t                      src_sockaddr, dst_sockaddr;<br class="">+    ngx_table_elt_t                    *t;<br class="">     ngx_proxy_protocol_t               *pp;<br class="">+    ngx_proxy_protocol_tlv_t           *tlv;<br class="">     ngx_proxy_protocol_header_t        *header;<br class="">     ngx_proxy_protocol_inet_addrs_t    *in;<br class=""> #if (NGX_HAVE_INET6)<br class="">@@ -412,8 +421,63 @@ ngx_proxy_protocol_v2_read(ngx_connectio<br class="">                    &pp->src_addr, pp->src_port, &pp->dst_addr, pp->dst_port);<br class=""><br class="">     if (buf < end) {<br class="">-        ngx_log_debug1(NGX_LOG_DEBUG_CORE, c->log, 0,<br class="">-                       "PROXY protocol v2 %z bytes of tlv ignored", end - buf);<br class="">+        pp_tlv = ngx_list_create(c->pool, 1, sizeof(ngx_table_elt_t));<br class="">+        if (pp_tlv == NULL) {<br class="">+            return NULL;<br class="">+        }<br class="">+<br class="">+        while (buf < end) {<br class="">+            if ((size_t) (end - buf) < sizeof(ngx_proxy_protocol_tlv_t)) {<br class="">+                ngx_log_error(NGX_LOG_ERR, c->log, 0,<br class="">+                              "broken PROXY protocol TLV");<br class="">+                return NULL;<br class="">+            }<br class="">+<br class="">+            tlv = (ngx_proxy_protocol_tlv_t *) buf;<br class="">+<br class="">+            buf += sizeof(ngx_proxy_protocol_tlv_t);<br class="">+<br class="">+            len = ngx_proxy_protocol_parse_uint16(tlv->len);<br class="">+<br class="">+            if ((size_t) (end - buf) < len) {<br class="">+                ngx_log_error(NGX_LOG_ERR, c->log, 0,<br class="">+                              "broken PROXY protocol TLV");<br class="">+                return NULL;<br class="">+            }<br class="">+<br class="">+            ngx_log_debug2(NGX_LOG_DEBUG_CORE, c->log, 0,<br class="">+                           "PROXY protocol v2 TLV type:0x%0xd len:%uz",<br class="">+                           tlv->type, len);<br class="">+<br class="">+            t = ngx_list_push(pp_tlv);<br class="">+            if (t == NULL) {<br class="">+                return NULL;<br class="">+            }<br class="">+<br class="">+            t->hash = 1;<br class="">+            t->lowcase_key = NULL;<br class="">+<br class="">+            t->key.data = ngx_pnalloc(c->pool, 2);<br class="">+            if (t->key.data == NULL) {<br class="">+                return NULL;<br class="">+            }<br class="">+<br class="">+            t->key.len = ngx_sprintf(t->key.data, "%0xd", tlv->type)<br class="">+                         - t->key.data;<br class="">+<br class="">+            t->value.data = ngx_pnalloc(c->pool, len);<br class="">+            if (t->value.data == NULL) {<br class="">+                return NULL;<br class="">+            }<br class="">+<br class="">+            ngx_memcpy(t->value.data, buf, len);<br class="">+<br class="">+            t->value.len = len;<br class="">+<br class="">+            buf += len;<br class="">+        }<br class="">+<br class="">+        pp->tlv = pp_tlv;<br class="">     }<br class=""><br class="">     c->proxy_protocol = pp;<br class="">diff --git a/src/core/ngx_proxy_protocol.h b/src/core/ngx_proxy_protocol.h<br class="">--- a/src/core/ngx_proxy_protocol.h<br class="">+++ b/src/core/ngx_proxy_protocol.h<br class="">@@ -21,6 +21,7 @@ struct ngx_proxy_protocol_s {<br class="">     ngx_str_t           dst_addr;<br class="">     in_port_t           src_port;<br class="">     in_port_t           dst_port;<br class="">+    ngx_list_t         *tlv;<br class=""> };<br class=""><br class=""><br class="">diff --git a/src/http/ngx_http_variables.c b/src/http/ngx_http_variables.c<br class="">--- a/src/http/ngx_http_variables.c<br class="">+++ b/src/http/ngx_http_variables.c<br class="">@@ -61,6 +61,8 @@ static ngx_int_t ngx_http_variable_proxy<br class="">     ngx_http_variable_value_t *v, uintptr_t data);<br class=""> static ngx_int_t ngx_http_variable_proxy_protocol_port(ngx_http_request_t *r,<br class="">     ngx_http_variable_value_t *v, uintptr_t data);<br class="">+static ngx_int_t ngx_http_variable_proxy_protocol_tlv(ngx_http_request_t *r,<br class="">+    ngx_http_variable_value_t *v, uintptr_t data);<br class=""> static ngx_int_t ngx_http_variable_server_addr(ngx_http_request_t *r,<br class="">     ngx_http_variable_value_t *v, uintptr_t data);<br class=""> static ngx_int_t ngx_http_variable_server_port(ngx_http_request_t *r,<br class="">@@ -214,6 +216,10 @@ static ngx_http_variable_t  ngx_http_cor<br class="">       ngx_http_variable_proxy_protocol_port,<br class="">       offsetof(ngx_proxy_protocol_t, dst_port), 0, 0 },<br class=""><br class="">+    { ngx_string("proxy_protocol_tlv_0x"), NULL,<br class="">+      ngx_http_variable_proxy_protocol_tlv,<br class="">+      0, NGX_HTTP_VAR_PREFIX, 0 },<br class="">+<br class="">     { ngx_string("server_addr"), NULL, ngx_http_variable_server_addr, 0, 0, 0 },<br class=""><br class="">     { ngx_string("server_port"), NULL, ngx_http_variable_server_port, 0, 0, 0 },<br class="">@@ -1387,6 +1393,24 @@ ngx_http_variable_proxy_protocol_port(ng<br class=""><br class=""><br class=""> static ngx_int_t<br class="">+ngx_http_variable_proxy_protocol_tlv(ngx_http_request_t *r,<br class="">+    ngx_http_variable_value_t *v, uintptr_t data)<br class="">+{<br class="">+    ngx_proxy_protocol_t  *pp;<br class="">+<br class="">+    pp = r->connection->proxy_protocol;<br class="">+    if (pp == NULL || pp->tlv == NULL) {<br class="">+        v->not_found = 1;<br class="">+        return NGX_OK;<br class="">+    }<br class="">+<br class="">+    return ngx_http_variable_unknown_header(r, v, (ngx_str_t *) data,<br class="">+                                          &pp->tlv->part,<br class="">+                                          sizeof("proxy_protocol_tlv_0x") - 1);<br class="">+}<br class="">+<br class="">+<br class="">+static ngx_int_t<br class=""> ngx_http_variable_server_addr(ngx_http_request_t *r,<br class="">     ngx_http_variable_value_t *v, uintptr_t data)<br class=""> {<br class="">diff --git a/src/stream/ngx_stream_variables.c b/src/stream/ngx_stream_variables.c<br class="">--- a/src/stream/ngx_stream_variables.c<br class="">+++ b/src/stream/ngx_stream_variables.c<br class="">@@ -23,6 +23,8 @@ static ngx_int_t ngx_stream_variable_pro<br class="">     ngx_stream_session_t *s, ngx_stream_variable_value_t *v, uintptr_t data);<br class=""> static ngx_int_t ngx_stream_variable_proxy_protocol_port(<br class="">     ngx_stream_session_t *s, ngx_stream_variable_value_t *v, uintptr_t data);<br class="">+static ngx_int_t ngx_stream_variable_proxy_protocol_tlv(<br class="">+    ngx_stream_session_t *s, ngx_stream_variable_value_t *v, uintptr_t data);<br class=""> static ngx_int_t ngx_stream_variable_server_addr(ngx_stream_session_t *s,<br class="">     ngx_stream_variable_value_t *v, uintptr_t data);<br class=""> static ngx_int_t ngx_stream_variable_server_port(ngx_stream_session_t *s,<br class="">@@ -51,6 +53,10 @@ static ngx_int_t ngx_stream_variable_tim<br class=""> static ngx_int_t ngx_stream_variable_protocol(ngx_stream_session_t *s,<br class="">     ngx_stream_variable_value_t *v, uintptr_t data);<br class=""><br class="">+static ngx_int_t ngx_stream_variable_unknown_header(ngx_stream_session_t *s,<br class="">+    ngx_stream_variable_value_t *v, ngx_str_t *var, ngx_list_part_t *part,<br class="">+    size_t prefix);<br class="">+<br class=""><br class=""> static ngx_stream_variable_t  ngx_stream_core_variables[] = {<br class=""><br class="">@@ -79,6 +85,10 @@ static ngx_stream_variable_t  ngx_stream<br class="">       ngx_stream_variable_proxy_protocol_port,<br class="">       offsetof(ngx_proxy_protocol_t, dst_port), 0, 0 },<br class=""><br class="">+    { ngx_string("proxy_protocol_tlv_0x"), NULL,<br class="">+      ngx_stream_variable_proxy_protocol_tlv,<br class="">+      0, NGX_STREAM_VAR_PREFIX, 0 },<br class="">+<br class="">     { ngx_string("server_addr"), NULL,<br class="">       ngx_stream_variable_server_addr, 0, 0, 0 },<br class=""><br class="">@@ -622,6 +632,24 @@ ngx_stream_variable_proxy_protocol_port(<br class=""><br class=""><br class=""> static ngx_int_t<br class="">+ngx_stream_variable_proxy_protocol_tlv(ngx_stream_session_t *s,<br class="">+    ngx_stream_variable_value_t *v, uintptr_t data)<br class="">+{<br class="">+    ngx_proxy_protocol_t  *pp;<br class="">+<br class="">+    pp = s->connection->proxy_protocol;<br class="">+    if (pp == NULL || pp->tlv == NULL) {<br class="">+        v->not_found = 1;<br class="">+        return NGX_OK;<br class="">+    }<br class="">+<br class="">+    return ngx_stream_variable_unknown_header(s, v, (ngx_str_t *) data,<br class="">+                                          &pp->tlv->part,<br class="">+                                          sizeof("proxy_protocol_tlv_0x") - 1);<br class="">+}<br class="">+<br class="">+<br class="">+static ngx_int_t<br class=""> ngx_stream_variable_server_addr(ngx_stream_session_t *s,<br class="">     ngx_stream_variable_value_t *v, uintptr_t data)<br class=""> {<br class="">@@ -911,6 +939,116 @@ ngx_stream_variable_protocol(ngx_stream_<br class=""> }<br class=""><br class=""><br class="">+static ngx_int_t<br class="">+ngx_stream_variable_unknown_header(ngx_stream_session_t *s,<br class="">+    ngx_stream_variable_value_t *v, ngx_str_t *var,<br class="">+    ngx_list_part_t *part, size_t prefix)<br class="">+{<br class="">+    u_char           *p, ch;<br class="">+    size_t            len;<br class="">+    ngx_uint_t        i, n;<br class="">+    ngx_table_elt_t  *header, *h, **ph;<br class="">+<br class="">+    ph = &h;<br class="">+#if (NGX_SUPPRESS_WARN)<br class="">+    len = 0;<br class="">+#endif<br class="">+<br class="">+    header = part->elts;<br class="">+<br class="">+    for (i = 0; /* void */ ; i++) {<br class="">+<br class="">+        if (i >= part->nelts) {<br class="">+            if (part->next == NULL) {<br class="">+                break;<br class="">+            }<br class="">+<br class="">+            part = part->next;<br class="">+            header = part->elts;<br class="">+            i = 0;<br class="">+        }<br class="">+<br class="">+        if (header[i].hash == 0) {<br class="">+            continue;<br class="">+        }<br class="">+<br class="">+        if (header[i].key.len != var->len - prefix) {<br class="">+            continue;<br class="">+        }<br class="">+<br class="">+        for (n = 0; n < var->len - prefix; n++) {<br class="">+            ch = header[i].key.data[n];<br class="">+<br class="">+            if (ch >= 'A' && ch <= 'Z') {<br class="">+                ch |= 0x20;<br class="">+<br class="">+            } else if (ch == '-') {<br class="">+                ch = '_';<br class="">+            }<br class="">+<br class="">+            if (var->data[n + prefix] != ch) {<br class="">+                break;<br class="">+            }<br class="">+        }<br class="">+<br class="">+        if (n != var->len - prefix) {<br class="">+            continue;<br class="">+        }<br class="">+<br class="">+        len += header[i].value.len + 2;<br class="">+<br class="">+        *ph = &header[i];<br class="">+        ph = &header[i].next;<br class="">+    }<br class="">+<br class="">+    *ph = NULL;<br class="">+<br class="">+    if (h == NULL) {<br class="">+        v->not_found = 1;<br class="">+        return NGX_OK;<br class="">+    }<br class="">+<br class="">+    len -= 2;<br class="">+<br class="">+    if (h->next == NULL) {<br class="">+<br class="">+        v->len = h->value.len;<br class="">+        v->valid = 1;<br class="">+        v->no_cacheable = 0;<br class="">+        v->not_found = 0;<br class="">+        v->data = h->value.data;<br class="">+<br class="">+        return NGX_OK;<br class="">+    }<br class="">+<br class="">+    p = ngx_pnalloc(s->connection->pool, len);<br class="">+    if (p == NULL) {<br class="">+        return NGX_ERROR;<br class="">+    }<br class="">+<br class="">+    v->len = len;<br class="">+    v->valid = 1;<br class="">+    v->no_cacheable = 0;<br class="">+    v->not_found = 0;<br class="">+    v->data = p;<br class="">+<br class="">+    for ( ;; ) {<br class="">+<br class="">+        p = ngx_copy(p, h->value.data, h->value.len);<br class="">+<br class="">+        if (h->next == NULL) {<br class="">+            break;<br class="">+        }<br class="">+<br class="">+        *p++ = ','; *p++ = ' ';<br class="">+<br class="">+        h = h->next;<br class="">+    }<br class="">+<br class="">+    return NGX_OK;<br class="">+}<br class="">+<br class="">+<br class=""> void *<br class=""> ngx_stream_map_find(ngx_stream_session_t *s, ngx_stream_map_t *map,<br class="">     ngx_str_t *match)<br class=""><br class="">_______________________________________________<br class="">nginx-devel mailing list -- <a href="mailto:nginx-devel@nginx.org" class="">nginx-devel@nginx.org</a><br class="">To unsubscribe send an email to <a href="mailto:nginx-devel-leave@nginx.org" class="">nginx-devel-leave@nginx.org</a><br class=""></div></div></blockquote></div><br class=""><div class="">
<div dir="auto" style="caret-color: rgb(0, 0, 0); color: rgb(0, 0, 0); letter-spacing: normal; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration: none; word-wrap: break-word; -webkit-nbsp-mode: space; line-break: after-white-space;" class=""><div>----</div><div>Roman Arutyunyan</div><div><a href="mailto:arut@nginx.com" class="">arut@nginx.com</a></div><div class=""><br class=""></div></div><br class="Apple-interchange-newline"><br class="Apple-interchange-newline">

</div>
<br class=""></div></body></html>