From: Wietse Venema Date: Wed, 1 Jan 2020 05:00:00 +0000 (-0500) Subject: postfix-3.5-20200101-nonprod X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=730b2770c21936aa090fff2ad519af632e879280;p=thirdparty%2Fpostfix.git postfix-3.5-20200101-nonprod --- diff --git a/postfix/HISTORY b/postfix/HISTORY index 5fdba30d0..fbd081c9d 100644 --- a/postfix/HISTORY +++ b/postfix/HISTORY @@ -24502,3 +24502,13 @@ Apologies for any names omitted. Bugfix: sanitize server responses before storing them in the verify database, to avoid Postfix warnings about malformed UTF8. File: verify/verify.c. + +20200101 + + Refactored the haproxy infrastructure in preparation for + haproxy version 2 support. This is necessary because version + 2 introduces a mutual dependency between the reader and the + parser. Files: global/haproxy_srvr.c, smtpd/smtpd_peer.c, + smtpd/smtpd_haproxy.c, smtpd/smtpd.h, postscreen/postscreen.h, + postscreen/postscreen_endpt.c, postscreen/postscreen_haproxy.c, + postscreen/postscreen_haproxy.h, global/haproxy_srvr.h. diff --git a/postfix/html/postconf.5.html b/postfix/html/postconf.5.html index df9c7c08e..f6cc50c1e 100644 --- a/postfix/html/postconf.5.html +++ b/postfix/html/postconf.5.html @@ -18510,7 +18510,7 @@ is unwise to choose an "bleeding-edge" curve supported by only a small subset of clients.

The default "strong" curve is rated in NSA Suite +href="https://web.archive.org/web/20160330034144/https://www.nsa.gov/ia/programs/suiteb_cryptography/">Suite B for information classified up to SECRET.

Note: elliptic curve names are poorly standardized; different @@ -18551,7 +18551,7 @@ curve must be implemented by OpenSSL (as reported by ecparam(1) with the of RFC 4492. You should not generally change this setting.

This default "ultra" curve is rated in NSA Suite +href="https://web.archive.org/web/20160330034144/https://www.nsa.gov/ia/programs/suiteb_cryptography/">Suite B for information classified up to TOP SECRET.

If you want to take maximal advantage of ciphers that offer /* -/* const char *haproxy_srvr_parse(str, +/* const char *haproxy_srvr_parse(str, str_len, non_proxy, /* smtp_client_addr, smtp_client_port, /* smtp_server_addr, smtp_server_port) /* const char *str; +/* ssize_t *str_len; +/* int *non_proxy; +/* MAI_HOSTADDR_STR *smtp_client_addr, +/* MAI_SERVPORT_STR *smtp_client_port, +/* MAI_HOSTADDR_STR *smtp_server_addr, +/* MAI_SERVPORT_STR *smtp_server_port; +/* +/* const char *haproxy_srvr_receive(fd, non_proxy, +/* smtp_client_addr, smtp_client_port, +/* smtp_server_addr, smtp_server_port) +/* int fd; +/* int *non_proxy; /* MAI_HOSTADDR_STR *smtp_client_addr, /* MAI_SERVPORT_STR *smtp_client_port, /* MAI_HOSTADDR_STR *smtp_server_addr, /* MAI_SERVPORT_STR *smtp_server_port; /* DESCRIPTION -/* haproxy_srvr_parse() parses a haproxy line. The result is -/* null in case of success, a pointer to text (with the error -/* type) in case of error. If both IPv6 and IPv4 support are -/* enabled, IPV4_IN_IPV6 address syntax (::ffff:1.2.3.4) is -/* converted to IPV4 syntax, provided that IPv4 support is -/* enabled. +/* haproxy_srvr_parse() parses a haproxy v1 or v2 protocol +/* message. The result is null in case of success, a pointer +/* to text (with the error type) in case of error. If both +/* IPv6 and IPv4 support are enabled, IPV4_IN_IPV6 address +/* syntax (::ffff:1.2.3.4) is converted to IPV4 syntax, provided +/* that IPv4 support is enabled. In case of success, the +/* str_len argument is updated with the number of bytes parsed, +/* and the non_proxy argument is true or false if the +/* haproxy message specifies a non-proxied connection. +/* +/* haproxy_srvr_receive() receives and parses a haproxy protocol +/* handshake. This must be called before any I/O is done on +/* the specified file descriptor. The result is 0 in case of +/* success, -1 in case of error. All errors are logged. /* LICENSE /* .ad /* .fi @@ -56,6 +76,7 @@ #include #include #include +#include /* Global library. */ @@ -65,6 +86,8 @@ static INET_PROTO_INFO *proto_info; +#define STR_OR_NULL(str) ((str) ? (str) : "(null)") + /* haproxy_srvr_parse_lit - extract and validate string literal */ static int haproxy_srvr_parse_lit(const char *str,...) @@ -74,7 +97,7 @@ static int haproxy_srvr_parse_lit(const char *str,...) int result = -1; if (msg_verbose) - msg_info("haproxy_srvr_parse: %s", str); + msg_info("haproxy_srvr_parse: %s", STR_OR_NULL(str)); if (str != 0) { va_start(ap, str); @@ -91,7 +114,7 @@ static int haproxy_srvr_parse_lit(const char *str,...) static int haproxy_srvr_parse_proto(const char *str, int *addr_family) { if (msg_verbose) - msg_info("haproxy_srvr_parse: proto=%s", str); + msg_info("haproxy_srvr_parse: proto=%s", STR_OR_NULL(str)); #ifdef AF_INET6 if (strcasecmp(str, "TCP6") == 0) { @@ -119,7 +142,8 @@ static int haproxy_srvr_parse_addr(const char *str, MAI_HOSTADDR_STR *addr, int err; if (msg_verbose) - msg_info("haproxy_srvr_parse: addr=%s proto=%d", str, addr_family); + msg_info("haproxy_srvr_parse: addr=%s proto=%d", + STR_OR_NULL(str), addr_family); if (str == 0 || strlen(str) >= sizeof(MAI_HOSTADDR_STR)) return (-1); @@ -156,7 +180,7 @@ static int haproxy_srvr_parse_addr(const char *str, MAI_HOSTADDR_STR *addr, static int haproxy_srvr_parse_port(const char *str, MAI_SERVPORT_STR *port) { if (msg_verbose) - msg_info("haproxy_srvr_parse: port=%s", str); + msg_info("haproxy_srvr_parse: port=%s", STR_OR_NULL(str)); if (str == 0 || strlen(str) >= sizeof(MAI_SERVPORT_STR) || !valid_hostport(str, DONT_GRIPE)) { return (-1); @@ -168,43 +192,113 @@ static int haproxy_srvr_parse_port(const char *str, MAI_SERVPORT_STR *port) /* haproxy_srvr_parse - parse haproxy line */ -const char *haproxy_srvr_parse(const char *str, +const char *haproxy_srvr_parse(const char *str, ssize_t *str_len, + int *non_proxy, MAI_HOSTADDR_STR *smtp_client_addr, MAI_SERVPORT_STR *smtp_client_port, MAI_HOSTADDR_STR *smtp_server_addr, MAI_SERVPORT_STR *smtp_server_port) { - char *saved_str = mystrdup(str); - char *cp = saved_str; const char *err; int addr_family; if (proto_info == 0) proto_info = inet_proto_info(); + *non_proxy = 0; + /* * XXX We don't accept connections with the "UNKNOWN" protocol type, * because those would sidestep address-based access control mechanisms. */ -#define NEXT_TOKEN mystrtok(&cp, " \r\n") - if (haproxy_srvr_parse_lit(NEXT_TOKEN, "PROXY", (char *) 0) < 0) - err = "unexpected protocol header"; - else if (haproxy_srvr_parse_proto(NEXT_TOKEN, &addr_family) < 0) - err = "unsupported protocol type"; - else if (haproxy_srvr_parse_addr(NEXT_TOKEN, smtp_client_addr, - addr_family) < 0) - err = "unexpected client address syntax"; - else if (haproxy_srvr_parse_addr(NEXT_TOKEN, smtp_server_addr, - addr_family) < 0) - err = "unexpected server address syntax"; - else if (haproxy_srvr_parse_port(NEXT_TOKEN, smtp_client_port) < 0) - err = "unexpected client port syntax"; - else if (haproxy_srvr_parse_port(NEXT_TOKEN, smtp_server_port) < 0) - err = "unexpected server port syntax"; - else - err = 0; - myfree(saved_str); - return (err); + + /* + * Try version 1 protocol. + */ + if (strncmp(str, "PROXY ", 6) == 0) { + char *saved_str = mystrdup(str); + char *cp = saved_str; + char *beyond_header = split_at(saved_str, '\n'); + +#define NEXT_TOKEN mystrtok(&cp, " \r") + if (beyond_header == 0) + err = "missing protocol header terminator"; + else if (haproxy_srvr_parse_lit(NEXT_TOKEN, "PROXY", (char *) 0) < 0) + err = "unexpected protocol header"; + else if (haproxy_srvr_parse_proto(NEXT_TOKEN, &addr_family) < 0) + err = "unsupported protocol type"; + else if (haproxy_srvr_parse_addr(NEXT_TOKEN, smtp_client_addr, + addr_family) < 0) + err = "unexpected client address syntax"; + else if (haproxy_srvr_parse_addr(NEXT_TOKEN, smtp_server_addr, + addr_family) < 0) + err = "unexpected server address syntax"; + else if (haproxy_srvr_parse_port(NEXT_TOKEN, smtp_client_port) < 0) + err = "unexpected client port syntax"; + else if (haproxy_srvr_parse_port(NEXT_TOKEN, smtp_server_port) < 0) + err = "unexpected server port syntax"; + else { + err = 0; + *str_len = beyond_header - saved_str; + } + myfree(saved_str); + return (err); + } + + /* + * Assume version 2 protocol. + */ + else { + return ("v2 protocol is not implemented"); + } +} + +/* haproxy_srvr_receive - redceive and parse haproxy protocol handshake */ + +int haproxy_srvr_receive(int fd, int *non_proxy, + MAI_HOSTADDR_STR *smtp_client_addr, + MAI_SERVPORT_STR *smtp_client_port, + MAI_HOSTADDR_STR *smtp_server_addr, + MAI_SERVPORT_STR *smtp_server_port) +{ + const char *err; + VSTRING *escape_buf; + char read_buf[HAPROXY_MAX_LEN]; + ssize_t read_len; + + /* + * We must not read(2) past the end of the HaProxy handshake. The v2 + * protocol assumes that the handshake will never be fragmented, + * therefore we peek, parse the entire input, then read(2) only the + * number of bytes parsed. + */ + if ((read_len = recv(fd, read_buf, sizeof(read_buf) - 1, MSG_PEEK)) <= 0) { + msg_warn("haproxy read: EOF"); + return (-1); + } + + /* + * Parse the haproxy handshake, and determine the handshake length. + */ + read_buf[read_len] = 0; + if ((err = haproxy_srvr_parse(read_buf, &read_len, non_proxy, + smtp_client_addr, smtp_client_port, + smtp_server_addr, smtp_server_port)) != 0) { + escape_buf = vstring_alloc(HAPROXY_MAX_LEN + 2); + escape(escape_buf, read_buf, read_len); + msg_warn("haproxy read: %s: %s", err, vstring_str(escape_buf)); + vstring_free(escape_buf); + return (-1); + } + + /* + * Try to pop the haproxy handshake off the input queue. + */ + if (recv(fd, read_buf, read_len, 0) <= 0) { + msg_warn("haproxy read: %m"); + return (-1); + } + return (0); } /* @@ -216,6 +310,8 @@ int main(int argc, char **argv) /* Test cases with inputs and expected outputs. */ typedef struct TEST_CASE { const char *haproxy_request; + ssize_t exp_str_len; + int exp_non_proxy; const char *exp_return; const char *exp_client_addr; const char *exp_server_addr; @@ -224,40 +320,42 @@ int main(int argc, char **argv) } TEST_CASE; static TEST_CASE test_cases[] = { /* IPv6. */ - {"PROXY TCP6 fc:00:00:00:1:2:3:4 fc:00:00:00:4:3:2:1 123 321", 0, "fc::1:2:3:4", "fc::4:3:2:1", "123", "321"}, - {"PROXY TCP6 FC:00:00:00:1:2:3:4 FC:00:00:00:4:3:2:1 123 321", 0, "fc::1:2:3:4", "fc::4:3:2:1", "123", "321"}, - {"PROXY TCP6 1.2.3.4 4.3.2.1 123 321", "unexpected client address syntax"}, - {"PROXY TCP6 fc:00:00:00:1:2:3:4 4.3.2.1 123 321", "unexpected server address syntax"}, + {"PROXY TCP6 fc:00:00:00:1:2:3:4 fc:00:00:00:4:3:2:1 123 321", 0, 0, 0, "fc::1:2:3:4", "fc::4:3:2:1", "123", "321"}, + {"PROXY TCP6 FC:00:00:00:1:2:3:4 FC:00:00:00:4:3:2:1 123 321", 0, 0, 0, "fc::1:2:3:4", "fc::4:3:2:1", "123", "321"}, + {"PROXY TCP6 1.2.3.4 4.3.2.1 123 321", 0, 0, "unexpected client address syntax"}, + {"PROXY TCP6 fc:00:00:00:1:2:3:4 4.3.2.1 123 321", 0, 0, "unexpected server address syntax"}, /* IPv4 in IPv6. */ - {"PROXY TCP6 ::ffff:1.2.3.4 ::ffff:4.3.2.1 123 321", 0, "1.2.3.4", "4.3.2.1", "123", "321"}, - {"PROXY TCP6 ::FFFF:1.2.3.4 ::FFFF:4.3.2.1 123 321", 0, "1.2.3.4", "4.3.2.1", "123", "321"}, - {"PROXY TCP4 ::ffff:1.2.3.4 ::ffff:4.3.2.1 123 321", "unexpected client address syntax"}, - {"PROXY TCP4 1.2.3.4 ::ffff:4.3.2.1 123 321", "unexpected server address syntax"}, + {"PROXY TCP6 ::ffff:1.2.3.4 ::ffff:4.3.2.1 123 321", 0, 0, 0, "1.2.3.4", "4.3.2.1", "123", "321"}, + {"PROXY TCP6 ::FFFF:1.2.3.4 ::FFFF:4.3.2.1 123 321", 0, 0, 0, "1.2.3.4", "4.3.2.1", "123", "321"}, + {"PROXY TCP4 ::ffff:1.2.3.4 ::ffff:4.3.2.1 123 321", 0, 0, "unexpected client address syntax"}, + {"PROXY TCP4 1.2.3.4 ::ffff:4.3.2.1 123 321", 0, 0, "unexpected server address syntax"}, /* IPv4. */ - {"PROXY TCP4 1.2.3.4 4.3.2.1 123 321", 0, "1.2.3.4", "4.3.2.1", "123", "321"}, - {"PROXY TCP4 01.02.03.04 04.03.02.01 123 321", 0, "1.2.3.4", "4.3.2.1", "123", "321"}, - {"PROXY TCP4 1.2.3.4 4.3.2.1 123456 321", "unexpected client port syntax"}, - {"PROXY TCP4 1.2.3.4 4.3.2.1 123 654321", "unexpected server port syntax"}, - {"PROXY TCP4 1.2.3.4 4.3.2.1 0123 321", "unexpected client port syntax"}, - {"PROXY TCP4 1.2.3.4 4.3.2.1 123 0321", "unexpected server port syntax"}, + {"PROXY TCP4 1.2.3.4 4.3.2.1 123 321", 0, 0, 0, "1.2.3.4", "4.3.2.1", "123", "321"}, + {"PROXY TCP4 01.02.03.04 04.03.02.01 123 321", 0, 0, 0, "1.2.3.4", "4.3.2.1", "123", "321"}, + {"PROXY TCP4 1.2.3.4 4.3.2.1 123456 321", 0, 0, "unexpected client port syntax"}, + {"PROXY TCP4 1.2.3.4 4.3.2.1 123 654321", 0, 0, "unexpected server port syntax"}, + {"PROXY TCP4 1.2.3.4 4.3.2.1 0123 321", 0, 0, "unexpected client port syntax"}, + {"PROXY TCP4 1.2.3.4 4.3.2.1 123 0321", 0, 0, "unexpected server port syntax"}, /* Missing fields. */ - {"PROXY TCP6 fc:00:00:00:1:2:3:4 fc:00:00:00:4:3:2:1 123", "unexpected server port syntax"}, - {"PROXY TCP6 fc:00:00:00:1:2:3:4 fc:00:00:00:4:3:2:1", "unexpected client port syntax"}, - {"PROXY TCP6 fc:00:00:00:1:2:3:4", "unexpected server address syntax"}, - {"PROXY TCP6", "unexpected client address syntax"}, - {"PROXY TCP4 1.2.3.4 4.3.2.1 123", "unexpected server port syntax"}, - {"PROXY TCP4 1.2.3.4 4.3.2.1", "unexpected client port syntax"}, - {"PROXY TCP4 1.2.3.4", "unexpected server address syntax"}, - {"PROXY TCP4", "unexpected client address syntax"}, + {"PROXY TCP6 fc:00:00:00:1:2:3:4 fc:00:00:00:4:3:2:1 123", 0, 0, "unexpected server port syntax"}, + {"PROXY TCP6 fc:00:00:00:1:2:3:4 fc:00:00:00:4:3:2:1", 0, 0, "unexpected client port syntax"}, + {"PROXY TCP6 fc:00:00:00:1:2:3:4", 0, 0, "unexpected server address syntax"}, + {"PROXY TCP6", 0, 0, "unexpected client address syntax"}, + {"PROXY TCP4 1.2.3.4 4.3.2.1 123", 0, 0, "unexpected server port syntax"}, + {"PROXY TCP4 1.2.3.4 4.3.2.1", 0, 0, "unexpected client port syntax"}, + {"PROXY TCP4 1.2.3.4", 0, 0, "unexpected server address syntax"}, + {"PROXY TCP4", 0, 0, "unexpected client address syntax"}, /* Other. */ - {"PROXY BLAH", "unsupported protocol type"}, - {"BLAH", "unexpected protocol header"}, + {"PROXY BLAH", 0, 0, "unsupported protocol type"}, + {"BLAH", 0, 0, "v2 protocol is not implemented"}, 0, }; TEST_CASE *test_case; /* Actual results. */ const char *act_return; + ssize_t act_str_len; + int act_non_proxy; MAI_HOSTADDR_STR act_smtp_client_addr; MAI_HOSTADDR_STR act_smtp_server_addr; MAI_SERVPORT_STR act_smtp_client_port; @@ -270,8 +368,12 @@ int main(int argc, char **argv) for (tests_failed = 0, test_case = test_cases; test_case->haproxy_request; tests_failed += test_failed, test_case++) { test_failed = 0; + act_str_len = strlen(test_case->haproxy_request); + if (test_case->exp_str_len == 0) + test_case->exp_str_len = act_str_len; act_return = - haproxy_srvr_parse(test_case->haproxy_request, + haproxy_srvr_parse(test_case->haproxy_request, &act_str_len, + &act_non_proxy, &act_smtp_client_addr, &act_smtp_client_port, &act_smtp_server_addr, &act_smtp_server_port); if (act_return != test_case->exp_return) { @@ -283,6 +385,20 @@ int main(int argc, char **argv) test_failed = 1; continue; } + if (act_str_len != test_case->exp_str_len) { + msg_warn("test case %d str_len expected=%ld actual=%ld", + (int) (test_case - test_cases), + (long) test_case->exp_str_len, (long) act_str_len); + test_failed = 1; + continue; + } + if (act_non_proxy != test_case->exp_non_proxy) { + msg_warn("test case %d non_proxy expected=%d actual=%d", + (int) (test_case - test_cases), + test_case->exp_non_proxy, act_non_proxy); + test_failed = 1; + continue; + } if (test_case->exp_return != 0) continue; if (strcmp(test_case->exp_client_addr, act_smtp_client_addr.buf)) { diff --git a/postfix/src/global/haproxy_srvr.h b/postfix/src/global/haproxy_srvr.h index 7cd3262a2..aafbcc4ff 100644 --- a/postfix/src/global/haproxy_srvr.h +++ b/postfix/src/global/haproxy_srvr.h @@ -19,7 +19,10 @@ /* * External interface. */ -extern const char *haproxy_srvr_parse(const char *, +extern const char *haproxy_srvr_parse(const char *, ssize_t *, int *, + MAI_HOSTADDR_STR *, MAI_SERVPORT_STR *, + MAI_HOSTADDR_STR *, MAI_SERVPORT_STR *); +extern int haproxy_srvr_receive(int, int *, MAI_HOSTADDR_STR *, MAI_SERVPORT_STR *, MAI_HOSTADDR_STR *, MAI_SERVPORT_STR *); @@ -40,6 +43,11 @@ extern const char *haproxy_srvr_parse(const char *, /* IBM T.J. Watson Research /* P.O. Box 704 /* Yorktown Heights, NY 10598, USA +/* +/* Wietse Venema +/* Google, Inc. +/* 111 8th Avenue +/* New York, NY 10011, USA /*--*/ #endif diff --git a/postfix/src/global/mail_version.h b/postfix/src/global/mail_version.h index f60409a08..92912f56b 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 "20191214" +#define MAIL_RELEASE_DATE "20200101" #define MAIL_VERSION_NUMBER "3.5" #ifdef SNAPSHOT diff --git a/postfix/src/postscreen/postscreen.h b/postfix/src/postscreen/postscreen.h index 0f9d41817..b38a604ad 100644 --- a/postfix/src/postscreen/postscreen.h +++ b/postfix/src/postscreen/postscreen.h @@ -568,6 +568,7 @@ typedef void (*PSC_ENDPT_LOOKUP_FN) (int, VSTREAM *, MAI_HOSTADDR_STR *, MAI_SERVPORT_STR *, MAI_HOSTADDR_STR *, MAI_SERVPORT_STR *); extern void psc_endpt_lookup(VSTREAM *, PSC_ENDPT_LOOKUP_FN); +extern void psc_endpt_local_lookup(VSTREAM *, PSC_ENDPT_LOOKUP_FN); /* * postscreen_access emulation. diff --git a/postfix/src/postscreen/postscreen_endpt.c b/postfix/src/postscreen/postscreen_endpt.c index 57655ac6a..335c511d0 100644 --- a/postfix/src/postscreen/postscreen_endpt.c +++ b/postfix/src/postscreen/postscreen_endpt.c @@ -16,6 +16,17 @@ /* MAI_SERVPORT_STR *smtp_client_port; /* MAI_HOSTADDR_STR *smtp_server_addr; /* MAI_SERVPORT_STR *smtp_server_port; +/* AUXILIARY METHODS +/* void psc_endpt_local_lookup(smtp_client_stream, lookup_done) +/* VSTREAM *smtp_client_stream; +/* void (*lookup_done)(status, smtp_client_stream, +/* smtp_client_addr, smtp_client_port, +/* smtp_server_addr, smtp_server_port) +/* int status; +/* MAI_HOSTADDR_STR *smtp_client_addr; +/* MAI_SERVPORT_STR *smtp_client_port; +/* MAI_HOSTADDR_STR *smtp_server_addr; +/* MAI_SERVPORT_STR *smtp_server_port; /* DESCRIPTION /* psc_endpt_lookup() looks up remote and local connection /* endpoint information, either through local system calls, @@ -26,6 +37,10 @@ /* .IP \(bu /* Accept the same arguments as psc_endpt_lookup(). /* .IP \(bu +/* Call psc_endpt_local_lookup() to look up connection info +/* when the upstream proxy indicates that the connection is +/* not proxied (e.g., health check probe). +/* .IP \(bu /* Validate protocol, address and port syntax. Permit only /* protocols that are configured with the main.cf:inet_protocols /* setting. @@ -41,10 +56,16 @@ /* Arguments: /* .IP client_stream /* A brand-new stream that is connected to the remote client. +/* This argument MUST be passed to psc_endpt_local_lookup() +/* if the up-stream proxy indicates that a connection is not +/* proxied. /* .IP lookup /* Call-back routine that reports the result status, address /* and port information. The result status is -1 in case of -/* error, 0 in case of success. +/* error, 0 in case of success. This MUST NOT be called directly +/* if the up-stream proxy indicates that a connection is not +/* proxied; instead this MUST be called indirectly by +/* psc_endpt_local_lookup(). /* LICENSE /* .ad /* .fi @@ -54,6 +75,11 @@ /* IBM T.J. Watson Research /* P.O. Box 704 /* Yorktown Heights, NY 10598, USA +/* +/* Wietse Venema +/* Google, Inc. +/* 111 8th Avenue +/* New York, NY 10011, USA /*--*/ /* System library. */ @@ -105,8 +131,8 @@ static int psc_sockaddr_to_hostaddr(struct sockaddr *addr_storage, /* psc_endpt_local_lookup - look up local system connection information */ -static void psc_endpt_local_lookup(VSTREAM *smtp_client_stream, - PSC_ENDPT_LOOKUP_FN lookup_done) +void psc_endpt_local_lookup(VSTREAM *smtp_client_stream, + PSC_ENDPT_LOOKUP_FN lookup_done) { struct sockaddr_storage addr_storage; SOCKADDR_SIZE addr_storage_len = sizeof(addr_storage); diff --git a/postfix/src/postscreen/postscreen_haproxy.c b/postfix/src/postscreen/postscreen_haproxy.c index d835bf073..240f91878 100644 --- a/postfix/src/postscreen/postscreen_haproxy.c +++ b/postfix/src/postscreen/postscreen_haproxy.c @@ -69,7 +69,6 @@ typedef struct { VSTREAM *stream; PSC_ENDPT_LOOKUP_FN notify; - VSTRING *buffer; } PSC_HAPROXY_STATE; /* psc_endpt_haproxy_event - read or time event */ @@ -83,94 +82,32 @@ static void psc_endpt_haproxy_event(int event, void *context) MAI_SERVPORT_STR smtp_client_port; MAI_HOSTADDR_STR smtp_server_addr; MAI_SERVPORT_STR smtp_server_port; - int last_char = 0; - const char *err; - VSTRING *escape_buf; - char read_buf[HAPROXY_MAX_LEN]; - ssize_t read_len; - char *cp; + int non_proxy = 0; - /* - * We must not read(2) past the that terminates the haproxy - * line. For efficiency reasons we read the entire haproxy line in one - * read(2) call when we know that the line is unfragmented. In the rare - * case that the line is fragmented, we fall back and read(2) it one - * character at a time. - */ switch (event) { case EVENT_TIME: msg_warn("haproxy read: time limit exceeded"); status = -1; break; case EVENT_READ: - /* Determine the initial VSTREAM read(2) buffer size. */ - if (VSTRING_LEN(state->buffer) == 0) { - if ((read_len = recv(vstream_fileno(state->stream), - read_buf, sizeof(read_buf) - 1, MSG_PEEK)) > 0 - && ((cp = memchr(read_buf, '\n', read_len)) != 0)) { - read_len = cp - read_buf + 1; - } else { - read_len = 1; - } - vstream_control(state->stream, CA_VSTREAM_CTL_BUFSIZE(read_len), - CA_VSTREAM_CTL_END); - } - /* Drain the VSTREAM buffer, otherwise this pseudo-thread will hang. */ - do { - if ((last_char = VSTREAM_GETC(state->stream)) == VSTREAM_EOF) { - if (vstream_ferror(state->stream)) - msg_warn("haproxy read: %m"); - else - msg_warn("haproxy read: lost connection"); - status = -1; - break; - } - if (VSTRING_LEN(state->buffer) >= HAPROXY_MAX_LEN) { - msg_warn("haproxy read: line too long"); - status = -1; - break; - } - VSTRING_ADDCH(state->buffer, last_char); - } while (vstream_peek(state->stream) > 0); - break; - } - - /* - * Parse the haproxy line. Note: the haproxy_srvr_parse() routine - * performs address protocol checks, address and port syntax checks, and - * converts IPv4-in-IPv6 address string syntax (::ffff:1.2.3.4) to IPv4 - * syntax where permitted by the main.cf:inet_protocols setting. - */ - if (status == 0 && last_char == '\n') { - VSTRING_TERMINATE(state->buffer); - if ((err = haproxy_srvr_parse(vstring_str(state->buffer), + status = haproxy_srvr_receive(vstream_fileno(state->stream), &non_proxy, &smtp_client_addr, &smtp_client_port, - &smtp_server_addr, &smtp_server_port)) != 0) { - escape_buf = vstring_alloc(HAPROXY_MAX_LEN + 2); - escape(escape_buf, vstring_str(state->buffer), - VSTRING_LEN(state->buffer)); - msg_warn("haproxy read: %s: %s", err, vstring_str(escape_buf)); - status = -1; - vstring_free(escape_buf); - } + &smtp_server_addr, &smtp_server_port); } /* - * Are we done yet? + * Terminate this pseudo thread, and notify the caller. */ - if (status < 0 || last_char == '\n') { - PSC_CLEAR_EVENT_REQUEST(vstream_fileno(state->stream), - psc_endpt_haproxy_event, context); - vstream_control(state->stream, - CA_VSTREAM_CTL_BUFSIZE(VSTREAM_BUFSIZE), - CA_VSTREAM_CTL_END); + PSC_CLEAR_EVENT_REQUEST(vstream_fileno(state->stream), + psc_endpt_haproxy_event, context); + if (non_proxy) + psc_endpt_local_lookup(state->stream, state->notify); + else state->notify(status, state->stream, &smtp_client_addr, &smtp_client_port, &smtp_server_addr, &smtp_server_port); - /* Note: the stream may be closed at this point. */ - vstring_free(state->buffer); - myfree((void *) state); - } + /* Note: the stream may be closed at this point. */ + myfree((void *) state); } /* psc_endpt_haproxy_lookup - event-driven haproxy client */ @@ -189,7 +126,6 @@ void psc_endpt_haproxy_lookup(VSTREAM *stream, state = (PSC_HAPROXY_STATE *) mymalloc(sizeof(*state)); state->stream = stream; state->notify = notify; - state->buffer = vstring_alloc(100); /* * Read the haproxy line. diff --git a/postfix/src/postscreen/postscreen_haproxy.h b/postfix/src/postscreen/postscreen_haproxy.h index 2691e481f..7557fb3a4 100644 --- a/postfix/src/postscreen/postscreen_haproxy.h +++ b/postfix/src/postscreen/postscreen_haproxy.h @@ -22,4 +22,9 @@ extern void psc_endpt_haproxy_lookup(VSTREAM *, PSC_ENDPT_LOOKUP_FN); /* IBM T.J. Watson Research /* P.O. Box 704 /* Yorktown Heights, NY 10598, USA +/* +/* Wietse Venema +/* Google, Inc. +/* 111 8th Avenue +/* New York, NY 10011, USA /*--*/ diff --git a/postfix/src/smtpd/Makefile.in b/postfix/src/smtpd/Makefile.in index 993ea1820..009f5bd17 100644 --- a/postfix/src/smtpd/Makefile.in +++ b/postfix/src/smtpd/Makefile.in @@ -421,6 +421,7 @@ smtpd_haproxy.o: ../../include/check_arg.h smtpd_haproxy.o: ../../include/dns.h smtpd_haproxy.o: ../../include/haproxy_srvr.h smtpd_haproxy.o: ../../include/htable.h +smtpd_haproxy.o: ../../include/iostuff.h smtpd_haproxy.o: ../../include/mail_params.h smtpd_haproxy.o: ../../include/mail_stream.h smtpd_haproxy.o: ../../include/milter.h diff --git a/postfix/src/smtpd/smtpd.h b/postfix/src/smtpd/smtpd.h index a584a2c83..093686c41 100644 --- a/postfix/src/smtpd/smtpd.h +++ b/postfix/src/smtpd/smtpd.h @@ -345,7 +345,7 @@ extern void smtpd_state_reset(SMTPD_STATE *); */ extern void smtpd_peer_init(SMTPD_STATE *state); extern void smtpd_peer_reset(SMTPD_STATE *state); -extern int smtpd_peer_from_haproxy(SMTPD_STATE *state); +extern int smtpd_peer_from_haproxy(SMTPD_STATE *, void (*) (SMTPD_STATE *)); #define SMTPD_PEER_CODE_OK 2 #define SMTPD_PEER_CODE_TEMP 4 diff --git a/postfix/src/smtpd/smtpd_haproxy.c b/postfix/src/smtpd/smtpd_haproxy.c index 3bcbffc6a..fe402eb68 100644 --- a/postfix/src/smtpd/smtpd_haproxy.c +++ b/postfix/src/smtpd/smtpd_haproxy.c @@ -6,8 +6,9 @@ /* SYNOPSIS /* #include "smtpd.h" /* -/* int smtpd_peer_from_haproxy(state) +/* int smtpd_peer_from_haproxy(state, default_lookup) /* SMTPD_STATE *state; +/* void (*default_lookup) (SMTPD_STATE *); /* DESCRIPTION /* smtpd_peer_from_haproxy() receives endpoint address and /* port information via the haproxy protocol. @@ -15,6 +16,11 @@ /* The following summarizes what the Postfix SMTP server expects /* from an up-stream proxy adapter. /* .IP \(bu +/* Call the default_lookup function if the up-stream proxy +/* indicates that the connection is not proxied. In that case, +/* a proxy adapter MUST NOT update the connection info: the +/* default_lookup function will do that instead. +/* .IP \(bu /* Validate protocol, address and port syntax. Permit only /* protocols that are configured with the main.cf:inet_protocols /* setting. @@ -70,6 +76,7 @@ #include #include #include +#include /* Global library. */ @@ -89,80 +96,42 @@ /* smtpd_peer_from_haproxy - initialize peer information from haproxy */ -int smtpd_peer_from_haproxy(SMTPD_STATE *state) +int smtpd_peer_from_haproxy(SMTPD_STATE *state, + void (*default_lookup) (SMTPD_STATE *)) { - const char *myname = "smtpd_peer_from_haproxy"; MAI_HOSTADDR_STR smtp_client_addr; MAI_SERVPORT_STR smtp_client_port; MAI_HOSTADDR_STR smtp_server_addr; MAI_SERVPORT_STR smtp_server_port; - const char *proxy_err; - int io_err; - VSTRING *escape_buf; + int non_proxy = 0; - /* - * While reading HAProxy handshake information, don't buffer input beyond - * the end-of-line. That would break the TLS wrappermode handshake. - */ - vstream_control(state->client, - VSTREAM_CTL_BUFSIZE, 1, - VSTREAM_CTL_END); - - /* - * Note: the haproxy_srvr_parse() routine performs address protocol - * checks, address and port syntax checks, and converts IPv4-in-IPv6 - * address string syntax (::ffff:1.2.3.4) to IPv4 syntax where permitted - * by the main.cf:inet_protocols setting, but logs no warnings. - */ -#define ENABLE_DEADLINE 1 - - smtp_stream_setup(state->client, var_smtpd_uproxy_tmout, ENABLE_DEADLINE); - switch (io_err = vstream_setjmp(state->client)) { - default: - msg_panic("%s: unhandled I/O error %d", myname, io_err); - case SMTP_ERR_EOF: - msg_warn("haproxy read: unexpected EOF"); - return (-1); - case SMTP_ERR_TIME: + if (read_wait(vstream_fileno(state->client), var_smtpd_uproxy_tmout) < 0) { msg_warn("haproxy read: timeout error"); return (-1); - case 0: - if (smtp_get(state->buffer, state->client, HAPROXY_MAX_LEN, - SMTP_GET_FLAG_NONE) != '\n') { - msg_warn("haproxy read: line > %d characters", HAPROXY_MAX_LEN); - return (-1); - } - if ((proxy_err = haproxy_srvr_parse(STR(state->buffer), - &smtp_client_addr, &smtp_client_port, - &smtp_server_addr, &smtp_server_port)) != 0) { - escape_buf = vstring_alloc(HAPROXY_MAX_LEN + 2); - escape(escape_buf, STR(state->buffer), LEN(state->buffer)); - msg_warn("haproxy read: %s: %s", proxy_err, STR(escape_buf)); - vstring_free(escape_buf); - return (-1); - } - state->addr = mystrdup(smtp_client_addr.buf); - if (strrchr(state->addr, ':') != 0) { - state->rfc_addr = concatenate(IPV6_COL, state->addr, (char *) 0); - state->addr_family = AF_INET6; - } else { - state->rfc_addr = mystrdup(state->addr); - state->addr_family = AF_INET; - } - state->port = mystrdup(smtp_client_port.buf); - - /* - * The Dovecot authentication server needs the server IP address. - */ - state->dest_addr = mystrdup(smtp_server_addr.buf); - state->dest_port = mystrdup(smtp_server_port.buf); - - /* - * Enable normal buffering. - */ - vstream_control(state->client, - VSTREAM_CTL_BUFSIZE, VSTREAM_BUFSIZE, - VSTREAM_CTL_END); + } + if (haproxy_srvr_receive(vstream_fileno(state->client), &non_proxy, + &smtp_client_addr, &smtp_client_port, + &smtp_server_addr, &smtp_server_port) < 0) { + return (-1); + } + if (non_proxy) { + default_lookup(state); return (0); } + state->addr = mystrdup(smtp_client_addr.buf); + if (strrchr(state->addr, ':') != 0) { + state->rfc_addr = concatenate(IPV6_COL, state->addr, (char *) 0); + state->addr_family = AF_INET6; + } else { + state->rfc_addr = mystrdup(state->addr); + state->addr_family = AF_INET; + } + state->port = mystrdup(smtp_client_port.buf); + + /* + * The Dovecot authentication server needs the server IP address. + */ + state->dest_addr = mystrdup(smtp_server_addr.buf); + state->dest_port = mystrdup(smtp_server_port.buf); + return (0); } diff --git a/postfix/src/smtpd/smtpd_peer.c b/postfix/src/smtpd/smtpd_peer.c index 073310a47..725d67cb6 100644 --- a/postfix/src/smtpd/smtpd_peer.c +++ b/postfix/src/smtpd/smtpd_peer.c @@ -544,7 +544,8 @@ static void smtpd_peer_from_proxy(SMTPD_STATE *state) { typedef struct { const char *name; - int (*endpt_lookup) (SMTPD_STATE *); + int (*endpt_lookup) (SMTPD_STATE *, + void (*default_lookup) (SMTPD_STATE *)); } SMTPD_ENDPT_LOOKUP_INFO; static const SMTPD_ENDPT_LOOKUP_INFO smtpd_endpt_lookup_info[] = { HAPROXY_PROTO_NAME, smtpd_peer_from_haproxy, @@ -563,7 +564,7 @@ static void smtpd_peer_from_proxy(SMTPD_STATE *state) if (strcmp(var_smtpd_uproxy_proto, pp->name) == 0) break; } - if (pp->endpt_lookup(state) < 0) { + if (pp->endpt_lookup(state, smtpd_peer_from_default) < 0) { smtpd_peer_no_client(state); state->flags |= SMTPD_FLAG_HANGUP; } else {