[PATCH] Mail: added PROXY PROTOCOL support

muradm mail at muradm.net
Tue Jan 19 15:34:30 UTC 2021


# HG changeset patch
# User muradm <mail at muradm.net>
# Date 1611069863 -10800
#      Tue Jan 19 18:24:23 2021 +0300
# Node ID 4618e767b84c5b3a7712466edb5bf37e3f0294ed
# Parent  83c4622053b02821a12d522d08eaff3ac27e65e3
Mail: added PROXY PROTOCOL support.

This implements propxy protocol support for both upstream and downstream.

Downstream proxy protocol support:

mail {
    server {
        listen <port> [ssl] proxy_protocol;
	protocol <imap|pop3|smtp>;
    }
}

This will properly handle incoming connections from load balancer sending
PROXY protocol header. Without this, it is impossible to run nginx mail
proxy behind such balancer. Header reading is done with existing function
"ngx_proxy_protocol_read", so it should support both v1 and v2 headers.
This will also set "sockaddr" and "local_sockaddr" addresses from received
header, mimicing "set_realip". While "realip_module" deals with variables
etc., which is necessary for HTTP protocol, mail protocols are pretty
strict, so there is no need for flexible handling of real addresses
received.

Upstream proxy protocol support:

mail {
    server {
        listen <port> [ssl];
	protocol <imap|pop3|smtp>;
	proxy_protocol on;
    }
}

With this, upstream server (like Postfix, Exim, Dovecot) will have PROXY
protocol header. Mentioned programs do support proxy protocol out of the
box. Header is written with existing function "ngx_proxy_protocol_write"
which supports only v1 header writing. Contents of header are written
from "sockaddr" and "local_sockaddr".

Downstream and upstream proxy protocol support:

mail {
    server {
        listen <port> [ssl] proxy_protocol;
	protocol <imap|pop3|smtp>;
	proxy_protocol on;
    }
}

This will combine both receiving PROXY header and sending PROXY header. With
this, upstream server (like Postfix, Exim, Dovecot) will receive the same
header as was sent by downstream load balancer.

Above configurations work for SSL as well and should be transparent to other
mail related configurations.

Added upstream server "connect_timeout" which defaults to 1 second.

Server configurations enabling proxy_protocol in listen directive, require
"set_real_ip_from" configuration. Like the following:

mail {
    # ...
    server {
        listen 587 proxy_protocol;
	set_real_ip_from "192.168.1.1";
	set_real_ip_from "10.10.0.0/16";
	set_real_ip_from "0.0.0.0/0";
    }
}

With enabled "proxy_protocol" and missing at least one "set_real_ip_from",
all connections will be dropped and at startup user will see in error_log:

    using PROXY protocol without set_real_ip_from \
        while reading PROXY protocol header

When "set_real_ip_from" is provided, but remote address on physical connection
does not satisfy any address criteria, at "notice" level, in error_log, user
will see:

    UNTRUSTED PROXY protocol provider: 127.0.0.1 \
        while reading PROXY protocol header, \
	client: 127.0.0.1, server: 127.0.0.1:8143

diff -r 83c4622053b0 -r 4618e767b84c src/mail/ngx_mail.c
--- a/src/mail/ngx_mail.c	Tue Jan 12 16:59:31 2021 +0300
+++ b/src/mail/ngx_mail.c	Tue Jan 19 18:24:23 2021 +0300
@@ -402,6 +402,7 @@
         addrs[i].addr = sin->sin_addr.s_addr;
 
         addrs[i].conf.ctx = addr[i].opt.ctx;
+        addrs[i].conf.proxy_protocol = addr[i].opt.proxy_protocol;
 #if (NGX_MAIL_SSL)
         addrs[i].conf.ssl = addr[i].opt.ssl;
 #endif
@@ -436,6 +437,7 @@
         addrs6[i].addr6 = sin6->sin6_addr;
 
         addrs6[i].conf.ctx = addr[i].opt.ctx;
+        addrs6[i].conf.proxy_protocol = addr[i].opt.proxy_protocol;
 #if (NGX_MAIL_SSL)
         addrs6[i].conf.ssl = addr[i].opt.ssl;
 #endif
diff -r 83c4622053b0 -r 4618e767b84c src/mail/ngx_mail.h
--- a/src/mail/ngx_mail.h	Tue Jan 12 16:59:31 2021 +0300
+++ b/src/mail/ngx_mail.h	Tue Jan 19 18:24:23 2021 +0300
@@ -37,6 +37,7 @@
     unsigned                bind:1;
     unsigned                wildcard:1;
     unsigned                ssl:1;
+    unsigned                proxy_protocol:1;
 #if (NGX_HAVE_INET6)
     unsigned                ipv6only:1;
 #endif
@@ -56,6 +57,7 @@
     ngx_mail_conf_ctx_t    *ctx;
     ngx_str_t               addr_text;
     ngx_uint_t              ssl;    /* unsigned   ssl:1; */
+    unsigned                proxy_protocol:1;
 } ngx_mail_addr_conf_t;
 
 typedef struct {
@@ -125,6 +127,8 @@
     ngx_mail_conf_ctx_t    *ctx;
 
     ngx_uint_t              listen;  /* unsigned  listen:1; */
+
+    ngx_array_t            *realip_from;     /* array of ngx_cidr_t */
 } ngx_mail_core_srv_conf_t;
 
 
@@ -190,6 +194,7 @@
     void                  **ctx;
     void                  **main_conf;
     void                  **srv_conf;
+    ngx_mail_addr_conf_t   *addr_conf;
 
     ngx_resolver_ctx_t     *resolver_ctx;
 
@@ -197,6 +202,7 @@
 
     ngx_uint_t              mail_state;
 
+    unsigned                proxy_protocol:1;
     unsigned                protocol:3;
     unsigned                blocked:1;
     unsigned                quit:1;
diff -r 83c4622053b0 -r 4618e767b84c src/mail/ngx_mail_core_module.c
--- a/src/mail/ngx_mail_core_module.c	Tue Jan 12 16:59:31 2021 +0300
+++ b/src/mail/ngx_mail_core_module.c	Tue Jan 19 18:24:23 2021 +0300
@@ -25,7 +25,7 @@
     void *conf);
 static char *ngx_mail_core_resolver(ngx_conf_t *cf, ngx_command_t *cmd,
     void *conf);
-
+static char *ngx_mail_core_realip_from(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
 
 static ngx_command_t  ngx_mail_core_commands[] = {
 
@@ -85,6 +85,13 @@
       offsetof(ngx_mail_core_srv_conf_t, resolver_timeout),
       NULL },
 
+    { ngx_string("set_real_ip_from"),
+      NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_TAKE1,
+      ngx_mail_core_realip_from,
+      NGX_MAIL_SRV_CONF_OFFSET,
+      offsetof(ngx_mail_core_srv_conf_t, realip_from),
+      NULL },
+
       ngx_null_command
 };
 
@@ -165,6 +172,8 @@
 
     cscf->resolver = NGX_CONF_UNSET_PTR;
 
+    cscf->realip_from = NGX_CONF_UNSET_PTR;
+
     cscf->file_name = cf->conf_file->file.name.data;
     cscf->line = cf->conf_file->line;
 
@@ -206,6 +215,10 @@
 
     ngx_conf_merge_ptr_value(conf->resolver, prev->resolver, NULL);
 
+    ngx_conf_merge_ptr_value(conf->realip_from,
+                             prev->realip_from,
+                             NGX_CONF_UNSET_PTR);
+
     return NGX_CONF_OK;
 }
 
@@ -548,6 +561,11 @@
 #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;
@@ -676,3 +694,104 @@
 
     return NGX_CONF_OK;
 }
+
+char *
+ngx_mail_core_realip_from(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
+{
+    ngx_mail_core_srv_conf_t *cscf = conf;
+
+    ngx_int_t             rc;
+    ngx_str_t            *value;
+    ngx_url_t             u;
+    ngx_cidr_t            c, *cidr;
+    ngx_uint_t            i;
+    struct sockaddr_in   *sin;
+#if (NGX_HAVE_INET6)
+    struct sockaddr_in6  *sin6;
+#endif
+
+    value = cf->args->elts;
+
+    if (cscf->realip_from == NGX_CONF_UNSET_PTR) {
+        cscf->realip_from = ngx_array_create(cf->pool, 2, sizeof(ngx_cidr_t));
+        if (cscf->realip_from == NULL) {
+            return NGX_CONF_ERROR;
+        }
+    }
+
+#if (NGX_HAVE_UNIX_DOMAIN)
+
+    if (ngx_strcmp(value[1].data, "unix:") == 0) {
+        cidr = ngx_array_push(cscf->realip_from);
+        if (cidr == NULL) {
+            return NGX_CONF_ERROR;
+        }
+
+        cidr->family = AF_UNIX;
+        return NGX_CONF_OK;
+    }
+
+#endif
+
+    rc = ngx_ptocidr(&value[1], &c);
+
+    if (rc != NGX_ERROR) {
+        if (rc == NGX_DONE) {
+            ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
+                               "low address bits of %V are meaningless",
+                               &value[1]);
+        }
+
+        cidr = ngx_array_push(cscf->realip_from);
+        if (cidr == NULL) {
+            return NGX_CONF_ERROR;
+        }
+
+        *cidr = c;
+
+        return NGX_CONF_OK;
+    }
+
+    ngx_memzero(&u, sizeof(ngx_url_t));
+    u.host = value[1];
+
+    if (ngx_inet_resolve_host(cf->pool, &u) != NGX_OK) {
+        if (u.err) {
+            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+                               "%s in set_real_ip_from \"%V\"",
+                               u.err, &u.host);
+        }
+
+        return NGX_CONF_ERROR;
+    }
+
+    cidr = ngx_array_push_n(cscf->realip_from, u.naddrs);
+    if (cidr == NULL) {
+        return NGX_CONF_ERROR;
+    }
+
+    ngx_memzero(cidr, u.naddrs * sizeof(ngx_cidr_t));
+
+    for (i = 0; i < u.naddrs; i++) {
+        cidr[i].family = u.addrs[i].sockaddr->sa_family;
+
+        switch (cidr[i].family) {
+
+#if (NGX_HAVE_INET6)
+        case AF_INET6:
+            sin6 = (struct sockaddr_in6 *) u.addrs[i].sockaddr;
+            cidr[i].u.in6.addr = sin6->sin6_addr;
+            ngx_memset(cidr[i].u.in6.mask.s6_addr, 0xff, 16);
+            break;
+#endif
+
+        default: /* AF_INET */
+            sin = (struct sockaddr_in *) u.addrs[i].sockaddr;
+            cidr[i].u.in.addr = sin->sin_addr.s_addr;
+            cidr[i].u.in.mask = 0xffffffff;
+            break;
+        }
+    }
+
+    return NGX_CONF_OK;
+}
diff -r 83c4622053b0 -r 4618e767b84c src/mail/ngx_mail_handler.c
--- a/src/mail/ngx_mail_handler.c	Tue Jan 12 16:59:31 2021 +0300
+++ b/src/mail/ngx_mail_handler.c	Tue Jan 19 18:24:23 2021 +0300
@@ -12,6 +12,8 @@
 
 
 static void ngx_mail_init_session(ngx_connection_t *c);
+static void ngx_mail_init_connection_complete(ngx_connection_t *c);
+static void ngx_mail_proxy_protocol_handler(ngx_event_t *rev);
 
 #if (NGX_MAIL_SSL)
 static void ngx_mail_ssl_init_connection(ngx_ssl_t *ssl, ngx_connection_t *c);
@@ -128,6 +130,7 @@
 
     s->main_conf = addr_conf->ctx->main_conf;
     s->srv_conf = addr_conf->ctx->srv_conf;
+    s->addr_conf = addr_conf;
 
     s->addr_text = &addr_conf->addr_text;
 
@@ -159,13 +162,181 @@
 
     c->log_error = NGX_ERROR_INFO;
 
+    /*
+     * Before all process proxy protocol
+     */
+
+    if (addr_conf->proxy_protocol) {
+        s->proxy_protocol = 1;
+        c->log->action = "reading PROXY protocol header";
+        c->read->handler = ngx_mail_proxy_protocol_handler;
+
+        ngx_add_timer(c->read, cscf->timeout);
+
+        if (ngx_handle_read_event(c->read, 0) != NGX_OK) {
+            ngx_mail_close_connection(c);
+        }
+
+        return;
+    }
+
+    ngx_mail_init_connection_complete(c);
+}
+
+
+ngx_int_t
+ngx_mail_proxy_protoco_set_addrs(ngx_connection_t *c)
+{
+    ngx_addr_t                addr_peer, addr_local;
+    u_char                   *p, text[NGX_SOCKADDR_STRLEN];
+    size_t                    len;
+
+    if (ngx_parse_addr(c->pool, &addr_peer,
+                       c->proxy_protocol->src_addr.data,
+                       c->proxy_protocol->src_addr.len) != NGX_OK)
+    {
+        return NGX_ERROR;
+    }
+
+    ngx_inet_set_port(addr_peer.sockaddr, c->proxy_protocol->src_port);
+
+    if (ngx_parse_addr(c->pool, &addr_local,
+                       c->proxy_protocol->dst_addr.data,
+                       c->proxy_protocol->dst_addr.len) != NGX_OK)
+    {
+        return NGX_ERROR;
+    }
+
+    ngx_inet_set_port(addr_local.sockaddr, c->proxy_protocol->dst_port);
+
+    len = ngx_sock_ntop(addr_peer.sockaddr, addr_peer.socklen, text,
+                        NGX_SOCKADDR_STRLEN, 0);
+    if (len == 0) {
+        return NGX_ERROR;
+    }
+
+    p = ngx_pnalloc(c->pool, len);
+    if (p == NULL) {
+        return NGX_ERROR;
+    }
+
+    ngx_memcpy(p, text, len);
+
+    c->sockaddr = addr_peer.sockaddr;
+    c->socklen = addr_peer.socklen;
+    c->addr_text.len = len;
+    c->addr_text.data = p;
+
+    len = ngx_sock_ntop(addr_local.sockaddr, addr_local.socklen, text,
+                        NGX_SOCKADDR_STRLEN, 0);
+    if (len == 0) {
+        return NGX_ERROR;
+    }
+
+    p = ngx_pnalloc(c->pool, len);
+    if (p == NULL) {
+        return NGX_ERROR;
+    }
+
+    ngx_memcpy(p, text, len);
+
+    c->local_sockaddr = addr_local.sockaddr;
+    c->local_socklen = addr_local.socklen;
+
+    return NGX_OK;
+}
+
+
+void
+ngx_mail_proxy_protocol_handler(ngx_event_t *rev)
+{
+    ngx_mail_core_srv_conf_t  *cscf;
+    ngx_mail_session_t        *s;
+    ngx_connection_t          *c;
+    u_char                    *p, buf[NGX_PROXY_PROTOCOL_MAX_HEADER];
+    size_t                     size;
+    ssize_t                    n;
+
+    c = rev->data;
+    s = c->data;
+
+    if (rev->timedout) {
+        ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT,
+                      "mail PROXY protocol header timed out");
+        c->timedout = 1;
+        ngx_mail_close_connection(c);
+        return;
+    }
+
+    ngx_log_debug0(NGX_LOG_DEBUG_MAIL, c->log, 0,
+                   "mail PROXY protocol handler");
+
+    cscf = ngx_mail_get_module_srv_conf(s, ngx_mail_core_module);
+
+    if (cscf->realip_from == NGX_CONF_UNSET_PTR) {
+        ngx_log_error(NGX_LOG_WARN, c->log, 0,
+                      "using PROXY protocol without set_real_ip_from");
+        ngx_mail_close_connection(c);
+        return;
+    }
+
+    if (ngx_cidr_match(c->sockaddr, cscf->realip_from) != NGX_OK) {
+        ngx_log_error(NGX_LOG_NOTICE, c->log, 0,
+                      "UNTRUSTED PROXY protocol provider: %V",
+                      &c->addr_text);
+        ngx_mail_close_connection(c);
+        return;
+    }
+
+    size = NGX_PROXY_PROTOCOL_MAX_HEADER;
+
+    n = recv(c->fd, (char *) buf, size, MSG_PEEK);
+
+    ngx_log_debug1(NGX_LOG_DEBUG, c->log, 0, "mail recv(): %z", n);
+
+    p = ngx_proxy_protocol_read(c, buf, buf + n);
+
+    if (p == NULL) {
+        ngx_mail_close_connection(c);
+        return;
+    }
+
+    ngx_log_error(NGX_LOG_NOTICE, c->log, 0,
+                  "PROXY protocol %V:%d => %V:%d",
+                  &c->proxy_protocol->src_addr,
+                  c->proxy_protocol->src_port,
+                  &c->proxy_protocol->dst_addr,
+                  c->proxy_protocol->dst_port);
+
+    size = p - buf;
+
+    if (c->recv(c, buf, size) != (ssize_t) size) {
+        ngx_mail_close_connection(c);
+        return;
+    }
+
+    if (ngx_mail_proxy_protoco_set_addrs(c) != NGX_OK) {
+        ngx_mail_close_connection(c);
+        return;
+    }
+
+    ngx_mail_init_connection_complete(c);
+}
+
+
+void
+ngx_mail_init_connection_complete(ngx_connection_t *c)
+{
 #if (NGX_MAIL_SSL)
     {
-    ngx_mail_ssl_conf_t  *sslcf;
+    ngx_mail_session_t        *s;
+    ngx_mail_ssl_conf_t       *sslcf;
+
+    s = c->data;
 
     sslcf = ngx_mail_get_module_srv_conf(s, ngx_mail_ssl_module);
 
-    if (sslcf->enable || addr_conf->ssl) {
+    if (sslcf->enable || s->addr_conf->ssl) {
         c->log->action = "SSL handshaking";
 
         ngx_mail_ssl_init_connection(&sslcf->ssl, c);
@@ -348,6 +519,7 @@
         return;
     }
 
+    c->log->action = "sending client greeting line";
     c->write->handler = ngx_mail_send;
 
     cscf->protocol->init_session(s, c);
diff -r 83c4622053b0 -r 4618e767b84c src/mail/ngx_mail_proxy_module.c
--- a/src/mail/ngx_mail_proxy_module.c	Tue Jan 12 16:59:31 2021 +0300
+++ b/src/mail/ngx_mail_proxy_module.c	Tue Jan 19 18:24:23 2021 +0300
@@ -19,6 +19,8 @@
     ngx_flag_t  smtp_auth;
     size_t      buffer_size;
     ngx_msec_t  timeout;
+    ngx_msec_t  connect_timeout;
+    ngx_flag_t  proxy_protocol;
 } ngx_mail_proxy_conf_t;
 
 
@@ -36,7 +38,9 @@
 static void *ngx_mail_proxy_create_conf(ngx_conf_t *cf);
 static char *ngx_mail_proxy_merge_conf(ngx_conf_t *cf, void *parent,
     void *child);
-
+static void ngx_mail_proxy_connect_handler(ngx_event_t *ev);
+static void ngx_mail_proxy_start(ngx_mail_session_t *s);
+static void ngx_mail_proxy_send_proxy_protocol(ngx_mail_session_t *s);
 
 static ngx_command_t  ngx_mail_proxy_commands[] = {
 
@@ -61,6 +65,13 @@
       offsetof(ngx_mail_proxy_conf_t, timeout),
       NULL },
 
+    { ngx_string("connect_timeout"),
+      NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_TAKE1,
+      ngx_conf_set_msec_slot,
+      NGX_MAIL_SRV_CONF_OFFSET,
+      offsetof(ngx_mail_proxy_conf_t, connect_timeout),
+      NULL },
+
     { ngx_string("proxy_pass_error_message"),
       NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_FLAG,
       ngx_conf_set_flag_slot,
@@ -82,6 +93,13 @@
       offsetof(ngx_mail_proxy_conf_t, smtp_auth),
       NULL },
 
+    { ngx_string("proxy_protocol"),
+      NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_FLAG,
+      ngx_conf_set_flag_slot,
+      NGX_MAIL_SRV_CONF_OFFSET,
+      offsetof(ngx_mail_proxy_conf_t, proxy_protocol),
+      NULL },
+
       ngx_null_command
 };
 
@@ -156,7 +174,6 @@
     p->upstream.connection->pool = s->connection->pool;
 
     s->connection->read->handler = ngx_mail_proxy_block_read;
-    p->upstream.connection->write->handler = ngx_mail_proxy_dummy_handler;
 
     pcf = ngx_mail_get_module_srv_conf(s, ngx_mail_proxy_module);
 
@@ -169,23 +186,139 @@
 
     s->out.len = 0;
 
+    if (rc == NGX_AGAIN) {
+        p->upstream.connection->write->handler = ngx_mail_proxy_connect_handler;
+        p->upstream.connection->read->handler = ngx_mail_proxy_connect_handler;
+
+        ngx_add_timer(p->upstream.connection->write, pcf->connect_timeout);
+
+        ngx_log_debug0(NGX_LOG_DEBUG_MAIL, s->connection->log, 0, "mail proxy delay connect");
+        return;
+    }
+
+    if (pcf->proxy_protocol) {
+        ngx_mail_proxy_send_proxy_protocol(s);
+        return;
+    }
+
+    ngx_mail_proxy_start(s);
+}
+
+
+void
+ngx_mail_proxy_connect_handler(ngx_event_t *ev)
+{
+    ngx_connection_t          *c;
+    ngx_mail_session_t        *s;
+    ngx_mail_proxy_conf_t     *pcf;
+
+    c = ev->data;
+    s = c->data;
+
+    if (ev->timedout) {
+        ngx_log_error(NGX_LOG_ERR, c->log, NGX_ETIMEDOUT, "upstream timed out");
+        ngx_mail_session_internal_server_error(s);
+        return;
+    }
+
+    ngx_del_timer(c->write);
+
+    ngx_log_debug0(NGX_LOG_DEBUG_MAIL, c->log, 0,
+                   "mail proxy connect upstream");
+
+    pcf = ngx_mail_get_module_srv_conf(s, ngx_mail_proxy_module);
+
+    if (pcf->proxy_protocol) {
+        ngx_mail_proxy_send_proxy_protocol(s);
+        return;
+    }
+
+    ngx_mail_proxy_start(s);
+}
+
+
+void
+ngx_mail_proxy_start(ngx_mail_session_t *s)
+{
+    ngx_connection_t             *pc;
+
+    pc = s->proxy->upstream.connection;
+
+    ngx_log_debug0(NGX_LOG_DEBUG_MAIL, s->connection->log, 0,
+                   "mail proxy starting");
+
+    pc->write->handler = ngx_mail_proxy_dummy_handler;
+
     switch (s->protocol) {
 
     case NGX_MAIL_POP3_PROTOCOL:
-        p->upstream.connection->read->handler = ngx_mail_proxy_pop3_handler;
+        pc->read->handler = ngx_mail_proxy_pop3_handler;
         s->mail_state = ngx_pop3_start;
         break;
 
     case NGX_MAIL_IMAP_PROTOCOL:
-        p->upstream.connection->read->handler = ngx_mail_proxy_imap_handler;
+        pc->read->handler = ngx_mail_proxy_imap_handler;
         s->mail_state = ngx_imap_start;
         break;
 
     default: /* NGX_MAIL_SMTP_PROTOCOL */
-        p->upstream.connection->read->handler = ngx_mail_proxy_smtp_handler;
+        pc->read->handler = ngx_mail_proxy_smtp_handler;
         s->mail_state = ngx_smtp_start;
         break;
     }
+
+    if (pc->read->ready) {
+        ngx_post_event(pc->read, &ngx_posted_events);
+    }
+}
+
+
+void
+ngx_mail_proxy_send_proxy_protocol(ngx_mail_session_t *s)
+{
+    u_char                       *p;
+    ssize_t                       n, size;
+    ngx_connection_t             *c, *pc;
+    ngx_peer_connection_t        *u;
+    u_char                        buf[NGX_PROXY_PROTOCOL_MAX_HEADER];
+
+    c = s->connection;
+
+    ngx_log_debug0(NGX_LOG_DEBUG_MAIL, c->log, 0,
+                   "mail proxy send PROXY protocol header");
+
+    p = ngx_proxy_protocol_write(c, buf, buf + NGX_PROXY_PROTOCOL_MAX_HEADER);
+    if (p == NULL) {
+        ngx_mail_proxy_internal_server_error(s);
+        return;
+    }
+
+    u = &s->proxy->upstream;
+
+    pc = u->connection;
+
+    size = p - buf;
+
+    n = pc->send(pc, buf, size);
+
+    if (n != size) {
+
+        /*
+         * PROXY protocol specification:
+         * The sender must always ensure that the header
+         * is sent at once, so that the transport layer
+         * maintains atomicity along the path to the receiver.
+         */
+
+        ngx_log_error(NGX_LOG_ERR, c->log, 0,
+                      "could not send PROXY protocol header at once (%z)", n);
+
+        ngx_mail_proxy_internal_server_error(s);
+
+        return;
+    }
+
+    ngx_mail_proxy_start(s);
 }
 
 
@@ -1184,6 +1317,8 @@
     pcf->smtp_auth = NGX_CONF_UNSET;
     pcf->buffer_size = NGX_CONF_UNSET_SIZE;
     pcf->timeout = NGX_CONF_UNSET_MSEC;
+    pcf->connect_timeout = NGX_CONF_UNSET_MSEC;
+    pcf->proxy_protocol = NGX_CONF_UNSET;
 
     return pcf;
 }
@@ -1202,6 +1337,8 @@
     ngx_conf_merge_size_value(conf->buffer_size, prev->buffer_size,
                               (size_t) ngx_pagesize);
     ngx_conf_merge_msec_value(conf->timeout, prev->timeout, 24 * 60 * 60000);
+    ngx_conf_merge_msec_value(conf->connect_timeout, prev->connect_timeout, 1000);
+    ngx_conf_merge_value(conf->proxy_protocol, prev->proxy_protocol, 0);
 
     return NGX_CONF_OK;
 }


More information about the nginx-devel mailing list