diff options
Diffstat (limited to 'nginx/src/core/ngx_proxy_protocol.c')
-rw-r--r-- | nginx/src/core/ngx_proxy_protocol.c | 343 |
1 files changed, 343 insertions, 0 deletions
diff --git a/nginx/src/core/ngx_proxy_protocol.c b/nginx/src/core/ngx_proxy_protocol.c new file mode 100644 index 0000000..c3d7fd3 --- /dev/null +++ b/nginx/src/core/ngx_proxy_protocol.c @@ -0,0 +1,343 @@ + +/* + * Copyright (C) Roman Arutyunyan + * Copyright (C) Nginx, Inc. + */ + + +#include <ngx_config.h> +#include <ngx_core.h> + + +#define NGX_PROXY_PROTOCOL_AF_INET 1 +#define NGX_PROXY_PROTOCOL_AF_INET6 2 + + +#define ngx_proxy_protocol_parse_uint16(p) ((p)[0] << 8 | (p)[1]) + + +typedef struct { + u_char signature[12]; + u_char version_command; + u_char family_transport; + u_char len[2]; +} ngx_proxy_protocol_header_t; + + +typedef struct { + u_char src_addr[4]; + u_char dst_addr[4]; + u_char src_port[2]; + u_char dst_port[2]; +} ngx_proxy_protocol_inet_addrs_t; + + +typedef struct { + u_char src_addr[16]; + u_char dst_addr[16]; + u_char src_port[2]; + u_char dst_port[2]; +} ngx_proxy_protocol_inet6_addrs_t; + + +static u_char *ngx_proxy_protocol_v2_read(ngx_connection_t *c, u_char *buf, + u_char *last); + + +u_char * +ngx_proxy_protocol_read(ngx_connection_t *c, u_char *buf, u_char *last) +{ + size_t len; + u_char ch, *p, *addr, *port; + ngx_int_t n; + + static const u_char signature[] = "\r\n\r\n\0\r\nQUIT\n"; + + p = buf; + len = last - buf; + + if (len >= sizeof(ngx_proxy_protocol_header_t) + && memcmp(p, signature, sizeof(signature) - 1) == 0) + { + return ngx_proxy_protocol_v2_read(c, buf, last); + } + + if (len < 8 || ngx_strncmp(p, "PROXY ", 6) != 0) { + goto invalid; + } + + p += 6; + len -= 6; + + if (len >= 7 && ngx_strncmp(p, "UNKNOWN", 7) == 0) { + ngx_log_debug0(NGX_LOG_DEBUG_CORE, c->log, 0, + "PROXY protocol unknown protocol"); + p += 7; + goto skip; + } + + if (len < 5 || ngx_strncmp(p, "TCP", 3) != 0 + || (p[3] != '4' && p[3] != '6') || p[4] != ' ') + { + goto invalid; + } + + p += 5; + addr = p; + + for ( ;; ) { + if (p == last) { + goto invalid; + } + + ch = *p++; + + if (ch == ' ') { + break; + } + + if (ch != ':' && ch != '.' + && (ch < 'a' || ch > 'f') + && (ch < 'A' || ch > 'F') + && (ch < '0' || ch > '9')) + { + goto invalid; + } + } + + len = p - addr - 1; + c->proxy_protocol_addr.data = ngx_pnalloc(c->pool, len); + + if (c->proxy_protocol_addr.data == NULL) { + return NULL; + } + + ngx_memcpy(c->proxy_protocol_addr.data, addr, len); + c->proxy_protocol_addr.len = len; + + for ( ;; ) { + if (p == last) { + goto invalid; + } + + if (*p++ == ' ') { + break; + } + } + + port = p; + + for ( ;; ) { + if (p == last) { + goto invalid; + } + + if (*p++ == ' ') { + break; + } + } + + len = p - port - 1; + + n = ngx_atoi(port, len); + + if (n < 0 || n > 65535) { + goto invalid; + } + + c->proxy_protocol_port = (in_port_t) n; + + ngx_log_debug2(NGX_LOG_DEBUG_CORE, c->log, 0, + "PROXY protocol address: %V %d", &c->proxy_protocol_addr, + c->proxy_protocol_port); + +skip: + + for ( /* void */ ; p < last - 1; p++) { + if (p[0] == CR && p[1] == LF) { + return p + 2; + } + } + +invalid: + + ngx_log_error(NGX_LOG_ERR, c->log, 0, + "broken header: \"%*s\"", (size_t) (last - buf), buf); + + return NULL; +} + + +u_char * +ngx_proxy_protocol_write(ngx_connection_t *c, u_char *buf, u_char *last) +{ + ngx_uint_t port, lport; + + if (last - buf < NGX_PROXY_PROTOCOL_MAX_HEADER) { + return NULL; + } + + if (ngx_connection_local_sockaddr(c, NULL, 0) != NGX_OK) { + return NULL; + } + + switch (c->sockaddr->sa_family) { + + case AF_INET: + buf = ngx_cpymem(buf, "PROXY TCP4 ", sizeof("PROXY TCP4 ") - 1); + break; + +#if (NGX_HAVE_INET6) + case AF_INET6: + buf = ngx_cpymem(buf, "PROXY TCP6 ", sizeof("PROXY TCP6 ") - 1); + break; +#endif + + default: + return ngx_cpymem(buf, "PROXY UNKNOWN" CRLF, + sizeof("PROXY UNKNOWN" CRLF) - 1); + } + + buf += ngx_sock_ntop(c->sockaddr, c->socklen, buf, last - buf, 0); + + *buf++ = ' '; + + buf += ngx_sock_ntop(c->local_sockaddr, c->local_socklen, buf, last - buf, + 0); + + port = ngx_inet_get_port(c->sockaddr); + lport = ngx_inet_get_port(c->local_sockaddr); + + return ngx_slprintf(buf, last, " %ui %ui" CRLF, port, lport); +} + + +static u_char * +ngx_proxy_protocol_v2_read(ngx_connection_t *c, u_char *buf, u_char *last) +{ + u_char *end; + size_t len; + socklen_t socklen; + ngx_uint_t version, command, family, transport; + ngx_sockaddr_t sockaddr; + ngx_proxy_protocol_header_t *header; + ngx_proxy_protocol_inet_addrs_t *in; +#if (NGX_HAVE_INET6) + ngx_proxy_protocol_inet6_addrs_t *in6; +#endif + + header = (ngx_proxy_protocol_header_t *) buf; + + buf += sizeof(ngx_proxy_protocol_header_t); + + version = header->version_command >> 4; + + if (version != 2) { + ngx_log_error(NGX_LOG_ERR, c->log, 0, + "unknown PROXY protocol version: %ui", version); + return NULL; + } + + len = ngx_proxy_protocol_parse_uint16(header->len); + + if ((size_t) (last - buf) < len) { + ngx_log_error(NGX_LOG_ERR, c->log, 0, "header is too large"); + return NULL; + } + + end = buf + len; + + command = header->version_command & 0x0f; + + /* only PROXY is supported */ + if (command != 1) { + ngx_log_debug1(NGX_LOG_DEBUG_CORE, c->log, 0, + "PROXY protocol v2 unsupported command %ui", command); + return end; + } + + transport = header->family_transport & 0x0f; + + /* only STREAM is supported */ + if (transport != 1) { + ngx_log_debug1(NGX_LOG_DEBUG_CORE, c->log, 0, + "PROXY protocol v2 unsupported transport %ui", + transport); + return end; + } + + family = header->family_transport >> 4; + + switch (family) { + + case NGX_PROXY_PROTOCOL_AF_INET: + + if ((size_t) (end - buf) < sizeof(ngx_proxy_protocol_inet_addrs_t)) { + return NULL; + } + + in = (ngx_proxy_protocol_inet_addrs_t *) buf; + + sockaddr.sockaddr_in.sin_family = AF_INET; + sockaddr.sockaddr_in.sin_port = 0; + memcpy(&sockaddr.sockaddr_in.sin_addr, in->src_addr, 4); + + c->proxy_protocol_port = ngx_proxy_protocol_parse_uint16(in->src_port); + + socklen = sizeof(struct sockaddr_in); + + buf += sizeof(ngx_proxy_protocol_inet_addrs_t); + + break; + +#if (NGX_HAVE_INET6) + + case NGX_PROXY_PROTOCOL_AF_INET6: + + if ((size_t) (end - buf) < sizeof(ngx_proxy_protocol_inet6_addrs_t)) { + return NULL; + } + + in6 = (ngx_proxy_protocol_inet6_addrs_t *) buf; + + sockaddr.sockaddr_in6.sin6_family = AF_INET6; + sockaddr.sockaddr_in6.sin6_port = 0; + memcpy(&sockaddr.sockaddr_in6.sin6_addr, in6->src_addr, 16); + + c->proxy_protocol_port = ngx_proxy_protocol_parse_uint16(in6->src_port); + + socklen = sizeof(struct sockaddr_in6); + + buf += sizeof(ngx_proxy_protocol_inet6_addrs_t); + + break; + +#endif + + default: + ngx_log_debug1(NGX_LOG_DEBUG_CORE, c->log, 0, + "PROXY protocol v2 unsupported address family %ui", + family); + return end; + } + + c->proxy_protocol_addr.data = ngx_pnalloc(c->pool, NGX_SOCKADDR_STRLEN); + if (c->proxy_protocol_addr.data == NULL) { + return NULL; + } + + c->proxy_protocol_addr.len = ngx_sock_ntop(&sockaddr.sockaddr, socklen, + c->proxy_protocol_addr.data, + NGX_SOCKADDR_STRLEN, 0); + + ngx_log_debug2(NGX_LOG_DEBUG_CORE, c->log, 0, + "PROXY protocol v2 address: %V %d", &c->proxy_protocol_addr, + c->proxy_protocol_port); + + if (buf < end) { + ngx_log_debug1(NGX_LOG_DEBUG_CORE, c->log, 0, + "PROXY protocol v2 %z bytes of tlv ignored", end - buf); + } + + return end; +} |