diff options
| author | Roman Arutyunyan <arut@nginx.com> | 2025-05-20 15:33:20 +0400 |
|---|---|---|
| committer | Roman Arutyunyan <arut@nginx.com> | 2025-05-25 22:16:04 +0400 |
| commit | d76e3d301644cfc6a2d914976b6098eb98b9e5b9 (patch) | |
| tree | b3aa4ef70b07d641e9c5b6d0693bb121b389ba7d /src/http/ngx_http_parse.c | |
| parent | 6a134dfd4888fc3850d22294687cfb3940994c69 (diff) | |
| download | nginx-d76e3d301644cfc6a2d914976b6098eb98b9e5b9.tar.gz nginx-d76e3d301644cfc6a2d914976b6098eb98b9e5b9.tar.bz2 | |
HTTP CONNECT proxy.tunnel
HTTP CONNECT method is now supported in HTTP/1 connections. It's disabled
in all currently existing standard modules. A new variable $port is added
that contains the port passed by client in HTTP CONNECT. The $host
variable contains the host part.
A new module ngx_http_tunnel module is added which establishes a tunnel
to a backend. It supports the newly added HTTP CONNECT method and can be
used to set up an HTTP CONNECT proxy.
As recommended by RFC 9110, proxy target should be restricted to ensure
safe proxying:
: Proxies that support CONNECT SHOULD restrict its use to a limited set
: of known ports or a configurable list of safe request targets.
Example config:
server {
listen 8000;
resolver dns.example.com;
map $port $tun_port {
80 1;
443 1;
}
map $host $tun_host {
hostnames;
example.com 1;
*.example.org 1;
}
map $tun_port$tun_host $tun {
11 $host:$port;
}
location / {
tunnel_pass $tun;
}
}
Request:
$ curl -px 127.0.0.1:8000 http://example.com
Diffstat (limited to 'src/http/ngx_http_parse.c')
| -rw-r--r-- | src/http/ngx_http_parse.c | 121 |
1 files changed, 120 insertions, 1 deletions
diff --git a/src/http/ngx_http_parse.c b/src/http/ngx_http_parse.c index a45c04554..7d684969b 100644 --- a/src/http/ngx_http_parse.c +++ b/src/http/ngx_http_parse.c @@ -116,6 +116,13 @@ ngx_http_parse_request_line(ngx_http_request_t *r, ngx_buf_t *b) sw_host_end, sw_host_ip_literal, sw_port, + sw_spaces_before_connect_host, + sw_connect_host_start, + sw_connect_host, + sw_connect_host_end, + sw_connect_port_start, + sw_connect_port, + sw_connect_host_ip_literal, sw_after_slash_in_uri, sw_check_uri, sw_uri, @@ -158,6 +165,7 @@ ngx_http_parse_request_line(ngx_http_request_t *r, ngx_buf_t *b) if (ch == ' ') { r->method_end = p - 1; m = r->request_start; + state = sw_spaces_before_uri; switch (p - m) { @@ -247,6 +255,7 @@ ngx_http_parse_request_line(ngx_http_request_t *r, ngx_buf_t *b) if (ngx_str7_cmp(m, 'C', 'O', 'N', 'N', 'E', 'C', 'T', ' ')) { r->method = NGX_HTTP_CONNECT; + state = sw_spaces_before_connect_host; } break; @@ -269,7 +278,6 @@ ngx_http_parse_request_line(ngx_http_request_t *r, ngx_buf_t *b) break; } - state = sw_spaces_before_uri; break; } @@ -630,6 +638,117 @@ ngx_http_parse_request_line(ngx_http_request_t *r, ngx_buf_t *b) } break; + case sw_spaces_before_connect_host: + + if (ch == ' ') { + break; + } + + /* fall through */ + + case sw_connect_host_start: + + r->host_start = p; + + if (ch == '[') { + state = sw_connect_host_ip_literal; + break; + } + + state = sw_connect_host; + + /* fall through */ + + case sw_connect_host: + + c = (u_char) (ch | 0x20); + if (c >= 'a' && c <= 'z') { + break; + } + + if ((ch >= '0' && ch <= '9') || ch == '.' || ch == '-') { + break; + } + + /* fall through */ + + case sw_connect_host_end: + + switch (ch) { + case ':': + r->host_end = p; + state = sw_connect_port_start; + break; + default: + return NGX_HTTP_PARSE_INVALID_REQUEST; + } + break; + + case sw_connect_port_start: + + r->port_start = p; + state = sw_connect_port; + + /* fall through */ + + case sw_connect_port: + + if (ch >= '0' && ch <= '9') { + break; + } + + r->port_end = p; + + switch (ch) { + case ' ': + state = sw_http_09; + break; + default: + return NGX_HTTP_PARSE_INVALID_REQUEST; + } + break; + + case sw_connect_host_ip_literal: + + if (ch >= '0' && ch <= '9') { + break; + } + + c = (u_char) (ch | 0x20); + if (c >= 'a' && c <= 'z') { + break; + } + + switch (ch) { + case ':': + break; + case ']': + state = sw_connect_host_end; + break; + case '-': + case '.': + case '_': + case '~': + /* unreserved */ + break; + case '!': + case '$': + case '&': + case '\'': + case '(': + case ')': + case '*': + case '+': + case ',': + case ';': + case '=': + /* sub-delims */ + break; + default: + return NGX_HTTP_PARSE_INVALID_REQUEST; + } + break; + /* space+ after URI */ case sw_http_09: switch (ch) { |
