From c8c7beb96f61e2251abbc345357116131cf91c22 Mon Sep 17 00:00:00 2001 From: Roman Arutyunyan Date: Mon, 29 Sep 2025 20:47:27 +0400 Subject: [PATCH] Added $request_port and $is_request_port variables. The $request_port variable contains the port passed by the client in the request line (for HTTP/1.x) or ":authority" pseudo-header (for HTTP/2 and HTTP/3). If the request line contains no host, or ":authority" is missing, then $request_port is taken from the "Host" header, similar to the $host variable. The $is_request_port variable contains ":" if $request_port is non-empty, and is empty otherwise. --- src/http/ngx_http_parse.c | 5 +++ src/http/ngx_http_request.c | 16 +++++++-- src/http/ngx_http_request.h | 2 ++ src/http/ngx_http_variables.c | 55 +++++++++++++++++++++++++++++++ src/http/v2/ngx_http_v2.c | 13 +++++++- src/http/v3/ngx_http_v3_request.c | 10 ++++++ 6 files changed, 98 insertions(+), 3 deletions(-) diff --git a/src/http/ngx_http_parse.c b/src/http/ngx_http_parse.c index a45c04554..68f604e10 100644 --- a/src/http/ngx_http_parse.c +++ b/src/http/ngx_http_parse.c @@ -446,6 +446,11 @@ ngx_http_parse_request_line(ngx_http_request_t *r, ngx_buf_t *b) case sw_port: if (ch >= '0' && ch <= '9') { + if (r->port >= 6553 && (r->port > 6553 || (ch - '0') > 5)) { + return NGX_HTTP_PARSE_INVALID_REQUEST; + } + + r->port = r->port * 10 + (ch - '0'); break; } diff --git a/src/http/ngx_http_request.c b/src/http/ngx_http_request.c index 16d79c490..7f2d04783 100644 --- a/src/http/ngx_http_request.c +++ b/src/http/ngx_http_request.c @@ -1846,8 +1846,9 @@ static ngx_int_t ngx_http_process_host(ngx_http_request_t *r, ngx_table_elt_t *h, ngx_uint_t offset) { - ngx_int_t rc; - ngx_str_t host; + u_char *p; + ngx_int_t rc; + ngx_str_t host; if (r->headers_in.host) { ngx_log_error(NGX_LOG_INFO, r->connection->log, 0, @@ -1888,6 +1889,17 @@ ngx_http_process_host(ngx_http_request_t *r, ngx_table_elt_t *h, r->headers_in.server = host; + p = ngx_strlchr(h->value.data + host.len, + h->value.data + h->value.len, ':'); + + if (p) { + rc = ngx_atoi(p + 1, h->value.data + h->value.len - p - 1); + + if (rc > 0 && rc < 65536) { + r->port = rc; + } + } + return NGX_OK; } diff --git a/src/http/ngx_http_request.h b/src/http/ngx_http_request.h index ad11f147f..1b012f810 100644 --- a/src/http/ngx_http_request.h +++ b/src/http/ngx_http_request.h @@ -461,6 +461,8 @@ struct ngx_http_request_s { ngx_http_cleanup_t *cleanup; + in_port_t port; + unsigned count:16; unsigned subrequests:8; unsigned blocked:8; diff --git a/src/http/ngx_http_variables.c b/src/http/ngx_http_variables.c index 4f0bd0e4b..dd69bcfcd 100644 --- a/src/http/ngx_http_variables.c +++ b/src/http/ngx_http_variables.c @@ -71,6 +71,10 @@ static ngx_int_t ngx_http_variable_scheme(ngx_http_request_t *r, ngx_http_variable_value_t *v, uintptr_t data); static ngx_int_t ngx_http_variable_https(ngx_http_request_t *r, ngx_http_variable_value_t *v, uintptr_t data); +static ngx_int_t ngx_http_variable_request_port(ngx_http_request_t *r, + ngx_http_variable_value_t *v, uintptr_t data); +static ngx_int_t ngx_http_variable_is_request_port(ngx_http_request_t *r, + ngx_http_variable_value_t *v, uintptr_t data); static void ngx_http_variable_set_args(ngx_http_request_t *r, ngx_http_variable_value_t *v, uintptr_t data); static ngx_int_t ngx_http_variable_is_args(ngx_http_request_t *r, @@ -231,6 +235,12 @@ static ngx_http_variable_t ngx_http_core_variables[] = { { ngx_string("https"), NULL, ngx_http_variable_https, 0, 0, 0 }, + { ngx_string("request_port"), NULL, + ngx_http_variable_request_port, 0, 0, 0 }, + + { ngx_string("is_request_port"), NULL, + ngx_http_variable_is_request_port, 0, 0, 0 }, + { ngx_string("request_uri"), NULL, ngx_http_variable_request, offsetof(ngx_http_request_t, unparsed_uri), 0, 0 }, @@ -1540,6 +1550,51 @@ ngx_http_variable_https(ngx_http_request_t *r, } +static ngx_int_t +ngx_http_variable_request_port(ngx_http_request_t *r, + ngx_http_variable_value_t *v, uintptr_t data) +{ + ngx_uint_t port; + + v->len = 0; + v->valid = 1; + v->no_cacheable = 0; + v->not_found = 0; + + v->data = ngx_pnalloc(r->pool, sizeof("65535") - 1); + if (v->data == NULL) { + return NGX_ERROR; + } + + port = r->port; + + if (port > 0 && port < 65536) { + v->len = ngx_sprintf(v->data, "%ui", port) - v->data; + } + + return NGX_OK; +} + + +static ngx_int_t +ngx_http_variable_is_request_port(ngx_http_request_t *r, + ngx_http_variable_value_t *v, uintptr_t data) +{ + if (r->port == 0) { + *v = ngx_http_variable_null_value; + return NGX_OK; + } + + v->len = 1; + v->valid = 1; + v->no_cacheable = 0; + v->not_found = 0; + v->data = (u_char *) ":"; + + return NGX_OK; +} + + static void ngx_http_variable_set_args(ngx_http_request_t *r, ngx_http_variable_value_t *v, uintptr_t data) diff --git a/src/http/v2/ngx_http_v2.c b/src/http/v2/ngx_http_v2.c index 13856583f..fe9ee5a88 100644 --- a/src/http/v2/ngx_http_v2.c +++ b/src/http/v2/ngx_http_v2.c @@ -3518,7 +3518,8 @@ ngx_http_v2_parse_scheme(ngx_http_request_t *r, ngx_str_t *value) static ngx_int_t ngx_http_v2_parse_authority(ngx_http_request_t *r, ngx_str_t *value) { - ngx_int_t rc; + u_char *p; + ngx_int_t rc; if (r->host_start) { ngx_log_error(NGX_LOG_INFO, r->connection->log, 0, @@ -3552,6 +3553,16 @@ ngx_http_v2_parse_authority(ngx_http_request_t *r, ngx_str_t *value) r->headers_in.server = *value; + p = ngx_strlchr(r->host_start + value->len, r->host_end, ':'); + + if (p) { + rc = ngx_atoi(p + 1, r->host_end - p - 1); + + if (rc > 0 && rc < 65536) { + r->port = rc; + } + } + return NGX_OK; } diff --git a/src/http/v3/ngx_http_v3_request.c b/src/http/v3/ngx_http_v3_request.c index 844a4000a..77c55bfee 100644 --- a/src/http/v3/ngx_http_v3_request.c +++ b/src/http/v3/ngx_http_v3_request.c @@ -979,6 +979,16 @@ ngx_http_v3_init_pseudo_headers(ngx_http_request_t *r) } r->headers_in.server = host; + + p = ngx_strlchr(r->host_start + host.len, r->host_end, ':'); + + if (p) { + rc = ngx_atoi(p + 1, r->host_end - p - 1); + + if (rc > 0 && rc < 65536) { + r->port = rc; + } + } } if (ngx_list_init(&r->headers_in.headers, r->pool, 20,