# HG changeset patch # User Vladimir Homutov # Date 1521723328 -10800 # Node ID 416953ef04286401354000f624ad5561ca1ade57 # Parent ec4d95eed062d0222786a4d4342935530ac91282 Core: added processing of version 2 of the PROXY protocol. The protocol used on inbound connection is auto-detected and corresponding parser is used to extract passed addresses. TLV parameters are ignored. The maximum supported size of PROXY protocol header is 107 bytes (similar to version 1). 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 @@ -9,6 +9,57 @@ #include +#define NGX_PP_V2_SIGLEN 12 +#define NGX_PP_V2_CMD_PROXY 1 +#define NGX_PP_V2_STREAM 1 + +#define NGX_PP_V2_AF_UNSPEC 0 +#define NGX_PP_V2_AF_INET 1 +#define NGX_PP_V2_AF_INET6 2 + + +#define ngx_pp_v2_get_u16(p) \ + ( ((uint16_t) ((u_char *) (p))[0] << 8) \ + + ( ((u_char *) (p))[1]) ) + + +typedef struct { + u_char signature[NGX_PP_V2_SIGLEN]; + u_char ver_cmd; + u_char fam_transp; + u_char len[2]; +} ngx_pp_v2_header_t; + + +typedef struct { + u_char src[4]; + u_char dst[4]; + u_char sport[2]; + u_char dport[2]; +} ngx_pp_v2_inet_addrs_t; + + +typedef struct { + u_char src[16]; + u_char dst[16]; + u_char sport[2]; + u_char dport[2]; +} ngx_pp_v2_inet6_addrs_t; + + +typedef union { + ngx_pp_v2_inet_addrs_t inet; + ngx_pp_v2_inet6_addrs_t inet6; +} ngx_pp_v2_addrs_t; + + +static u_char *ngx_proxy_protocol_v2_read(ngx_connection_t *c, u_char *buf, + u_char *last); + +static const u_char ngx_pp_v2_signature[NGX_PP_V2_SIGLEN] = + { 0x0D, 0x0A, 0x0D, 0x0A, 0x00, 0x0D, 0x0A, 0x51, 0x55, 0x49, 0x54, 0x0A }; + + u_char * ngx_proxy_protocol_read(ngx_connection_t *c, u_char *buf, u_char *last) { @@ -19,6 +70,12 @@ ngx_proxy_protocol_read(ngx_connection_t p = buf; len = last - buf; + if (len >= sizeof(ngx_pp_v2_header_t) + && memcmp(p, ngx_pp_v2_signature, NGX_PP_V2_SIGLEN) == 0) + { + return ngx_proxy_protocol_v2_read(c, buf, last); + } + if (len < 8 || ngx_strncmp(p, "PROXY ", 6) != 0) { goto invalid; } @@ -166,3 +223,138 @@ ngx_proxy_protocol_write(ngx_connection_ 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_str_t *name; + ngx_uint_t ver, cmd, family, transport; + ngx_sockaddr_t sockaddr; + ngx_pp_v2_addrs_t *addrs; + ngx_pp_v2_header_t *hdr; + + hdr = (ngx_pp_v2_header_t *) buf; + + buf += sizeof(ngx_pp_v2_header_t); + + ver = hdr->ver_cmd >> 4; + + if (ver != 2) { + ngx_log_error(NGX_LOG_ERR, c->log, 0, + "unsupported PROXY protocol version: %ui", ver); + return NULL; + } + + len = ngx_pp_v2_get_u16(hdr->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; + + cmd = hdr->ver_cmd & 0x0F; + + if (cmd != NGX_PP_V2_CMD_PROXY) { + ngx_log_debug1(NGX_LOG_DEBUG_CORE, c->log, 0, + "PROXY protocol v2 unsupported cmd 0x%xi", cmd); + return end; + } + + transport = hdr->fam_transp & 0x0F; + + if (transport != NGX_PP_V2_STREAM) { + ngx_log_debug1(NGX_LOG_DEBUG_CORE, c->log, 0, + "PROXY protocol v2 unsupported transport 0x%xi", + transport); + return end; + } + + family = hdr->fam_transp >> 4; + + addrs = (ngx_pp_v2_addrs_t *) buf; + + switch (family) { + + case NGX_PP_V2_AF_UNSPEC: + ngx_log_debug0(NGX_LOG_DEBUG_CORE, c->log, 0, + "PROXY protocol v2 AF_UNSPEC ignored"); + return end; + + case NGX_PP_V2_AF_INET: + + if ((size_t) (end - buf) < sizeof(ngx_pp_v2_inet_addrs_t)) { + return NULL; + } + + sockaddr.sockaddr_in.sin_family = AF_INET; + sockaddr.sockaddr_in.sin_port = 0; + memcpy(&sockaddr.sockaddr_in.sin_addr, addrs->inet.src, 4); + + c->proxy_protocol_port = ngx_pp_v2_get_u16(addrs->inet.sport); + + socklen = sizeof(struct sockaddr_in); + + buf += sizeof(ngx_pp_v2_inet_addrs_t); + + break; + +#if (NGX_HAVE_INET6) + + case NGX_PP_V2_AF_INET6: + + if ((size_t) (end - buf) < sizeof(ngx_pp_v2_inet6_addrs_t)) { + return NULL; + } + + sockaddr.sockaddr_in6.sin6_family = AF_INET6; + sockaddr.sockaddr_in6.sin6_port = 0; + memcpy(&sockaddr.sockaddr_in6.sin6_addr, addrs->inet6.src, 16); + + c->proxy_protocol_port = ngx_pp_v2_get_u16(addrs->inet6.sport); + + socklen = sizeof(struct sockaddr_in6); + + buf += sizeof(ngx_pp_v2_inet6_addrs_t); + + break; + +#endif + + default: + + ngx_log_debug1(NGX_LOG_DEBUG_CORE, c->log, 0, + "PROXY_protocol v2 unsupported address family " + "0x%xi", family); + return end; + } + + name = &c->proxy_protocol_addr; + + name->data = ngx_pnalloc(c->pool, NGX_SOCKADDR_STRLEN); + if (name->data == NULL) { + return NULL; + } + + name->len = ngx_sock_ntop(&sockaddr.sockaddr, socklen, name->data, + NGX_SOCKADDR_STRLEN, 0); + if (name->len == 0) { + return NULL; + } + + ngx_log_debug2(NGX_LOG_DEBUG_CORE, c->log, 0, + "PROXY protocol v2 address: %V %i", name, + (ngx_int_t) c->proxy_protocol_port); + + if (buf < end) { + ngx_log_debug1(NGX_LOG_DEBUG_CORE, c->log, 0, + "PROXY protocol v2 %z bytes tlv ignored", end - buf); + } + + return end; +}