Merge branch 'nginx' (nginx-1.19.8).

Change-Id: I38faddb630d8f73659d9c01132999e6d58059ed3
Signed-off-by: Piotr Sikora <piotrsikora@google.com>
diff --git a/.hgtags b/.hgtags
index 139bd2f..316ef60 100644
--- a/.hgtags
+++ b/.hgtags
@@ -457,3 +457,4 @@
 8e5b068f761cd512d10c9671fbde0b568c1fd08b release-1.19.5
 f618488eb769e0ed74ef0d93cd118d2ad79ef94d release-1.19.6
 3fa6e2095a7a51acc630517e1c27a7b7ac41f7b3 release-1.19.7
+8c65d21464aaa5923775f80c32474adc7a320068 release-1.19.8
diff --git a/BUILD b/BUILD
index 8f63498..55ed1ac 100644
--- a/BUILD
+++ b/BUILD
@@ -1075,6 +1075,7 @@
         "src/mail/ngx_mail_core_module.c",
         "src/mail/ngx_mail_handler.c",
         "src/mail/ngx_mail_proxy_module.c",
+        "src/mail/ngx_mail_realip_module.c",
         "src/mail/ngx_mail_ssl_module.c",
         "src/mail/ngx_mail_ssl_module.h",
     ],
@@ -1536,5 +1537,5 @@
     preinst = "@nginx_pkgoss//:debian_preinst",
     prerm = "@nginx_pkgoss//:debian_prerm",
     section = "httpd",
-    version = "1.19.7",
+    version = "1.19.8",
 )
diff --git a/auto/init b/auto/init
index 71fd93f..979d267 100644
--- a/auto/init
+++ b/auto/init
@@ -50,6 +50,8 @@
 
 clean:
 	rm -rf Makefile $NGX_OBJS
+
+.PHONY:	default clean
 END
 
 fi
diff --git a/auto/install b/auto/install
index c97b12b..fc61fa0 100644
--- a/auto/install
+++ b/auto/install
@@ -215,4 +215,6 @@
 	test -f $NGX_PID_PATH.oldbin
 
 	kill -QUIT \`cat $NGX_PID_PATH.oldbin\`
+
+.PHONY:	build install modules upgrade
 END
diff --git a/auto/modules b/auto/modules
index e9d2923..d56e2f6 100644
--- a/auto/modules
+++ b/auto/modules
@@ -985,6 +985,12 @@
     ngx_module_srcs=src/mail/ngx_mail_proxy_module.c
 
     . $NGX_AUTO/module
+
+    ngx_module_name=ngx_mail_realip_module
+    ngx_module_deps=
+    ngx_module_srcs=src/mail/ngx_mail_realip_module.c
+
+    . $NGX_AUTO/module
 fi
 
 
diff --git a/auto/unix b/auto/unix
index c3276f2..e4f9159 100644
--- a/auto/unix
+++ b/auto/unix
@@ -727,19 +727,35 @@
 . $NGX_AUTO/feature
 
 
-ngx_feature="sys_nerr"
-ngx_feature_name="NGX_SYS_NERR"
-ngx_feature_run=value
-ngx_feature_incs='#include <errno.h>
-                  #include <stdio.h>'
+# strerrordesc_np(), introduced in glibc 2.32
+
+ngx_feature="strerrordesc_np()"
+ngx_feature_name="NGX_HAVE_STRERRORDESC_NP"
+ngx_feature_run=no
+ngx_feature_incs='#include <string.h>'
 ngx_feature_path=
 ngx_feature_libs=
-ngx_feature_test='printf("%d", sys_nerr);'
+ngx_feature_test="char *p; p = strerrordesc_np(0);
+                  if (p == NULL) return 1"
 . $NGX_AUTO/feature
 
 
 if [ $ngx_found = no ]; then
 
+    ngx_feature="sys_nerr"
+    ngx_feature_name="NGX_SYS_NERR"
+    ngx_feature_run=value
+    ngx_feature_incs='#include <errno.h>
+                      #include <stdio.h>'
+    ngx_feature_path=
+    ngx_feature_libs=
+    ngx_feature_test='printf("%d", sys_nerr);'
+    . $NGX_AUTO/feature
+fi
+
+
+if [ $ngx_found = no ]; then
+
     # Cygiwn defines _sys_nerr
     ngx_feature="_sys_nerr"
     ngx_feature_name="NGX_SYS_NERR"
@@ -753,34 +769,6 @@
 fi
 
 
-if [ $ngx_found = no ]; then
-
-    # Solaris has no sys_nerr
-    ngx_feature='maximum errno'
-    ngx_feature_name=NGX_SYS_NERR
-    ngx_feature_run=value
-    ngx_feature_incs='#include <errno.h>
-                      #include <string.h>
-                      #include <stdio.h>'
-    ngx_feature_path=
-    ngx_feature_libs=
-    ngx_feature_test='int  n;
-                      char *p;
-                      for (n = 1; n < 1000; n++) {
-                          errno = 0;
-                          p = strerror(n);
-                          if (errno == EINVAL
-                              || p == NULL
-                              || strncmp(p, "Unknown error", 13) == 0)
-                          {
-                              break;
-                          }
-                      }
-                      printf("%d", n);'
-    . $NGX_AUTO/feature
-fi
-
-
 ngx_feature="localtime_r()"
 ngx_feature_name="NGX_HAVE_LOCALTIME_R"
 ngx_feature_run=no
diff --git a/build.bzl b/build.bzl
index 1b74db3..76b3e06 100644
--- a/build.bzl
+++ b/build.bzl
@@ -673,9 +673,9 @@
         name = "nginx_pkgoss",
         build_file_content = _PKGOSS_BUILD_FILE.format(nginx = nginx) +
                              _PKGOSS_BUILD_FILE_TAIL,
-        commit = "07ce85eee27f01a69cd70bb1f251e546f2fa750b",  # nginx-1.19.7
+        commit = "f6674ffb8cbba3096aea8ad52951ce0eb71a0f31",  # nginx-1.19.8
         remote = "https://nginx.googlesource.com/nginx-pkgoss",
-        shallow_since = "1613491796 +0300",
+        shallow_since = "1615305446 +0300",
     )
 
 def nginx_repositories_zlib(bind):
diff --git a/contrib/vim/syntax/nginx.vim b/contrib/vim/syntax/nginx.vim
index 2d5ed06..88ec847 100644
--- a/contrib/vim/syntax/nginx.vim
+++ b/contrib/vim/syntax/nginx.vim
@@ -2414,26 +2414,26 @@
 
 " highlight
 
-hi link ngxComment Comment
-hi link ngxParamComment Comment
-hi link ngxListenComment Comment
-hi link ngxVariable Identifier
-hi link ngxVariableString PreProc
-hi link ngxString String
-hi link ngxListenString String
+hi def link ngxComment Comment
+hi def link ngxParamComment Comment
+hi def link ngxListenComment Comment
+hi def link ngxVariable Identifier
+hi def link ngxVariableString PreProc
+hi def link ngxString String
+hi def link ngxListenString String
 
-hi link ngxBoolean Boolean
-hi link ngxDirectiveBlock Statement
-hi link ngxDirectiveImportant Type
-hi link ngxDirectiveListen Type
-hi link ngxDirectiveControl Keyword
-hi link ngxDirectiveError Constant
-hi link ngxDirectiveDeprecated Error
-hi link ngxDirective Identifier
-hi link ngxDirectiveThirdParty Special
-hi link ngxDirectiveThirdPartyDeprecated Error
+hi def link ngxBoolean Boolean
+hi def link ngxDirectiveBlock Statement
+hi def link ngxDirectiveImportant Type
+hi def link ngxDirectiveListen Type
+hi def link ngxDirectiveControl Keyword
+hi def link ngxDirectiveError Constant
+hi def link ngxDirectiveDeprecated Error
+hi def link ngxDirective Identifier
+hi def link ngxDirectiveThirdParty Special
+hi def link ngxDirectiveThirdPartyDeprecated Error
 
-hi link ngxListenOptions Keyword
-hi link ngxListenOptionsDeprecated Error
+hi def link ngxListenOptions Keyword
+hi def link ngxListenOptionsDeprecated Error
 
 let b:current_syntax = "nginx"
diff --git a/docs/xml/nginx/changes.xml b/docs/xml/nginx/changes.xml
index 6d407f0..1c7fb26 100644
--- a/docs/xml/nginx/changes.xml
+++ b/docs/xml/nginx/changes.xml
@@ -5,6 +5,68 @@
 <change_log title="nginx">
 
 
+<changes ver="1.19.8" date="2021-03-09">
+
+<change type="feature">
+<para lang="ru">
+в директиве proxy_cookie_flags теперь
+флаги можно задавать с помощью переменных.
+</para>
+<para lang="en">
+flags in the "proxy_cookie_flags" directive
+can now contain variables.
+</para>
+</change>
+
+<change type="feature">
+<para lang="ru">
+параметр proxy_protocol в директиве listen,
+директивы proxy_protocol и set_real_ip_from
+в почтовом прокси-сервере.
+</para>
+<para lang="en">
+the "proxy_protocol" parameter of the "listen" directive,
+the "proxy_protocol" and "set_real_ip_from" directives
+in mail proxy.
+</para>
+</change>
+
+<change type="bugfix">
+<para lang="ru">
+HTTP/2-соединения сразу закрывались
+при использовании "keepalive_timeout 0";
+ошибка появилась в 1.19.7.
+</para>
+<para lang="en">
+HTTP/2 connections were immediately closed
+when using "keepalive_timeout 0";
+the bug had appeared in 1.19.7.
+</para>
+</change>
+
+<change type="bugfix">
+<para lang="ru">
+некоторые ошибки логгировались как неизвестные,
+если nginx был собран с glibc 2.32.
+</para>
+<para lang="en">
+some errors were logged as unknown
+if nginx was built with glibc 2.32.
+</para>
+</change>
+
+<change type="bugfix">
+<para lang="ru">
+в методе обработки соединений eventport.
+</para>
+<para lang="en">
+in the eventport method.
+</para>
+</change>
+
+</changes>
+
+
 <changes ver="1.19.7" date="2021-02-16">
 
 <change type="change">
diff --git a/misc/GNUmakefile b/misc/GNUmakefile
index 5c75505..3a2ef5b 100644
--- a/misc/GNUmakefile
+++ b/misc/GNUmakefile
@@ -6,7 +6,7 @@
 
 CC =		cl
 OBJS =		objs.msvc8
-OPENSSL =	openssl-1.1.1i
+OPENSSL =	openssl-1.1.1j
 ZLIB =		zlib-1.2.11
 PCRE =		pcre-8.44
 
diff --git a/src/core/nginx.h b/src/core/nginx.h
index 26b017e..0b9c56f 100644
--- a/src/core/nginx.h
+++ b/src/core/nginx.h
@@ -13,8 +13,8 @@
 #define NGINX_NAME         "nginx"
 #endif
 
-#define nginx_version      1019007
-#define NGINX_VERSION      "1.19.7"
+#define nginx_version      1019008
+#define NGINX_VERSION      "1.19.8"
 #define NGINX_VER          NGINX_NAME "/" NGINX_VERSION
 
 #ifdef NGX_BUILD
diff --git a/src/event/ngx_event.c b/src/event/ngx_event.c
index b96d25b..b97ddee 100644
--- a/src/event/ngx_event.c
+++ b/src/event/ngx_event.c
@@ -318,7 +318,7 @@
             return NGX_OK;
         }
 
-        if (rev->oneshot && !rev->ready) {
+        if (rev->oneshot && rev->ready) {
             if (ngx_del_event(rev, NGX_READ_EVENT, 0) == NGX_ERROR) {
                 return NGX_ERROR;
             }
diff --git a/src/event/ngx_event_openssl.c b/src/event/ngx_event_openssl.c
index 2929a2e..69ec660 100644
--- a/src/event/ngx_event_openssl.c
+++ b/src/event/ngx_event_openssl.c
@@ -83,7 +83,7 @@
 #if OPENSSL_VERSION_NUMBER > 0x10100000L
     const
 #endif
-    ASN1_TIME *asn1time);
+    ASN1_TIME *asn1time, ngx_log_t *log);
 
 static void *ngx_openssl_create_conf(ngx_cycle_t *cycle);
 static char *ngx_openssl_engine(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
@@ -1037,26 +1037,52 @@
 
     c = ngx_ssl_get_connection(ssl_conn);
 
+    if (!(c->log->log_level & NGX_LOG_DEBUG_EVENT)) {
+        return 1;
+    }
+
     cert = X509_STORE_CTX_get_current_cert(x509_store);
     err = X509_STORE_CTX_get_error(x509_store);
     depth = X509_STORE_CTX_get_error_depth(x509_store);
 
     sname = X509_get_subject_name(cert);
-    subject = sname ? X509_NAME_oneline(sname, NULL, 0) : "(none)";
+
+    if (sname) {
+        subject = X509_NAME_oneline(sname, NULL, 0);
+        if (subject == NULL) {
+            ngx_ssl_error(NGX_LOG_ALERT, c->log, 0,
+                          "X509_NAME_oneline() failed");
+        }
+
+    } else {
+        subject = NULL;
+    }
 
     iname = X509_get_issuer_name(cert);
-    issuer = iname ? X509_NAME_oneline(iname, NULL, 0) : "(none)";
+
+    if (iname) {
+        issuer = X509_NAME_oneline(iname, NULL, 0);
+        if (issuer == NULL) {
+            ngx_ssl_error(NGX_LOG_ALERT, c->log, 0,
+                          "X509_NAME_oneline() failed");
+        }
+
+    } else {
+        issuer = NULL;
+    }
 
     ngx_log_debug5(NGX_LOG_DEBUG_EVENT, c->log, 0,
                    "verify:%d, error:%d, depth:%d, "
                    "subject:\"%s\", issuer:\"%s\"",
-                   ok, err, depth, subject, issuer);
+                   ok, err, depth,
+                   subject ? subject : "(none)",
+                   issuer ? issuer : "(none)");
 
-    if (sname) {
+    if (subject) {
         OPENSSL_free(subject);
     }
 
-    if (iname) {
+    if (issuer) {
         OPENSSL_free(issuer);
     }
 #endif
@@ -1971,6 +1997,10 @@
 #endif
     SSL_CIPHER  *cipher;
 
+    if (!(c->log->log_level & NGX_LOG_DEBUG_EVENT)) {
+        return;
+    }
+
     cipher = SSL_get_current_cipher(c->ssl->connection);
 
     if (cipher) {
@@ -4821,11 +4851,13 @@
 
     bio = BIO_new(BIO_s_mem());
     if (bio == NULL) {
+        ngx_ssl_error(NGX_LOG_ALERT, c->log, 0, "BIO_new() failed");
         X509_free(cert);
         return NGX_ERROR;
     }
 
     if (X509_NAME_print_ex(bio, name, 0, XN_FLAG_RFC2253) < 0) {
+        ngx_ssl_error(NGX_LOG_ALERT, c->log, 0, "X509_NAME_print_ex() failed");
         goto failed;
     }
 
@@ -4873,11 +4905,13 @@
 
     bio = BIO_new(BIO_s_mem());
     if (bio == NULL) {
+        ngx_ssl_error(NGX_LOG_ALERT, c->log, 0, "BIO_new() failed");
         X509_free(cert);
         return NGX_ERROR;
     }
 
     if (X509_NAME_print_ex(bio, name, 0, XN_FLAG_RFC2253) < 0) {
+        ngx_ssl_error(NGX_LOG_ALERT, c->log, 0, "X509_NAME_print_ex() failed");
         goto failed;
     }
 
@@ -4926,6 +4960,11 @@
     }
 
     p = X509_NAME_oneline(name, NULL, 0);
+    if (p == NULL) {
+        ngx_ssl_error(NGX_LOG_ALERT, c->log, 0, "X509_NAME_oneline() failed");
+        X509_free(cert);
+        return NGX_ERROR;
+    }
 
     for (len = 0; p[len]; len++) { /* void */ }
 
@@ -4969,6 +5008,11 @@
     }
 
     p = X509_NAME_oneline(name, NULL, 0);
+    if (p == NULL) {
+        ngx_ssl_error(NGX_LOG_ALERT, c->log, 0, "X509_NAME_oneline() failed");
+        X509_free(cert);
+        return NGX_ERROR;
+    }
 
     for (len = 0; p[len]; len++) { /* void */ }
 
@@ -5005,6 +5049,7 @@
 
     bio = BIO_new(BIO_s_mem());
     if (bio == NULL) {
+        ngx_ssl_error(NGX_LOG_ALERT, c->log, 0, "BIO_new() failed");
         X509_free(cert);
         return NGX_ERROR;
     }
@@ -5043,6 +5088,7 @@
     }
 
     if (!X509_digest(cert, EVP_sha1(), buf, &len)) {
+        ngx_ssl_error(NGX_LOG_ALERT, c->log, 0, "X509_digest() failed");
         X509_free(cert);
         return NGX_ERROR;
     }
@@ -5116,6 +5162,7 @@
 
     bio = BIO_new(BIO_s_mem());
     if (bio == NULL) {
+        ngx_ssl_error(NGX_LOG_ALERT, c->log, 0, "BIO_new() failed");
         X509_free(cert);
         return NGX_ERROR;
     }
@@ -5160,6 +5207,7 @@
 
     bio = BIO_new(BIO_s_mem());
     if (bio == NULL) {
+        ngx_ssl_error(NGX_LOG_ALERT, c->log, 0, "BIO_new() failed");
         X509_free(cert);
         return NGX_ERROR;
     }
@@ -5202,9 +5250,9 @@
     }
 
 #if OPENSSL_VERSION_NUMBER > 0x10100000L
-    end = ngx_ssl_parse_time(X509_get0_notAfter(cert));
+    end = ngx_ssl_parse_time(X509_get0_notAfter(cert), c->log);
 #else
-    end = ngx_ssl_parse_time(X509_get_notAfter(cert));
+    end = ngx_ssl_parse_time(X509_get_notAfter(cert), c->log);
 #endif
 
     if (end == (time_t) NGX_ERROR) {
@@ -5239,7 +5287,7 @@
 #if OPENSSL_VERSION_NUMBER > 0x10100000L
     const
 #endif
-    ASN1_TIME *asn1time)
+    ASN1_TIME *asn1time, ngx_log_t *log)
 {
     BIO     *bio;
     char    *value;
@@ -5255,6 +5303,7 @@
 
     bio = BIO_new(BIO_s_mem());
     if (bio == NULL) {
+        ngx_ssl_error(NGX_LOG_ALERT, log, 0, "BIO_new() failed");
         return NGX_ERROR;
     }
 
diff --git a/src/http/modules/ngx_http_grpc_module.c b/src/http/modules/ngx_http_grpc_module.c
index aa75765..f5bf575 100644
--- a/src/http/modules/ngx_http_grpc_module.c
+++ b/src/http/modules/ngx_http_grpc_module.c
@@ -4841,9 +4841,9 @@
 {
 #ifndef SSL_CONF_FLAG_FILE
     return "is not supported on this platform";
-#endif
-
+#else
     return NGX_CONF_OK;
+#endif
 }
 
 
diff --git a/src/http/modules/ngx_http_proxy_module.c b/src/http/modules/ngx_http_proxy_module.c
index e2bf547..c309ab9 100644
--- a/src/http/modules/ngx_http_proxy_module.c
+++ b/src/http/modules/ngx_http_proxy_module.c
@@ -56,7 +56,7 @@
 #endif
     } cookie;
 
-    ngx_uint_t                     flags;
+    ngx_array_t                    flags_values;
     ngx_uint_t                     regex;
 } ngx_http_proxy_cookie_flags_t;
 
@@ -2923,12 +2923,14 @@
 ngx_http_proxy_rewrite_cookie_flags(ngx_http_request_t *r, ngx_array_t *attrs,
     ngx_array_t *flags)
 {
-    ngx_str_t                       pattern;
+    ngx_str_t                       pattern, value;
 #if (NGX_PCRE)
     ngx_int_t                       rc;
 #endif
-    ngx_uint_t                      i;
+    ngx_uint_t                      i, m, f, nelts;
     ngx_keyval_t                   *attr;
+    ngx_conf_bitmask_t             *mask;
+    ngx_http_complex_value_t       *flags_values;
     ngx_http_proxy_cookie_flags_t  *pcf;
 
     attr = attrs->elts;
@@ -2972,7 +2974,47 @@
         return NGX_DECLINED;
     }
 
-    return ngx_http_proxy_edit_cookie_flags(r, attrs, pcf[i].flags);
+    nelts = pcf[i].flags_values.nelts;
+    flags_values = pcf[i].flags_values.elts;
+
+    mask = ngx_http_proxy_cookie_flags_masks;
+    f = 0;
+
+    for (i = 0; i < nelts; i++) {
+
+        if (ngx_http_complex_value(r, &flags_values[i], &value) != NGX_OK) {
+            return NGX_ERROR;
+        }
+
+        if (value.len == 0) {
+            continue;
+        }
+
+        for (m = 0; mask[m].name.len != 0; m++) {
+
+            if (mask[m].name.len != value.len
+                || ngx_strncasecmp(mask[m].name.data, value.data, value.len)
+                   != 0)
+            {
+                continue;
+            }
+
+            f |= mask[m].mask;
+
+            break;
+        }
+
+        if (mask[m].name.len == 0) {
+            ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+                           "invalid proxy_cookie_flags flag \"%V\"", &value);
+        }
+    }
+
+    if (f == 0) {
+        return NGX_DECLINED;
+    }
+
+    return ngx_http_proxy_edit_cookie_flags(r, attrs, f);
 }
 
 
@@ -4544,8 +4586,8 @@
     ngx_http_proxy_loc_conf_t *plcf = conf;
 
     ngx_str_t                         *value;
-    ngx_uint_t                         i, m;
-    ngx_conf_bitmask_t                *mask;
+    ngx_uint_t                         i;
+    ngx_http_complex_value_t          *cv;
     ngx_http_proxy_cookie_flags_t     *pcf;
     ngx_http_compile_complex_value_t   ccv;
 #if (NGX_PCRE)
@@ -4629,32 +4671,27 @@
         }
     }
 
-    mask = ngx_http_proxy_cookie_flags_masks;
-    pcf->flags = 0;
+    if (ngx_array_init(&pcf->flags_values, cf->pool, cf->args->nelts - 2,
+                       sizeof(ngx_http_complex_value_t))
+        != NGX_OK)
+    {
+        return NGX_CONF_ERROR;
+    }
 
     for (i = 2; i < cf->args->nelts; i++) {
-        for (m = 0; mask[m].name.len != 0; m++) {
 
-            if (mask[m].name.len != value[i].len
-                || ngx_strcasecmp(mask[m].name.data, value[i].data) != 0)
-            {
-                continue;
-            }
-
-            if (pcf->flags & mask[m].mask) {
-                ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
-                                   "duplicate parameter \"%V\"", &value[i]);
-                return NGX_CONF_ERROR;
-            }
-
-            pcf->flags |= mask[m].mask;
-
-            break;
+        cv = ngx_array_push(&pcf->flags_values);
+        if (cv == NULL) {
+            return NGX_CONF_ERROR;
         }
 
-        if (mask[m].name.len == 0) {
-            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
-                               "invalid parameter \"%V\"", &value[i]);
+        ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t));
+
+        ccv.cf = cf;
+        ccv.value = &value[i];
+        ccv.complex_value = cv;
+
+        if (ngx_http_compile_complex_value(&ccv) != NGX_OK) {
             return NGX_CONF_ERROR;
         }
     }
@@ -4906,9 +4943,9 @@
 {
 #ifndef SSL_CONF_FLAG_FILE
     return "is not supported on this platform";
-#endif
-
+#else
     return NGX_CONF_OK;
+#endif
 }
 
 
diff --git a/src/http/modules/ngx_http_ssl_module.c b/src/http/modules/ngx_http_ssl_module.c
index b8ea065..8ec1415 100644
--- a/src/http/modules/ngx_http_ssl_module.c
+++ b/src/http/modules/ngx_http_ssl_module.c
@@ -1287,9 +1287,9 @@
 {
 #ifndef SSL_CONF_FLAG_FILE
     return "is not supported on this platform";
-#endif
-
+#else
     return NGX_CONF_OK;
+#endif
 }
 
 
diff --git a/src/http/modules/ngx_http_uwsgi_module.c b/src/http/modules/ngx_http_uwsgi_module.c
index bf27326..1334f44 100644
--- a/src/http/modules/ngx_http_uwsgi_module.c
+++ b/src/http/modules/ngx_http_uwsgi_module.c
@@ -2398,9 +2398,9 @@
 {
 #ifndef SSL_CONF_FLAG_FILE
     return "is not supported on this platform";
-#endif
-
+#else
     return NGX_CONF_OK;
+#endif
 }
 
 
diff --git a/src/http/v2/ngx_http_v2.c b/src/http/v2/ngx_http_v2.c
index 98d4abd..ac3e869 100644
--- a/src/http/v2/ngx_http_v2.c
+++ b/src/http/v2/ngx_http_v2.c
@@ -239,6 +239,7 @@
     ngx_http_v2_srv_conf_t    *h2scf;
     ngx_http_v2_main_conf_t   *h2mcf;
     ngx_http_v2_connection_t  *h2c;
+    ngx_http_core_srv_conf_t  *cscf;
 
     c = rev->data;
     hc = c->data;
@@ -326,8 +327,10 @@
     rev->handler = ngx_http_v2_read_handler;
     c->write->handler = ngx_http_v2_write_handler;
 
-    if (c->read->timer_set) {
-        ngx_del_timer(c->read);
+    if (!rev->timer_set) {
+        cscf = ngx_http_get_module_srv_conf(hc->conf_ctx,
+                                            ngx_http_core_module);
+        ngx_add_timer(rev, cscf->client_header_timeout);
     }
 
     c->idle = 1;
diff --git a/src/mail/ngx_mail.c b/src/mail/ngx_mail.c
index f17c2cc..890d815 100644
--- a/src/mail/ngx_mail.c
+++ b/src/mail/ngx_mail.c
@@ -405,6 +405,7 @@
 #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 @@
 #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
index 25ac432..b865a3b 100644
--- a/src/mail/ngx_mail.h
+++ b/src/mail/ngx_mail.h
@@ -41,6 +41,7 @@
     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 {
     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 {
@@ -176,6 +178,7 @@
 typedef struct {
     ngx_peer_connection_t   upstream;
     ngx_buf_t              *buffer;
+    ngx_uint_t              proxy_protocol;  /* unsigned  proxy_protocol:1; */
 } ngx_mail_proxy_ctx_t;
 
 
@@ -197,6 +200,7 @@
 
     ngx_uint_t              mail_state;
 
+    unsigned                ssl:1;
     unsigned                protocol:3;
     unsigned                blocked:1;
     unsigned                quit:1;
@@ -405,6 +409,7 @@
 /* STUB */
 void ngx_mail_proxy_init(ngx_mail_session_t *s, ngx_addr_t *peer);
 void ngx_mail_auth_http_init(ngx_mail_session_t *s);
+ngx_int_t ngx_mail_realip_handler(ngx_mail_session_t *s);
 /**/
 
 
diff --git a/src/mail/ngx_mail_auth_http_module.c b/src/mail/ngx_mail_auth_http_module.c
index 6b57358..06ded47 100644
--- a/src/mail/ngx_mail_auth_http_module.c
+++ b/src/mail/ngx_mail_auth_http_module.c
@@ -1224,22 +1224,49 @@
           + sizeof("Client-IP: ") - 1 + s->connection->addr_text.len
                 + sizeof(CRLF) - 1
           + sizeof("Client-Host: ") - 1 + s->host.len + sizeof(CRLF) - 1
-          + sizeof("Auth-SMTP-Helo: ") - 1 + s->smtp_helo.len + sizeof(CRLF) - 1
-          + sizeof("Auth-SMTP-From: ") - 1 + s->smtp_from.len + sizeof(CRLF) - 1
-          + sizeof("Auth-SMTP-To: ") - 1 + s->smtp_to.len + sizeof(CRLF) - 1
-#if (NGX_MAIL_SSL)
-          + sizeof("Auth-SSL: on" CRLF) - 1
-          + sizeof("Auth-SSL-Verify: ") - 1 + verify.len + sizeof(CRLF) - 1
-          + sizeof("Auth-SSL-Subject: ") - 1 + subject.len + sizeof(CRLF) - 1
-          + sizeof("Auth-SSL-Issuer: ") - 1 + issuer.len + sizeof(CRLF) - 1
-          + sizeof("Auth-SSL-Serial: ") - 1 + serial.len + sizeof(CRLF) - 1
-          + sizeof("Auth-SSL-Fingerprint: ") - 1 + fingerprint.len
-              + sizeof(CRLF) - 1
-          + sizeof("Auth-SSL-Cert: ") - 1 + cert.len + sizeof(CRLF) - 1
-#endif
           + 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
+               + sizeof("Auth-SMTP-From: ") - 1 + s->smtp_from.len
+                     + sizeof(CRLF) - 1
+               + sizeof("Auth-SMTP-To: ") - 1 + s->smtp_to.len
+                     + sizeof(CRLF) - 1;
+    }
+
+#if (NGX_MAIL_SSL)
+
+    if (c->ssl) {
+        len += sizeof("Auth-SSL: on" CRLF) - 1
+               + sizeof("Auth-SSL-Verify: ") - 1 + verify.len
+                     + sizeof(CRLF) - 1
+               + sizeof("Auth-SSL-Subject: ") - 1 + subject.len
+                     + sizeof(CRLF) - 1
+               + sizeof("Auth-SSL-Issuer: ") - 1 + issuer.len
+                     + sizeof(CRLF) - 1
+               + sizeof("Auth-SSL-Serial: ") - 1 + serial.len
+                     + sizeof(CRLF) - 1
+               + sizeof("Auth-SSL-Fingerprint: ") - 1 + fingerprint.len
+                     + sizeof(CRLF) - 1
+               + sizeof("Auth-SSL-Cert: ") - 1 + cert.len
+                     + sizeof(CRLF) - 1;
+    }
+
+#endif
+
     b = ngx_create_temp_buf(pool, len);
     if (b == NULL) {
         return NULL;
@@ -1298,6 +1325,26 @@
         *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
index e16d702..4083124 100644
--- a/src/mail/ngx_mail_core_module.c
+++ b/src/mail/ngx_mail_core_module.c
@@ -548,6 +548,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;
diff --git a/src/mail/ngx_mail_handler.c b/src/mail/ngx_mail_handler.c
index 803a247..b901053 100644
--- a/src/mail/ngx_mail_handler.c
+++ b/src/mail/ngx_mail_handler.c
@@ -11,6 +11,8 @@
 #include <ngx_mail.h>
 
 
+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);
 
 #if (NGX_MAIL_SSL)
@@ -26,6 +28,7 @@
 {
     size_t                     len;
     ngx_uint_t                 i;
+    ngx_event_t               *rev;
     ngx_mail_port_t           *port;
     struct sockaddr           *sa;
     struct sockaddr_in        *sin;
@@ -129,6 +132,10 @@
     s->main_conf = addr_conf->ctx->main_conf;
     s->srv_conf = addr_conf->ctx->srv_conf;
 
+#if (NGX_MAIL_SSL)
+    s->ssl = addr_conf->ssl;
+#endif
+
     s->addr_text = &addr_conf->addr_text;
 
     c->data = s;
@@ -159,13 +166,125 @@
 
     c->log_error = NGX_ERROR_INFO;
 
+    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;
+    }
+
+    rev->handler(rev);
+}
+
+
+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;
+    }
+
+    if (ngx_mail_realip_handler(s) != NGX_OK) {
+        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;
+    ngx_mail_session_t  *s;
+
+    c = rev->data;
+    s = c->data;
+
 #if (NGX_MAIL_SSL)
     {
     ngx_mail_ssl_conf_t  *sslcf;
 
     sslcf = ngx_mail_get_module_srv_conf(s, ngx_mail_ssl_module);
 
-    if (sslcf->enable || addr_conf->ssl) {
+    if (sslcf->enable || s->ssl) {
         c->log->action = "SSL handshaking";
 
         ngx_mail_ssl_init_connection(&sslcf->ssl, c);
@@ -215,9 +334,10 @@
 
         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;
 
@@ -338,6 +458,8 @@
 
     s = c->data;
 
+    c->log->action = "sending client greeting line";
+
     cscf = ngx_mail_get_module_srv_conf(s, ngx_mail_core_module);
 
     s->protocol = cscf->protocol->type;
@@ -722,11 +844,6 @@
     }
 
     if (n == NGX_AGAIN) {
-        if (ngx_handle_read_event(c->read, 0) != NGX_OK) {
-            ngx_mail_session_internal_server_error(s);
-            return NGX_ERROR;
-        }
-
         if (s->buffer->pos == s->buffer->last) {
             return NGX_AGAIN;
         }
diff --git a/src/mail/ngx_mail_imap_handler.c b/src/mail/ngx_mail_imap_handler.c
index 3bf09ec..5dfdd76 100644
--- a/src/mail/ngx_mail_imap_handler.c
+++ b/src/mail/ngx_mail_imap_handler.c
@@ -123,6 +123,12 @@
     if (s->out.len) {
         ngx_log_debug0(NGX_LOG_DEBUG_MAIL, c->log, 0, "imap send handler busy");
         s->blocked = 1;
+
+        if (ngx_handle_read_event(c->read, 0) != NGX_OK) {
+            ngx_mail_close_connection(c);
+            return;
+        }
+
         return;
     }
 
@@ -130,7 +136,16 @@
 
     rc = ngx_mail_read_command(s, c);
 
-    if (rc == NGX_AGAIN || rc == NGX_ERROR) {
+    if (rc == NGX_AGAIN) {
+        if (ngx_handle_read_event(c->read, 0) != NGX_OK) {
+            ngx_mail_session_internal_server_error(s);
+            return;
+        }
+
+        return;
+    }
+
+    if (rc == NGX_ERROR) {
         return;
     }
 
@@ -293,6 +308,11 @@
         }
     }
 
+    if (ngx_handle_read_event(c->read, 0) != NGX_OK) {
+        ngx_mail_session_internal_server_error(s);
+        return;
+    }
+
     ngx_mail_send(c->write);
 }
 
diff --git a/src/mail/ngx_mail_pop3_handler.c b/src/mail/ngx_mail_pop3_handler.c
index 9310c27..edfd986 100644
--- a/src/mail/ngx_mail_pop3_handler.c
+++ b/src/mail/ngx_mail_pop3_handler.c
@@ -138,6 +138,12 @@
     if (s->out.len) {
         ngx_log_debug0(NGX_LOG_DEBUG_MAIL, c->log, 0, "pop3 send handler busy");
         s->blocked = 1;
+
+        if (ngx_handle_read_event(c->read, 0) != NGX_OK) {
+            ngx_mail_close_connection(c);
+            return;
+        }
+
         return;
     }
 
@@ -145,7 +151,16 @@
 
     rc = ngx_mail_read_command(s, c);
 
-    if (rc == NGX_AGAIN || rc == NGX_ERROR) {
+    if (rc == NGX_AGAIN) {
+        if (ngx_handle_read_event(c->read, 0) != NGX_OK) {
+            ngx_mail_session_internal_server_error(s);
+            return;
+        }
+
+        return;
+    }
+
+    if (rc == NGX_ERROR) {
         return;
     }
 
@@ -275,6 +290,11 @@
             s->arg_start = s->buffer->start;
         }
 
+        if (ngx_handle_read_event(c->read, 0) != NGX_OK) {
+            ngx_mail_session_internal_server_error(s);
+            return;
+        }
+
         ngx_mail_send(c->write);
     }
 }
diff --git a/src/mail/ngx_mail_proxy_module.c b/src/mail/ngx_mail_proxy_module.c
index 610f547..66aa0ba 100644
--- a/src/mail/ngx_mail_proxy_module.c
+++ b/src/mail/ngx_mail_proxy_module.c
@@ -17,6 +17,7 @@
     ngx_flag_t  pass_error_message;
     ngx_flag_t  xclient;
     ngx_flag_t  smtp_auth;
+    ngx_flag_t  proxy_protocol;
     size_t      buffer_size;
     ngx_msec_t  timeout;
 } ngx_mail_proxy_conf_t;
@@ -26,7 +27,8 @@
 static void ngx_mail_proxy_pop3_handler(ngx_event_t *rev);
 static void ngx_mail_proxy_imap_handler(ngx_event_t *rev);
 static void ngx_mail_proxy_smtp_handler(ngx_event_t *rev);
-static void ngx_mail_proxy_dummy_handler(ngx_event_t *ev);
+static void ngx_mail_proxy_write_handler(ngx_event_t *wev);
+static ngx_int_t ngx_mail_proxy_send_proxy_protocol(ngx_mail_session_t *s);
 static ngx_int_t ngx_mail_proxy_read_response(ngx_mail_session_t *s,
     ngx_uint_t state);
 static void ngx_mail_proxy_handler(ngx_event_t *ev);
@@ -82,6 +84,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 +165,7 @@
     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;
+    p->upstream.connection->write->handler = ngx_mail_proxy_write_handler;
 
     pcf = ngx_mail_get_module_srv_conf(s, ngx_mail_proxy_module);
 
@@ -167,6 +176,8 @@
         return;
     }
 
+    s->proxy->proxy_protocol = pcf->proxy_protocol;
+
     s->out.len = 0;
 
     switch (s->protocol) {
@@ -186,6 +197,12 @@
         s->mail_state = ngx_smtp_start;
         break;
     }
+
+    if (rc == NGX_AGAIN) {
+        return;
+    }
+
+    ngx_mail_proxy_write_handler(p->upstream.connection->write);
 }
 
 
@@ -230,9 +247,25 @@
         return;
     }
 
+    if (s->proxy->proxy_protocol) {
+        ngx_log_debug0(NGX_LOG_DEBUG_MAIL, c->log, 0, "mail proxy pop3 busy");
+
+        if (ngx_handle_read_event(c->read, 0) != NGX_OK) {
+            ngx_mail_proxy_internal_server_error(s);
+            return;
+        }
+
+        return;
+    }
+
     rc = ngx_mail_proxy_read_response(s, 0);
 
     if (rc == NGX_AGAIN) {
+        if (ngx_handle_read_event(c->read, 0) != NGX_OK) {
+            ngx_mail_proxy_internal_server_error(s);
+            return;
+        }
+
         return;
     }
 
@@ -314,6 +347,11 @@
         return;
     }
 
+    if (ngx_handle_read_event(c->read, 0) != NGX_OK) {
+        ngx_mail_proxy_internal_server_error(s);
+        return;
+    }
+
     s->proxy->buffer->pos = s->proxy->buffer->start;
     s->proxy->buffer->last = s->proxy->buffer->start;
 }
@@ -343,9 +381,25 @@
         return;
     }
 
+    if (s->proxy->proxy_protocol) {
+        ngx_log_debug0(NGX_LOG_DEBUG_MAIL, c->log, 0, "mail proxy imap busy");
+
+        if (ngx_handle_read_event(c->read, 0) != NGX_OK) {
+            ngx_mail_proxy_internal_server_error(s);
+            return;
+        }
+
+        return;
+    }
+
     rc = ngx_mail_proxy_read_response(s, s->mail_state);
 
     if (rc == NGX_AGAIN) {
+        if (ngx_handle_read_event(c->read, 0) != NGX_OK) {
+            ngx_mail_proxy_internal_server_error(s);
+            return;
+        }
+
         return;
     }
 
@@ -448,6 +502,11 @@
         return;
     }
 
+    if (ngx_handle_read_event(c->read, 0) != NGX_OK) {
+        ngx_mail_proxy_internal_server_error(s);
+        return;
+    }
+
     s->proxy->buffer->pos = s->proxy->buffer->start;
     s->proxy->buffer->last = s->proxy->buffer->start;
 }
@@ -479,9 +538,25 @@
         return;
     }
 
+    if (s->proxy->proxy_protocol) {
+        ngx_log_debug0(NGX_LOG_DEBUG_MAIL, c->log, 0, "mail proxy smtp busy");
+
+        if (ngx_handle_read_event(c->read, 0) != NGX_OK) {
+            ngx_mail_proxy_internal_server_error(s);
+            return;
+        }
+
+        return;
+    }
+
     rc = ngx_mail_proxy_read_response(s, s->mail_state);
 
     if (rc == NGX_AGAIN) {
+        if (ngx_handle_read_event(c->read, 0) != NGX_OK) {
+            ngx_mail_proxy_internal_server_error(s);
+            return;
+        }
+
         return;
     }
 
@@ -763,25 +838,103 @@
         return;
     }
 
+    if (ngx_handle_read_event(c->read, 0) != NGX_OK) {
+        ngx_mail_proxy_internal_server_error(s);
+        return;
+    }
+
     s->proxy->buffer->pos = s->proxy->buffer->start;
     s->proxy->buffer->last = s->proxy->buffer->start;
 }
 
 
 static void
-ngx_mail_proxy_dummy_handler(ngx_event_t *wev)
+ngx_mail_proxy_write_handler(ngx_event_t *wev)
 {
     ngx_connection_t    *c;
     ngx_mail_session_t  *s;
 
-    ngx_log_debug0(NGX_LOG_DEBUG_MAIL, wev->log, 0, "mail proxy dummy handler");
+    ngx_log_debug0(NGX_LOG_DEBUG_MAIL, wev->log, 0, "mail proxy write handler");
+
+    c = wev->data;
+    s = c->data;
+
+    if (s->proxy->proxy_protocol) {
+        if (ngx_mail_proxy_send_proxy_protocol(s) != NGX_OK) {
+            return;
+        }
+
+        s->proxy->proxy_protocol = 0;
+    }
 
     if (ngx_handle_write_event(wev, 0) != NGX_OK) {
-        c = wev->data;
-        s = c->data;
-
-        ngx_mail_proxy_close_session(s);
+        ngx_mail_proxy_internal_server_error(s);
     }
+
+    if (c->read->ready) {
+        ngx_post_event(c->read, &ngx_posted_events);
+    }
+}
+
+
+static ngx_int_t
+ngx_mail_proxy_send_proxy_protocol(ngx_mail_session_t *s)
+{
+    u_char            *p;
+    ssize_t            n, size;
+    ngx_connection_t  *c;
+    u_char             buf[NGX_PROXY_PROTOCOL_MAX_HEADER];
+
+    s->connection->log->action = "sending PROXY protocol header to upstream";
+
+    ngx_log_debug0(NGX_LOG_DEBUG_MAIL, s->connection->log, 0,
+                   "mail proxy send PROXY protocol header");
+
+    p = ngx_proxy_protocol_write(s->connection, buf,
+                                 buf + NGX_PROXY_PROTOCOL_MAX_HEADER);
+    if (p == NULL) {
+        ngx_mail_proxy_internal_server_error(s);
+        return NGX_ERROR;
+    }
+
+    c = s->proxy->upstream.connection;
+
+    size = p - buf;
+
+    n = c->send(c, buf, size);
+
+    if (n == NGX_AGAIN) {
+        if (ngx_handle_write_event(c->write, 0) != NGX_OK) {
+            ngx_mail_proxy_internal_server_error(s);
+            return NGX_ERROR;
+        }
+
+        return NGX_AGAIN;
+    }
+
+    if (n == NGX_ERROR) {
+        ngx_mail_proxy_internal_server_error(s);
+        return NGX_ERROR;
+    }
+
+    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, s->connection->log, 0,
+                      "could not send PROXY protocol header at once");
+
+        ngx_mail_proxy_internal_server_error(s);
+
+        return NGX_ERROR;
+    }
+
+    return NGX_OK;
 }
 
 
@@ -1182,6 +1335,7 @@
     pcf->pass_error_message = NGX_CONF_UNSET;
     pcf->xclient = NGX_CONF_UNSET;
     pcf->smtp_auth = NGX_CONF_UNSET;
+    pcf->proxy_protocol = NGX_CONF_UNSET;
     pcf->buffer_size = NGX_CONF_UNSET_SIZE;
     pcf->timeout = NGX_CONF_UNSET_MSEC;
 
@@ -1199,6 +1353,7 @@
     ngx_conf_merge_value(conf->pass_error_message, prev->pass_error_message, 0);
     ngx_conf_merge_value(conf->xclient, prev->xclient, 1);
     ngx_conf_merge_value(conf->smtp_auth, prev->smtp_auth, 0);
+    ngx_conf_merge_value(conf->proxy_protocol, prev->proxy_protocol, 0);
     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);
diff --git a/src/mail/ngx_mail_realip_module.c b/src/mail/ngx_mail_realip_module.c
new file mode 100644
index 0000000..c93d7d3
--- /dev/null
+++ b/src/mail/ngx_mail_realip_module.c
@@ -0,0 +1,269 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ * Copyright (C) Nginx, Inc.
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_mail.h>
+
+
+typedef struct {
+    ngx_array_t       *from;     /* array of ngx_cidr_t */
+} ngx_mail_realip_srv_conf_t;
+
+
+static ngx_int_t ngx_mail_realip_set_addr(ngx_mail_session_t *s,
+    ngx_addr_t *addr);
+static char *ngx_mail_realip_from(ngx_conf_t *cf, ngx_command_t *cmd,
+    void *conf);
+static void *ngx_mail_realip_create_srv_conf(ngx_conf_t *cf);
+static char *ngx_mail_realip_merge_srv_conf(ngx_conf_t *cf, void *parent,
+    void *child);
+
+
+static ngx_command_t  ngx_mail_realip_commands[] = {
+
+    { ngx_string("set_real_ip_from"),
+      NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_TAKE1,
+      ngx_mail_realip_from,
+      NGX_MAIL_SRV_CONF_OFFSET,
+      0,
+      NULL },
+
+      ngx_null_command
+};
+
+
+static ngx_mail_module_t  ngx_mail_realip_module_ctx = {
+    NULL,                                  /* protocol */
+
+    NULL,                                  /* create main configuration */
+    NULL,                                  /* init main configuration */
+
+    ngx_mail_realip_create_srv_conf,       /* create server configuration */
+    ngx_mail_realip_merge_srv_conf         /* merge server configuration */
+};
+
+
+ngx_module_t  ngx_mail_realip_module = {
+    NGX_MODULE_V1,
+    &ngx_mail_realip_module_ctx,           /* module context */
+    ngx_mail_realip_commands,              /* module directives */
+    NGX_MAIL_MODULE,                       /* module type */
+    NULL,                                  /* init master */
+    NULL,                                  /* init module */
+    NULL,                                  /* init process */
+    NULL,                                  /* init thread */
+    NULL,                                  /* exit thread */
+    NULL,                                  /* exit process */
+    NULL,                                  /* exit master */
+    NGX_MODULE_V1_PADDING
+};
+
+
+ngx_int_t
+ngx_mail_realip_handler(ngx_mail_session_t *s)
+{
+    ngx_addr_t                   addr;
+    ngx_connection_t            *c;
+    ngx_mail_realip_srv_conf_t  *rscf;
+
+    rscf = ngx_mail_get_module_srv_conf(s, ngx_mail_realip_module);
+
+    if (rscf->from == NULL) {
+        return NGX_OK;
+    }
+
+    c = s->connection;
+
+    if (c->proxy_protocol == NULL) {
+        return NGX_OK;
+    }
+
+    if (ngx_cidr_match(c->sockaddr, rscf->from) != NGX_OK) {
+        return NGX_OK;
+    }
+
+    if (ngx_parse_addr(c->pool, &addr, c->proxy_protocol->src_addr.data,
+                       c->proxy_protocol->src_addr.len)
+        != NGX_OK)
+    {
+        return NGX_OK;
+    }
+
+    ngx_inet_set_port(addr.sockaddr, c->proxy_protocol->src_port);
+
+    return ngx_mail_realip_set_addr(s, &addr);
+}
+
+
+static ngx_int_t
+ngx_mail_realip_set_addr(ngx_mail_session_t *s, ngx_addr_t *addr)
+{
+    size_t             len;
+    u_char            *p;
+    u_char             text[NGX_SOCKADDR_STRLEN];
+    ngx_connection_t  *c;
+
+    c = s->connection;
+
+    len = ngx_sock_ntop(addr->sockaddr, addr->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->sockaddr;
+    c->socklen = addr->socklen;
+    c->addr_text.len = len;
+    c->addr_text.data = p;
+
+    return NGX_OK;
+}
+
+
+static char *
+ngx_mail_realip_from(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
+{
+    ngx_mail_realip_srv_conf_t *rscf = 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 (rscf->from == NULL) {
+        rscf->from = ngx_array_create(cf->pool, 2,
+                                      sizeof(ngx_cidr_t));
+        if (rscf->from == NULL) {
+            return NGX_CONF_ERROR;
+        }
+    }
+
+#if (NGX_HAVE_UNIX_DOMAIN)
+
+    if (ngx_strcmp(value[1].data, "unix:") == 0) {
+        cidr = ngx_array_push(rscf->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(rscf->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(rscf->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;
+}
+
+
+static void *
+ngx_mail_realip_create_srv_conf(ngx_conf_t *cf)
+{
+    ngx_mail_realip_srv_conf_t  *conf;
+
+    conf = ngx_pcalloc(cf->pool, sizeof(ngx_mail_realip_srv_conf_t));
+    if (conf == NULL) {
+        return NULL;
+    }
+
+    /*
+     * set by ngx_pcalloc():
+     *
+     *     conf->from = NULL;
+     */
+
+    return conf;
+}
+
+
+static char *
+ngx_mail_realip_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child)
+{
+    ngx_mail_realip_srv_conf_t *prev = parent;
+    ngx_mail_realip_srv_conf_t *conf = child;
+
+    if (conf->from == NULL) {
+        conf->from = prev->from;
+    }
+
+    return NGX_CONF_OK;
+}
diff --git a/src/mail/ngx_mail_smtp_handler.c b/src/mail/ngx_mail_smtp_handler.c
index f1017e0..e68ceed 100644
--- a/src/mail/ngx_mail_smtp_handler.c
+++ b/src/mail/ngx_mail_smtp_handler.c
@@ -449,6 +449,12 @@
     if (s->out.len) {
         ngx_log_debug0(NGX_LOG_DEBUG_MAIL, c->log, 0, "smtp send handler busy");
         s->blocked = 1;
+
+        if (ngx_handle_read_event(c->read, 0) != NGX_OK) {
+            ngx_mail_close_connection(c);
+            return;
+        }
+
         return;
     }
 
@@ -456,7 +462,16 @@
 
     rc = ngx_mail_read_command(s, c);
 
-    if (rc == NGX_AGAIN || rc == NGX_ERROR) {
+    if (rc == NGX_AGAIN) {
+        if (ngx_handle_read_event(c->read, 0) != NGX_OK) {
+            ngx_mail_session_internal_server_error(s);
+            return;
+        }
+
+        return;
+    }
+
+    if (rc == NGX_ERROR) {
         return;
     }
 
@@ -568,6 +583,11 @@
             s->arg_start = s->buffer->pos;
         }
 
+        if (ngx_handle_read_event(c->read, 0) != NGX_OK) {
+            ngx_mail_session_internal_server_error(s);
+            return;
+        }
+
         ngx_mail_send(c->write);
     }
 }
diff --git a/src/mail/ngx_mail_ssl_module.c b/src/mail/ngx_mail_ssl_module.c
index d560bd6..7eae83e 100644
--- a/src/mail/ngx_mail_ssl_module.c
+++ b/src/mail/ngx_mail_ssl_module.c
@@ -682,7 +682,7 @@
 {
 #ifndef SSL_CONF_FLAG_FILE
     return "is not supported on this platform";
-#endif
-
+#else
     return NGX_CONF_OK;
+#endif
 }
diff --git a/src/ngx_modules.c b/src/ngx_modules.c
index 304a2d1..2f1a4db 100644
--- a/src/ngx_modules.c
+++ b/src/ngx_modules.c
@@ -242,6 +242,7 @@
 #if (NGX_MAIL)
 extern ngx_module_t  ngx_mail_auth_http_module;
 extern ngx_module_t  ngx_mail_proxy_module;
+extern ngx_module_t  ngx_mail_realip_module;
 #endif
 
 #if (NGX_STREAM)
@@ -534,6 +535,7 @@
 #if (NGX_MAIL)
     &ngx_mail_auth_http_module,
     &ngx_mail_proxy_module,
+    &ngx_mail_realip_module,
 #endif
 
 #if (NGX_STREAM)
@@ -828,6 +830,7 @@
 #if (NGX_MAIL)
     "ngx_mail_auth_http_module",
     "ngx_mail_proxy_module",
+    "ngx_mail_realip_module",
 #endif
 
 #if (NGX_STREAM)
diff --git a/src/os/unix/ngx_errno.c b/src/os/unix/ngx_errno.c
index e787b23..ca23b2d 100644
--- a/src/os/unix/ngx_errno.c
+++ b/src/os/unix/ngx_errno.c
@@ -9,6 +9,49 @@
 #include <ngx_core.h>
 
 
+static ngx_str_t   ngx_unknown_error = ngx_string("Unknown error");
+
+
+#if (NGX_HAVE_STRERRORDESC_NP)
+
+/*
+ * The strerrordesc_np() function, introduced in glibc 2.32, is
+ * async-signal-safe.  This makes it possible to use it directly,
+ * without copying error messages.
+ */
+
+
+u_char *
+ngx_strerror(ngx_err_t err, u_char *errstr, size_t size)
+{
+    size_t       len;
+    const char  *msg;
+
+    msg = strerrordesc_np(err);
+
+    if (msg == NULL) {
+        msg = (char *) ngx_unknown_error.data;
+        len = ngx_unknown_error.len;
+
+    } else {
+        len = ngx_strlen(msg);
+    }
+
+    size = ngx_min(size, len);
+
+    return ngx_cpymem(errstr, msg, size);
+}
+
+
+ngx_int_t
+ngx_strerror_init(void)
+{
+    return NGX_OK;
+}
+
+
+#else
+
 /*
  * The strerror() messages are copied because:
  *
@@ -26,7 +69,8 @@
 
 
 static ngx_str_t  *ngx_sys_errlist;
-static ngx_str_t   ngx_unknown_error = ngx_string("Unknown error");
+static ngx_err_t   ngx_first_error;
+static ngx_err_t   ngx_last_error;
 
 
 u_char *
@@ -34,8 +78,13 @@
 {
     ngx_str_t  *msg;
 
-    msg = ((ngx_uint_t) err < NGX_SYS_NERR) ? &ngx_sys_errlist[err]:
-                                              &ngx_unknown_error;
+    if (err >= ngx_first_error && err < ngx_last_error) {
+        msg = &ngx_sys_errlist[err - ngx_first_error];
+
+    } else {
+        msg = &ngx_unknown_error;
+    }
+
     size = ngx_min(size, msg->len);
 
     return ngx_cpymem(errstr, msg->data, size);
@@ -50,20 +99,92 @@
     size_t      len;
     ngx_err_t   err;
 
+#if (NGX_SYS_NERR)
+    ngx_first_error = 0;
+    ngx_last_error = NGX_SYS_NERR;
+
+#elif (EPERM > 1000 && EPERM < 0x7fffffff - 1000)
+
+    /*
+     * If number of errors is not known, and EPERM error code has large
+     * but reasonable value, guess possible error codes based on the error
+     * messages returned by strerror(), starting from EPERM.  Notably,
+     * this covers GNU/Hurd, where errors start at 0x40000001.
+     */
+
+    for (err = EPERM; err > EPERM - 1000; err--) {
+        ngx_set_errno(0);
+        msg = strerror(err);
+
+        if (errno == EINVAL
+            || msg == NULL
+            || strncmp(msg, "Unknown error", 13) == 0)
+        {
+            continue;
+        }
+
+        ngx_first_error = err;
+    }
+
+    for (err = EPERM; err < EPERM + 1000; err++) {
+        ngx_set_errno(0);
+        msg = strerror(err);
+
+        if (errno == EINVAL
+            || msg == NULL
+            || strncmp(msg, "Unknown error", 13) == 0)
+        {
+            continue;
+        }
+
+        ngx_last_error = err + 1;
+    }
+
+#else
+
+    /*
+     * If number of errors is not known, guess it based on the error
+     * messages returned by strerror().
+     */
+
+    ngx_first_error = 0;
+
+    for (err = 0; err < 1000; err++) {
+        ngx_set_errno(0);
+        msg = strerror(err);
+
+        if (errno == EINVAL
+            || msg == NULL
+            || strncmp(msg, "Unknown error", 13) == 0)
+        {
+            continue;
+        }
+
+        ngx_last_error = err + 1;
+    }
+
+#endif
+
     /*
      * ngx_strerror() is not ready to work at this stage, therefore,
      * malloc() is used and possible errors are logged using strerror().
      */
 
-    len = NGX_SYS_NERR * sizeof(ngx_str_t);
+    len = (ngx_last_error - ngx_first_error) * sizeof(ngx_str_t);
 
     ngx_sys_errlist = malloc(len);
     if (ngx_sys_errlist == NULL) {
         goto failed;
     }
 
-    for (err = 0; err < NGX_SYS_NERR; err++) {
+    for (err = ngx_first_error; err < ngx_last_error; err++) {
         msg = strerror(err);
+
+        if (msg == NULL) {
+            ngx_sys_errlist[err - ngx_first_error] = ngx_unknown_error;
+            continue;
+        }
+
         len = ngx_strlen(msg);
 
         p = malloc(len);
@@ -72,8 +193,8 @@
         }
 
         ngx_memcpy(p, msg, len);
-        ngx_sys_errlist[err].len = len;
-        ngx_sys_errlist[err].data = p;
+        ngx_sys_errlist[err - ngx_first_error].len = len;
+        ngx_sys_errlist[err - ngx_first_error].data = p;
     }
 
     return NGX_OK;
@@ -85,3 +206,5 @@
 
     return NGX_ERROR;
 }
+
+#endif
diff --git a/src/stream/ngx_stream_proxy_module.c b/src/stream/ngx_stream_proxy_module.c
index 0c86083..01cda7a 100644
--- a/src/stream/ngx_stream_proxy_module.c
+++ b/src/stream/ngx_stream_proxy_module.c
@@ -1026,9 +1026,9 @@
 {
 #ifndef SSL_CONF_FLAG_FILE
     return "is not supported on this platform";
-#endif
-
+#else
     return NGX_CONF_OK;
+#endif
 }
 
 
diff --git a/src/stream/ngx_stream_ssl_module.c b/src/stream/ngx_stream_ssl_module.c
index ccd359f..d8c0471 100644
--- a/src/stream/ngx_stream_ssl_module.c
+++ b/src/stream/ngx_stream_ssl_module.c
@@ -1061,9 +1061,9 @@
 {
 #ifndef SSL_CONF_FLAG_FILE
     return "is not supported on this platform";
-#endif
-
+#else
     return NGX_CONF_OK;
+#endif
 }