[PATCH] Core: support for reading PROXY protocol v2 TLVs
Roman Arutyunyan
arut at nginx.com
Wed Aug 31 15:52:15 UTC 2022
# HG changeset patch
# User Roman Arutyunyan <arut at nginx.com>
# Date 1661436099 -14400
# Thu Aug 25 18:01:39 2022 +0400
# Node ID 4b856f1dff939e4eb9c131e17af061cf2c38cfac
# Parent 069a4813e8d6d7ec662d282a10f5f7062ebd817f
Core: support for reading PROXY protocol v2 TLVs.
The TLV values are available in HTTP and Stream variables
$proxy_protocol_tlv_0xN, where N is a hexadecimal TLV type number with no
leading zeroes.
diff --git a/src/core/ngx_proxy_protocol.c b/src/core/ngx_proxy_protocol.c
--- a/src/core/ngx_proxy_protocol.c
+++ b/src/core/ngx_proxy_protocol.c
@@ -40,6 +40,12 @@ typedef struct {
} ngx_proxy_protocol_inet6_addrs_t;
+typedef struct {
+ u_char type;
+ u_char len[2];
+} ngx_proxy_protocol_tlv_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,
@@ -273,8 +279,11 @@ ngx_proxy_protocol_v2_read(ngx_connectio
size_t len;
socklen_t socklen;
ngx_uint_t version, command, family, transport;
+ ngx_list_t *pp_tlv;
ngx_sockaddr_t src_sockaddr, dst_sockaddr;
+ ngx_table_elt_t *t;
ngx_proxy_protocol_t *pp;
+ ngx_proxy_protocol_tlv_t *tlv;
ngx_proxy_protocol_header_t *header;
ngx_proxy_protocol_inet_addrs_t *in;
#if (NGX_HAVE_INET6)
@@ -412,8 +421,63 @@ 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_tlv = ngx_list_create(c->pool, 1, sizeof(ngx_table_elt_t));
+ if (pp_tlv == NULL) {
+ return NULL;
+ }
+
+ while (buf < end) {
+ if ((size_t) (end - buf) < sizeof(ngx_proxy_protocol_tlv_t)) {
+ ngx_log_error(NGX_LOG_ERR, c->log, 0,
+ "broken PROXY protocol TLV");
+ return NULL;
+ }
+
+ tlv = (ngx_proxy_protocol_tlv_t *) buf;
+
+ buf += sizeof(ngx_proxy_protocol_tlv_t);
+
+ len = ngx_proxy_protocol_parse_uint16(tlv->len);
+
+ if ((size_t) (end - buf) < len) {
+ ngx_log_error(NGX_LOG_ERR, c->log, 0,
+ "broken PROXY protocol TLV");
+ return NULL;
+ }
+
+ ngx_log_debug2(NGX_LOG_DEBUG_CORE, c->log, 0,
+ "PROXY protocol v2 TLV type:0x%0xd len:%uz",
+ tlv->type, len);
+
+ t = ngx_list_push(pp_tlv);
+ if (t == NULL) {
+ return NULL;
+ }
+
+ t->hash = 1;
+ t->lowcase_key = NULL;
+
+ t->key.data = ngx_pnalloc(c->pool, 2);
+ if (t->key.data == NULL) {
+ return NULL;
+ }
+
+ t->key.len = ngx_sprintf(t->key.data, "%0xd", tlv->type)
+ - t->key.data;
+
+ t->value.data = ngx_pnalloc(c->pool, len);
+ if (t->value.data == NULL) {
+ return NULL;
+ }
+
+ ngx_memcpy(t->value.data, buf, len);
+
+ t->value.len = len;
+
+ buf += len;
+ }
+
+ pp->tlv = pp_tlv;
}
c->proxy_protocol = pp;
diff --git a/src/core/ngx_proxy_protocol.h b/src/core/ngx_proxy_protocol.h
--- a/src/core/ngx_proxy_protocol.h
+++ b/src/core/ngx_proxy_protocol.h
@@ -21,6 +21,7 @@ struct ngx_proxy_protocol_s {
ngx_str_t dst_addr;
in_port_t src_port;
in_port_t dst_port;
+ ngx_list_t *tlv;
};
diff --git a/src/http/ngx_http_variables.c b/src/http/ngx_http_variables.c
--- a/src/http/ngx_http_variables.c
+++ b/src/http/ngx_http_variables.c
@@ -61,6 +61,8 @@ static ngx_int_t ngx_http_variable_proxy
ngx_http_variable_value_t *v, uintptr_t data);
static ngx_int_t ngx_http_variable_proxy_protocol_port(ngx_http_request_t *r,
ngx_http_variable_value_t *v, uintptr_t data);
+static ngx_int_t ngx_http_variable_proxy_protocol_tlv(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data);
static ngx_int_t ngx_http_variable_server_addr(ngx_http_request_t *r,
ngx_http_variable_value_t *v, uintptr_t data);
static ngx_int_t ngx_http_variable_server_port(ngx_http_request_t *r,
@@ -214,6 +216,10 @@ static ngx_http_variable_t ngx_http_cor
ngx_http_variable_proxy_protocol_port,
offsetof(ngx_proxy_protocol_t, dst_port), 0, 0 },
+ { ngx_string("proxy_protocol_tlv_0x"), NULL,
+ ngx_http_variable_proxy_protocol_tlv,
+ 0, NGX_HTTP_VAR_PREFIX, 0 },
+
{ ngx_string("server_addr"), NULL, ngx_http_variable_server_addr, 0, 0, 0 },
{ ngx_string("server_port"), NULL, ngx_http_variable_server_port, 0, 0, 0 },
@@ -1387,6 +1393,24 @@ ngx_http_variable_proxy_protocol_port(ng
static ngx_int_t
+ngx_http_variable_proxy_protocol_tlv(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data)
+{
+ ngx_proxy_protocol_t *pp;
+
+ pp = r->connection->proxy_protocol;
+ if (pp == NULL || pp->tlv == NULL) {
+ v->not_found = 1;
+ return NGX_OK;
+ }
+
+ return ngx_http_variable_unknown_header(r, v, (ngx_str_t *) data,
+ &pp->tlv->part,
+ sizeof("proxy_protocol_tlv_0x") - 1);
+}
+
+
+static ngx_int_t
ngx_http_variable_server_addr(ngx_http_request_t *r,
ngx_http_variable_value_t *v, uintptr_t data)
{
diff --git a/src/stream/ngx_stream_variables.c b/src/stream/ngx_stream_variables.c
--- a/src/stream/ngx_stream_variables.c
+++ b/src/stream/ngx_stream_variables.c
@@ -23,6 +23,8 @@ static ngx_int_t ngx_stream_variable_pro
ngx_stream_session_t *s, ngx_stream_variable_value_t *v, uintptr_t data);
static ngx_int_t ngx_stream_variable_proxy_protocol_port(
ngx_stream_session_t *s, ngx_stream_variable_value_t *v, uintptr_t data);
+static ngx_int_t ngx_stream_variable_proxy_protocol_tlv(
+ ngx_stream_session_t *s, ngx_stream_variable_value_t *v, uintptr_t data);
static ngx_int_t ngx_stream_variable_server_addr(ngx_stream_session_t *s,
ngx_stream_variable_value_t *v, uintptr_t data);
static ngx_int_t ngx_stream_variable_server_port(ngx_stream_session_t *s,
@@ -51,6 +53,10 @@ static ngx_int_t ngx_stream_variable_tim
static ngx_int_t ngx_stream_variable_protocol(ngx_stream_session_t *s,
ngx_stream_variable_value_t *v, uintptr_t data);
+static ngx_int_t ngx_stream_variable_unknown_header(ngx_stream_session_t *s,
+ ngx_stream_variable_value_t *v, ngx_str_t *var, ngx_list_part_t *part,
+ size_t prefix);
+
static ngx_stream_variable_t ngx_stream_core_variables[] = {
@@ -79,6 +85,10 @@ static ngx_stream_variable_t ngx_stream
ngx_stream_variable_proxy_protocol_port,
offsetof(ngx_proxy_protocol_t, dst_port), 0, 0 },
+ { ngx_string("proxy_protocol_tlv_0x"), NULL,
+ ngx_stream_variable_proxy_protocol_tlv,
+ 0, NGX_STREAM_VAR_PREFIX, 0 },
+
{ ngx_string("server_addr"), NULL,
ngx_stream_variable_server_addr, 0, 0, 0 },
@@ -622,6 +632,24 @@ ngx_stream_variable_proxy_protocol_port(
static ngx_int_t
+ngx_stream_variable_proxy_protocol_tlv(ngx_stream_session_t *s,
+ ngx_stream_variable_value_t *v, uintptr_t data)
+{
+ ngx_proxy_protocol_t *pp;
+
+ pp = s->connection->proxy_protocol;
+ if (pp == NULL || pp->tlv == NULL) {
+ v->not_found = 1;
+ return NGX_OK;
+ }
+
+ return ngx_stream_variable_unknown_header(s, v, (ngx_str_t *) data,
+ &pp->tlv->part,
+ sizeof("proxy_protocol_tlv_0x") - 1);
+}
+
+
+static ngx_int_t
ngx_stream_variable_server_addr(ngx_stream_session_t *s,
ngx_stream_variable_value_t *v, uintptr_t data)
{
@@ -911,6 +939,116 @@ ngx_stream_variable_protocol(ngx_stream_
}
+static ngx_int_t
+ngx_stream_variable_unknown_header(ngx_stream_session_t *s,
+ ngx_stream_variable_value_t *v, ngx_str_t *var,
+ ngx_list_part_t *part, size_t prefix)
+{
+ u_char *p, ch;
+ size_t len;
+ ngx_uint_t i, n;
+ ngx_table_elt_t *header, *h, **ph;
+
+ ph = &h;
+#if (NGX_SUPPRESS_WARN)
+ len = 0;
+#endif
+
+ header = part->elts;
+
+ for (i = 0; /* void */ ; i++) {
+
+ if (i >= part->nelts) {
+ if (part->next == NULL) {
+ break;
+ }
+
+ part = part->next;
+ header = part->elts;
+ i = 0;
+ }
+
+ if (header[i].hash == 0) {
+ continue;
+ }
+
+ if (header[i].key.len != var->len - prefix) {
+ continue;
+ }
+
+ for (n = 0; n < var->len - prefix; n++) {
+ ch = header[i].key.data[n];
+
+ if (ch >= 'A' && ch <= 'Z') {
+ ch |= 0x20;
+
+ } else if (ch == '-') {
+ ch = '_';
+ }
+
+ if (var->data[n + prefix] != ch) {
+ break;
+ }
+ }
+
+ if (n != var->len - prefix) {
+ continue;
+ }
+
+ len += header[i].value.len + 2;
+
+ *ph = &header[i];
+ ph = &header[i].next;
+ }
+
+ *ph = NULL;
+
+ if (h == NULL) {
+ v->not_found = 1;
+ return NGX_OK;
+ }
+
+ len -= 2;
+
+ if (h->next == NULL) {
+
+ v->len = h->value.len;
+ v->valid = 1;
+ v->no_cacheable = 0;
+ v->not_found = 0;
+ v->data = h->value.data;
+
+ return NGX_OK;
+ }
+
+ p = ngx_pnalloc(s->connection->pool, len);
+ if (p == NULL) {
+ return NGX_ERROR;
+ }
+
+ v->len = len;
+ v->valid = 1;
+ v->no_cacheable = 0;
+ v->not_found = 0;
+ v->data = p;
+
+ for ( ;; ) {
+
+ p = ngx_copy(p, h->value.data, h->value.len);
+
+ if (h->next == NULL) {
+ break;
+ }
+
+ *p++ = ','; *p++ = ' ';
+
+ h = h->next;
+ }
+
+ return NGX_OK;
+}
+
+
void *
ngx_stream_map_find(ngx_stream_session_t *s, ngx_stream_map_t *map,
ngx_str_t *match)
More information about the nginx-devel
mailing list