# HG changeset patch # User Maxim Dounin # Date 1614953784 -10800 # Node ID 12ea1de7d87ce45a243c5bc3d44b606da9c36a88 # Parent 44ebeeceb70ef4b0eb9e1e4ecc03042b4afebb0d Mail: parsing of the PROXY protocol from clients. Activated with the "proxy_protocol" parameter of the "listen" directive. Obtained information is passed to the auth_http script in Proxy-Protocol-Addr, Proxy-Protocol-Port, Proxy-Protocol-Server-Addr, and Proxy-Protocol-Server-Port headers. diff --git a/src/mail/ngx_mail.c b/src/mail/ngx_mail.c --- a/src/mail/ngx_mail.c +++ b/src/mail/ngx_mail.c @@ -405,6 +405,7 @@ ngx_mail_add_addrs(ngx_conf_t *cf, ngx_m #if (NGX_MAIL_SSL) addrs[i].conf.ssl = addr[i].opt.ssl; #endif + addrs[i].conf.proxy_protocol = addr[i].opt.proxy_protocol; addrs[i].conf.addr_text = addr[i].opt.addr_text; } @@ -439,6 +440,7 @@ ngx_mail_add_addrs6(ngx_conf_t *cf, ngx_ #if (NGX_MAIL_SSL) addrs6[i].conf.ssl = addr[i].opt.ssl; #endif + addrs6[i].conf.proxy_protocol = addr[i].opt.proxy_protocol; addrs6[i].conf.addr_text = addr[i].opt.addr_text; } diff --git a/src/mail/ngx_mail.h b/src/mail/ngx_mail.h --- a/src/mail/ngx_mail.h +++ b/src/mail/ngx_mail.h @@ -41,6 +41,7 @@ typedef struct { unsigned ipv6only:1; #endif unsigned so_keepalive:2; + unsigned proxy_protocol:1; #if (NGX_HAVE_KEEPALIVE_TUNABLE) int tcp_keepidle; int tcp_keepintvl; @@ -55,7 +56,8 @@ typedef struct { typedef struct { ngx_mail_conf_ctx_t *ctx; ngx_str_t addr_text; - ngx_uint_t ssl; /* unsigned ssl:1; */ + unsigned ssl:1; + unsigned proxy_protocol:1; } ngx_mail_addr_conf_t; typedef struct { diff --git a/src/mail/ngx_mail_auth_http_module.c b/src/mail/ngx_mail_auth_http_module.c --- a/src/mail/ngx_mail_auth_http_module.c +++ b/src/mail/ngx_mail_auth_http_module.c @@ -1227,6 +1227,17 @@ ngx_mail_auth_http_create_request(ngx_ma + ahcf->header.len + sizeof(CRLF) - 1; + if (c->proxy_protocol) { + len += sizeof("Proxy-Protocol-Addr: ") - 1 + + c->proxy_protocol->src_addr.len + sizeof(CRLF) - 1 + + sizeof("Proxy-Protocol-Port: ") - 1 + + sizeof("65535") - 1 + sizeof(CRLF) - 1 + + sizeof("Proxy-Protocol-Server-Addr: ") - 1 + + c->proxy_protocol->dst_addr.len + sizeof(CRLF) - 1 + + sizeof("Proxy-Protocol-Server-Port: ") - 1 + + sizeof("65535") - 1 + sizeof(CRLF) - 1; + } + if (s->auth_method == NGX_MAIL_AUTH_NONE) { len += sizeof("Auth-SMTP-Helo: ") - 1 + s->smtp_helo.len + sizeof(CRLF) - 1 @@ -1314,6 +1325,26 @@ ngx_mail_auth_http_create_request(ngx_ma *b->last++ = CR; *b->last++ = LF; } + if (c->proxy_protocol) { + b->last = ngx_cpymem(b->last, "Proxy-Protocol-Addr: ", + sizeof("Proxy-Protocol-Addr: ") - 1); + b->last = ngx_copy(b->last, c->proxy_protocol->src_addr.data, + c->proxy_protocol->src_addr.len); + *b->last++ = CR; *b->last++ = LF; + + b->last = ngx_sprintf(b->last, "Proxy-Protocol-Port: %d" CRLF, + c->proxy_protocol->src_port); + + b->last = ngx_cpymem(b->last, "Proxy-Protocol-Server-Addr: ", + sizeof("Proxy-Protocol-Server-Addr: ") - 1); + b->last = ngx_copy(b->last, c->proxy_protocol->dst_addr.data, + c->proxy_protocol->dst_addr.len); + *b->last++ = CR; *b->last++ = LF; + + b->last = ngx_sprintf(b->last, "Proxy-Protocol-Server-Port: %d" CRLF, + c->proxy_protocol->dst_port); + } + if (s->auth_method == NGX_MAIL_AUTH_NONE) { /* HELO, MAIL FROM, and RCPT TO can't contain CRLF, no need to escape */ diff --git a/src/mail/ngx_mail_core_module.c b/src/mail/ngx_mail_core_module.c --- a/src/mail/ngx_mail_core_module.c +++ b/src/mail/ngx_mail_core_module.c @@ -548,6 +548,11 @@ ngx_mail_core_listen(ngx_conf_t *cf, ngx #endif } + if (ngx_strcmp(value[i].data, "proxy_protocol") == 0) { + ls->proxy_protocol = 1; + continue; + } + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "the invalid \"%V\" parameter", &value[i]); return NGX_CONF_ERROR; diff --git a/src/mail/ngx_mail_handler.c b/src/mail/ngx_mail_handler.c --- a/src/mail/ngx_mail_handler.c +++ b/src/mail/ngx_mail_handler.c @@ -11,6 +11,7 @@ #include +static void ngx_mail_proxy_protocol_handler(ngx_event_t *rev); static void ngx_mail_init_session_handler(ngx_event_t *rev); static void ngx_mail_init_session(ngx_connection_t *c); @@ -168,6 +169,22 @@ ngx_mail_init_connection(ngx_connection_ rev = c->read; rev->handler = ngx_mail_init_session_handler; + if (addr_conf->proxy_protocol) { + c->log->action = "reading PROXY protocol"; + + rev->handler = ngx_mail_proxy_protocol_handler; + + if (!rev->ready) { + ngx_add_timer(rev, cscf->timeout); + + if (ngx_handle_read_event(rev, 0) != NGX_OK) { + ngx_mail_close_connection(c); + } + + return; + } + } + if (ngx_use_accept_mutex) { ngx_post_event(rev, &ngx_posted_events); return; @@ -178,6 +195,76 @@ ngx_mail_init_connection(ngx_connection_ static void +ngx_mail_proxy_protocol_handler(ngx_event_t *rev) +{ + u_char *p, buf[NGX_PROXY_PROTOCOL_MAX_HEADER]; + size_t size; + ssize_t n; + ngx_err_t err; + ngx_connection_t *c; + ngx_mail_session_t *s; + ngx_mail_core_srv_conf_t *cscf; + + c = rev->data; + s = c->data; + + ngx_log_debug0(NGX_LOG_DEBUG_MAIL, c->log, 0, + "mail PROXY protocol handler"); + + if (rev->timedout) { + ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT, "client timed out"); + c->timedout = 1; + ngx_mail_close_connection(c); + return; + } + + n = recv(c->fd, (char *) buf, sizeof(buf), MSG_PEEK); + + err = ngx_socket_errno; + + ngx_log_debug1(NGX_LOG_DEBUG_MAIL, c->log, 0, "recv(): %z", n); + + if (n == -1) { + if (err == NGX_EAGAIN) { + rev->ready = 0; + + if (!rev->timer_set) { + cscf = ngx_mail_get_module_srv_conf(s, ngx_mail_core_module); + ngx_add_timer(rev, cscf->timeout); + } + + if (ngx_handle_read_event(rev, 0) != NGX_OK) { + ngx_mail_close_connection(c); + } + + return; + } + + ngx_connection_error(c, err, "recv() failed"); + + ngx_mail_close_connection(c); + return; + } + + p = ngx_proxy_protocol_read(c, buf, buf + n); + + if (p == NULL) { + ngx_mail_close_connection(c); + return; + } + + size = p - buf; + + if (c->recv(c, buf, size) != (ssize_t) size) { + ngx_mail_close_connection(c); + return; + } + + ngx_mail_init_session_handler(rev); +} + + +static void ngx_mail_init_session_handler(ngx_event_t *rev) { ngx_connection_t *c; @@ -242,9 +329,10 @@ ngx_mail_ssl_init_connection(ngx_ssl_t * s = c->data; - cscf = ngx_mail_get_module_srv_conf(s, ngx_mail_core_module); - - ngx_add_timer(c->read, cscf->timeout); + if (!c->read->timer_set) { + cscf = ngx_mail_get_module_srv_conf(s, ngx_mail_core_module); + ngx_add_timer(c->read, cscf->timeout); + } c->ssl->handler = ngx_mail_ssl_handshake_handler;