mirror of
https://github.com/vdukhovni/postfix
synced 2025-08-29 13:18:12 +00:00
postfix-3.5-20200112
This commit is contained in:
parent
960a802133
commit
e1b52f45e3
83
postfix/.indent.pro
vendored
83
postfix/.indent.pro
vendored
@ -20,7 +20,6 @@
|
||||
-TBH_TABLE
|
||||
-TBINATTR
|
||||
-TBINATTR_INFO
|
||||
-Tbind_props
|
||||
-TBINHASH
|
||||
-TBINHASH_INFO
|
||||
-TBIO
|
||||
@ -38,10 +37,9 @@
|
||||
-TBYTE_MASK
|
||||
-TCFG_PARSER
|
||||
-TCIDR_MATCH
|
||||
-Tcipher_probe_t
|
||||
-TCLEANUP_REGION
|
||||
-TCLEANUP_STAT_DETAIL
|
||||
-TCLEANUP_STATE
|
||||
-TCLEANUP_STAT_DETAIL
|
||||
-TCLIENT_LIST
|
||||
-TCLNT_STREAM
|
||||
-TCONFIG_BOOL_FN_TABLE
|
||||
@ -65,11 +63,9 @@
|
||||
-TCRYPTO_EX_DATA
|
||||
-TCTABLE
|
||||
-TCTABLE_ENTRY
|
||||
-Td2i_X509_t
|
||||
-Tdane_digest
|
||||
-TDB_COMMON_CTX
|
||||
-TDELIVER_ATTR
|
||||
-TDELIVERED_HDR_INFO
|
||||
-TDELIVER_ATTR
|
||||
-TDELIVER_REQUEST
|
||||
-TDELTA_TIME
|
||||
-TDICT
|
||||
@ -150,9 +146,7 @@
|
||||
-TEVP_PKEY
|
||||
-TEXPAND_ATTR
|
||||
-TFILE
|
||||
-Tfilter_ctx
|
||||
-TFORWARD_INFO
|
||||
-Tgeneral_name_stack_t
|
||||
-THBC_ACTION_CALL_BACKS
|
||||
-THBC_CALL_BACKS
|
||||
-THBC_CHECKS
|
||||
@ -164,18 +158,17 @@
|
||||
-THOST
|
||||
-THTABLE
|
||||
-THTABLE_INFO
|
||||
-Tiana_digest
|
||||
-TINET_ADDR_LIST
|
||||
-TINET_PROTO_INFO
|
||||
-TINSTANCE
|
||||
-TINST_SELECTION
|
||||
-TINT32_TYPE
|
||||
-TINT_TABLE
|
||||
-TINTV
|
||||
-TINT_TABLE
|
||||
-TJMP_BUF_WRAPPER
|
||||
-TLDAP
|
||||
-TLDAP_CONN
|
||||
-TLDAPMessage
|
||||
-TLDAP_CONN
|
||||
-TLIB_DP
|
||||
-TLIB_FN
|
||||
-TLMTP_ATTR
|
||||
@ -190,14 +183,14 @@
|
||||
-TMAC_EXP_OP_INFO
|
||||
-TMAC_HEAD
|
||||
-TMAC_PARSE
|
||||
-TMAI_HOSTADDR_STR
|
||||
-TMAI_HOSTNAME_STR
|
||||
-TMAIL_ADDR_FORMATTER
|
||||
-TMAIL_ADDR_MAP_TEST
|
||||
-TMAIL_PRINT
|
||||
-TMAIL_SCAN
|
||||
-TMAIL_STREAM
|
||||
-TMAIL_VERSION
|
||||
-TMAI_HOSTADDR_STR
|
||||
-TMAI_HOSTNAME_STR
|
||||
-TMAI_SERVNAME_STR
|
||||
-TMAI_SERVPORT_STR
|
||||
-TMAPS
|
||||
@ -216,9 +209,9 @@
|
||||
-TMDB_val
|
||||
-TMILTER
|
||||
-TMILTER8
|
||||
-TMILTERS
|
||||
-TMILTER_MACROS
|
||||
-TMILTER_MSG_CONTEXT
|
||||
-TMILTERS
|
||||
-TMIME_ENCODING
|
||||
-TMIME_INFO
|
||||
-TMIME_STACK
|
||||
@ -243,7 +236,6 @@
|
||||
-TNAME_CODE
|
||||
-TNAME_MASK
|
||||
-TNBBIO
|
||||
-Toff_t
|
||||
-TOPTIONS
|
||||
-TPCF_DBMS_INFO
|
||||
-TPCF_EVAL_CTX
|
||||
@ -257,7 +249,6 @@
|
||||
-TPCF_SERVICE_PATTERN
|
||||
-TPCF_STRING_NV
|
||||
-TPEER_NAME
|
||||
-Tpem_load_state_t
|
||||
-TPGSQL_NAME
|
||||
-TPICKUP_INFO
|
||||
-TPIPE_ATTR
|
||||
@ -265,9 +256,9 @@
|
||||
-TPIPE_STATE
|
||||
-TPLMYSQL
|
||||
-TPLPGSQL
|
||||
-TPOSTMAP_KEY_STATE
|
||||
-TPOST_MAIL_FCLOSE_STATE
|
||||
-TPOST_MAIL_STATE
|
||||
-TPOSTMAP_KEY_STATE
|
||||
-TPRIVATE_STR_TABLE
|
||||
-TPSC_CALL_BACK_ENTRY
|
||||
-TPSC_CLIENT_INFO
|
||||
@ -295,15 +286,11 @@
|
||||
-TRECIPIENT
|
||||
-TRECIPIENT_LIST
|
||||
-TREC_TYPE_NAME
|
||||
-Tregex_t
|
||||
-Tregmatch_t
|
||||
-TRES_CONTEXT
|
||||
-TRESOLVE_REPLY
|
||||
-TRESPONSE
|
||||
-TREST_TABLE
|
||||
-TRES_CONTEXT
|
||||
-TRWR_CONTEXT
|
||||
-Tsasl_conn_t
|
||||
-Tsasl_secret_t
|
||||
-TSCACHE
|
||||
-TSCACHE_CLNT
|
||||
-TSCACHE_MULTI
|
||||
@ -318,19 +305,12 @@
|
||||
-TSCAN_INFO
|
||||
-TSCAN_OBJ
|
||||
-TSESSION
|
||||
-Tsfsistat
|
||||
-TSHARED_PATH
|
||||
-Tsigset_t
|
||||
-TSINGLE_SERVER
|
||||
-TSINK_COMMAND
|
||||
-TSINK_STATE
|
||||
-Tsize_t
|
||||
-TSLMDB
|
||||
-TSMFICTX
|
||||
-TSM_STATE
|
||||
-TSMTP_ADDR
|
||||
-TSMTP_CLI_ATTR
|
||||
-TSMTP_CMD
|
||||
-TSMTPD_CMD
|
||||
-TSMTPD_DEFER
|
||||
-TSMTPD_ENDPT_LOOKUP_INFO
|
||||
@ -342,6 +322,9 @@
|
||||
-TSMTPD_STATE
|
||||
-TSMTPD_TOKEN
|
||||
-TSMTPD_XFORWARD_ATTR
|
||||
-TSMTP_ADDR
|
||||
-TSMTP_CLI_ATTR
|
||||
-TSMTP_CMD
|
||||
-TSMTP_ITERATOR
|
||||
-TSMTP_RESP
|
||||
-TSMTP_SASL_AUTH_CACHE
|
||||
@ -350,13 +333,10 @@
|
||||
-TSMTP_TLS_POLICY
|
||||
-TSMTP_TLS_SESS
|
||||
-TSMTP_TLS_SITE_POLICY
|
||||
-Tsockaddr
|
||||
-TSM_STATE
|
||||
-TSOCKADDR_SIZE
|
||||
-TSPAWN_ATTR
|
||||
-Tssize_t
|
||||
-TSSL
|
||||
-Tssl_cipher_stack_t
|
||||
-Tssl_comp_stack_t
|
||||
-TSSL_CTX
|
||||
-TSSL_SESSION
|
||||
-TSTATE
|
||||
@ -364,20 +344,17 @@
|
||||
-TSTRING_TABLE
|
||||
-TSYS_EXITS_DETAIL
|
||||
-TTEST_CASE
|
||||
-Ttime_t
|
||||
-Ttlsa_filter
|
||||
-TTLSMGR_SCACHE
|
||||
-TTLSP_STATE
|
||||
-TTLS_APPL_STATE
|
||||
-TTLS_CERTS
|
||||
-TTLS_CLIENT_INIT_PROPS
|
||||
-TTLS_CLIENT_PARAMS
|
||||
-TTLS_CLIENT_START_PROPS
|
||||
-TTLScontext_t
|
||||
-TTLS_DANE
|
||||
-TTLSMGR_SCACHE
|
||||
-TTLS_PKEYS
|
||||
-TTLS_PRNG_SEED_INFO
|
||||
-TTLS_PRNG_SRC
|
||||
-TTLSP_STATE
|
||||
-TTLS_ROLE
|
||||
-TTLS_SCACHE
|
||||
-TTLS_SCACHE_ENTRY
|
||||
@ -388,6 +365,7 @@
|
||||
-TTLS_TLSA
|
||||
-TTLS_USAGE
|
||||
-TTLS_VINFO
|
||||
-TTLScontext_t
|
||||
-TTOK822
|
||||
-TTRANSPORT_INFO
|
||||
-TTRIGGER_SERVER
|
||||
@ -400,11 +378,10 @@
|
||||
-TWATCHDOG
|
||||
-TWATCH_FD
|
||||
-TX509
|
||||
-TX509V3_CTX
|
||||
-TX509_EXTENSION
|
||||
-TX509_NAME
|
||||
-Tx509_stack_t
|
||||
-TX509_STORE_CTX
|
||||
-TX509V3_CTX
|
||||
-TXSASL_CLIENT
|
||||
-TXSASL_CLIENT_CREATE_ARGS
|
||||
-TXSASL_CLIENT_IMPL
|
||||
@ -421,3 +398,29 @@
|
||||
-TXSASL_SERVER_CREATE_ARGS
|
||||
-TXSASL_SERVER_IMPL
|
||||
-TXSASL_SERVER_IMPL_INFO
|
||||
-Tbind_props
|
||||
-Tcipher_probe_t
|
||||
-Td2i_X509_t
|
||||
-Tdane_digest
|
||||
-Tfilter_ctx
|
||||
-Tgeneral_name_stack_t
|
||||
-Tiana_digest
|
||||
-Toff_t
|
||||
-Tpem_load_state_t
|
||||
-Tregex_t
|
||||
-Tregmatch_t
|
||||
-Tsasl_conn_t
|
||||
-Tsasl_secret_t
|
||||
-Tsfsistat
|
||||
-Tsigset_t
|
||||
-Tsize_t
|
||||
-Tsockaddr
|
||||
-Tssize_t
|
||||
-Tssl_cipher_stack_t
|
||||
-Tssl_comp_stack_t
|
||||
-Ttime_t
|
||||
-Ttlsa_filter
|
||||
-Tuint16_t
|
||||
-Tuint32_t
|
||||
-Tuint8_t
|
||||
-Tx509_stack_t
|
||||
|
@ -24534,3 +24534,33 @@ Apologies for any names omitted.
|
||||
= smtp:foo.exmple, bar.example". Files: smtp/smtp.c,
|
||||
smtp/smtp_connect.c, trivial-rewrite/resolve.c, proto/transport,
|
||||
proto/postconf.proto, global/mail_params.c.
|
||||
|
||||
20200112
|
||||
|
||||
[intially released as part of postfix-20200101-nonprod]
|
||||
Refactored the haproxy infrastructure in preparation for
|
||||
haproxy version 2 support. This is necessary because version
|
||||
2 introduces a dependency of the reader on the parser.
|
||||
Additionally, 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. Initial release 3.5-20200101-nonprod.
|
||||
|
||||
[intially released as part of postfix-20200105-nonprod]
|
||||
Support for the haproxy v2 protocol. The haproxy v2 protocol
|
||||
support is limited to TCP over IPv4 and TCP over IPv6. It
|
||||
also supports non-proxied connections (typically used for
|
||||
heartbeat tests). File: global/haproxy_srvr.c.
|
||||
|
||||
[intially released as part of postfix-20200105-nonprod]
|
||||
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.
|
||||
|
||||
Cleanup: miscellaneous comments, constants, error checks,
|
||||
no normal behavior change. Files: global/haproxy_srvr.c,
|
||||
postscreen/postscreen_haproxy.c.
|
||||
|
@ -1401,6 +1401,7 @@ haproxy_srvr.o: ../../include/inet_proto.h
|
||||
haproxy_srvr.o: ../../include/msg.h
|
||||
haproxy_srvr.o: ../../include/myaddrinfo.h
|
||||
haproxy_srvr.o: ../../include/mymalloc.h
|
||||
haproxy_srvr.o: ../../include/split_at.h
|
||||
haproxy_srvr.o: ../../include/stringops.h
|
||||
haproxy_srvr.o: ../../include/sys_defs.h
|
||||
haproxy_srvr.o: ../../include/valid_hostname.h
|
||||
@ -1448,12 +1449,15 @@ header_token.o: header_token.c
|
||||
header_token.o: header_token.h
|
||||
header_token.o: lex_822.h
|
||||
info_log_addr_form.o: ../../include/check_arg.h
|
||||
info_log_addr_form.o: ../../include/msg.h
|
||||
info_log_addr_form.o: ../../include/name_code.h
|
||||
info_log_addr_form.o: ../../include/sys_defs.h
|
||||
info_log_addr_form.o: ../../include/vbuf.h
|
||||
info_log_addr_form.o: ../../include/vstring.h
|
||||
info_log_addr_form.o: info_log_addr_form.c
|
||||
info_log_addr_form.o: info_log_addr_form.h
|
||||
info_log_addr_form.o: mail_addr_form.h
|
||||
info_log_addr_form.o: mail_params.h
|
||||
info_log_addr_form.o: quote_822_local.h
|
||||
info_log_addr_form.o: quote_flags.h
|
||||
input_transp.o: ../../include/check_arg.h
|
||||
|
@ -6,21 +6,45 @@
|
||||
/* SYNOPSIS
|
||||
/* #include <haproxy_srvr.h>
|
||||
/*
|
||||
/* const char *haproxy_srvr_parse(str,
|
||||
/* const char *haproxy_srvr_parse(str, str_len, non_proxy,
|
||||
/* smtp_client_addr, smtp_client_port,
|
||||
/* smtp_server_addr, smtp_server_port)
|
||||
/* const char *str;
|
||||
/* ssize_t *str_len;
|
||||
/* int *non_proxy;
|
||||
/* MAI_HOSTADDR_STR *smtp_client_addr,
|
||||
/* MAI_SERVPORT_STR *smtp_client_port,
|
||||
/* MAI_HOSTADDR_STR *smtp_server_addr,
|
||||
/* MAI_SERVPORT_STR *smtp_server_port;
|
||||
/*
|
||||
/* const char *haproxy_srvr_receive(fd, non_proxy,
|
||||
/* smtp_client_addr, smtp_client_port,
|
||||
/* smtp_server_addr, smtp_server_port)
|
||||
/* int fd;
|
||||
/* int *non_proxy;
|
||||
/* MAI_HOSTADDR_STR *smtp_client_addr,
|
||||
/* MAI_SERVPORT_STR *smtp_client_port,
|
||||
/* MAI_HOSTADDR_STR *smtp_server_addr,
|
||||
/* MAI_SERVPORT_STR *smtp_server_port;
|
||||
/* DESCRIPTION
|
||||
/* haproxy_srvr_parse() parses a haproxy line. The result is
|
||||
/* null in case of success, a pointer to text (with the error
|
||||
/* type) in case of error. If both IPv6 and IPv4 support are
|
||||
/* enabled, IPV4_IN_IPV6 address syntax (::ffff:1.2.3.4) is
|
||||
/* converted to IPV4 syntax, provided that IPv4 support is
|
||||
/* enabled.
|
||||
/* haproxy_srvr_parse() parses a haproxy v1 or v2 protocol
|
||||
/* message. The result is null in case of success, a pointer
|
||||
/* to text (with the error type) in case of error. If both
|
||||
/* IPv6 and IPv4 support are enabled, IPV4_IN_IPV6 address
|
||||
/* form (::ffff:1.2.3.4) is converted to IPV4 form. 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.
|
||||
/*
|
||||
/* 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
|
||||
@ -56,6 +80,8 @@
|
||||
#include <stringops.h>
|
||||
#include <mymalloc.h>
|
||||
#include <inet_proto.h>
|
||||
#include <split_at.h>
|
||||
#include <sock_addr.h>
|
||||
|
||||
/* Global library. */
|
||||
|
||||
@ -63,8 +89,86 @@
|
||||
|
||||
/* Application-specific. */
|
||||
|
||||
/*
|
||||
* The haproxy protocol assumes that a haproxy header will normally not
|
||||
* exceed the default IPv4 TCP MSS, i.e. 576-40=536 bytes (the IPv6 default
|
||||
* is larger: 1280-60=1220). With a proxy header that contains IPv6
|
||||
* addresses, that leaves room for 536-52=484 bytes of TLVs. The Postfix
|
||||
* implementation does not support headers with UNIX-domain addresses.
|
||||
*/
|
||||
#define HAPROXY_HEADER_MAX_LEN 536
|
||||
|
||||
/*
|
||||
* Begin protocol v2 definitions from haproxy/include/types/connection.h.
|
||||
*/
|
||||
#define PP2_SIGNATURE "\r\n\r\n\0\r\nQUIT\n"
|
||||
#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 (2 bytes) */
|
||||
#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)")
|
||||
|
||||
/* haproxy_srvr_parse_lit - extract and validate string literal */
|
||||
|
||||
static int haproxy_srvr_parse_lit(const char *str,...)
|
||||
@ -72,15 +176,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);
|
||||
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);
|
||||
@ -91,7 +199,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 +227,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 +265,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);
|
||||
@ -166,18 +275,161 @@ static int haproxy_srvr_parse_port(const char *str, MAI_SERVPORT_STR *port)
|
||||
}
|
||||
}
|
||||
|
||||
/* haproxy_srvr_parse - parse haproxy line */
|
||||
/* haproxy_srvr_parse_v2_addr_v4 - parse IPv4 info from v2 header */
|
||||
|
||||
const char *haproxy_srvr_parse(const char *str,
|
||||
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");
|
||||
}
|
||||
/* For now, skip and ignore TLVs. */
|
||||
*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:
|
||||
/* For now, skip and ignore TLVs. */
|
||||
*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,
|
||||
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();
|
||||
@ -186,8 +438,20 @@ const char *haproxy_srvr_parse(const char *str,
|
||||
* 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)
|
||||
|
||||
/*
|
||||
* Try version 1 protocol.
|
||||
*/
|
||||
if (strncmp(str, "PROXY ", 6) == 0) {
|
||||
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)
|
||||
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";
|
||||
@ -201,115 +465,419 @@ const char *haproxy_srvr_parse(const char *str,
|
||||
err = "unexpected client port syntax";
|
||||
else if (haproxy_srvr_parse_port(NEXT_TOKEN, smtp_server_port) < 0)
|
||||
err = "unexpected server port syntax";
|
||||
else
|
||||
else {
|
||||
err = 0;
|
||||
*str_len = beyond_header - saved_str;
|
||||
}
|
||||
myfree(saved_str);
|
||||
|
||||
*non_proxy = 0;
|
||||
return (err);
|
||||
}
|
||||
|
||||
/*
|
||||
* Try version 2 protocol.
|
||||
*/
|
||||
else {
|
||||
return (haproxy_srvr_parse_v2_hdr(str, str_len, non_proxy,
|
||||
smtp_client_addr, smtp_client_port,
|
||||
smtp_server_addr, smtp_server_port));
|
||||
}
|
||||
}
|
||||
|
||||
/* 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_HEADER_MAX_LEN + 1];
|
||||
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(read_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) != read_len) {
|
||||
msg_warn("haproxy read: %m");
|
||||
return (-1);
|
||||
}
|
||||
return (0);
|
||||
}
|
||||
|
||||
/*
|
||||
* 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;
|
||||
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, "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"},
|
||||
/* 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"},
|
||||
/* 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"},
|
||||
/* 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"},
|
||||
/* Other. */
|
||||
{"PROXY BLAH", "unsupported protocol type"},
|
||||
{"BLAH", "unexpected protocol header"},
|
||||
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_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_return =
|
||||
haproxy_srvr_parse(test_case->haproxy_request,
|
||||
&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;
|
||||
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;
|
||||
|
||||
/*
|
||||
* 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);
|
||||
}
|
||||
if (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;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* 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);
|
||||
|
@ -19,12 +19,14 @@
|
||||
/*
|
||||
* 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 *);
|
||||
|
||||
#define HAPROXY_PROTO_NAME "haproxy"
|
||||
#define HAPROXY_MAX_LEN (256 + 2)
|
||||
|
||||
#ifndef DO_GRIPE
|
||||
#define DO_GRIPE 1
|
||||
@ -40,6 +42,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
|
||||
|
@ -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 "20200111"
|
||||
#define MAIL_RELEASE_DATE "20200112"
|
||||
#define MAIL_VERSION_NUMBER "3.5"
|
||||
|
||||
#ifdef SNAPSHOT
|
||||
|
@ -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.
|
||||
|
@ -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,7 +131,7 @@ 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,
|
||||
void psc_endpt_local_lookup(VSTREAM *smtp_client_stream,
|
||||
PSC_ENDPT_LOOKUP_FN lookup_done)
|
||||
{
|
||||
struct sockaddr_storage addr_storage;
|
||||
|
@ -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
|
||||
@ -69,7 +71,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 +84,32 @@ static void psc_endpt_haproxy_event(int event, void *context)
|
||||
MAI_SERVPORT_STR smtp_client_port;
|
||||
MAI_HOSTADDR_STR smtp_server_addr;
|
||||
MAI_SERVPORT_STR smtp_server_port;
|
||||
int last_char = 0;
|
||||
const char *err;
|
||||
VSTRING *escape_buf;
|
||||
char read_buf[HAPROXY_MAX_LEN];
|
||||
ssize_t read_len;
|
||||
char *cp;
|
||||
int non_proxy = 0;
|
||||
|
||||
/*
|
||||
* We must not read(2) past the <CR><LF> that terminates the haproxy
|
||||
* line. For efficiency reasons we read the entire haproxy line in one
|
||||
* read(2) call when we know that the line is unfragmented. In the rare
|
||||
* case that the line is fragmented, we fall back and read(2) it one
|
||||
* character at a time.
|
||||
*/
|
||||
switch (event) {
|
||||
case EVENT_TIME:
|
||||
msg_warn("haproxy read: time limit exceeded");
|
||||
status = -1;
|
||||
break;
|
||||
case EVENT_READ:
|
||||
/* Determine the initial VSTREAM read(2) buffer size. */
|
||||
if (VSTRING_LEN(state->buffer) == 0) {
|
||||
if ((read_len = recv(vstream_fileno(state->stream),
|
||||
read_buf, sizeof(read_buf) - 1, MSG_PEEK)) > 0
|
||||
&& ((cp = memchr(read_buf, '\n', read_len)) != 0)) {
|
||||
read_len = cp - read_buf + 1;
|
||||
} else {
|
||||
read_len = 1;
|
||||
}
|
||||
vstream_control(state->stream, CA_VSTREAM_CTL_BUFSIZE(read_len),
|
||||
CA_VSTREAM_CTL_END);
|
||||
}
|
||||
/* Drain the VSTREAM buffer, otherwise this pseudo-thread will hang. */
|
||||
do {
|
||||
if ((last_char = VSTREAM_GETC(state->stream)) == VSTREAM_EOF) {
|
||||
if (vstream_ferror(state->stream))
|
||||
msg_warn("haproxy read: %m");
|
||||
else
|
||||
msg_warn("haproxy read: lost connection");
|
||||
status = -1;
|
||||
break;
|
||||
}
|
||||
if (VSTRING_LEN(state->buffer) >= HAPROXY_MAX_LEN) {
|
||||
msg_warn("haproxy read: line too long");
|
||||
status = -1;
|
||||
break;
|
||||
}
|
||||
VSTRING_ADDCH(state->buffer, last_char);
|
||||
} while (vstream_peek(state->stream) > 0);
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
* Parse the haproxy line. Note: the haproxy_srvr_parse() routine
|
||||
* performs address protocol checks, address and port syntax checks, and
|
||||
* converts IPv4-in-IPv6 address string syntax (::ffff:1.2.3.4) to IPv4
|
||||
* syntax where permitted by the main.cf:inet_protocols setting.
|
||||
*/
|
||||
if (status == 0 && last_char == '\n') {
|
||||
VSTRING_TERMINATE(state->buffer);
|
||||
if ((err = haproxy_srvr_parse(vstring_str(state->buffer),
|
||||
status = haproxy_srvr_receive(vstream_fileno(state->stream), &non_proxy,
|
||||
&smtp_client_addr, &smtp_client_port,
|
||||
&smtp_server_addr, &smtp_server_port)) != 0) {
|
||||
escape_buf = vstring_alloc(HAPROXY_MAX_LEN + 2);
|
||||
escape(escape_buf, vstring_str(state->buffer),
|
||||
VSTRING_LEN(state->buffer));
|
||||
msg_warn("haproxy read: %s: %s", err, vstring_str(escape_buf));
|
||||
status = -1;
|
||||
vstring_free(escape_buf);
|
||||
}
|
||||
&smtp_server_addr, &smtp_server_port);
|
||||
}
|
||||
|
||||
/*
|
||||
* Are we done yet?
|
||||
* Terminate this pseudo thread, and notify the caller.
|
||||
*/
|
||||
if (status < 0 || last_char == '\n') {
|
||||
PSC_CLEAR_EVENT_REQUEST(vstream_fileno(state->stream),
|
||||
psc_endpt_haproxy_event, context);
|
||||
vstream_control(state->stream,
|
||||
CA_VSTREAM_CTL_BUFSIZE(VSTREAM_BUFSIZE),
|
||||
CA_VSTREAM_CTL_END);
|
||||
if (status == 0 && 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);
|
||||
}
|
||||
}
|
||||
|
||||
/* psc_endpt_haproxy_lookup - event-driven haproxy client */
|
||||
@ -189,7 +128,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.
|
||||
|
@ -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
|
||||
/*--*/
|
||||
|
@ -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
|
||||
|
@ -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,7 +5988,6 @@ static void smtpd_service(VSTREAM *stream, char *service, char **argv)
|
||||
/*
|
||||
* Provide the SMTP service.
|
||||
*/
|
||||
if ((state.flags & SMTPD_FLAG_HANGUP) == 0)
|
||||
smtpd_proto(&state);
|
||||
|
||||
/*
|
||||
|
@ -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 *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
|
||||
|
@ -15,6 +15,11 @@
|
||||
/* The following summarizes what the Postfix SMTP server expects
|
||||
/* from an up-stream proxy adapter.
|
||||
/* .IP \(bu
|
||||
/* 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 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
|
||||
/* setting.
|
||||
@ -70,6 +75,7 @@
|
||||
#include <myaddrinfo.h>
|
||||
#include <mymalloc.h>
|
||||
#include <stringops.h>
|
||||
#include <iostuff.h>
|
||||
|
||||
/* Global library. */
|
||||
|
||||
@ -91,55 +97,24 @@
|
||||
|
||||
int smtpd_peer_from_haproxy(SMTPD_STATE *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);
|
||||
}
|
||||
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 ((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);
|
||||
if (non_proxy) {
|
||||
smtpd_peer_from_default(state);
|
||||
return (0);
|
||||
}
|
||||
state->addr = mystrdup(smtp_client_addr.buf);
|
||||
if (strrchr(state->addr, ':') != 0) {
|
||||
@ -156,13 +131,5 @@ int smtpd_peer_from_haproxy(SMTPD_STATE *state)
|
||||
*/
|
||||
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);
|
||||
return (0);
|
||||
}
|
||||
}
|
||||
|
@ -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)
|
||||
{
|
||||
|
||||
/*
|
||||
@ -564,7 +571,7 @@ static void smtpd_peer_from_proxy(SMTPD_STATE *state)
|
||||
break;
|
||||
}
|
||||
if (pp->endpt_lookup(state) < 0) {
|
||||
smtpd_peer_no_client(state);
|
||||
smtpd_peer_from_default(state);
|
||||
state->flags |= SMTPD_FLAG_HANGUP;
|
||||
} else {
|
||||
smtpd_peer_hostaddr_to_sockaddr(state);
|
||||
|
Loading…
x
Reference in New Issue
Block a user