]> git.ipfire.org Git - thirdparty/postfix.git/commitdiff
postfix-3.5-20200101-nonprod
authorWietse Venema <wietse@porcupine.org>
Wed, 1 Jan 2020 05:00:00 +0000 (00:00 -0500)
committerViktor Dukhovni <postfix-users@dukhovni.org>
Mon, 6 Jan 2020 02:30:57 +0000 (21:30 -0500)
15 files changed:
postfix/HISTORY
postfix/html/postconf.5.html
postfix/makedefs
postfix/src/global/Makefile.in
postfix/src/global/haproxy_srvr.c
postfix/src/global/haproxy_srvr.h
postfix/src/global/mail_version.h
postfix/src/postscreen/postscreen.h
postfix/src/postscreen/postscreen_endpt.c
postfix/src/postscreen/postscreen_haproxy.c
postfix/src/postscreen/postscreen_haproxy.h
postfix/src/smtpd/Makefile.in
postfix/src/smtpd/smtpd.h
postfix/src/smtpd/smtpd_haproxy.c
postfix/src/smtpd/smtpd_peer.c

index 5fdba30d05d028ce80963200ff2158b79cc62307..fbd081c9d9b6c3a653901a176464499a4cb6a098 100644 (file)
@@ -24502,3 +24502,13 @@ Apologies for any names omitted.
        Bugfix: sanitize server responses before storing them in
        the verify database, to avoid Postfix warnings about malformed
        UTF8. File: verify/verify.c.
+
+20200101
+
+       Refactored the haproxy infrastructure in preparation for
+       haproxy version 2 support. This is necessary because version
+       2 introduces a mutual dependency between the reader and the
+       parser. Files: global/haproxy_srvr.c, smtpd/smtpd_peer.c,
+       smtpd/smtpd_haproxy.c, smtpd/smtpd.h, postscreen/postscreen.h,
+       postscreen/postscreen_endpt.c, postscreen/postscreen_haproxy.c,
+       postscreen/postscreen_haproxy.h, global/haproxy_srvr.h.
index df9c7c08e43caa1cd929c0d381e749feefd5fccd..f6cc50c1ea7f1bee8969846e528b092d322c5f48 100644 (file)
@@ -18510,7 +18510,7 @@ is unwise to choose an "bleeding-edge" curve supported by only a
 small subset of clients.  </p>
 
 <p> The default "strong" curve is rated in NSA <a
-href="http://www.nsa.gov/ia/programs/suiteb_cryptography/">Suite
+href="https://web.archive.org/web/20160330034144/https://www.nsa.gov/ia/programs/suiteb_cryptography/">Suite
 B</a> for information classified up to SECRET.  </p>
 
 <p> Note: elliptic curve names are poorly standardized; different
@@ -18551,7 +18551,7 @@ curve must be implemented by OpenSSL (as reported by ecparam(1) with the
 of <a href="http://tools.ietf.org/html/rfc4492">RFC 4492</a>. You should not generally change this setting. </p>
 
 <p> This default "ultra" curve is rated in NSA <a
-href="http://www.nsa.gov/ia/programs/suiteb_cryptography/">Suite
+href="https://web.archive.org/web/20160330034144/https://www.nsa.gov/ia/programs/suiteb_cryptography/">Suite
 B</a> for information classified up to TOP SECRET. </p>
 
 <p> If you want to take maximal advantage of ciphers that offer <a
index c9e50db73b5f13aca5c6ada3390d813f63e2e567..21599bcb327042decf46fb455877413d646ff97e 100644 (file)
@@ -882,7 +882,7 @@ CCARGS="$CCARGS -DSNAPSHOT"
 
 # Non-production: needs thorough testing, or major changes are still
 # needed before the code stabilizes.
-#CCARGS="$CCARGS -DNONPROD"
+CCARGS="$CCARGS -DNONPROD"
 
 # Workaround: prepend Postfix include files before other include files.
 CCARGS="-I. -I../../include $CCARGS"
index 7a1c56b901f7660721153f3403e0c4640042a316..14f7283e306070a50bf187f9745bcd4423bace6a 100644 (file)
@@ -1401,6 +1401,7 @@ haproxy_srvr.o: ../../include/inet_proto.h
 haproxy_srvr.o: ../../include/msg.h
 haproxy_srvr.o: ../../include/myaddrinfo.h
 haproxy_srvr.o: ../../include/mymalloc.h
+haproxy_srvr.o: ../../include/split_at.h
 haproxy_srvr.o: ../../include/stringops.h
 haproxy_srvr.o: ../../include/sys_defs.h
 haproxy_srvr.o: ../../include/valid_hostname.h
@@ -1448,12 +1449,15 @@ header_token.o: header_token.c
 header_token.o: header_token.h
 header_token.o: lex_822.h
 info_log_addr_form.o: ../../include/check_arg.h
+info_log_addr_form.o: ../../include/msg.h
+info_log_addr_form.o: ../../include/name_code.h
 info_log_addr_form.o: ../../include/sys_defs.h
 info_log_addr_form.o: ../../include/vbuf.h
 info_log_addr_form.o: ../../include/vstring.h
 info_log_addr_form.o: info_log_addr_form.c
 info_log_addr_form.o: info_log_addr_form.h
 info_log_addr_form.o: mail_addr_form.h
+info_log_addr_form.o: mail_params.h
 info_log_addr_form.o: quote_822_local.h
 info_log_addr_form.o: quote_flags.h
 input_transp.o: ../../include/check_arg.h
index 87a660801545e6347c406555ad22fe5c5bec4177..30148ec2b04e3ea68af71f827c516f4d9a95015a 100644 (file)
@@ -6,21 +6,41 @@
 /* SYNOPSIS
 /*     #include <haproxy_srvr.h>
 /*
-/*     const char *haproxy_srvr_parse(str,
+/*     const char *haproxy_srvr_parse(str, str_len, non_proxy,
 /*                     smtp_client_addr, smtp_client_port,
 /*                     smtp_server_addr, smtp_server_port)
 /*     const char *str;
+/*     ssize_t *str_len;
+/*     int     *non_proxy;
+/*     MAI_HOSTADDR_STR *smtp_client_addr,
+/*     MAI_SERVPORT_STR *smtp_client_port,
+/*     MAI_HOSTADDR_STR *smtp_server_addr,
+/*     MAI_SERVPORT_STR *smtp_server_port;
+/*
+/*     const char *haproxy_srvr_receive(fd, non_proxy,
+/*                     smtp_client_addr, smtp_client_port,
+/*                     smtp_server_addr, smtp_server_port)
+/*     int     fd;
+/*     int     *non_proxy;
 /*     MAI_HOSTADDR_STR *smtp_client_addr,
 /*     MAI_SERVPORT_STR *smtp_client_port,
 /*     MAI_HOSTADDR_STR *smtp_server_addr,
 /*     MAI_SERVPORT_STR *smtp_server_port;
 /* DESCRIPTION
-/*     haproxy_srvr_parse() parses a haproxy line. The result is
-/*     null in case of success, a pointer to text (with the error
-/*     type) in case of error. If both IPv6 and IPv4 support are
-/*     enabled, IPV4_IN_IPV6 address syntax (::ffff:1.2.3.4) is
-/*     converted to IPV4 syntax, provided that IPv4 support is
-/*     enabled.
+/*     haproxy_srvr_parse() parses a haproxy v1 or v2 protocol
+/*     message. The result is null in case of success, a pointer
+/*     to text (with the error type) in case of error. If both
+/*     IPv6 and IPv4 support are enabled, IPV4_IN_IPV6 address
+/*     syntax (::ffff:1.2.3.4) is converted to IPV4 syntax, provided
+/*     that IPv4 support is enabled. In case of success, the
+/*     str_len argument is updated with the number of bytes parsed,
+/*     and the non_proxy argument is true or false if the
+/*     haproxy message specifies a non-proxied connection.
+/*
+/*     haproxy_srvr_receive() receives and parses a haproxy protocol
+/*     handshake. This must be called before any I/O is done on
+/*     the specified file descriptor. The result is 0 in case of
+/*     success, -1 in case of error. All errors are logged.
 /* LICENSE
 /* .ad
 /* .fi
@@ -56,6 +76,7 @@
 #include <stringops.h>
 #include <mymalloc.h>
 #include <inet_proto.h>
+#include <split_at.h>
 
 /* Global library. */
 
@@ -65,6 +86,8 @@
 
 static INET_PROTO_INFO *proto_info;
 
+#define STR_OR_NULL(str) ((str) ? (str) : "(null)")
+
 /* haproxy_srvr_parse_lit - extract and validate string literal */
 
 static int haproxy_srvr_parse_lit(const char *str,...)
@@ -74,7 +97,7 @@ static int haproxy_srvr_parse_lit(const char *str,...)
     int     result = -1;
 
     if (msg_verbose)
-       msg_info("haproxy_srvr_parse: %s", str);
+       msg_info("haproxy_srvr_parse: %s", STR_OR_NULL(str));
 
     if (str != 0) {
        va_start(ap, str);
@@ -91,7 +114,7 @@ static int haproxy_srvr_parse_lit(const char *str,...)
 static int haproxy_srvr_parse_proto(const char *str, int *addr_family)
 {
     if (msg_verbose)
-       msg_info("haproxy_srvr_parse: proto=%s", str);
+       msg_info("haproxy_srvr_parse: proto=%s", STR_OR_NULL(str));
 
 #ifdef AF_INET6
     if (strcasecmp(str, "TCP6") == 0) {
@@ -119,7 +142,8 @@ static int haproxy_srvr_parse_addr(const char *str, MAI_HOSTADDR_STR *addr,
     int     err;
 
     if (msg_verbose)
-       msg_info("haproxy_srvr_parse: addr=%s proto=%d", str, addr_family);
+       msg_info("haproxy_srvr_parse: addr=%s proto=%d",
+                STR_OR_NULL(str), addr_family);
 
     if (str == 0 || strlen(str) >= sizeof(MAI_HOSTADDR_STR))
        return (-1);
@@ -156,7 +180,7 @@ static int haproxy_srvr_parse_addr(const char *str, MAI_HOSTADDR_STR *addr,
 static int haproxy_srvr_parse_port(const char *str, MAI_SERVPORT_STR *port)
 {
     if (msg_verbose)
-       msg_info("haproxy_srvr_parse: port=%s", str);
+       msg_info("haproxy_srvr_parse: port=%s", STR_OR_NULL(str));
     if (str == 0 || strlen(str) >= sizeof(MAI_SERVPORT_STR)
        || !valid_hostport(str, DONT_GRIPE)) {
        return (-1);
@@ -168,43 +192,113 @@ static int haproxy_srvr_parse_port(const char *str, MAI_SERVPORT_STR *port)
 
 /* haproxy_srvr_parse - parse haproxy line */
 
-const char *haproxy_srvr_parse(const char *str,
+const char *haproxy_srvr_parse(const char *str, ssize_t *str_len,
+                                      int *non_proxy,
                                       MAI_HOSTADDR_STR *smtp_client_addr,
                                       MAI_SERVPORT_STR *smtp_client_port,
                                       MAI_HOSTADDR_STR *smtp_server_addr,
                                       MAI_SERVPORT_STR *smtp_server_port)
 {
-    char   *saved_str = mystrdup(str);
-    char   *cp = saved_str;
     const char *err;
     int     addr_family;
 
     if (proto_info == 0)
        proto_info = inet_proto_info();
 
+    *non_proxy = 0;
+
     /*
      * XXX We don't accept connections with the "UNKNOWN" protocol type,
      * because those would sidestep address-based access control mechanisms.
      */
-#define NEXT_TOKEN mystrtok(&cp, " \r\n")
-    if (haproxy_srvr_parse_lit(NEXT_TOKEN, "PROXY", (char *) 0) < 0)
-       err = "unexpected protocol header";
-    else if (haproxy_srvr_parse_proto(NEXT_TOKEN, &addr_family) < 0)
-       err = "unsupported protocol type";
-    else if (haproxy_srvr_parse_addr(NEXT_TOKEN, smtp_client_addr,
-                                    addr_family) < 0)
-       err = "unexpected client address syntax";
-    else if (haproxy_srvr_parse_addr(NEXT_TOKEN, smtp_server_addr,
-                                    addr_family) < 0)
-       err = "unexpected server address syntax";
-    else if (haproxy_srvr_parse_port(NEXT_TOKEN, smtp_client_port) < 0)
-       err = "unexpected client port syntax";
-    else if (haproxy_srvr_parse_port(NEXT_TOKEN, smtp_server_port) < 0)
-       err = "unexpected server port syntax";
-    else
-       err = 0;
-    myfree(saved_str);
-    return (err);
+
+    /*
+     * Try version 1 protocol.
+     */
+    if (strncmp(str, "PROXY ", 6) == 0) {
+       char   *saved_str = mystrdup(str);
+       char   *cp = saved_str;
+       char   *beyond_header = split_at(saved_str, '\n');
+
+#define NEXT_TOKEN mystrtok(&cp, " \r")
+       if (beyond_header == 0)
+           err = "missing protocol header terminator";
+       else if (haproxy_srvr_parse_lit(NEXT_TOKEN, "PROXY", (char *) 0) < 0)
+           err = "unexpected protocol header";
+       else if (haproxy_srvr_parse_proto(NEXT_TOKEN, &addr_family) < 0)
+           err = "unsupported protocol type";
+       else if (haproxy_srvr_parse_addr(NEXT_TOKEN, smtp_client_addr,
+                                        addr_family) < 0)
+           err = "unexpected client address syntax";
+       else if (haproxy_srvr_parse_addr(NEXT_TOKEN, smtp_server_addr,
+                                        addr_family) < 0)
+           err = "unexpected server address syntax";
+       else if (haproxy_srvr_parse_port(NEXT_TOKEN, smtp_client_port) < 0)
+           err = "unexpected client port syntax";
+       else if (haproxy_srvr_parse_port(NEXT_TOKEN, smtp_server_port) < 0)
+           err = "unexpected server port syntax";
+       else {
+           err = 0;
+           *str_len = beyond_header - saved_str;
+       }
+       myfree(saved_str);
+       return (err);
+    }
+
+    /*
+     * Assume version 2 protocol.
+     */
+    else {
+       return ("v2 protocol is not implemented");
+    }
+}
+
+/* haproxy_srvr_receive - redceive and parse haproxy protocol handshake */
+
+int     haproxy_srvr_receive(int fd, int *non_proxy,
+                                    MAI_HOSTADDR_STR *smtp_client_addr,
+                                    MAI_SERVPORT_STR *smtp_client_port,
+                                    MAI_HOSTADDR_STR *smtp_server_addr,
+                                    MAI_SERVPORT_STR *smtp_server_port)
+{
+    const char *err;
+    VSTRING *escape_buf;
+    char    read_buf[HAPROXY_MAX_LEN];
+    ssize_t read_len;
+
+    /*
+     * We must not read(2) past the end of the HaProxy handshake. The v2
+     * protocol assumes that the handshake will never be fragmented,
+     * therefore we peek, parse the entire input, then read(2) only the
+     * number of bytes parsed.
+     */
+    if ((read_len = recv(fd, read_buf, sizeof(read_buf) - 1, MSG_PEEK)) <= 0) {
+       msg_warn("haproxy read: EOF");
+       return (-1);
+    }
+
+    /*
+     * Parse the haproxy handshake, and determine the handshake length.
+     */
+    read_buf[read_len] = 0;
+    if ((err = haproxy_srvr_parse(read_buf, &read_len, non_proxy,
+                                 smtp_client_addr, smtp_client_port,
+                               smtp_server_addr, smtp_server_port)) != 0) {
+       escape_buf = vstring_alloc(HAPROXY_MAX_LEN + 2);
+       escape(escape_buf, read_buf, read_len);
+       msg_warn("haproxy read: %s: %s", err, vstring_str(escape_buf));
+       vstring_free(escape_buf);
+       return (-1);
+    }
+
+    /*
+     * Try to pop the haproxy handshake off the input queue.
+     */
+    if (recv(fd, read_buf, read_len, 0) <= 0) {
+       msg_warn("haproxy read: %m");
+       return (-1);
+    }
+    return (0);
 }
 
  /*
@@ -216,6 +310,8 @@ int     main(int argc, char **argv)
     /* Test cases with inputs and expected outputs. */
     typedef struct TEST_CASE {
        const char *haproxy_request;
+       ssize_t exp_str_len;
+       int     exp_non_proxy;
        const char *exp_return;
        const char *exp_client_addr;
        const char *exp_server_addr;
@@ -224,40 +320,42 @@ int     main(int argc, char **argv)
     } TEST_CASE;
     static TEST_CASE test_cases[] = {
        /* IPv6. */
-       {"PROXY TCP6 fc:00:00:00:1:2:3:4 fc:00:00:00:4:3:2:1 123 321", 0, "fc::1:2:3:4", "fc::4:3:2:1", "123", "321"},
-       {"PROXY TCP6 FC:00:00:00:1:2:3:4 FC:00:00:00:4:3:2:1 123 321", 0, "fc::1:2:3:4", "fc::4:3:2:1", "123", "321"},
-       {"PROXY TCP6 1.2.3.4 4.3.2.1 123 321", "unexpected client address syntax"},
-       {"PROXY TCP6 fc:00:00:00:1:2:3:4 4.3.2.1 123 321", "unexpected server address syntax"},
+       {"PROXY TCP6 fc:00:00:00:1:2:3:4 fc:00:00:00:4:3:2:1 123 321", 0, 0, 0, "fc::1:2:3:4", "fc::4:3:2:1", "123", "321"},
+       {"PROXY TCP6 FC:00:00:00:1:2:3:4 FC:00:00:00:4:3:2:1 123 321", 0, 0, 0, "fc::1:2:3:4", "fc::4:3:2:1", "123", "321"},
+       {"PROXY TCP6 1.2.3.4 4.3.2.1 123 321", 0, 0, "unexpected client address syntax"},
+       {"PROXY TCP6 fc:00:00:00:1:2:3:4 4.3.2.1 123 321", 0, 0, "unexpected server address syntax"},
        /* IPv4 in IPv6. */
-       {"PROXY TCP6 ::ffff:1.2.3.4 ::ffff:4.3.2.1 123 321", 0, "1.2.3.4", "4.3.2.1", "123", "321"},
-       {"PROXY TCP6 ::FFFF:1.2.3.4 ::FFFF:4.3.2.1 123 321", 0, "1.2.3.4", "4.3.2.1", "123", "321"},
-       {"PROXY TCP4 ::ffff:1.2.3.4 ::ffff:4.3.2.1 123 321", "unexpected client address syntax"},
-       {"PROXY TCP4 1.2.3.4 ::ffff:4.3.2.1 123 321", "unexpected server address syntax"},
+       {"PROXY TCP6 ::ffff:1.2.3.4 ::ffff:4.3.2.1 123 321", 0, 0, 0, "1.2.3.4", "4.3.2.1", "123", "321"},
+       {"PROXY TCP6 ::FFFF:1.2.3.4 ::FFFF:4.3.2.1 123 321", 0, 0, 0, "1.2.3.4", "4.3.2.1", "123", "321"},
+       {"PROXY TCP4 ::ffff:1.2.3.4 ::ffff:4.3.2.1 123 321", 0, 0, "unexpected client address syntax"},
+       {"PROXY TCP4 1.2.3.4 ::ffff:4.3.2.1 123 321", 0, 0, "unexpected server address syntax"},
        /* IPv4. */
-       {"PROXY TCP4 1.2.3.4 4.3.2.1 123 321", 0, "1.2.3.4", "4.3.2.1", "123", "321"},
-       {"PROXY TCP4 01.02.03.04 04.03.02.01 123 321", 0, "1.2.3.4", "4.3.2.1", "123", "321"},
-       {"PROXY TCP4 1.2.3.4 4.3.2.1 123456 321", "unexpected client port syntax"},
-       {"PROXY TCP4 1.2.3.4 4.3.2.1 123 654321", "unexpected server port syntax"},
-       {"PROXY TCP4 1.2.3.4 4.3.2.1 0123 321", "unexpected client port syntax"},
-       {"PROXY TCP4 1.2.3.4 4.3.2.1 123 0321", "unexpected server port syntax"},
+       {"PROXY TCP4 1.2.3.4 4.3.2.1 123 321", 0, 0, 0, "1.2.3.4", "4.3.2.1", "123", "321"},
+       {"PROXY TCP4 01.02.03.04 04.03.02.01 123 321", 0, 0, 0, "1.2.3.4", "4.3.2.1", "123", "321"},
+       {"PROXY TCP4 1.2.3.4 4.3.2.1 123456 321", 0, 0, "unexpected client port syntax"},
+       {"PROXY TCP4 1.2.3.4 4.3.2.1 123 654321", 0, 0, "unexpected server port syntax"},
+       {"PROXY TCP4 1.2.3.4 4.3.2.1 0123 321", 0, 0, "unexpected client port syntax"},
+       {"PROXY TCP4 1.2.3.4 4.3.2.1 123 0321", 0, 0, "unexpected server port syntax"},
        /* Missing fields. */
-       {"PROXY TCP6 fc:00:00:00:1:2:3:4 fc:00:00:00:4:3:2:1 123", "unexpected server port syntax"},
-       {"PROXY TCP6 fc:00:00:00:1:2:3:4 fc:00:00:00:4:3:2:1", "unexpected client port syntax"},
-       {"PROXY TCP6 fc:00:00:00:1:2:3:4", "unexpected server address syntax"},
-       {"PROXY TCP6", "unexpected client address syntax"},
-       {"PROXY TCP4 1.2.3.4 4.3.2.1 123", "unexpected server port syntax"},
-       {"PROXY TCP4 1.2.3.4 4.3.2.1", "unexpected client port syntax"},
-       {"PROXY TCP4 1.2.3.4", "unexpected server address syntax"},
-       {"PROXY TCP4", "unexpected client address syntax"},
+       {"PROXY TCP6 fc:00:00:00:1:2:3:4 fc:00:00:00:4:3:2:1 123", 0, 0, "unexpected server port syntax"},
+       {"PROXY TCP6 fc:00:00:00:1:2:3:4 fc:00:00:00:4:3:2:1", 0, 0, "unexpected client port syntax"},
+       {"PROXY TCP6 fc:00:00:00:1:2:3:4", 0, 0, "unexpected server address syntax"},
+       {"PROXY TCP6", 0, 0, "unexpected client address syntax"},
+       {"PROXY TCP4 1.2.3.4 4.3.2.1 123", 0, 0, "unexpected server port syntax"},
+       {"PROXY TCP4 1.2.3.4 4.3.2.1", 0, 0, "unexpected client port syntax"},
+       {"PROXY TCP4 1.2.3.4", 0, 0, "unexpected server address syntax"},
+       {"PROXY TCP4", 0, 0, "unexpected client address syntax"},
        /* Other. */
-       {"PROXY BLAH", "unsupported protocol type"},
-       {"BLAH", "unexpected protocol header"},
+       {"PROXY BLAH", 0, 0, "unsupported protocol type"},
+       {"BLAH", 0, 0, "v2 protocol is not implemented"},
        0,
     };
     TEST_CASE *test_case;
 
     /* Actual results. */
     const char *act_return;
+    ssize_t act_str_len;
+    int     act_non_proxy;
     MAI_HOSTADDR_STR act_smtp_client_addr;
     MAI_HOSTADDR_STR act_smtp_server_addr;
     MAI_SERVPORT_STR act_smtp_client_port;
@@ -270,8 +368,12 @@ int     main(int argc, char **argv)
     for (tests_failed = 0, test_case = test_cases; test_case->haproxy_request;
         tests_failed += test_failed, test_case++) {
        test_failed = 0;
+       act_str_len = strlen(test_case->haproxy_request);
+       if (test_case->exp_str_len == 0)
+           test_case->exp_str_len = act_str_len;
        act_return =
-           haproxy_srvr_parse(test_case->haproxy_request,
+           haproxy_srvr_parse(test_case->haproxy_request, &act_str_len,
+                              &act_non_proxy,
                               &act_smtp_client_addr, &act_smtp_client_port,
                               &act_smtp_server_addr, &act_smtp_server_port);
        if (act_return != test_case->exp_return) {
@@ -283,6 +385,20 @@ int     main(int argc, char **argv)
            test_failed = 1;
            continue;
        }
+       if (act_str_len != test_case->exp_str_len) {
+           msg_warn("test case %d str_len expected=%ld actual=%ld",
+                    (int) (test_case - test_cases),
+                    (long) test_case->exp_str_len, (long) act_str_len);
+           test_failed = 1;
+           continue;
+       }
+       if (act_non_proxy != test_case->exp_non_proxy) {
+           msg_warn("test case %d non_proxy expected=%d actual=%d",
+                    (int) (test_case - test_cases),
+                    test_case->exp_non_proxy, act_non_proxy);
+           test_failed = 1;
+           continue;
+       }
        if (test_case->exp_return != 0)
            continue;
        if (strcmp(test_case->exp_client_addr, act_smtp_client_addr.buf)) {
index 7cd3262a2c3aa1537674cd4922f44298f820a6aa..aafbcc4ff89737b1fba5258bb383698f90acf941 100644 (file)
  /*
   * External interface.
   */
-extern const char *haproxy_srvr_parse(const char *,
+extern const char *haproxy_srvr_parse(const char *, ssize_t *, int *,
+                                    MAI_HOSTADDR_STR *, MAI_SERVPORT_STR *,
+                                   MAI_HOSTADDR_STR *, MAI_SERVPORT_STR *);
+extern int haproxy_srvr_receive(int, int *,
                                     MAI_HOSTADDR_STR *, MAI_SERVPORT_STR *,
                                    MAI_HOSTADDR_STR *, MAI_SERVPORT_STR *);
 
@@ -40,6 +43,11 @@ extern const char *haproxy_srvr_parse(const char *,
 /*     IBM T.J. Watson Research
 /*     P.O. Box 704
 /*     Yorktown Heights, NY 10598, USA
+/*
+/*     Wietse Venema
+/*     Google, Inc.
+/*     111 8th Avenue
+/*     New York, NY 10011, USA
 /*--*/
 
 #endif
index f60409a084dab8ecd523553cf6870ca6df792520..92912f56b9143a5529868a12b7b603f67292ca45 100644 (file)
@@ -20,7 +20,7 @@
   * Patches change both the patchlevel and the release date. Snapshots have no
   * patchlevel; they change the release date only.
   */
-#define MAIL_RELEASE_DATE      "20191214"
+#define MAIL_RELEASE_DATE      "20200101"
 #define MAIL_VERSION_NUMBER    "3.5"
 
 #ifdef SNAPSHOT
index 0f9d41817a8b6891a14a055294e1db758968840a..b38a604ad49e4a65592fd6eeef19987565b0abf3 100644 (file)
@@ -568,6 +568,7 @@ typedef void (*PSC_ENDPT_LOOKUP_FN) (int, VSTREAM *,
                                     MAI_HOSTADDR_STR *, MAI_SERVPORT_STR *,
                                    MAI_HOSTADDR_STR *, MAI_SERVPORT_STR *);
 extern void psc_endpt_lookup(VSTREAM *, PSC_ENDPT_LOOKUP_FN);
+extern void psc_endpt_local_lookup(VSTREAM *, PSC_ENDPT_LOOKUP_FN);
 
  /*
   * postscreen_access emulation.
index 57655ac6a4d8dbcbb924b1543e2a6fab234c8989..335c511d05f7a2ee651d2adc66a53c02328c8678 100644 (file)
 /*     MAI_SERVPORT_STR *smtp_client_port;
 /*     MAI_HOSTADDR_STR *smtp_server_addr;
 /*     MAI_SERVPORT_STR *smtp_server_port;
+/* AUXILIARY METHODS
+/*     void    psc_endpt_local_lookup(smtp_client_stream, lookup_done)
+/*     VSTREAM *smtp_client_stream;
+/*     void    (*lookup_done)(status, smtp_client_stream,
+/*                             smtp_client_addr, smtp_client_port,
+/*                             smtp_server_addr, smtp_server_port)
+/*     int     status;
+/*     MAI_HOSTADDR_STR *smtp_client_addr;
+/*     MAI_SERVPORT_STR *smtp_client_port;
+/*     MAI_HOSTADDR_STR *smtp_server_addr;
+/*     MAI_SERVPORT_STR *smtp_server_port;
 /* DESCRIPTION
 /*     psc_endpt_lookup() looks up remote and local connection
 /*     endpoint information, either through local system calls,
 /* .IP \(bu
 /*     Accept the same arguments as psc_endpt_lookup().
 /* .IP \(bu
+/*     Call psc_endpt_local_lookup() to look up connection info
+/*     when the upstream proxy indicates that the connection is
+/*     not proxied (e.g., health check probe).
+/* .IP \(bu
 /*     Validate protocol, address and port syntax. Permit only
 /*     protocols that are configured with the main.cf:inet_protocols
 /*     setting.
 /*     Arguments:
 /* .IP client_stream
 /*     A brand-new stream that is connected to the remote client.
+/*     This argument MUST be passed to psc_endpt_local_lookup()
+/*     if the up-stream proxy indicates that a connection is not
+/*     proxied.
 /* .IP lookup
 /*     Call-back routine that reports the result status, address
 /*     and port information. The result status is -1 in case of
-/*     error, 0 in case of success.
+/*     error, 0 in case of success. This MUST NOT be called directly
+/*     if the up-stream proxy indicates that a connection is not
+/*     proxied; instead this MUST be called indirectly by
+/*     psc_endpt_local_lookup().
 /* LICENSE
 /* .ad
 /* .fi
 /*     IBM T.J. Watson Research
 /*     P.O. Box 704
 /*     Yorktown Heights, NY 10598, USA
+/*
+/*     Wietse Venema
+/*     Google, Inc.
+/*     111 8th Avenue
+/*     New York, NY 10011, USA
 /*--*/
 
 /* System library. */
@@ -105,8 +131,8 @@ static int psc_sockaddr_to_hostaddr(struct sockaddr *addr_storage,
 
 /* psc_endpt_local_lookup - look up local system connection information */
 
-static void psc_endpt_local_lookup(VSTREAM *smtp_client_stream,
-                                          PSC_ENDPT_LOOKUP_FN lookup_done)
+void    psc_endpt_local_lookup(VSTREAM *smtp_client_stream,
+                                      PSC_ENDPT_LOOKUP_FN lookup_done)
 {
     struct sockaddr_storage addr_storage;
     SOCKADDR_SIZE addr_storage_len = sizeof(addr_storage);
index d835bf073293ce8066f8e19455afba18e5ea55d3..240f918789880b8fbeac23409d7c1036038f5d45 100644 (file)
@@ -69,7 +69,6 @@
 typedef struct {
     VSTREAM *stream;
     PSC_ENDPT_LOOKUP_FN notify;
-    VSTRING *buffer;
 } PSC_HAPROXY_STATE;
 
 /* psc_endpt_haproxy_event - read or time event */
@@ -83,94 +82,32 @@ static void psc_endpt_haproxy_event(int event, void *context)
     MAI_SERVPORT_STR smtp_client_port;
     MAI_HOSTADDR_STR smtp_server_addr;
     MAI_SERVPORT_STR smtp_server_port;
-    int     last_char = 0;
-    const char *err;
-    VSTRING *escape_buf;
-    char    read_buf[HAPROXY_MAX_LEN];
-    ssize_t read_len;
-    char   *cp;
+    int     non_proxy = 0;
 
-    /*
-     * We must not read(2) past the <CR><LF> that terminates the haproxy
-     * line. For efficiency reasons we read the entire haproxy line in one
-     * read(2) call when we know that the line is unfragmented. In the rare
-     * case that the line is fragmented, we fall back and read(2) it one
-     * character at a time.
-     */
     switch (event) {
     case EVENT_TIME:
        msg_warn("haproxy read: time limit exceeded");
        status = -1;
        break;
     case EVENT_READ:
-       /* Determine the initial VSTREAM read(2) buffer size. */
-       if (VSTRING_LEN(state->buffer) == 0) {
-           if ((read_len = recv(vstream_fileno(state->stream),
-                             read_buf, sizeof(read_buf) - 1, MSG_PEEK)) > 0
-               && ((cp = memchr(read_buf, '\n', read_len)) != 0)) {
-               read_len = cp - read_buf + 1;
-           } else {
-               read_len = 1;
-           }
-           vstream_control(state->stream, CA_VSTREAM_CTL_BUFSIZE(read_len),
-                           CA_VSTREAM_CTL_END);
-       }
-       /* Drain the VSTREAM buffer, otherwise this pseudo-thread will hang. */
-       do {
-           if ((last_char = VSTREAM_GETC(state->stream)) == VSTREAM_EOF) {
-               if (vstream_ferror(state->stream))
-                   msg_warn("haproxy read: %m");
-               else
-                   msg_warn("haproxy read: lost connection");
-               status = -1;
-               break;
-           }
-           if (VSTRING_LEN(state->buffer) >= HAPROXY_MAX_LEN) {
-               msg_warn("haproxy read: line too long");
-               status = -1;
-               break;
-           }
-           VSTRING_ADDCH(state->buffer, last_char);
-       } while (vstream_peek(state->stream) > 0);
-       break;
-    }
-
-    /*
-     * Parse the haproxy line. Note: the haproxy_srvr_parse() routine
-     * performs address protocol checks, address and port syntax checks, and
-     * converts IPv4-in-IPv6 address string syntax (::ffff:1.2.3.4) to IPv4
-     * syntax where permitted by the main.cf:inet_protocols setting.
-     */
-    if (status == 0 && last_char == '\n') {
-       VSTRING_TERMINATE(state->buffer);
-       if ((err = haproxy_srvr_parse(vstring_str(state->buffer),
+       status = haproxy_srvr_receive(vstream_fileno(state->stream), &non_proxy,
                                      &smtp_client_addr, &smtp_client_port,
-                             &smtp_server_addr, &smtp_server_port)) != 0) {
-           escape_buf = vstring_alloc(HAPROXY_MAX_LEN + 2);
-           escape(escape_buf, vstring_str(state->buffer),
-                  VSTRING_LEN(state->buffer));
-           msg_warn("haproxy read: %s: %s", err, vstring_str(escape_buf));
-           status = -1;
-           vstring_free(escape_buf);
-       }
+                                     &smtp_server_addr, &smtp_server_port);
     }
 
     /*
-     * Are we done yet?
+     * Terminate this pseudo thread, and notify the caller.
      */
-    if (status < 0 || last_char == '\n') {
-       PSC_CLEAR_EVENT_REQUEST(vstream_fileno(state->stream),
-                               psc_endpt_haproxy_event, context);
-       vstream_control(state->stream,
-                       CA_VSTREAM_CTL_BUFSIZE(VSTREAM_BUFSIZE),
-                       CA_VSTREAM_CTL_END);
+    PSC_CLEAR_EVENT_REQUEST(vstream_fileno(state->stream),
+                           psc_endpt_haproxy_event, context);
+    if (non_proxy)
+       psc_endpt_local_lookup(state->stream, state->notify);
+    else
        state->notify(status, state->stream,
                      &smtp_client_addr, &smtp_client_port,
                      &smtp_server_addr, &smtp_server_port);
-       /* Note: the stream may be closed at this point. */
-       vstring_free(state->buffer);
-       myfree((void *) state);
-    }
+    /* Note: the stream may be closed at this point. */
+    myfree((void *) state);
 }
 
 /* psc_endpt_haproxy_lookup - event-driven haproxy client */
@@ -189,7 +126,6 @@ void    psc_endpt_haproxy_lookup(VSTREAM *stream,
     state = (PSC_HAPROXY_STATE *) mymalloc(sizeof(*state));
     state->stream = stream;
     state->notify = notify;
-    state->buffer = vstring_alloc(100);
 
     /*
      * Read the haproxy line.
index 2691e481f350cf668e4875b1ace16e31c1772064..7557fb3a4e6d71420512607eaf8ee75b47befd8b 100644 (file)
@@ -22,4 +22,9 @@ extern void psc_endpt_haproxy_lookup(VSTREAM *, PSC_ENDPT_LOOKUP_FN);
 /*     IBM T.J. Watson Research
 /*     P.O. Box 704
 /*     Yorktown Heights, NY 10598, USA
+/*
+/*     Wietse Venema
+/*     Google, Inc.
+/*     111 8th Avenue
+/*     New York, NY 10011, USA
 /*--*/
index 993ea18207daa2f590363f15bb18e8770744747d..009f5bd17257e8120f17853ed2ff37ad71d25b83 100644 (file)
@@ -421,6 +421,7 @@ smtpd_haproxy.o: ../../include/check_arg.h
 smtpd_haproxy.o: ../../include/dns.h
 smtpd_haproxy.o: ../../include/haproxy_srvr.h
 smtpd_haproxy.o: ../../include/htable.h
+smtpd_haproxy.o: ../../include/iostuff.h
 smtpd_haproxy.o: ../../include/mail_params.h
 smtpd_haproxy.o: ../../include/mail_stream.h
 smtpd_haproxy.o: ../../include/milter.h
index a584a2c83cca9c6392b33a25e60d152833de6a0b..093686c41a87c01d4c0a86dc627b1c9545a3f96d 100644 (file)
@@ -345,7 +345,7 @@ extern void smtpd_state_reset(SMTPD_STATE *);
   */
 extern void smtpd_peer_init(SMTPD_STATE *state);
 extern void smtpd_peer_reset(SMTPD_STATE *state);
-extern int smtpd_peer_from_haproxy(SMTPD_STATE *state);
+extern int smtpd_peer_from_haproxy(SMTPD_STATE *, void (*) (SMTPD_STATE *));
 
 #define        SMTPD_PEER_CODE_OK      2
 #define SMTPD_PEER_CODE_TEMP   4
index 3bcbffc6aebe5740b04c3ed127866ee04cc8da75..fe402eb681ab06664aaf8c9cb492b1c148b44415 100644 (file)
@@ -6,8 +6,9 @@
 /* SYNOPSIS
 /*     #include "smtpd.h"
 /*
-/*     int     smtpd_peer_from_haproxy(state)
+/*     int     smtpd_peer_from_haproxy(state, default_lookup)
 /*     SMTPD_STATE *state;
+/*     void    (*default_lookup) (SMTPD_STATE *);
 /* DESCRIPTION
 /*     smtpd_peer_from_haproxy() receives endpoint address and
 /*     port information via the haproxy protocol.
 /*     The following summarizes what the Postfix SMTP server expects
 /*     from an up-stream proxy adapter.
 /* .IP \(bu
+/*     Call the default_lookup function if the up-stream proxy
+/*     indicates that the connection is not proxied. In that case,
+/*     a proxy adapter MUST NOT update the connection info: the
+/*     default_lookup function will do that instead.
+/* .IP \(bu
 /*     Validate protocol, address and port syntax. Permit only
 /*     protocols that are configured with the main.cf:inet_protocols
 /*     setting.
@@ -70,6 +76,7 @@
 #include <myaddrinfo.h>
 #include <mymalloc.h>
 #include <stringops.h>
+#include <iostuff.h>
 
 /* Global library. */
 
 
 /* smtpd_peer_from_haproxy - initialize peer information from haproxy */
 
-int     smtpd_peer_from_haproxy(SMTPD_STATE *state)
+int     smtpd_peer_from_haproxy(SMTPD_STATE *state,
+                                    void (*default_lookup) (SMTPD_STATE *))
 {
-    const char *myname = "smtpd_peer_from_haproxy";
     MAI_HOSTADDR_STR smtp_client_addr;
     MAI_SERVPORT_STR smtp_client_port;
     MAI_HOSTADDR_STR smtp_server_addr;
     MAI_SERVPORT_STR smtp_server_port;
-    const char *proxy_err;
-    int     io_err;
-    VSTRING *escape_buf;
+    int     non_proxy = 0;
 
-    /*
-     * While reading HAProxy handshake information, don't buffer input beyond
-     * the end-of-line. That would break the TLS wrappermode handshake.
-     */
-    vstream_control(state->client,
-                   VSTREAM_CTL_BUFSIZE, 1,
-                   VSTREAM_CTL_END);
-
-    /*
-     * Note: the haproxy_srvr_parse() routine performs address protocol
-     * checks, address and port syntax checks, and converts IPv4-in-IPv6
-     * address string syntax (::ffff:1.2.3.4) to IPv4 syntax where permitted
-     * by the main.cf:inet_protocols setting, but logs no warnings.
-     */
-#define ENABLE_DEADLINE        1
-
-    smtp_stream_setup(state->client, var_smtpd_uproxy_tmout, ENABLE_DEADLINE);
-    switch (io_err = vstream_setjmp(state->client)) {
-    default:
-       msg_panic("%s: unhandled I/O error %d", myname, io_err);
-    case SMTP_ERR_EOF:
-       msg_warn("haproxy read: unexpected EOF");
-       return (-1);
-    case SMTP_ERR_TIME:
+    if (read_wait(vstream_fileno(state->client), var_smtpd_uproxy_tmout) < 0) {
        msg_warn("haproxy read: timeout error");
        return (-1);
-    case 0:
-       if (smtp_get(state->buffer, state->client, HAPROXY_MAX_LEN,
-                    SMTP_GET_FLAG_NONE) != '\n') {
-           msg_warn("haproxy read: line > %d characters", HAPROXY_MAX_LEN);
-           return (-1);
-       }
-       if ((proxy_err = haproxy_srvr_parse(STR(state->buffer),
-                                      &smtp_client_addr, &smtp_client_port,
-                             &smtp_server_addr, &smtp_server_port)) != 0) {
-           escape_buf = vstring_alloc(HAPROXY_MAX_LEN + 2);
-           escape(escape_buf, STR(state->buffer), LEN(state->buffer));
-           msg_warn("haproxy read: %s: %s", proxy_err, STR(escape_buf));
-           vstring_free(escape_buf);
-           return (-1);
-       }
-       state->addr = mystrdup(smtp_client_addr.buf);
-       if (strrchr(state->addr, ':') != 0) {
-           state->rfc_addr = concatenate(IPV6_COL, state->addr, (char *) 0);
-           state->addr_family = AF_INET6;
-       } else {
-           state->rfc_addr = mystrdup(state->addr);
-           state->addr_family = AF_INET;
-       }
-       state->port = mystrdup(smtp_client_port.buf);
-
-       /*
-        * The Dovecot authentication server needs the server IP address.
-        */
-       state->dest_addr = mystrdup(smtp_server_addr.buf);
-       state->dest_port = mystrdup(smtp_server_port.buf);
-
-       /*
-        * Enable normal buffering.
-        */
-       vstream_control(state->client,
-                       VSTREAM_CTL_BUFSIZE, VSTREAM_BUFSIZE,
-                       VSTREAM_CTL_END);
+    }
+    if (haproxy_srvr_receive(vstream_fileno(state->client), &non_proxy,
+                            &smtp_client_addr, &smtp_client_port,
+                            &smtp_server_addr, &smtp_server_port) < 0) {
+       return (-1);
+    }
+    if (non_proxy) {
+       default_lookup(state);
        return (0);
     }
+    state->addr = mystrdup(smtp_client_addr.buf);
+    if (strrchr(state->addr, ':') != 0) {
+       state->rfc_addr = concatenate(IPV6_COL, state->addr, (char *) 0);
+       state->addr_family = AF_INET6;
+    } else {
+       state->rfc_addr = mystrdup(state->addr);
+       state->addr_family = AF_INET;
+    }
+    state->port = mystrdup(smtp_client_port.buf);
+
+    /*
+     * The Dovecot authentication server needs the server IP address.
+     */
+    state->dest_addr = mystrdup(smtp_server_addr.buf);
+    state->dest_port = mystrdup(smtp_server_port.buf);
+    return (0);
 }
index 073310a4788dd84fcebb9deb1d40ab7efa72589c..725d67cb649799fe71912c12f55b4d0f1a6e2e21 100644 (file)
@@ -544,7 +544,8 @@ static void smtpd_peer_from_proxy(SMTPD_STATE *state)
 {
     typedef struct {
        const char *name;
-       int     (*endpt_lookup) (SMTPD_STATE *);
+       int     (*endpt_lookup) (SMTPD_STATE *,
+                                   void (*default_lookup) (SMTPD_STATE *));
     } SMTPD_ENDPT_LOOKUP_INFO;
     static const SMTPD_ENDPT_LOOKUP_INFO smtpd_endpt_lookup_info[] = {
        HAPROXY_PROTO_NAME, smtpd_peer_from_haproxy,
@@ -563,7 +564,7 @@ static void smtpd_peer_from_proxy(SMTPD_STATE *state)
        if (strcmp(var_smtpd_uproxy_proto, pp->name) == 0)
            break;
     }
-    if (pp->endpt_lookup(state) < 0) {
+    if (pp->endpt_lookup(state, smtpd_peer_from_default) < 0) {
        smtpd_peer_no_client(state);
        state->flags |= SMTPD_FLAG_HANGUP;
     } else {