]> git.ipfire.org Git - thirdparty/postfix.git/commitdiff
postfix-3.5-20200105-nonprod 20200101-nonprod
authorWietse Venema <wietse@porcupine.org>
Sun, 5 Jan 2020 05:00:00 +0000 (00:00 -0500)
committerViktor Dukhovni <postfix-users@dukhovni.org>
Mon, 6 Jan 2020 02:31:42 +0000 (21:31 -0500)
13 files changed:
postfix/HISTORY
postfix/conf/master.cf
postfix/src/dns/dns_lookup.c
postfix/src/global/haproxy_srvr.c
postfix/src/global/mail_version.h
postfix/src/global/maillog_client.c
postfix/src/postscreen/postscreen_haproxy.c
postfix/src/smtpd/smtpd.c
postfix/src/smtpd/smtpd.h
postfix/src/smtpd/smtpd_haproxy.c
postfix/src/smtpd/smtpd_peer.c
postfix/src/util/msg_logger.c
postfix/src/util/msg_logger.h

index fbd081c9d9b6c3a653901a176464499a4cb6a098..66c0e678fdffe997da4145930c212c005699861d 100644 (file)
@@ -24503,12 +24503,48 @@ Apologies for any names omitted.
        the verify database, to avoid Postfix warnings about malformed
        UTF8. File: verify/verify.c.
 
+20191215
+
+       Future proofing: the Postfix DNS library logs a warning if
+       the DNS_REQ_FLAG_NCACHE_TTL dns_lookup flag is set and the
+       RES_DNSRCH or RES_DEFNAMES resolver flags are set, and
+       disables those resolver flags. File: dns/dns_lookup.c.
+
+20191230
+
+       Documentation: added the 'X' flag (final delivery) to the
+       pipe-based final delivery examples in the default master.cf
+       file. File: conf/master.cf
+
 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.
+       2 introduces a dependency of the reader on the parser, and
+       because version 2 introduces support for non-proxied
+       connections (used by health checks). 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.
+
+20201005
+
+       Workaround: postlog clients open the socket before entering
+       the chroot jail and before dropping privileges. This is needed
+       on MacOS and would not hurt otherwise. Files: util/msg_logger.[hc],
+       global/maillog_client.c.
+
+20200105
+
+       Support for the haproxy v2 protocol. The haproxy v2 protocol
+       support is limited to TCP over IPv4, TCP over IPv6, and
+       non-proxied connections. In the latter case, the caller
+       is responsible for any local or remote address/port lookup.
+       File: global/haproxy_srvr.c.
+
+       Cleanup: after haproxy handshake error, the Postfix SMTP
+       daemon now logs the proxy connection information instead
+       of unknown/unknown, and replies with "421 4.3.0 $myhostname
+       Server local error" instead of just hanging up. Error
+       details are logged to the maillog file. File: smtpd/smtpd.c.
index c0f2508033f6ac89ba2a2f91867fd767a3590488..30c3458e6d92b32ce6331009f6b5b9a2d4f47bd0 100644 (file)
@@ -79,7 +79,7 @@ postlog   unix-dgram n  -       n       -       1       postlogd
 # Also specify in main.cf: maildrop_destination_recipient_limit=1
 #
 #maildrop  unix  -       n       n       -       -       pipe
-#  flags=DRhu user=vmail argv=/usr/local/bin/maildrop -d ${recipient}
+#  flags=DRXhu user=vmail argv=/usr/local/bin/maildrop -d ${recipient}
 #
 # ====================================================================
 #
@@ -98,7 +98,7 @@ postlog   unix-dgram n  -       n       -       1       postlogd
 # Also specify in main.cf: cyrus_destination_recipient_limit=1
 #
 #cyrus     unix  -       n       n       -       -       pipe
-#  user=cyrus argv=/cyrus/bin/deliver -e -r ${sender} -m ${extension} ${user}
+#  flags=DRX user=cyrus argv=/cyrus/bin/deliver -e -r ${sender} -m ${extension} ${user}
 #
 # ====================================================================
 #
@@ -129,5 +129,5 @@ postlog   unix-dgram n  -       n       -       1       postlogd
 #  ${nexthop} ${user} ${extension}
 #
 #mailman   unix  -       n       n       -       -       pipe
-#  flags=FR user=list argv=/usr/lib/mailman/bin/postfix-to-mailman.py
+#  flags=FRX user=list argv=/usr/lib/mailman/bin/postfix-to-mailman.py
 #  ${nexthop} ${user}
index 14aec33141e996df71eb3ae09255bc310ba0a625..173775308599265d4c60f7c5a9b5d19accab1489 100644 (file)
 /*     available. The per-record reply TTL specifies how long the
 /*     DNS_NOTFOUND answer is valid. The caller should pass the
 /*     record(s) to dns_rr_free().
+/*     Logs a warning if the RES_DNSRCH or RES_DEFNAMES resolver
+/*     flags are set, and disables those flags.
 /* .RE
 /* .IP ltype
 /*     The resource record types to be looked up. In the case of
@@ -461,6 +463,16 @@ static int dns_query(const char *name, int type, unsigned flags,
     if (flags & RES_USE_DNSSEC)
        flags |= RES_USE_EDNS0;
 
+    /*
+     * Can't append domains: we need the right SOA TTL.
+     */
+#define APPEND_DOMAIN_FLAGS (RES_DNSRCH | RES_DEFNAMES)
+
+    if (keep_notfound && (flags & APPEND_DOMAIN_FLAGS)) {
+       msg_warn("negative caching disables RES_DEFNAMES and RES_DNSRCH");
+       flags &= ~APPEND_DOMAIN_FLAGS;
+    }
+
     /*
      * Save and restore resolver options that we overwrite, to avoid
      * surprising behavior in other code that also invokes the resolver.
index 30148ec2b04e3ea68af71f827c516f4d9a95015a..32638818bb8e10c1609cffbc06af90ddcd62fc09 100644 (file)
 /*     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.
+/*
+/*     The haproxy v2 protocol support is limited to TCP over IPv4,
+/*     TCP over IPv6, and non-proxied connections. In the latter
+/*     case, the caller is responsible for any local or remote
+/*     address/port lookup.
 /* LICENSE
 /* .ad
 /* .fi
@@ -77,6 +82,7 @@
 #include <mymalloc.h>
 #include <inet_proto.h>
 #include <split_at.h>
+#include <sock_addr.h>
 
 /* Global library. */
 
 
 /* Application-specific. */
 
+ /*
+  * Begin protocol v2 definitions from haproxy/include/types/connection.h.
+  */
+#define PP2_SIGNATURE          "\x0D\x0A\x0D\x0A\x00\x0D\x0A\x51\x55\x49\x54\x0A"
+#define PP2_SIGNATURE_LEN      12
+#define PP2_HEADER_LEN         16
+
+/* ver_cmd byte */
+#define PP2_CMD_LOCAL          0x00
+#define PP2_CMD_PROXY          0x01
+#define PP2_CMD_MASK           0x0F
+
+#define PP2_VERSION            0x20
+#define PP2_VERSION_MASK       0xF0
+
+/* fam byte */
+#define PP2_TRANS_UNSPEC       0x00
+#define PP2_TRANS_STREAM       0x01
+#define PP2_TRANS_DGRAM                0x02
+#define PP2_TRANS_MASK         0x0F
+
+#define PP2_FAM_UNSPEC         0x00
+#define PP2_FAM_INET           0x10
+#define PP2_FAM_INET6          0x20
+#define PP2_FAM_UNIX           0x30
+#define PP2_FAM_MASK           0xF0
+
+/* len field */
+#define PP2_ADDR_LEN_UNSPEC    (0)
+#define PP2_ADDR_LEN_INET      (4 + 4 + 2 + 2)
+#define PP2_ADDR_LEN_INET6     (16 + 16 + 2 + 2)
+#define PP2_ADDR_LEN_UNIX      (108 + 108)
+
+#define PP2_HDR_LEN_UNSPEC     (PP2_HEADER_LEN + PP2_ADDR_LEN_UNSPEC)
+#define PP2_HDR_LEN_INET       (PP2_HEADER_LEN + PP2_ADDR_LEN_INET)
+#define PP2_HDR_LEN_INET6      (PP2_HEADER_LEN + PP2_ADDR_LEN_INET6)
+#define PP2_HDR_LEN_UNIX       (PP2_HEADER_LEN + PP2_ADDR_LEN_UNIX)
+
+struct proxy_hdr_v2 {
+    uint8_t sig[PP2_SIGNATURE_LEN];    /* PP2_SIGNATURE */
+    uint8_t ver_cmd;                   /* protocol version | command */
+    uint8_t fam;                       /* protocol family and transport */
+    uint16_t len;                      /* length of remainder */
+    union {
+       struct {                        /* for TCP/UDP over IPv4, len = 12 */
+           uint32_t src_addr;
+           uint32_t dst_addr;
+           uint16_t src_port;
+           uint16_t dst_port;
+       }       ip4;
+       struct {                        /* for TCP/UDP over IPv6, len = 36 */
+           uint8_t src_addr[16];
+           uint8_t dst_addr[16];
+           uint16_t src_port;
+           uint16_t dst_port;
+       }       ip6;
+       struct {                        /* for AF_UNIX sockets, len = 216 */
+           uint8_t src_addr[108];
+           uint8_t dst_addr[108];
+       }       unx;
+    }       addr;
+};
+
+ /*
+  * End protocol v2 definitions from haproxy/include/types/connection.h.
+  */
+
 static INET_PROTO_INFO *proto_info;
 
 #define STR_OR_NULL(str) ((str) ? (str) : "(null)")
@@ -95,15 +168,19 @@ static int haproxy_srvr_parse_lit(const char *str,...)
     va_list ap;
     const char *cp;
     int     result = -1;
+    int     count;
 
     if (msg_verbose)
        msg_info("haproxy_srvr_parse: %s", STR_OR_NULL(str));
 
     if (str != 0) {
        va_start(ap, str);
-       while (result < 0 && (cp = va_arg(ap, const char *)) != 0)
-           if (strcmp(str, cp) == 0)
-               result = 0;
+       for (count = 0; (cp = va_arg(ap, const char *)) != 0; count++) {
+           if (strcmp(str, cp) == 0) {
+               result = count;
+               break;
+           }
+       }
        va_end(ap);
     }
     return (result);
@@ -190,6 +267,149 @@ static int haproxy_srvr_parse_port(const char *str, MAI_SERVPORT_STR *port)
     }
 }
 
+/* haproxy_srvr_parse_v2_addr_v4 - parse IPv4 info from v2 header */
+
+static int haproxy_srvr_parse_v2_addr_v4(uint32_t sin_addr,
+                                                unsigned sin_port,
+                                                MAI_HOSTADDR_STR *addr,
+                                                MAI_SERVPORT_STR *port)
+{
+    struct sockaddr_in sin;
+
+    memset((void *) &sin, 0, sizeof(sin));
+    sin.sin_family = AF_INET;
+    sin.sin_addr.s_addr = sin_addr;
+    sin.sin_port = sin_port;
+    if (sockaddr_to_hostaddr((struct sockaddr *) &sin, sizeof(sin),
+                            addr, port, 0) < 0)
+       return (-1);
+    return (0);
+}
+
+#ifdef AF_INET6
+
+/* haproxy_srvr_parse_v2_addr_v6 - parse IPv6 info from v2 header */
+
+static int haproxy_srvr_parse_v2_addr_v6(uint8_t * sin6_addr,
+                                                unsigned sin6_port,
+                                                MAI_HOSTADDR_STR *addr,
+                                                MAI_SERVPORT_STR *port)
+{
+    struct sockaddr_in6 sin6;
+
+    memset((void *) &sin6, 0, sizeof(sin6));
+    sin6.sin6_family = AF_INET6;
+    memcpy(&sin6.sin6_addr, sin6_addr, 16);
+    sin6.sin6_port = sin6_port;
+    if (sockaddr_to_hostaddr((struct sockaddr *) &sin6,
+                            sizeof(sin6), addr, port, 0) < 0)
+       return (-1);
+    if (addr->buf[0] == ':'
+       && strncasecmp("::ffff:", addr->buf, 7) == 0
+       && strchr((char *) proto_info->sa_family_list, AF_INET) != 0)
+       memmove(addr->buf, addr->buf + 7,
+               strlen(addr->buf) + 1 - 7);
+    return (0);
+}
+
+#endif
+
+/* haproxy_srvr_parse_v2_hdr - parse v2 header address info */
+
+static const char *haproxy_srvr_parse_v2_hdr(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 myname[] = "haproxy_srvr_parse_v2_hdr";
+    struct proxy_hdr_v2 *hdr_v2;
+
+    if (*str_len < PP2_HEADER_LEN)
+       return ("short protocol header");
+    hdr_v2 = (struct proxy_hdr_v2 *) str;
+    if (memcmp(hdr_v2->sig, PP2_SIGNATURE, PP2_SIGNATURE_LEN) != 0)
+       return ("unrecognized protocol header");
+    if ((hdr_v2->ver_cmd & PP2_VERSION_MASK) != PP2_VERSION)
+       return ("unrecognized protocol version");
+    if (*str_len < PP2_HEADER_LEN + ntohs(hdr_v2->len))
+       return ("short version 2 protocol header");
+
+    switch (hdr_v2->ver_cmd & PP2_CMD_MASK) {
+
+       /*
+        * Proxied connection, use the proxy-provided connection info.
+        */
+    case PP2_CMD_PROXY:
+       switch (hdr_v2->fam) {
+       case PP2_FAM_INET | PP2_TRANS_STREAM:{  /* TCP4 */
+               if (strchr((char *) proto_info->sa_family_list, AF_INET) == 0)
+                   return ("Postfix IPv4 support is disabled");
+               if (ntohs(hdr_v2->len) < PP2_ADDR_LEN_INET)
+                   return ("short address field");
+               if (haproxy_srvr_parse_v2_addr_v4(hdr_v2->addr.ip4.src_addr,
+                                                 hdr_v2->addr.ip4.src_port,
+                                   smtp_client_addr, smtp_client_port) < 0)
+                   return ("client network address conversion error");
+               if (msg_verbose)
+                   msg_info("%s: smtp_client_addr=%s smtp_client_port=%s",
+                     myname, smtp_client_addr->buf, smtp_client_port->buf);
+               if (haproxy_srvr_parse_v2_addr_v4(hdr_v2->addr.ip4.dst_addr,
+                                                 hdr_v2->addr.ip4.dst_port,
+                                   smtp_server_addr, smtp_server_port) < 0)
+                   return ("server network address conversion error");
+               if (msg_verbose)
+                   msg_info("%s: smtp_server_addr=%s smtp_server_port=%s",
+                     myname, smtp_server_addr->buf, smtp_server_port->buf);
+               break;
+           }
+       case PP2_FAM_INET6 | PP2_TRANS_STREAM:{/* TCP6 */
+#ifdef AF_INET6
+               if (strchr((char *) proto_info->sa_family_list, AF_INET6) == 0)
+                   return ("Postfix IPv6 support is disabled");
+               if (ntohs(hdr_v2->len) < PP2_ADDR_LEN_INET6)
+                   return ("short address field");
+               if (haproxy_srvr_parse_v2_addr_v6(hdr_v2->addr.ip6.src_addr,
+                                                 hdr_v2->addr.ip6.src_port,
+                                                 smtp_client_addr,
+                                                 smtp_client_port) < 0)
+                   return ("client network address conversion error");
+               if (msg_verbose)
+                   msg_info("%s: smtp_client_addr=%s smtp_client_port=%s",
+                     myname, smtp_client_addr->buf, smtp_client_port->buf);
+               if (haproxy_srvr_parse_v2_addr_v6(hdr_v2->addr.ip6.dst_addr,
+                                                 hdr_v2->addr.ip6.dst_port,
+                                                 smtp_server_addr,
+                                                 smtp_server_port) < 0)
+                   return ("server network address conversion error");
+               if (msg_verbose)
+                   msg_info("%s: smtp_server_addr=%s smtp_server_port=%s",
+                     myname, smtp_server_addr->buf, smtp_server_port->buf);
+               break;
+#else
+               return ("Postfix IPv6 support is not compiled in");
+#endif
+           }
+       default:
+           return ("unsupported network protocol");
+       }
+       *non_proxy = 0;
+       *str_len = PP2_HEADER_LEN + ntohs(hdr_v2->len);
+       return (0);
+
+       /*
+        * Non-proxied connection, use the proxy-to-server connection info.
+        */
+    case PP2_CMD_LOCAL:
+       *non_proxy = 1;
+       *str_len = PP2_HEADER_LEN + ntohs(hdr_v2->len);
+       return (0);
+    default:
+       return ("bad command in proxy header");
+    }
+}
+
 /* haproxy_srvr_parse - parse haproxy line */
 
 const char *haproxy_srvr_parse(const char *str, ssize_t *str_len,
@@ -200,13 +420,10 @@ const char *haproxy_srvr_parse(const char *str, ssize_t *str_len,
                                       MAI_SERVPORT_STR *smtp_server_port)
 {
     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.
@@ -216,9 +433,10 @@ const char *haproxy_srvr_parse(const char *str, ssize_t *str_len,
      * Try version 1 protocol.
      */
     if (strncmp(str, "PROXY ", 6) == 0) {
-       char   *saved_str = mystrdup(str);
+       char   *saved_str = mystrndup(str, *str_len);
        char   *cp = saved_str;
        char   *beyond_header = split_at(saved_str, '\n');
+       int     addr_family;
 
 #define NEXT_TOKEN mystrtok(&cp, " \r")
        if (beyond_header == 0)
@@ -242,14 +460,18 @@ const char *haproxy_srvr_parse(const char *str, ssize_t *str_len,
            *str_len = beyond_header - saved_str;
        }
        myfree(saved_str);
+
+       *non_proxy = 0;
        return (err);
     }
 
     /*
-     * Assume version 2 protocol.
+     * Try version 2 protocol.
      */
     else {
-       return ("v2 protocol is not implemented");
+       return (haproxy_srvr_parse_v2_hdr(str, str_len, non_proxy,
+                                         smtp_client_addr, smtp_client_port,
+                                      smtp_server_addr, smtp_server_port));
     }
 }
 
@@ -281,6 +503,7 @@ int     haproxy_srvr_receive(int fd, int *non_proxy,
      * 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) {
@@ -305,127 +528,346 @@ int     haproxy_srvr_receive(int fd, int *non_proxy,
   * Test program.
   */
 #ifdef TEST
-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;
-       const char *exp_client_port;
-       const char *exp_server_port;
-    } 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, 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, 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, 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", 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", 0, 0, "unsupported protocol type"},
-       {"BLAH", 0, 0, "v2 protocol is not implemented"},
-       0,
-    };
-    TEST_CASE *test_case;
 
+ /*
+  * Test cases with inputs and expected outputs. A request may contain
+  * trailing garbage, and it may be too short. A v1 request may also contain
+  * malformed address or port information.
+  */
+typedef struct TEST_CASE {
+    const char *haproxy_request;       /* v1 or v2 request including thrash */
+    ssize_t haproxy_req_len;           /* request length including thrash */
+    ssize_t exp_req_len;               /* parsed request length */
+    int     exp_non_proxy;             /* request is not proxied */
+    const char *exp_return;            /* expected error string */
+    const char *exp_client_addr;       /* expected client address string */
+    const char *exp_server_addr;       /* expected client port string */
+    const char *exp_client_port;       /* expected client address string */
+    const char *exp_server_port;       /* expected server port string */
+} TEST_CASE;
+static TEST_CASE v1_test_cases[] = {
+    /* IPv6. */
+    {"PROXY TCP6 fc:00:00:00:1:2:3:4 fc:00:00:00:4:3:2:1 123 321\n", 0, 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\n", 0, 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\n", 0, 0, 0, "unexpected client address syntax"},
+    {"PROXY TCP6 fc:00:00:00:1:2:3:4 4.3.2.1 123 321\n", 0, 0, 0, "unexpected server address syntax"},
+    /* IPv4 in IPv6. */
+    {"PROXY TCP6 ::ffff:1.2.3.4 ::ffff:4.3.2.1 123 321\n", 0, 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\n", 0, 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\n", 0, 0, 0, "unexpected client address syntax"},
+    {"PROXY TCP4 1.2.3.4 ::ffff:4.3.2.1 123 321\n", 0, 0, 0, "unexpected server address syntax"},
+    /* IPv4. */
+    {"PROXY TCP4 1.2.3.4 4.3.2.1 123 321\n", 0, 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\n", 0, 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\n", 0, 0, 0, "unexpected client port syntax"},
+    {"PROXY TCP4 1.2.3.4 4.3.2.1 123 654321\n", 0, 0, 0, "unexpected server port syntax"},
+    {"PROXY TCP4 1.2.3.4 4.3.2.1 0123 321\n", 0, 0, 0, "unexpected client port syntax"},
+    {"PROXY TCP4 1.2.3.4 4.3.2.1 123 0321\n", 0, 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\n", 0, 0, 0, "unexpected server port syntax"},
+    {"PROXY TCP6 fc:00:00:00:1:2:3:4 fc:00:00:00:4:3:2:1\n", 0, 0, 0, "unexpected client port syntax"},
+    {"PROXY TCP6 fc:00:00:00:1:2:3:4\n", 0, 0, 0, "unexpected server address syntax"},
+    {"PROXY TCP6\n", 0, 0, 0, "unexpected client address syntax"},
+    {"PROXY TCP4 1.2.3.4 4.3.2.1 123\n", 0, 0, 0, "unexpected server port syntax"},
+    {"PROXY TCP4 1.2.3.4 4.3.2.1\n", 0, 0, 0, "unexpected client port syntax"},
+    {"PROXY TCP4 1.2.3.4\n", 0, 0, 0, "unexpected server address syntax"},
+    {"PROXY TCP4\n", 0, 0, 0, "unexpected client address syntax"},
+    /* Other. */
+    {"PROXY BLAH\n", 0, 0, 0, "unsupported protocol type"},
+    {"BLAH\n", 0, 0, 0, "short protocol header"},
+    0,
+};
+
+static struct proxy_hdr_v2 v2_local_request = {
+    PP2_SIGNATURE, PP2_VERSION | PP2_CMD_LOCAL,
+};
+static TEST_CASE v2_non_proxy_test = {
+    (char *) &v2_local_request, PP2_HEADER_LEN, PP2_HEADER_LEN, 1,
+};
+
+#define STR(x) vstring_str(x)
+#define LEN(x) VSTRING_LEN(x)
+
+/* evaluate_test_case - evaluate one test case */
+
+static int evaluate_test_case(const char *test_label,
+                                     const TEST_CASE *test_case)
+{
     /* Actual results. */
     const char *act_return;
-    ssize_t act_str_len;
+    ssize_t act_req_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;
     MAI_SERVPORT_STR act_smtp_server_port;
+    int     test_failed;
+
+    if (msg_verbose)
+       msg_info("test case=%s exp_client_addr=%s exp_server_addr=%s "
+                "exp_client_port=%s exp_server_port=%s",
+                test_label, STR_OR_NULL(test_case->exp_client_addr),
+                STR_OR_NULL(test_case->exp_server_addr),
+                STR_OR_NULL(test_case->exp_client_port),
+                STR_OR_NULL(test_case->exp_server_port));
+
+    /*
+     * Start the test.
+     */
+    test_failed = 0;
+    act_req_len = test_case->haproxy_req_len;
+    act_return =
+       haproxy_srvr_parse(test_case->haproxy_request, &act_req_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) {
+       msg_warn("test case %s return expected=%s actual=%s",
+                test_label, STR_OR_NULL(test_case->exp_return),
+                STR_OR_NULL(act_return));
+       test_failed = 1;
+       return (test_failed);
+    }
+    if (act_req_len != test_case->exp_req_len) {
+       msg_warn("test case %s str_len expected=%ld actual=%ld",
+                test_label,
+                (long) test_case->exp_req_len, (long) act_req_len);
+       test_failed = 1;
+       return (test_failed);
+    }
+    if (act_non_proxy != test_case->exp_non_proxy) {
+       msg_warn("test case %s non_proxy expected=%d actual=%d",
+                test_label,
+                test_case->exp_non_proxy, act_non_proxy);
+       test_failed = 1;
+       return (test_failed);
+    }
+    if (test_case->exp_non_proxy || test_case->exp_return != 0)
+       /* No expected address/port results. */
+       return (test_failed);
+
+    /*
+     * Compare address/port results against expected results.
+     */
+    if (strcmp(test_case->exp_client_addr, act_smtp_client_addr.buf)) {
+       msg_warn("test case %s client_addr  expected=%s actual=%s",
+                test_label,
+                test_case->exp_client_addr, act_smtp_client_addr.buf);
+       test_failed = 1;
+    }
+    if (strcmp(test_case->exp_server_addr, act_smtp_server_addr.buf)) {
+       msg_warn("test case %s server_addr  expected=%s actual=%s",
+                test_label,
+                test_case->exp_server_addr, act_smtp_server_addr.buf);
+       test_failed = 1;
+    }
+    if (strcmp(test_case->exp_client_port, act_smtp_client_port.buf)) {
+       msg_warn("test case %s client_port  expected=%s actual=%s",
+                test_label,
+                test_case->exp_client_port, act_smtp_client_port.buf);
+       test_failed = 1;
+    }
+    if (strcmp(test_case->exp_server_port, act_smtp_server_port.buf)) {
+       msg_warn("test case %s server_port  expected=%s actual=%s",
+                test_label,
+                test_case->exp_server_port, act_smtp_server_port.buf);
+       test_failed = 1;
+    }
+    return (test_failed);
+}
+
+/* convert_v1_proxy_req_to_v2 - convert well-formed v1 proxy request to v2 */
+
+static void convert_v1_proxy_req_to_v2(VSTRING *buf, const char *req,
+                                              ssize_t req_len)
+{
+    const char myname[] = "convert_v1_proxy_req_to_v2";
+    const char *err;
+    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;
+    struct proxy_hdr_v2 *hdr_v2;
+    struct addrinfo *src_res;
+    struct addrinfo *dst_res;
+
+    /*
+     * Allocate buffer space for the largest possible protocol header, so we
+     * don't have to worry about hidden realloc() calls.
+     */
+    VSTRING_RESET(buf);
+    VSTRING_SPACE(buf, sizeof(struct proxy_hdr_v2));
+    hdr_v2 = (struct proxy_hdr_v2 *) STR(buf);
+
+    /*
+     * Fill in the header,
+     */
+    memcpy(hdr_v2->sig, PP2_SIGNATURE, PP2_SIGNATURE_LEN);
+    hdr_v2->ver_cmd = PP2_VERSION | PP2_CMD_PROXY;
+    if ((err = haproxy_srvr_parse(req, &req_len, &non_proxy, &smtp_client_addr,
+                                 &smtp_client_port, &smtp_server_addr,
+                                 &smtp_server_port)) != 0 || non_proxy)
+       msg_fatal("%s: malformed or non-proxy request: %s",
+                 myname, req);
+
+    if (hostaddr_to_sockaddr(smtp_client_addr.buf, smtp_client_port.buf, 0,
+                            &src_res) != 0)
+       msg_fatal("%s: unable to convert source address %s port %s",
+                 myname, smtp_client_addr.buf, smtp_client_port.buf);
+    if (hostaddr_to_sockaddr(smtp_server_addr.buf, smtp_server_port.buf, 0,
+                            &dst_res) != 0)
+       msg_fatal("%s: unable to convert destination address %s port %s",
+                 myname, smtp_server_addr.buf, smtp_server_port.buf);
+    if (src_res->ai_family != dst_res->ai_family)
+       msg_fatal("%s: mixed source/destination address families", myname);
+#ifdef AF_INET6
+    if (src_res->ai_family == PF_INET6) {
+       hdr_v2->fam = PP2_FAM_INET6 | PP2_TRANS_STREAM;
+       hdr_v2->len = htons(PP2_ADDR_LEN_INET6);
+       memcpy(hdr_v2->addr.ip6.src_addr,
+              &SOCK_ADDR_IN6_ADDR(src_res->ai_addr),
+              sizeof(hdr_v2->addr.ip6.src_addr));
+       hdr_v2->addr.ip6.src_port = SOCK_ADDR_IN6_PORT(src_res->ai_addr);
+       memcpy(hdr_v2->addr.ip6.dst_addr,
+              &SOCK_ADDR_IN6_ADDR(dst_res->ai_addr),
+              sizeof(hdr_v2->addr.ip6.dst_addr));
+       hdr_v2->addr.ip6.dst_port = SOCK_ADDR_IN6_PORT(dst_res->ai_addr);
+    } else
+#endif
+    if (src_res->ai_family == PF_INET) {
+       hdr_v2->fam = PP2_FAM_INET | PP2_TRANS_STREAM;
+       hdr_v2->len = htons(PP2_ADDR_LEN_INET);
+       hdr_v2->addr.ip4.src_addr = SOCK_ADDR_IN_ADDR(src_res->ai_addr).s_addr;
+       hdr_v2->addr.ip4.src_port = SOCK_ADDR_IN_PORT(src_res->ai_addr);
+       hdr_v2->addr.ip4.dst_addr = SOCK_ADDR_IN_ADDR(dst_res->ai_addr).s_addr;
+       hdr_v2->addr.ip4.dst_port = SOCK_ADDR_IN_PORT(dst_res->ai_addr);
+    } else {
+       msg_panic("unknown address family 0x%x", src_res->ai_family);
+    }
+    vstring_set_payload_size(buf, PP2_SIGNATURE_LEN + ntohs(hdr_v2->len));
+    freeaddrinfo(src_res);
+    freeaddrinfo(dst_res);
+}
+
+int     main(int argc, char **argv)
+{
+    VSTRING *test_label;
+    TEST_CASE *v1_test_case;
+    TEST_CASE v2_test_case;
+    TEST_CASE mutated_test_case;
+    VSTRING *v2_request_buf;
+    VSTRING *mutated_request_buf;
 
     /* Findings. */
     int     tests_failed = 0;
     int     test_failed;
 
-    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, &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) {
-           msg_warn("test case %d return expected=%s actual=%s",
-                    (int) (test_case - test_cases),
-                    test_case->exp_return ?
-                    test_case->exp_return : "(null)",
-                    act_return ? act_return : "(null)");
-           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)
+    test_label = vstring_alloc(100);
+    v2_request_buf = vstring_alloc(100);
+    mutated_request_buf = vstring_alloc(100);
+
+    for (tests_failed = 0, v1_test_case = v1_test_cases;
+        v1_test_case->haproxy_request != 0;
+        tests_failed += test_failed, v1_test_case++) {
+
+       /*
+        * Fill in missing string length info in v1 test data.
+        */
+       if (v1_test_case->haproxy_req_len == 0)
+           v1_test_case->haproxy_req_len =
+               strlen(v1_test_case->haproxy_request);
+       if (v1_test_case->exp_req_len == 0)
+           v1_test_case->exp_req_len = v1_test_case->haproxy_req_len;
+
+       /*
+        * Evaluate each v1 test case.
+        */
+       vstring_sprintf(test_label, "%d", (int) (v1_test_case - v1_test_cases));
+       test_failed = evaluate_test_case(STR(test_label), v1_test_case);
+
+       /*
+        * If the v1 test input is malformed, skip the mutation tests.
+        */
+       if (v1_test_case->exp_return != 0)
            continue;
-       if (strcmp(test_case->exp_client_addr, act_smtp_client_addr.buf)) {
-           msg_warn("test case %d client_addr  expected=%s actual=%s",
-                    (int) (test_case - test_cases),
-                    test_case->exp_client_addr, act_smtp_client_addr.buf);
-           test_failed = 1;
-       }
-       if (strcmp(test_case->exp_server_addr, act_smtp_server_addr.buf)) {
-           msg_warn("test case %d server_addr  expected=%s actual=%s",
-                    (int) (test_case - test_cases),
-                    test_case->exp_server_addr, act_smtp_server_addr.buf);
-           test_failed = 1;
-       }
-       if (strcmp(test_case->exp_client_port, act_smtp_client_port.buf)) {
-           msg_warn("test case %d client_port  expected=%s actual=%s",
-                    (int) (test_case - test_cases),
-                    test_case->exp_client_port, act_smtp_client_port.buf);
-           test_failed = 1;
-       }
-       if (strcmp(test_case->exp_server_port, act_smtp_server_port.buf)) {
-           msg_warn("test case %d server_port  expected=%s actual=%s",
-                    (int) (test_case - test_cases),
-                    test_case->exp_server_port, act_smtp_server_port.buf);
-           test_failed = 1;
-       }
+
+       /*
+        * Mutation test: a well-formed v1 test case should still pass after
+        * appending a byte, and should return the actual parsed header
+        * length. The test uses the implicit VSTRING null safety byte.
+        */
+       vstring_sprintf(test_label, "%d (one byte appended)",
+                       (int) (v1_test_case - v1_test_cases));
+       mutated_test_case = *v1_test_case;
+       mutated_test_case.haproxy_req_len += 1;
+       /* reuse v1_test_case->exp_req_len */
+       test_failed += evaluate_test_case(STR(test_label), &mutated_test_case);
+
+       /*
+        * Mutation test: a well-formed v1 test case should fail after
+        * stripping the terminator.
+        */
+       vstring_sprintf(test_label, "%d (last byte stripped)",
+                       (int) (v1_test_case - v1_test_cases));
+       mutated_test_case = *v1_test_case;
+       mutated_test_case.exp_return = "missing protocol header terminator";
+       mutated_test_case.haproxy_req_len -= 1;
+       mutated_test_case.exp_req_len = mutated_test_case.haproxy_req_len;
+       test_failed += evaluate_test_case(STR(test_label), &mutated_test_case);
+
+       /*
+        * A 'well-formed' v1 test case should pass after conversion to v2.
+        */
+       vstring_sprintf(test_label, "%d (converted to v2)",
+                       (int) (v1_test_case - v1_test_cases));
+       v2_test_case = *v1_test_case;
+       convert_v1_proxy_req_to_v2(v2_request_buf,
+                                  v1_test_case->haproxy_request,
+                                  v1_test_case->haproxy_req_len);
+       v2_test_case.haproxy_request = STR(v2_request_buf);
+       v2_test_case.haproxy_req_len = PP2_HEADER_LEN
+           + ntohs(((struct proxy_hdr_v2 *) STR(v2_request_buf))->len);
+       v2_test_case.exp_req_len = v2_test_case.haproxy_req_len;
+       test_failed += evaluate_test_case(STR(test_label), &v2_test_case);
+
+       /*
+        * Mutation test: a well-formed v2 test case should still pass after
+        * appending a byte, and should return the actual parsed header
+        * length. The test uses the implicit VSTRING null safety byte.
+        */
+       vstring_sprintf(test_label, "%d (converted to v2, one byte appended)",
+                       (int) (v1_test_case - v1_test_cases));
+       mutated_test_case = v2_test_case;
+       mutated_test_case.haproxy_req_len += 1;
+       /* reuse v2_test_case->exp_req_len */
+       test_failed += evaluate_test_case(STR(test_label), &mutated_test_case);
+
+       /*
+        * Mutation test: a well-formed v2 test case should fail after
+        * stripping one byte
+        */
+       vstring_sprintf(test_label, "%d (converted to v2, last byte stripped)",
+                       (int) (v1_test_case - v1_test_cases));
+       mutated_test_case = v2_test_case;
+       mutated_test_case.haproxy_req_len -= 1;
+       mutated_test_case.exp_req_len = mutated_test_case.haproxy_req_len;
+       mutated_test_case.exp_return = "short version 2 protocol header";
+       test_failed += evaluate_test_case(STR(test_label), &mutated_test_case);
     }
+
+    /*
+     * Additional V2-only tests.
+     */
+    test_failed +=
+       evaluate_test_case("v2 non-proxy request", &v2_non_proxy_test);
+
+    /*
+     * Clean up.
+     */
+    vstring_free(v2_request_buf);
+    vstring_free(mutated_request_buf);
+    vstring_free(test_label);
     if (tests_failed)
        msg_info("tests failed: %d", tests_failed);
     exit(tests_failed != 0);
index 92912f56b9143a5529868a12b7b603f67292ca45..edf56475de3df650ceb655377a60d5d29ba370ab 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      "20200101"
+#define MAIL_RELEASE_DATE      "20200105"
 #define MAIL_VERSION_NUMBER    "3.5"
 
 #ifdef SNAPSHOT
index 36def640ddc8c78f288ff57b6a6a5907d9ca6304..6d6a528847cb00278322f29f5872678bc9885d57 100644 (file)
@@ -210,7 +210,7 @@ void    maillog_client_init(const char *progname, int flags)
        if (var_maillog_file && *var_maillog_file) {
            ARGV   *good_prefixes = argv_split(var_maillog_file_pfxs,
                                               CHARS_COMMA_SP);
-           char **cpp;
+           char  **cpp;
 
            for (cpp = good_prefixes->argv; /* see below */ ; cpp++) {
                if (*cpp == 0)
@@ -264,6 +264,8 @@ void    maillog_client_init(const char *progname, int flags)
        }
        if (service_path != import_service_path)
            myfree(service_path);
+       msg_logger_control(CA_MSG_LOGGER_CTL_CONNECT_NOW,
+                          CA_MSG_LOGGER_CTL_END);
     }
 
     /*
index 240f918789880b8fbeac23409d7c1036038f5d45..59a1852cd00cfb51b544d2b11a458858d4adab53 100644 (file)
 /*     MAI_SERVPORT_STR *smtp_server_port;
 /* DESCRIPTION
 /*     psc_endpt_haproxy_lookup() looks up connection endpoint
-/*     information via the haproxy protocol.  Arguments and results
-/*     conform to the postscreen_endpt(3) API.
+/*     information via the haproxy protocol, or looks up local
+/*     information if the haproxy handshake indicates that a
+/*     connection is not proxied. Arguments and results conform
+/*     to the postscreen_endpt(3) API.
 /* LICENSE
 /* .ad
 /* .fi
index 4521bd1da4ce5b24f751e43c5492a0978b9d6632..3321bd4e9dda7ee0451c8e61f2b98d9cd5f3af4b 100644 (file)
@@ -5430,6 +5430,16 @@ static void smtpd_proto(SMTPD_STATE *state)
 
     case 0:
 
+       /*
+        * Don't bother doing anything if some pre-SMTP handshake (haproxy)
+        * did not work out.
+        */
+       if (state->flags & SMTPD_FLAG_HANGUP) {
+           smtpd_chat_reply(state, "421 4.3.0 %s Server local error",
+                            var_myhostname);
+           break;
+       }
+
        /*
         * In TLS wrapper mode, turn on TLS using code that is shared with
         * the STARTTLS command. This code does not return when the handshake
@@ -5978,8 +5988,7 @@ static void smtpd_service(VSTREAM *stream, char *service, char **argv)
     /*
      * Provide the SMTP service.
      */
-    if ((state.flags & SMTPD_FLAG_HANGUP) == 0)
-       smtpd_proto(&state);
+    smtpd_proto(&state);
 
     /*
      * After the client has gone away, clean up whatever we have set up at
index 093686c41a87c01d4c0a86dc627b1c9545a3f96d..ae194035cc735eb4130d9bae85cf9c7d2670d681 100644 (file)
@@ -345,7 +345,8 @@ 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 *, void (*) (SMTPD_STATE *));
+extern void smtpd_peer_from_default(SMTPD_STATE *);
+extern int smtpd_peer_from_haproxy(SMTPD_STATE *);
 
 #define        SMTPD_PEER_CODE_OK      2
 #define SMTPD_PEER_CODE_TEMP   4
index fe402eb681ab06664aaf8c9cb492b1c148b44415..542c3fe3d5e3e724bc46e32df3d68444c66d1e2a 100644 (file)
@@ -6,9 +6,8 @@
 /* SYNOPSIS
 /*     #include "smtpd.h"
 /*
-/*     int     smtpd_peer_from_haproxy(state, default_lookup)
+/*     int     smtpd_peer_from_haproxy(state)
 /*     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
+/*     Call smtpd_peer_from_default() 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.
+/*     a proxy adapter MUST NOT update any STATE fields: the
+/*     smtpd_peer_from_default() 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
@@ -96,8 +95,7 @@
 
 /* smtpd_peer_from_haproxy - initialize peer information from haproxy */
 
-int     smtpd_peer_from_haproxy(SMTPD_STATE *state,
-                                    void (*default_lookup) (SMTPD_STATE *))
+int     smtpd_peer_from_haproxy(SMTPD_STATE *state)
 {
     MAI_HOSTADDR_STR smtp_client_addr;
     MAI_SERVPORT_STR smtp_client_port;
@@ -115,7 +113,7 @@ int     smtpd_peer_from_haproxy(SMTPD_STATE *state,
        return (-1);
     }
     if (non_proxy) {
-       default_lookup(state);
+       smtpd_peer_from_default(state);
        return (0);
     }
     state->addr = mystrdup(smtp_client_addr.buf);
index 725d67cb649799fe71912c12f55b4d0f1a6e2e21..7a48f85373ffdd917e2037befed924e29eab8201 100644 (file)
@@ -11,6 +11,9 @@
 /*
 /*     void    smtpd_peer_reset(state)
 /*     SMTPD_STATE *state;
+/* AUXILIARY METHODS
+/*     void    smtpd_peer_from_default(state)
+/*     SMTPD_STATE *state;
 /* DESCRIPTION
 /*     The smtpd_peer_init() routine attempts to produce a printable
 /*     version of the peer name and address of the specified socket.
 /* .RE
 /* .PP
 /*     smtpd_peer_reset() releases memory allocated by smtpd_peer_init().
+/*
+/*     smtpd_peer_from_default() looks up connection information
+/*     when an up-stream proxy indicates that a connection is not
+/*     proxied.
 /* LICENSE
 /* .ad
 /* .fi
@@ -511,7 +518,7 @@ static void smtpd_peer_from_pass_attr(SMTPD_STATE *state)
 
 /* smtpd_peer_from_default - try to initialize peer information from socket */
 
-static void smtpd_peer_from_default(SMTPD_STATE *state)
+void    smtpd_peer_from_default(SMTPD_STATE *state)
 {
 
     /*
@@ -544,8 +551,7 @@ static void smtpd_peer_from_proxy(SMTPD_STATE *state)
 {
     typedef struct {
        const char *name;
-       int     (*endpt_lookup) (SMTPD_STATE *,
-                                   void (*default_lookup) (SMTPD_STATE *));
+       int     (*endpt_lookup) (SMTPD_STATE *);
     } SMTPD_ENDPT_LOOKUP_INFO;
     static const SMTPD_ENDPT_LOOKUP_INFO smtpd_endpt_lookup_info[] = {
        HAPROXY_PROTO_NAME, smtpd_peer_from_haproxy,
@@ -564,8 +570,8 @@ static void smtpd_peer_from_proxy(SMTPD_STATE *state)
        if (strcmp(var_smtpd_uproxy_proto, pp->name) == 0)
            break;
     }
-    if (pp->endpt_lookup(state, smtpd_peer_from_default) < 0) {
-       smtpd_peer_no_client(state);
+    if (pp->endpt_lookup(state) < 0) {
+       smtpd_peer_from_default(state);
        state->flags |= SMTPD_FLAG_HANGUP;
     } else {
        smtpd_peer_hostaddr_to_sockaddr(state);
index 7c8f34dd6c65f54071e84c2bcc50b15b102fdd51..5a00c34762798e79e71c4dd4454ecae70cf8563a 100644 (file)
 /* .IP CA_MSG_LOGGER_CTL_DISABLE
 /*     Disable the msg_logger. This remains in effect until the
 /*     next msg_logger_init() call.
+/* .IP CA_MSG_LOGGER_CTL_CONNECT_NOW
+/*     Close the logging socket if it was already open, and open
+/*     the logging socket now, if permitted by current settings.
+/*     Otherwise, the open is delayed until a logging request.
 /* SEE ALSO
 /*     msg(3)  diagnostics module
 /* BUGS
@@ -111,6 +115,8 @@ static void (*msg_logger_fallback_fn) (const char *);
 static int msg_logger_fallback_only_override = 0;
 static int msg_logger_enable = 0;
 
+#define MSG_LOGGER_NEED_SOCKET() (msg_logger_fallback_only_override == 0)
+
  /*
   * Other state.
   */
@@ -130,6 +136,26 @@ static int msg_logger_sock = MSG_LOGGER_SOCK_NONE;
 #define STR(x) vstring_str(x)
 #define LEN(x) VSTRING_LEN(x)
 
+/* msg_logger_connect - connect to logger service */
+
+static void msg_logger_connect(void)
+{
+    if (msg_logger_sock == MSG_LOGGER_SOCK_NONE) {
+       msg_logger_sock = unix_dgram_connect(msg_logger_unix_path, BLOCKING);
+       if (msg_logger_sock >= 0)
+           close_on_exec(msg_logger_sock, CLOSE_ON_EXEC);
+    }
+}
+
+/* msg_logger_disconnect - disconnect from logger service */
+
+static void msg_logger_disconnect(void)
+{
+    if (msg_logger_sock != MSG_LOGGER_SOCK_NONE) {
+       (void) close(msg_logger_sock);
+       msg_logger_sock = MSG_LOGGER_SOCK_NONE;
+    }
+}
 
 /* msg_logger_print - log info to service or file */
 
@@ -203,12 +229,8 @@ static void msg_logger_print(int level, const char *text)
      * will report ENOENT if the endpoint does not exist, ECONNREFUSED if no
      * server has opened the endpoint.
      */
-    if (msg_logger_fallback_only_override == 0
-       && msg_logger_sock == MSG_LOGGER_SOCK_NONE) {
-       msg_logger_sock = unix_dgram_connect(msg_logger_unix_path, BLOCKING);
-       if (msg_logger_sock >= 0)
-           close_on_exec(msg_logger_sock, CLOSE_ON_EXEC);
-    }
+    if (MSG_LOGGER_NEED_SOCKET())
+       msg_logger_connect();
     if (msg_logger_sock != MSG_LOGGER_SOCK_NONE) {
        send(msg_logger_sock, STR(msg_logger_buf), LEN(msg_logger_buf), 0);
     } else if (msg_logger_fallback_fn) {
@@ -286,10 +308,7 @@ void    msg_logger_control(int name,...)
        switch (name) {
        case MSG_LOGGER_CTL_FALLBACK_ONLY:
            msg_logger_fallback_only_override = 1;
-           if (msg_logger_sock != MSG_LOGGER_SOCK_NONE) {
-               (void) close(msg_logger_sock);
-               msg_logger_sock = MSG_LOGGER_SOCK_NONE;
-           }
+           msg_logger_disconnect();
            break;
        case MSG_LOGGER_CTL_FALLBACK_FN:
            msg_logger_fallback_fn = va_arg(ap, MSG_LOGGER_FALLBACK_FN);
@@ -297,6 +316,11 @@ void    msg_logger_control(int name,...)
        case MSG_LOGGER_CTL_DISABLE:
            msg_logger_enable = 0;
            break;
+       case MSG_LOGGER_CTL_CONNECT_NOW:
+           msg_logger_disconnect();
+           if (MSG_LOGGER_NEED_SOCKET())
+               msg_logger_connect();
+           break;
        default:
            msg_panic("%s: bad name %d", myname, name);
        }
index e135e7e380dbbe2c4da88b960465b0799b921abb..4179f8b06cb56069461ed58bc5eb655e5e11d929 100644 (file)
@@ -35,6 +35,7 @@ extern void msg_logger_control(int,...);
 #define MSG_LOGGER_CTL_FALLBACK_ONLY   1
 #define MSG_LOGGER_CTL_FALLBACK_FN     2
 #define MSG_LOGGER_CTL_DISABLE         3
+#define MSG_LOGGER_CTL_CONNECT_NOW     4
 
 /* Safer API: type-checked arguments, external use. */
 #define CA_MSG_LOGGER_CTL_END          MSG_LOGGER_CTL_END
@@ -43,6 +44,7 @@ extern void msg_logger_control(int,...);
        MSG_LOGGER_CTL_FALLBACK_FN, CHECK_VAL(MSG_LOGGER_CTL, \
                MSG_LOGGER_FALLBACK_FN, (v))
 #define CA_MSG_LOGGER_CTL_DISABLE      MSG_LOGGER_CTL_DISABLE
+#define CA_MSG_LOGGER_CTL_CONNECT_NOW  MSG_LOGGER_CTL_CONNECT_NOW
 
 CHECK_VAL_HELPER_DCL(MSG_LOGGER_CTL, MSG_LOGGER_FALLBACK_FN);