From: Wietse Venema Date: Sun, 5 Jan 2020 05:00:00 +0000 (-0500) Subject: postfix-3.5-20200105-nonprod X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=refs%2Fheads%2F20200101-nonprod;p=thirdparty%2Fpostfix.git postfix-3.5-20200105-nonprod --- diff --git a/postfix/HISTORY b/postfix/HISTORY index fbd081c9d..66c0e678f 100644 --- a/postfix/HISTORY +++ b/postfix/HISTORY @@ -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. diff --git a/postfix/conf/master.cf b/postfix/conf/master.cf index c0f250803..30c3458e6 100644 --- a/postfix/conf/master.cf +++ b/postfix/conf/master.cf @@ -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} diff --git a/postfix/src/dns/dns_lookup.c b/postfix/src/dns/dns_lookup.c index 14aec3314..173775308 100644 --- a/postfix/src/dns/dns_lookup.c +++ b/postfix/src/dns/dns_lookup.c @@ -145,6 +145,8 @@ /* 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. diff --git a/postfix/src/global/haproxy_srvr.c b/postfix/src/global/haproxy_srvr.c index 30148ec2b..32638818b 100644 --- a/postfix/src/global/haproxy_srvr.c +++ b/postfix/src/global/haproxy_srvr.c @@ -41,6 +41,11 @@ /* 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 #include #include +#include /* Global library. */ @@ -84,6 +90,73 @@ /* 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); diff --git a/postfix/src/global/mail_version.h b/postfix/src/global/mail_version.h index 92912f56b..edf56475d 100644 --- a/postfix/src/global/mail_version.h +++ b/postfix/src/global/mail_version.h @@ -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 diff --git a/postfix/src/global/maillog_client.c b/postfix/src/global/maillog_client.c index 36def640d..6d6a52884 100644 --- a/postfix/src/global/maillog_client.c +++ b/postfix/src/global/maillog_client.c @@ -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); } /* diff --git a/postfix/src/postscreen/postscreen_haproxy.c b/postfix/src/postscreen/postscreen_haproxy.c index 240f91878..59a1852cd 100644 --- a/postfix/src/postscreen/postscreen_haproxy.c +++ b/postfix/src/postscreen/postscreen_haproxy.c @@ -18,8 +18,10 @@ /* 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 diff --git a/postfix/src/smtpd/smtpd.c b/postfix/src/smtpd/smtpd.c index 4521bd1da..3321bd4e9 100644 --- a/postfix/src/smtpd/smtpd.c +++ b/postfix/src/smtpd/smtpd.c @@ -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 diff --git a/postfix/src/smtpd/smtpd.h b/postfix/src/smtpd/smtpd.h index 093686c41..ae194035c 100644 --- a/postfix/src/smtpd/smtpd.h +++ b/postfix/src/smtpd/smtpd.h @@ -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 diff --git a/postfix/src/smtpd/smtpd_haproxy.c b/postfix/src/smtpd/smtpd_haproxy.c index fe402eb68..542c3fe3d 100644 --- a/postfix/src/smtpd/smtpd_haproxy.c +++ b/postfix/src/smtpd/smtpd_haproxy.c @@ -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. @@ -16,10 +15,10 @@ /* 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); diff --git a/postfix/src/smtpd/smtpd_peer.c b/postfix/src/smtpd/smtpd_peer.c index 725d67cb6..7a48f8537 100644 --- a/postfix/src/smtpd/smtpd_peer.c +++ b/postfix/src/smtpd/smtpd_peer.c @@ -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. @@ -96,6 +99,10 @@ /* .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); diff --git a/postfix/src/util/msg_logger.c b/postfix/src/util/msg_logger.c index 7c8f34dd6..5a00c3476 100644 --- a/postfix/src/util/msg_logger.c +++ b/postfix/src/util/msg_logger.c @@ -62,6 +62,10 @@ /* .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); } diff --git a/postfix/src/util/msg_logger.h b/postfix/src/util/msg_logger.h index e135e7e38..4179f8b06 100644 --- a/postfix/src/util/msg_logger.h +++ b/postfix/src/util/msg_logger.h @@ -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);