Index: nginx-1.4.7/auto/modules =================================================================== --- nginx-1.4.7.orig/auto/modules +++ nginx-1.4.7/auto/modules @@ -297,6 +297,10 @@ if [ $HTTP_SSL = YES ]; then HTTP_SRCS="$HTTP_SRCS $HTTP_SSL_SRCS" fi +if [ $PROXY_PROTOCOL = YES ]; then + have=NGX_PROXY_PROTOCOL . auto/have +fi + if [ $HTTP_PROXY = YES ]; then have=NGX_HTTP_X_FORWARDED_FOR . auto/have #USE_MD5=YES Index: nginx-1.4.7/auto/options =================================================================== --- nginx-1.4.7.orig/auto/options +++ nginx-1.4.7/auto/options @@ -47,6 +47,8 @@ USE_THREADS=NO NGX_FILE_AIO=NO NGX_IPV6=NO +PROXY_PROTOCOL=NO + HTTP=YES NGX_HTTP_LOG_PATH= @@ -192,6 +194,8 @@ do --with-file-aio) NGX_FILE_AIO=YES ;; --with-ipv6) NGX_IPV6=YES ;; + --with-proxy-protocol) PROXY_PROTOCOL=YES ;; + --without-http) HTTP=NO ;; --without-http-cache) HTTP_CACHE=NO ;; @@ -350,6 +354,8 @@ cat << END --with-file-aio enable file AIO support --with-ipv6 enable IPv6 support + --with-proxy-protocol enable proxy protocol support + --with-http_ssl_module enable ngx_http_ssl_module --with-http_spdy_module enable ngx_http_spdy_module --with-http_realip_module enable ngx_http_realip_module Index: nginx-1.4.7/auto/sources =================================================================== --- nginx-1.4.7.orig/auto/sources +++ nginx-1.4.7/auto/sources @@ -36,7 +36,8 @@ CORE_DEPS="src/core/nginx.h \ src/core/ngx_conf_file.h \ src/core/ngx_resolver.h \ src/core/ngx_open_file_cache.h \ - src/core/ngx_crypt.h" + src/core/ngx_crypt.h \ + src/core/ngx_proxy_protocol.h" CORE_SRCS="src/core/nginx.c \ @@ -67,7 +68,8 @@ CORE_SRCS="src/core/nginx.c \ src/core/ngx_conf_file.c \ src/core/ngx_resolver.c \ src/core/ngx_open_file_cache.c \ - src/core/ngx_crypt.c" + src/core/ngx_crypt.c \ + src/core/ngx_proxy_protocol.c" REGEX_MODULE=ngx_regex_module Index: nginx-1.4.7/src/core/ngx_connection.h =================================================================== --- nginx-1.4.7.orig/src/core/ngx_connection.h +++ nginx-1.4.7/src/core/ngx_connection.h @@ -63,6 +63,10 @@ struct ngx_listening_s { unsigned shared:1; /* shared between threads or processes */ unsigned addr_ntop:1; +#if (NGX_PROXY_PROTOCOL) + unsigned accept_proxy_protocol:2; /* proxy_protocol flag */ +#endif + #if (NGX_HAVE_INET6 && defined IPV6_V6ONLY) unsigned ipv6only:1; #endif @@ -148,6 +152,10 @@ struct ngx_connection_s { ngx_uint_t requests; +#if (NGX_PROXY_PROTOCOL) + ngx_uint_t proxy_protocol; +#endif + unsigned buffered:8; unsigned log_error:3; /* ngx_connection_log_error_e */ Index: nginx-1.4.7/src/core/ngx_core.h =================================================================== --- nginx-1.4.7.orig/src/core/ngx_core.h +++ nginx-1.4.7/src/core/ngx_core.h @@ -77,6 +77,9 @@ typedef void (*ngx_connection_handler_pt #include #include #include +#if (NGX_PROXY_PROTOCOL) +#include +#endif #define LF (u_char) 10 Index: nginx-1.4.7/src/core/ngx_proxy_protocol.c =================================================================== --- /dev/null +++ nginx-1.4.7/src/core/ngx_proxy_protocol.c @@ -0,0 +1,430 @@ + +/* + * Copyright (C) Baptiste Assmann + * Copyright (C) Exceliance + */ + + +#include +#include +#include + +#if (NGX_PROXY_PROTOCOL) + +int +ngx_recv_proxy_protocol(ngx_connection_t *c, u_char *buf, ssize_t n) +{ + u_char *end, *p, *t; + size_t len; + ssize_t s; + int step = 0; + ngx_proxy_protocol_t pp; + + ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0, "processing proxy protocol"); + + /* 16 is the minimal length of the proxy protocol string */ + if (n < 18) { + step = 1; + goto fail; + } + + s = n; + end = memchr(buf, '\n', n); + if (end == NULL) { + step = 2; + goto fail; + } + + p = buf; + if (memcmp(p, "PROXY ", 6) != 0) { + ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0, + "incorrect proxy protocol header string"); + step = 3; + goto fail; + } + p += 6; + s -= 6; + if (s <= 0) { + step = 4; + goto fail; + } + + ngx_memzero(&pp, sizeof(ngx_proxy_protocol_t)); + + if (memcmp(p, "TCP4 ", 5) == 0) { + struct sockaddr_in *sin_src; + struct sockaddr_in *sin_dst; + + pp.pp_proto = NGX_PP_PROTO_TCP4; + pp.pp_src3.ss_family = AF_INET; + pp.pp_dst3.ss_family = AF_INET; + sin_src = (struct sockaddr_in *) &pp.pp_src3; + sin_dst = (struct sockaddr_in *) &pp.pp_dst3; + + p += 5; + s -= 5; + if (s <= 0) { + step = 5; + goto fail; + } + + /* l3 source address */ + if ( (t = (u_char *)memchr(p, ' ', s)) == NULL ) { + step = 6; + goto fail; + } + len = t - p; + if ((sin_src->sin_addr.s_addr = ngx_inet_addr(p, len)) == INADDR_NONE) { + step = 7; + goto fail; + } + pp.pp_src3_text.data = ngx_pcalloc(c->pool, len + 1); + ngx_memcpy(pp.pp_src3_text.data, p, len); + pp.pp_src3_text.len = len; + + p += (len + 1); + s -= (len + 1); + if (s <= 0) { + step = 8; + goto fail; + } + + /* l3 destination address */ + if ( (t = (u_char *)memchr(p, ' ', s)) == NULL ) { + step = 9; + goto fail; + } + len = t - p; + if ((sin_dst->sin_addr.s_addr = ngx_inet_addr(p, len)) == INADDR_NONE) { + step = 10; + goto fail; + } +// FIXME pointer shift ??? + pp.pp_dst3_text.data = ngx_pcalloc(c->pool, len + 1); + ngx_memcpy(pp.pp_dst3_text.data, p, len); + pp.pp_dst3_text.len = len; + + p += (len + 1); + s -= (len + 1); + if (s <= 0) { + step = 11; + goto fail; + } + + /* l4 source port */ + if ( (t = (u_char *)memchr(p, ' ', s)) == NULL ) { + step = 12; + goto fail; + } + len = t - p; + pp.pp_src4 = ngx_atoi(p, len); + if ((pp.pp_src4 < 1024) + || (pp.pp_src4 > 65535)) { + step = 13; + goto fail; + } + sin_src->sin_port = htons(pp.pp_src4); + + p += (len + 1); + s -= (len + 1); + if (s <= 0) { + step = 14; + goto fail; + } + + /* l4 destination port */ + if ( (t = (u_char *)memchr(p, '\r', s)) == NULL ) { + step = 15; + goto fail; + } + len = t - p; + pp.pp_dst4 = ngx_atoi(p, len); + if (pp.pp_dst4 > 65535) { + step = 16; + goto fail; + } + sin_dst->sin_port = htons(pp.pp_dst4); + + if (p[len + 1] != '\n') { + step = 17; + goto fail; + } + + p += (len + 2); + s -= (len + 2); + + + /* if we managed to get there, then we can safely replace the + * information in the connection structure + */ + + /* updating connection with source information provided by proxy protocol */ + if (pp.pp_src3_text.len > c->addr_text.len) { + ngx_pfree(c->pool, c->addr_text.data); + c->addr_text.data = ngx_pcalloc(c->pool, pp.pp_src3_text.len); + } else { + ngx_memzero(c->addr_text.data, c->addr_text.len); + } + ngx_memcpy(c->addr_text.data, pp.pp_src3_text.data, pp.pp_src3_text.len); + c->addr_text.len = pp.pp_src3_text.len; + + ngx_pfree(c->pool, c->sockaddr); + c->socklen = NGX_SOCKADDRLEN; + c->sockaddr = ngx_pcalloc(c->pool, c->socklen); + ngx_memcpy(c->sockaddr, sin_src, c->socklen); + + if (c->sockaddr->sa_family != AF_INET) { + ngx_pfree(c->pool, c->sockaddr); + c->socklen = NGX_SOCKADDRLEN; + c->sockaddr = ngx_pcalloc(c->pool, c->socklen); + } else { + ngx_memzero(c->sockaddr, sizeof(struct sockaddr_in)); + c->socklen = NGX_SOCKADDRLEN; + } + ngx_memcpy(c->sockaddr, sin_src, c->socklen); + + /* updating connection with destination information provided by proxy protocol */ + ngx_pfree(c->pool, c->local_sockaddr); + c->local_sockaddr = ngx_pcalloc(c->pool, NGX_SOCKADDRLEN); + ngx_memcpy(c->local_sockaddr, sin_dst, NGX_SOCKADDRLEN); + + } + +#if (NGX_HAVE_INET6) + + else if (memcmp(p, "TCP6 ", 5) == 0) { + + struct sockaddr_in6 *sin6_src; + struct sockaddr_in6 *sin6_dst; + + pp.pp_proto = NGX_PP_PROTO_TCP6; + pp.pp_src3.ss_family = AF_INET6; + pp.pp_dst3.ss_family = AF_INET6; + sin6_src = (struct sockaddr_in6 *) &pp.pp_src3; + sin6_dst = (struct sockaddr_in6 *) &pp.pp_dst3; + + p += 5; + s -= 5; + if (s <= 0) { + step = 18; + goto fail; + } + + /* l3 source address */ + if ( (t = (u_char *)memchr(p, ' ', s)) == NULL ) { + step = 19; + goto fail; + } + len = t - p; + if (ngx_inet6_addr(p, len, sin6_src->sin6_addr.s6_addr) != NGX_OK) { + step = 20; + goto fail; + } + pp.pp_src3_text.data = ngx_pcalloc(c->pool, len + 1); + ngx_memcpy(pp.pp_src3_text.data, p, len); + pp.pp_src3_text.len = len; + + p += (len + 1); + s -= (len + 1); + if (s <= 0) { + step = 21; + goto fail; + } + + /* l3 destination address */ + if ( (t = (u_char *)memchr(p, ' ', s)) == NULL ) { + step = 22; + goto fail; + } + len = t - p; + if (ngx_inet6_addr(p, len, sin6_dst->sin6_addr.s6_addr) != NGX_OK) { + step = 23; + goto fail; + } + pp.pp_dst3_text.data = ngx_pcalloc(c->pool, len + 1); + ngx_memcpy(pp.pp_dst3_text.data, p, len); + pp.pp_dst3_text.len = len; + + p += (len + 1); + s -= (len + 1); + if (s <= 0) { + step = 24; + goto fail; + } + + /* l4 source port */ + if ( (t = (u_char *)memchr(p, ' ', s)) == NULL ) { + step = 25; + goto fail; + } + len = t - p; + pp.pp_src4 = ngx_atoi(p, len); + if ((pp.pp_src4 < 1024) + || (pp.pp_src4 > 65535)) { + step = 26; + goto fail; + } + sin6_src->sin6_port = htons(pp.pp_src4); + + p += (len + 1); + s -= (len + 1); + if (s <= 0) { + step = 27; + goto fail; + } + + /* l4 destination port */ + if ( (t = (u_char *)memchr(p, '\r', s)) == NULL ) { + step = 28; + goto fail; + } + len = t - p; + pp.pp_dst4 = ngx_atoi(p, len); + if (pp.pp_dst4 > 65535) { + step = 29; + goto fail; + } + sin6_dst->sin6_port = htons(pp.pp_dst4); + + if (p[len + 1] != '\n') { + step = 30; + goto fail; + } + + p += (len + 2); + s -= (len + 2); + + /* if we managed to get there, then we can safely replace the + * information in the connection structure + */ + + /* updating connection with source provided by proxy protocol */ + if (pp.pp_src3_text.len > c->addr_text.len) { + ngx_pfree(c->pool, c->addr_text.data); + c->addr_text.data = ngx_pcalloc(c->pool, pp.pp_src3_text.len); + } else { + ngx_memzero(c->addr_text.data, c->addr_text.len); + } + ngx_memcpy(c->addr_text.data, pp.pp_src3_text.data, pp.pp_src3_text.len); + c->addr_text.len = pp.pp_src3_text.len; + + ngx_pfree(c->pool, c->sockaddr); + c->socklen = NGX_SOCKADDRLEN; + c->sockaddr = ngx_pcalloc(c->pool, c->socklen); + ngx_memcpy(c->sockaddr, sin6_src, c->socklen); + + /* updating connection with destination provided by proxy protocol */ + if (c->sockaddr->sa_family != AF_INET6) { + ngx_pfree(c->pool, c->local_sockaddr); + c->local_sockaddr = ngx_pcalloc(c->pool, NGX_SOCKADDRLEN); + } else { + ngx_memzero(c->sockaddr, sizeof(struct sockaddr_in6)); + c->socklen = NGX_SOCKADDRLEN; + } +// ngx_memcpy(c->local_sockaddr, sin6_dst, NGX_SOCKADDRLEN); +//FIXME must be finished here + + } + +#endif + + else { + step = 31; + goto fail; + } + + ngx_print_proxy_protocol(&pp, c->log); + + ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, + "proxy_protocol, asking to remove %z chars", + end + 1 - buf); + + return (end + 1 - buf); + +fail: + ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, + "proxy_protocol error at step: %d", step); + + return 0; + +} + + +void +ngx_print_proxy_protocol(ngx_proxy_protocol_t *p, ngx_log_t *log) +{ + switch (p->pp_proto) { + case NGX_PP_PROTO_TCP4: + ngx_log_debug0(NGX_LOG_DEBUG_EVENT, log, 0, + "proxy_protocol, proto: TCP4"); + break; + case NGX_PP_PROTO_TCP6: + ngx_log_debug0(NGX_LOG_DEBUG_EVENT, log, 0, + "proxy_protocol, proto: TCP6"); + break; + } + + ngx_log_debug1(NGX_LOG_DEBUG_EVENT, log, 0, + "proxy_protocol, string length: %d", ngx_proxy_protocol_string_length(p)); + ngx_log_debug2(NGX_LOG_DEBUG_EVENT, log, 0, + "proxy_protocol, src3: %s, %d", p->pp_src3_text.data, p->pp_src3_text.len); + ngx_log_debug2(NGX_LOG_DEBUG_EVENT, log, 0, + "proxy_protocol, dst3: %s, %d", p->pp_dst3_text.data, p->pp_dst3_text.len); + ngx_log_debug1(NGX_LOG_DEBUG_EVENT, log, 0, + "proxy_protocol, src4: %d", p->pp_src4); + ngx_log_debug1(NGX_LOG_DEBUG_EVENT, log, 0, + "proxy_protocol, dst4: %d", p->pp_dst4); +} + + +int +ngx_proxy_protocol_string_length(ngx_proxy_protocol_t *p) +{ + int len = 0; + + /* 'PROXY ' */ + len += (sizeof("PROXY ") - 1); + + /* protocol version (TCP4 or TCP6) + space */ + len += (sizeof("TCP0 ") - 1); + + /* src3 + space */ + len += p->pp_src3_text.len; + len += 1; + + /* dst3 + space */ + len += p->pp_dst3_text.len; + len += 1; + + /* src4 */ + if (p->pp_src4 < 10000) + /* 4 digits + 1 space */ + len += (sizeof("0000 ") - 1); + else + /* 5 digits + 1 space */ + len += (sizeof("00000 ") - 1); + + /* dst4 */ + if (p->pp_dst4 >= 10000) + /* 5 digits */ + len += (sizeof("00000 ") - 1); + else if (p->pp_dst4 >= 1000) + /* 4 digits */ + len += (sizeof("0000 ") - 1); + else if (p->pp_dst4 >= 100) + /* 3 digits */ + len += (sizeof("000 ") - 1); + else if (p->pp_dst4 >= 10) + /* 2 digits */ + len += (sizeof("00 ") - 1); + else + /* 1 digit */ + len += (sizeof("0 ") - 1); + + /* CRLF */ + len += (sizeof(CRLF) - 1); + + return len - 1; +} + +#endif Index: nginx-1.4.7/src/core/ngx_proxy_protocol.h =================================================================== --- /dev/null +++ nginx-1.4.7/src/core/ngx_proxy_protocol.h @@ -0,0 +1,45 @@ + +/* + * Copyright (C) Baptiste Assmann + * Copyright (C) Exceliance + */ + + +#ifndef _NGX_PROXY_PROTOCOL_H_INCLUDED_ +#define _NGX_PROXY_PROTOCOL_H_INCLUDED_ + + +#include +#include + + +#if (NGX_PROXY_PROTOCOL) + +typedef struct ngx_proxy_protocol_s ngx_proxy_protocol_t; + +typedef enum { + NGX_PP_PROTO_TCP4 = 1, + NGX_PP_PROTO_TCP6 +} ngx_pp_proto; + + +struct ngx_proxy_protocol_s { + unsigned int pp_proto; /* proxy protocol related information */ + struct sockaddr_storage pp_src3; + ngx_str_t pp_src3_text; + struct sockaddr_storage pp_dst3; + ngx_str_t pp_dst3_text; + unsigned int pp_src4; + unsigned int pp_dst4; +}; + + +int ngx_recv_proxy_protocol(ngx_connection_t *, u_char *, ssize_t); +void ngx_print_proxy_protocol(ngx_proxy_protocol_t *, ngx_log_t *); +int ngx_proxy_protocol_string_length(ngx_proxy_protocol_t *); + + +#endif + +#endif /* _NGX_CONNECTION_H_INCLUDED_ */ + Index: nginx-1.4.7/src/http/modules/ngx_http_proxy_module.c =================================================================== --- nginx-1.4.7.orig/src/http/modules/ngx_http_proxy_module.c +++ nginx-1.4.7/src/http/modules/ngx_http_proxy_module.c @@ -8,7 +8,9 @@ #include #include #include - +#if (NGX_PROXY_PROTOCOL) +#include +#endif typedef struct ngx_http_proxy_rewrite_s ngx_http_proxy_rewrite_t; @@ -365,6 +367,17 @@ static ngx_command_t ngx_http_proxy_com offsetof(ngx_http_proxy_loc_conf_t, upstream.busy_buffers_size_conf), NULL }, +#if (NGX_PROXY_PROTOCOL) + + { ngx_string("send_proxy_protocol"), + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG, + ngx_conf_set_flag_slot, + NGX_HTTP_LOC_CONF_OFFSET, + offsetof(ngx_http_proxy_loc_conf_t, upstream.send_proxy_protocol), + NULL }, + +#endif + #if (NGX_HTTP_CACHE) { ngx_string("proxy_cache"), @@ -2420,6 +2433,11 @@ ngx_http_proxy_create_loc_conf(ngx_conf_ conf->upstream.pass_headers = NGX_CONF_UNSET_PTR; conf->upstream.intercept_errors = NGX_CONF_UNSET; + +#if (NGX_PROXY_PROTOCOL) + conf->upstream.send_proxy_protocol = NGX_CONF_UNSET; +#endif + #if (NGX_HTTP_SSL) conf->upstream.ssl_session_reuse = NGX_CONF_UNSET; #endif @@ -2695,6 +2713,11 @@ ngx_http_proxy_merge_loc_conf(ngx_conf_t ngx_conf_merge_value(conf->upstream.intercept_errors, prev->upstream.intercept_errors, 0); +#if (NGX_PROXY_PROTOCOL) + ngx_conf_merge_value(conf->upstream.send_proxy_protocol, + prev->upstream.send_proxy_protocol, 0); +#endif + #if (NGX_HTTP_SSL) ngx_conf_merge_value(conf->upstream.ssl_session_reuse, prev->upstream.ssl_session_reuse, 1); Index: nginx-1.4.7/src/http/ngx_http.c =================================================================== --- nginx-1.4.7.orig/src/http/ngx_http.c +++ nginx-1.4.7/src/http/ngx_http.c @@ -1228,6 +1228,9 @@ ngx_http_add_addresses(ngx_conf_t *cf, n #if (NGX_HTTP_SPDY) ngx_uint_t spdy; #endif +#if (NGX_PROXY_PROTOCOL) + ngx_uint_t accept_proxy_protocol; +#endif /* * we cannot compare whole sockaddr struct's as kernel @@ -1283,6 +1286,10 @@ ngx_http_add_addresses(ngx_conf_t *cf, n #if (NGX_HTTP_SPDY) spdy = lsopt->spdy || addr[i].opt.spdy; #endif +#if (NGX_PROXY_PROTOCOL) + accept_proxy_protocol = lsopt->accept_proxy_protocol + || addr[i].opt.accept_proxy_protocol; +#endif if (lsopt->set) { @@ -1316,6 +1323,9 @@ ngx_http_add_addresses(ngx_conf_t *cf, n #if (NGX_HTTP_SPDY) addr[i].opt.spdy = spdy; #endif +#if (NGX_PROXY_PROTOCOL) + addr[i].opt.accept_proxy_protocol = accept_proxy_protocol; +#endif return NGX_OK; } @@ -1762,6 +1772,11 @@ ngx_http_add_listening(ngx_conf_t *cf, n ls->pool_size = cscf->connection_pool_size; ls->post_accept_timeout = cscf->client_header_timeout; +#if (NGX_PROXY_PROTOCOL) +// CLEANUP: ls->accept_proxy_protocol = cscf->accept_proxy_protocol; + ls->accept_proxy_protocol = addr->opt.accept_proxy_protocol; +#endif + clcf = cscf->ctx->loc_conf[ngx_http_core_module.ctx_index]; ls->logp = clcf->error_log; @@ -1840,6 +1855,9 @@ ngx_http_add_addrs(ngx_conf_t *cf, ngx_h #if (NGX_HTTP_SPDY) addrs[i].conf.spdy = addr[i].opt.spdy; #endif +#if (NGX_PROXY_PROTOCOL) + addrs[i].conf.accept_proxy_protocol = addr[i].opt.accept_proxy_protocol; +#endif if (addr[i].hash.buckets == NULL && (addr[i].wc_head == NULL @@ -1904,6 +1922,9 @@ ngx_http_add_addrs6(ngx_conf_t *cf, ngx_ #if (NGX_HTTP_SPDY) addrs6[i].conf.spdy = addr[i].opt.spdy; #endif +#if (NGX_PROXY_PROTOCOL) + addrs6[i].conf.accept_proxy_protocol = addr[i].opt.accept_proxy_protocol; +#endif if (addr[i].hash.buckets == NULL && (addr[i].wc_head == NULL Index: nginx-1.4.7/src/http/ngx_http_core_module.c =================================================================== --- nginx-1.4.7.orig/src/http/ngx_http_core_module.c +++ nginx-1.4.7/src/http/ngx_http_core_module.c @@ -4090,6 +4090,15 @@ ngx_http_core_listen(ngx_conf_t *cf, ngx continue; } +#if (NGX_PROXY_PROTOCOL) + if (ngx_strncmp(value[n].data, "accept_proxy_protocol=on", 24) == 0) { + lsopt.accept_proxy_protocol = 1; + lsopt.set = 1; + lsopt.bind = 1; + continue; + } +#endif + if (ngx_strncmp(value[n].data, "ipv6only=o", 10) == 0) { #if (NGX_HAVE_INET6 && defined IPV6_V6ONLY) struct sockaddr *sa; Index: nginx-1.4.7/src/http/ngx_http_core_module.h =================================================================== --- nginx-1.4.7.orig/src/http/ngx_http_core_module.h +++ nginx-1.4.7/src/http/ngx_http_core_module.h @@ -78,6 +78,11 @@ typedef struct { #if (NGX_HTTP_SPDY) unsigned spdy:1; #endif + +#if (NGX_PROXY_PROTOCOL) + unsigned accept_proxy_protocol:2; +#endif + #if (NGX_HAVE_INET6 && defined IPV6_V6ONLY) unsigned ipv6only:1; #endif @@ -234,6 +239,10 @@ struct ngx_http_addr_conf_s { ngx_http_virtual_names_t *virtual_names; +#if (NGX_PROXY_PROTOCOL) + ngx_flag_t accept_proxy_protocol; +#endif + #if (NGX_HTTP_SSL) unsigned ssl:1; #endif Index: nginx-1.4.7/src/http/ngx_http_request.c =================================================================== --- nginx-1.4.7.orig/src/http/ngx_http_request.c +++ nginx-1.4.7/src/http/ngx_http_request.c @@ -63,6 +63,9 @@ static void ngx_http_ssl_handshake(ngx_e static void ngx_http_ssl_handshake_handler(ngx_connection_t *c); #endif +#if (NGX_PROXY_PROTOCOL) +static void ngx_http_proxy_protocol(ngx_event_t *rev); +#endif static char *ngx_http_client_errors[] = { @@ -343,6 +346,14 @@ ngx_http_init_connection(ngx_connection_ } #endif +#if (NGX_PROXY_PROTOCOL) + { + if (hc->addr_conf->accept_proxy_protocol) { + rev->handler = ngx_http_proxy_protocol; + } + } +#endif + if (rev->ready) { /* the deferred accept(), rtsig, aio, iocp */ @@ -364,7 +375,6 @@ ngx_http_init_connection(ngx_connection_ } } - static void ngx_http_wait_request_handler(ngx_event_t *rev) { @@ -469,6 +479,12 @@ ngx_http_wait_request_handler(ngx_event_ } rev->handler = ngx_http_process_request_line; + +#if (NGX_PROXY_PROTOCOL) + if (hc->addr_conf->accept_proxy_protocol) + rev->handler = ngx_http_proxy_protocol; +#endif + ngx_http_process_request_line(rev); } @@ -582,6 +598,67 @@ ngx_http_create_request(ngx_connection_t return r; } +#if (NGX_PROXY_PROTOCOL) + +static void +ngx_http_proxy_protocol(ngx_event_t *rev) +{ + ssize_t n; + size_t size = 1024; + u_char tmpbuf[size]; + ngx_connection_t *c; + ngx_http_connection_t *hc; + + c = rev->data; + hc = c->data; + rev->handler = ngx_http_wait_request_handler; + +#if (NGX_HTTP_SPDY) + { + if (hc->addr_conf->spdy) { + rev->handler = ngx_http_spdy_init; + } + } +#endif + +#if (NGX_HTTP_SSL) + { + if (hc->addr_conf->ssl) { + rev->handler = ngx_http_ssl_handshake; + } + } +#endif + + n = recv(c->fd, tmpbuf, size, MSG_PEEK); + + if ((n <= 0) && (c->listening) + && (hc->addr_conf->accept_proxy_protocol) + && (!c->proxy_protocol)) { + ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0, "ngx_http_proxy_protocol: pp required but not found"); + return; + } + if ((n > 0) && (c->listening) + && (hc->addr_conf->accept_proxy_protocol) + && (!c->proxy_protocol)) { + ssize_t m; + if (!(m = ngx_recv_proxy_protocol(c, tmpbuf, n))) { + ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0, "ngx_http_proxy_protocol: pp required but not found"); + ngx_http_close_connection(c); + return; + } + ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0, "ngx_http_proxy_protocol: pp required and found"); + + c->proxy_protocol = 1; + + /* strip the proxy protocol string from the buffer */ + recv(c->fd, tmpbuf, m, 0); + } + + rev->handler(rev); +} + +#endif + #if (NGX_HTTP_SSL) Index: nginx-1.4.7/src/http/ngx_http_upstream.c =================================================================== --- nginx-1.4.7.orig/src/http/ngx_http_upstream.c +++ nginx-1.4.7/src/http/ngx_http_upstream.c @@ -31,6 +31,10 @@ static ngx_int_t ngx_http_upstream_reini ngx_http_upstream_t *u); static void ngx_http_upstream_send_request(ngx_http_request_t *r, ngx_http_upstream_t *u); +#if (NGX_PROXY_PROTOCOL) +static void ngx_http_upstream_send_proxy_protocol(ngx_http_request_t *r, + ngx_http_upstream_t *u); +#endif static void ngx_http_upstream_send_request_handler(ngx_http_request_t *r, ngx_http_upstream_t *u); static void ngx_http_upstream_process_header(ngx_http_request_t *r, @@ -1255,6 +1259,13 @@ ngx_http_upstream_connect(ngx_http_reque u->request_sent = 0; +#if (NGX_PROXY_PROTOCOL) + if (u->conf->send_proxy_protocol && !(u->ssl && c->ssl == NULL)) { + ngx_http_upstream_send_proxy_protocol(r, u); + return; + } +#endif + if (rc == NGX_AGAIN) { ngx_add_timer(c->write, u->conf->connect_timeout); return; @@ -1498,6 +1509,228 @@ ngx_http_upstream_send_request(ngx_http_ } +#if (NGX_PROXY_PROTOCOL) + +static void +ngx_http_upstream_send_proxy_protocol(ngx_http_request_t *r, ngx_http_upstream_t *u) +{ + size_t len; + ngx_int_t rc; + ngx_connection_t *uc; + ngx_connection_t *cc; + ngx_chain_t *pp_string; + ngx_proxy_protocol_t pp; + ngx_buf_t *b; + char port[6]; + u_char *addr; + struct sockaddr_storage sa_src; + struct sockaddr_storage sa_dst; + socklen_t addrlen = NGX_SOCKADDRLEN; + struct sockaddr_in *sin_src; + struct sockaddr_in *sin_dst; + +#if (NGX_HAVE_INET6) + + struct sockaddr_in6 *sin6_src; + struct sockaddr_in6 *sin6_dst; + +#endif + + + uc = u->peer.connection; + cc = r->connection; + + if ( !(u->conf->send_proxy_protocol) ) { + return; + } + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, uc->log, 0, + "http upstream send proxy protocol"); + + if (!u->request_sent && ngx_http_upstream_test_connect(uc) != NGX_OK) { + ngx_http_upstream_next(r, u, NGX_HTTP_UPSTREAM_FT_ERROR); + return; + } + + uc->log->action = "sending proxy protocol to upstream"; + + len = 0; + + if (r->connection->proxy_protocol) { + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, uc->log, 0, + "PP: got proxy-protocol from client connection"); + + switch (cc->sockaddr->sa_family) { + +#if (NGX_HAVE_INET6) + + case AF_INET6: + + pp.pp_proto = NGX_PP_PROTO_TCP6; + sin6_dst = (struct sockaddr_in6 *) cc->local_sockaddr; + sin6_src = (struct sockaddr_in6 *) cc->sockaddr; + + break; + +#endif + + default: + pp.pp_proto = NGX_PP_PROTO_TCP4; + sin_dst = (struct sockaddr_in *) cc->local_sockaddr; + sin_src = (struct sockaddr_in *) cc->sockaddr; + + } + + } else { + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, uc->log, 0, + "PP: collecting information from socket fd"); + + getsockname(cc->fd, (struct sockaddr *) &sa_dst, &addrlen); + + switch (sa_dst.ss_family) { + +#if (NGX_HAVE_INET6) + + case AF_INET6: + + pp.pp_proto = NGX_PP_PROTO_TCP6; + sin6_dst = (struct sockaddr_in6 *) &sa_dst; + + getpeername(cc->fd, (struct sockaddr *) &sa_src, &addrlen); + sin6_src = (struct sockaddr_in6 *) &sa_src; + + break; + +#endif + + default: + + pp.pp_proto = NGX_PP_PROTO_TCP4; + sin_dst = (struct sockaddr_in *) &sa_dst; + getpeername(cc->fd, (struct sockaddr *) &sa_src, &addrlen); + sin_src = (struct sockaddr_in *) &sa_src; + } + + + } + + switch (pp.pp_proto) { + +#if (NGX_HAVE_INET6) + + case NGX_PP_PROTO_TCP6: + + /* dst3 and dst4 */ + addr = ngx_pcalloc(r->pool, NGX_INET6_ADDRSTRLEN); + ngx_inet_ntop(AF_INET6, &sin6_dst->sin6_addr, addr, NGX_INET6_ADDRSTRLEN); + pp.pp_dst3_text.data = ngx_pcalloc(r->pool, NGX_INET6_ADDRSTRLEN); + pp.pp_dst3_text.len = ngx_strlen(addr); + ngx_memcpy(pp.pp_dst3_text.data, addr, pp.pp_dst3_text.len); + pp.pp_dst4 = htons(sin6_dst->sin6_port); + + ngx_memzero(addr, NGX_INET6_ADDRSTRLEN); + + /* src3 and src4 */ + ngx_inet_ntop(AF_INET6, &sin6_src->sin6_addr, addr, NGX_INET6_ADDRSTRLEN); + pp.pp_src3_text.data = ngx_pcalloc(r->pool, NGX_INET6_ADDRSTRLEN); + pp.pp_src3_text.len = ngx_strlen(addr); + ngx_memcpy(pp.pp_src3_text.data, addr, pp.pp_src3_text.len); + pp.pp_src4 = htons(sin6_src->sin6_port); + + break; + +#endif + + default: + + /* dst3 and dst4 */ + addr = ngx_pcalloc(r->pool, NGX_INET_ADDRSTRLEN); + ngx_inet_ntop(AF_INET, &sin_dst->sin_addr, addr, NGX_INET_ADDRSTRLEN); + pp.pp_dst3_text.data = ngx_pcalloc(r->pool, NGX_INET_ADDRSTRLEN); + pp.pp_dst3_text.len = ngx_strlen(addr); + ngx_memcpy(pp.pp_dst3_text.data, addr, pp.pp_dst3_text.len); + pp.pp_dst4 = htons(sin_dst->sin_port); + + ngx_memzero(addr, NGX_INET_ADDRSTRLEN); + + /* src3 and src4 */ + ngx_inet_ntop(AF_INET, &sin_src->sin_addr, addr, NGX_INET_ADDRSTRLEN); + pp.pp_src3_text.data = ngx_pcalloc(r->pool, NGX_INET_ADDRSTRLEN); + pp.pp_src3_text.len = ngx_strlen(addr); + ngx_memcpy(pp.pp_src3_text.data, addr, pp.pp_src3_text.len); + pp.pp_src4 = htons(sin_src->sin_port); + + } + + len += ngx_proxy_protocol_string_length(&pp); + + ngx_print_proxy_protocol(&pp, uc->log); + + b = ngx_create_temp_buf(uc->pool, len); + if (b == NULL) { + return; + } + + pp_string = ngx_alloc_chain_link(uc->pool); + if (pp_string == NULL) { + return; + } + + pp_string->buf = b; + pp_string->next = NULL; + + b->last = ngx_cpymem(b->last, "PROXY ", sizeof("PROXY ") - 1); + + switch (pp.pp_proto) { + case NGX_PP_PROTO_TCP4: + b->last = ngx_cpymem(b->last, "TCP4 ", sizeof("TCP4 ") - 1); + break; + case NGX_PP_PROTO_TCP6: + b->last = ngx_cpymem(b->last, "TCP6 ", sizeof("TCP6 ") - 1); + break; + } + + /* src3 */ + b->last = ngx_cpymem(b->last, pp.pp_src3_text.data, pp.pp_src3_text.len); + b->last = ngx_cpymem(b->last, " ", 1); + + /* dst3 */ + b->last = ngx_cpymem(b->last, pp.pp_dst3_text.data, pp.pp_dst3_text.len); + b->last = ngx_cpymem(b->last, " ", 1); + + /* src4 */ + ngx_memzero(port, 6); + sprintf(port,"%d", pp.pp_src4); + b->last = ngx_cpymem(b->last, port, strlen(port)); + b->last = ngx_cpymem(b->last, " ", 1); + + /* dst4 */ + ngx_memzero(port, 6); + sprintf(port,"%d", pp.pp_dst4); + b->last = ngx_cpymem(b->last, port, strlen(port)); + + /* CRLF */ + b->last = ngx_cpymem(b->last, CRLF, sizeof(CRLF) - 1); + + ngx_log_debug3(NGX_LOG_DEBUG_HTTP, uc->log, 0, + "http upstream send proxy protocol: %d -%*s-", + ngx_proxy_protocol_string_length(&pp), + ngx_proxy_protocol_string_length(&pp) - 2, + b->start); + + rc = ngx_output_chain(&u->output, pp_string); + + if (rc == NGX_ERROR) { + ngx_http_upstream_next(r, u, NGX_HTTP_UPSTREAM_FT_ERROR); + return; + } + +} + +#endif + + static void ngx_http_upstream_send_request_handler(ngx_http_request_t *r, ngx_http_upstream_t *u) Index: nginx-1.4.7/src/http/ngx_http_upstream.h =================================================================== --- nginx-1.4.7.orig/src/http/ngx_http_upstream.h +++ nginx-1.4.7/src/http/ngx_http_upstream.h @@ -188,6 +188,10 @@ typedef struct { unsigned intercept_404:1; unsigned change_buffering:1; +#if (NGX_PROXY_PROTOCOL) + ngx_flag_t send_proxy_protocol; +#endif + #if (NGX_HTTP_SSL) ngx_ssl_t *ssl; ngx_flag_t ssl_session_reuse; Index: nginx-1.4.7/auto/cc/gcc =================================================================== --- nginx-1.4.7.orig/auto/cc/gcc +++ nginx-1.4.7/auto/cc/gcc @@ -168,7 +168,7 @@ esac # stop on warning -CFLAGS="$CFLAGS -Werror" +CFLAGS="$CFLAGS" # debug CFLAGS="$CFLAGS -g" Index: nginx-1.4.7/auto/cc/icc =================================================================== --- nginx-1.4.7.orig/auto/cc/icc +++ nginx-1.4.7/auto/cc/icc @@ -115,7 +115,7 @@ case "$NGX_ICC_VER" in esac # stop on warning -CFLAGS="$CFLAGS -Werror" +CFLAGS="$CFLAGS " # debug CFLAGS="$CFLAGS -g"