2014-03-17 21:41:24 +08:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Copyright (C) Roman Arutyunyan
|
|
|
|
* Copyright (C) Nginx, Inc.
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
#include <ngx_config.h>
|
|
|
|
#include <ngx_core.h>
|
|
|
|
|
|
|
|
|
2018-04-02 23:40:04 +08:00
|
|
|
#define NGX_PROXY_PROTOCOL_AF_INET 1
|
|
|
|
#define NGX_PROXY_PROTOCOL_AF_INET6 2
|
2018-03-22 20:55:28 +08:00
|
|
|
|
|
|
|
|
2018-04-02 23:40:04 +08:00
|
|
|
#define ngx_proxy_protocol_parse_uint16(p) ((p)[0] << 8 | (p)[1])
|
2018-03-22 20:55:28 +08:00
|
|
|
|
|
|
|
|
|
|
|
typedef struct {
|
2018-04-02 23:40:04 +08:00
|
|
|
u_char signature[12];
|
|
|
|
u_char version_command;
|
|
|
|
u_char family_transport;
|
|
|
|
u_char len[2];
|
|
|
|
} ngx_proxy_protocol_header_t;
|
2018-03-22 20:55:28 +08:00
|
|
|
|
|
|
|
|
|
|
|
typedef struct {
|
2018-04-02 23:40:04 +08:00
|
|
|
u_char s_addr[4];
|
|
|
|
u_char d_addr[4];
|
|
|
|
u_char s_port[2];
|
|
|
|
u_char d_port[2];
|
|
|
|
} ngx_proxy_protocol_inet_addrs_t;
|
2018-03-22 20:55:28 +08:00
|
|
|
|
|
|
|
|
|
|
|
typedef struct {
|
2018-04-02 23:40:04 +08:00
|
|
|
u_char s_addr[16];
|
|
|
|
u_char d_addr[16];
|
|
|
|
u_char s_port[2];
|
|
|
|
u_char d_port[2];
|
|
|
|
} ngx_proxy_protocol_inet6_addrs_t;
|
2018-03-22 20:55:28 +08:00
|
|
|
|
|
|
|
|
|
|
|
static u_char *ngx_proxy_protocol_v2_read(ngx_connection_t *c, u_char *buf,
|
|
|
|
u_char *last);
|
|
|
|
|
|
|
|
|
2014-03-17 21:41:24 +08:00
|
|
|
u_char *
|
2015-06-16 18:45:19 +08:00
|
|
|
ngx_proxy_protocol_read(ngx_connection_t *c, u_char *buf, u_char *last)
|
2014-03-17 21:41:24 +08:00
|
|
|
{
|
2016-05-23 23:44:21 +08:00
|
|
|
size_t len;
|
|
|
|
u_char ch, *p, *addr, *port;
|
|
|
|
ngx_int_t n;
|
2014-03-17 21:41:24 +08:00
|
|
|
|
2018-04-02 23:40:04 +08:00
|
|
|
static const u_char signature[] = "\r\n\r\n\0\r\nQUIT\n";
|
|
|
|
|
2014-03-17 21:41:24 +08:00
|
|
|
p = buf;
|
|
|
|
len = last - buf;
|
|
|
|
|
2018-04-02 23:40:04 +08:00
|
|
|
if (len >= sizeof(ngx_proxy_protocol_header_t)
|
|
|
|
&& memcmp(p, signature, sizeof(signature) - 1) == 0)
|
2018-03-22 20:55:28 +08:00
|
|
|
{
|
|
|
|
return ngx_proxy_protocol_v2_read(c, buf, last);
|
|
|
|
}
|
|
|
|
|
2014-03-17 21:41:24 +08:00
|
|
|
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;
|
|
|
|
|
2016-05-23 23:44:21 +08:00
|
|
|
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,
|
2018-03-27 23:39:38 +08:00
|
|
|
"PROXY protocol address: %V %d", &c->proxy_protocol_addr,
|
|
|
|
c->proxy_protocol_port);
|
2014-03-17 21:41:24 +08:00
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
2015-06-16 18:45:16 +08:00
|
|
|
|
|
|
|
|
|
|
|
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);
|
|
|
|
|
2016-06-20 16:50:39 +08:00
|
|
|
port = ngx_inet_get_port(c->sockaddr);
|
|
|
|
lport = ngx_inet_get_port(c->local_sockaddr);
|
|
|
|
|
2015-06-16 18:45:16 +08:00
|
|
|
return ngx_slprintf(buf, last, " %ui %ui" CRLF, port, lport);
|
|
|
|
}
|
2018-03-22 20:55:28 +08:00
|
|
|
|
|
|
|
|
|
|
|
static u_char *
|
|
|
|
ngx_proxy_protocol_v2_read(ngx_connection_t *c, u_char *buf, u_char *last)
|
|
|
|
{
|
2018-04-02 23:40:04 +08:00
|
|
|
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 *inet;
|
|
|
|
#if (NGX_HAVE_INET6)
|
|
|
|
ngx_proxy_protocol_inet6_addrs_t *inet6;
|
|
|
|
#endif
|
2018-03-22 20:55:28 +08:00
|
|
|
|
2018-04-02 23:40:04 +08:00
|
|
|
header = (ngx_proxy_protocol_header_t *) buf;
|
2018-03-22 20:55:28 +08:00
|
|
|
|
2018-04-02 23:40:04 +08:00
|
|
|
buf += sizeof(ngx_proxy_protocol_header_t);
|
2018-03-22 20:55:28 +08:00
|
|
|
|
2018-04-02 23:40:04 +08:00
|
|
|
version = header->version_command >> 4;
|
2018-03-22 20:55:28 +08:00
|
|
|
|
2018-04-02 23:40:04 +08:00
|
|
|
if (version != 2) {
|
2018-03-22 20:55:28 +08:00
|
|
|
ngx_log_error(NGX_LOG_ERR, c->log, 0,
|
2018-04-02 23:40:04 +08:00
|
|
|
"unknown PROXY protocol version: %ui", version);
|
2018-03-22 20:55:28 +08:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2018-04-02 23:40:04 +08:00
|
|
|
len = ngx_proxy_protocol_parse_uint16(header->len);
|
2018-03-22 20:55:28 +08:00
|
|
|
|
|
|
|
if ((size_t) (last - buf) < len) {
|
|
|
|
ngx_log_error(NGX_LOG_ERR, c->log, 0, "header is too large");
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
end = buf + len;
|
|
|
|
|
2018-04-02 23:40:04 +08:00
|
|
|
command = header->version_command & 0x0f;
|
2018-03-22 20:55:28 +08:00
|
|
|
|
2018-04-02 23:40:04 +08:00
|
|
|
/* only PROXY is supported */
|
|
|
|
if (command != 1) {
|
2018-03-22 20:55:28 +08:00
|
|
|
ngx_log_debug1(NGX_LOG_DEBUG_CORE, c->log, 0,
|
2018-04-02 23:40:04 +08:00
|
|
|
"PROXY protocol v2 unsupported command %ui", command);
|
2018-03-22 20:55:28 +08:00
|
|
|
return end;
|
|
|
|
}
|
|
|
|
|
2018-04-02 23:40:04 +08:00
|
|
|
transport = header->family_transport & 0x0f;
|
2018-03-22 20:55:28 +08:00
|
|
|
|
2018-04-02 23:40:04 +08:00
|
|
|
/* only STREAM is supported */
|
|
|
|
if (transport != 1) {
|
2018-03-22 20:55:28 +08:00
|
|
|
ngx_log_debug1(NGX_LOG_DEBUG_CORE, c->log, 0,
|
2018-04-02 23:40:04 +08:00
|
|
|
"PROXY protocol v2 unsupported transport %ui",
|
2018-03-22 20:55:28 +08:00
|
|
|
transport);
|
|
|
|
return end;
|
|
|
|
}
|
|
|
|
|
2018-04-02 23:40:04 +08:00
|
|
|
family = header->family_transport >> 4;
|
2018-03-22 20:55:28 +08:00
|
|
|
|
|
|
|
switch (family) {
|
|
|
|
|
2018-04-02 23:40:04 +08:00
|
|
|
case NGX_PROXY_PROTOCOL_AF_INET:
|
2018-03-22 20:55:28 +08:00
|
|
|
|
2018-04-02 23:40:04 +08:00
|
|
|
if ((size_t) (end - buf) < sizeof(ngx_proxy_protocol_inet_addrs_t)) {
|
2018-03-22 20:55:28 +08:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2018-04-02 23:40:04 +08:00
|
|
|
inet = (ngx_proxy_protocol_inet_addrs_t *) buf;
|
|
|
|
|
2018-03-22 20:55:28 +08:00
|
|
|
sockaddr.sockaddr_in.sin_family = AF_INET;
|
|
|
|
sockaddr.sockaddr_in.sin_port = 0;
|
2018-04-02 23:40:04 +08:00
|
|
|
memcpy(&sockaddr.sockaddr_in.sin_addr, inet->s_addr, 4);
|
2018-03-22 20:55:28 +08:00
|
|
|
|
2018-04-02 23:40:04 +08:00
|
|
|
c->proxy_protocol_port = ngx_proxy_protocol_parse_uint16(inet->s_port);
|
2018-03-22 20:55:28 +08:00
|
|
|
|
|
|
|
socklen = sizeof(struct sockaddr_in);
|
|
|
|
|
2018-04-02 23:40:04 +08:00
|
|
|
buf += sizeof(ngx_proxy_protocol_inet_addrs_t);
|
2018-03-22 20:55:28 +08:00
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
#if (NGX_HAVE_INET6)
|
|
|
|
|
2018-04-02 23:40:04 +08:00
|
|
|
case NGX_PROXY_PROTOCOL_AF_INET6:
|
2018-03-22 20:55:28 +08:00
|
|
|
|
2018-04-02 23:40:04 +08:00
|
|
|
if ((size_t) (end - buf) < sizeof(ngx_proxy_protocol_inet6_addrs_t)) {
|
2018-03-22 20:55:28 +08:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2018-04-02 23:40:04 +08:00
|
|
|
inet6 = (ngx_proxy_protocol_inet6_addrs_t *) buf;
|
|
|
|
|
2018-03-22 20:55:28 +08:00
|
|
|
sockaddr.sockaddr_in6.sin6_family = AF_INET6;
|
|
|
|
sockaddr.sockaddr_in6.sin6_port = 0;
|
2018-04-02 23:40:04 +08:00
|
|
|
memcpy(&sockaddr.sockaddr_in6.sin6_addr, inet6->s_addr, 16);
|
2018-03-22 20:55:28 +08:00
|
|
|
|
2018-04-02 23:40:04 +08:00
|
|
|
c->proxy_protocol_port = ngx_proxy_protocol_parse_uint16(inet6->s_port);
|
2018-03-22 20:55:28 +08:00
|
|
|
|
|
|
|
socklen = sizeof(struct sockaddr_in6);
|
|
|
|
|
2018-04-02 23:40:04 +08:00
|
|
|
buf += sizeof(ngx_proxy_protocol_inet6_addrs_t);
|
2018-03-22 20:55:28 +08:00
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
default:
|
|
|
|
ngx_log_debug1(NGX_LOG_DEBUG_CORE, c->log, 0,
|
2018-04-02 23:40:04 +08:00
|
|
|
"PROXY protocol v2 unsupported address family %ui",
|
2018-03-27 23:39:38 +08:00
|
|
|
family);
|
2018-03-22 20:55:28 +08:00
|
|
|
return end;
|
|
|
|
}
|
|
|
|
|
2018-04-02 23:40:04 +08:00
|
|
|
c->proxy_protocol_addr.data = ngx_pnalloc(c->pool, NGX_SOCKADDR_STRLEN);
|
|
|
|
if (c->proxy_protocol_addr.data == NULL) {
|
2018-03-22 20:55:28 +08:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2018-04-02 23:40:04 +08:00
|
|
|
c->proxy_protocol_addr.len = ngx_sock_ntop(&sockaddr.sockaddr, socklen,
|
|
|
|
c->proxy_protocol_addr.data,
|
|
|
|
NGX_SOCKADDR_STRLEN, 0);
|
2018-03-22 20:55:28 +08:00
|
|
|
|
|
|
|
ngx_log_debug2(NGX_LOG_DEBUG_CORE, c->log, 0,
|
2018-04-02 23:40:04 +08:00
|
|
|
"PROXY protocol v2 address: %V %d", &c->proxy_protocol_addr,
|
2018-03-27 23:39:38 +08:00
|
|
|
c->proxy_protocol_port);
|
2018-03-22 20:55:28 +08:00
|
|
|
|
|
|
|
if (buf < end) {
|
|
|
|
ngx_log_debug1(NGX_LOG_DEBUG_CORE, c->log, 0,
|
2018-04-02 23:40:04 +08:00
|
|
|
"PROXY protocol v2 %z bytes of tlv ignored", end - buf);
|
2018-03-22 20:55:28 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
return end;
|
|
|
|
}
|