]> git.ipfire.org Git - thirdparty/postfix.git/commitdiff
postfix-3.5-20190427
authorWietse Venema <wietse@porcupine.org>
Sat, 27 Apr 2019 05:00:00 +0000 (00:00 -0500)
committerViktor Dukhovni <postfix-users@dukhovni.org>
Sun, 28 Apr 2019 22:46:30 +0000 (18:46 -0400)
postfix/.indent.pro
postfix/HISTORY
postfix/RELEASE_NOTES
postfix/WISHLIST
postfix/src/global/Makefile.in
postfix/src/global/haproxy_srvr.c
postfix/src/global/mail_version.h
postfix/src/global/normalize_mailhost_addr.c [new file with mode: 0644]
postfix/src/global/normalize_mailhost_addr.h [new file with mode: 0644]
postfix/src/smtpd/Makefile.in
postfix/src/smtpd/smtpd.c

index 1e56ee0b1781efe12ca57b90c93e4408fce85ace..6cb7b6335c03e7088bd5143181ebb65ef4ba4114 100644 (file)
@@ -20,7 +20,6 @@
 -TBH_TABLE
 -TBINATTR
 -TBINATTR_INFO
--Tbind_props
 -TBINHASH
 -TBINHASH_INFO
 -TBIO
 -TBOUNCE_TIME_PARAMETER
 -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
 -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
 -TEVP_PKEY
 -TEXPAND_ATTR
 -TFILE
--Tfilter_ctx
 -TFORWARD_INFO
--Tgeneral_name_stack_t
 -THBC_ACTION_CALL_BACKS
 -THBC_CALL_BACKS
 -THBC_CHECKS
 -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
 -TMAC_EXP_OP_INFO
 -TMAC_HEAD
 -TMAC_PARSE
--TMAI_HOSTADDR_STR
--TMAI_HOSTNAME_STR
 -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
 -TMDB_val
 -TMILTER
 -TMILTER8
+-TMILTERS
 -TMILTER_MACROS
 -TMILTER_MSG_CONTEXT
--TMILTERS
 -TMIME_ENCODING
 -TMIME_INFO
 -TMIME_STACK
 -TNAME_CODE
 -TNAME_MASK
 -TNBBIO
--Toff_t
 -TOPTIONS
 -TPCF_DBMS_INFO
 -TPCF_EVAL_CTX
 -TPCF_SERVICE_PATTERN
 -TPCF_STRING_NV
 -TPEER_NAME
--Tpem_load_state_t
 -TPGSQL_NAME
 -TPICKUP_INFO
 -TPIPE_ATTR
 -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
 -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
 -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_CMD
 -TSMTPD_CMD
 -TSMTPD_DEFER
 -TSMTPD_ENDPT_LOOKUP_INFO
 -TSMTPD_STATE
 -TSMTPD_TOKEN
 -TSMTPD_XFORWARD_ATTR
+-TSMTP_ADDR
+-TSMTP_CMD
 -TSMTP_ITERATOR
 -TSMTP_RESP
 -TSMTP_SASL_AUTH_CACHE
 -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
 -TSTRING_LIST
 -TSTRING_TABLE
 -TSYS_EXITS_DETAIL
--Ttime_t
--Ttlsa_filter
+-TTEST_CASE
+-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_CLIENT_PARAMS
 -TTLS_PKEYS
 -TTLS_PRNG_SEED_INFO
 -TTLS_PRNG_SRC
--TTLSP_STATE
 -TTLS_ROLE
 -TTLS_SCACHE
 -TTLS_SCACHE_ENTRY
 -TTLS_TLSA
 -TTLS_USAGE
 -TTLS_VINFO
+-TTLScontext_t
 -TTOK822
 -TTRANSPORT_INFO
 -TTRIGGER_SERVER
 -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
 -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
+-Tx509_stack_t
index d8dacdcad65a1bb25f35308d7a97e6168c599ac5..1fbbfee103dbb786181133fe20c2a5aad3eb4e41 100644 (file)
@@ -24240,3 +24240,19 @@ Apologies for any names omitted.
        Bugfix (introduced: Postfix 3.3): "smtp_mx_address_limit = 0"
        no longer meant 'unlimited'. Problem report by Luc Pardon.
        File: smtp/smtp_addr.c.
+
+20190427
+
+       Cleanup: normalize the IP address string forms received with
+       XCLIENT, XFORWARD, and HaProxy, for consistency with address
+       information for direct connections to Postfix, and add unit
+       tests. This casefolds and removes redundant nulls from the
+       string representation of an IPv6 address, normalizes the
+       "IPv6:" address prefix of RFC 2821 IPv6 address forms, and
+       converts IPv4 address octets with leading zeros (octal form)
+       into decimal form. Files: global/haproxy.c,
+       global/normalize_mailhost_addr.[hc], smtpd/smtpd.c.
+
+       Incompatibility: this may change the appearance of logging,
+       and the way that check_client_access will match subnets of
+       an IPv6 address.
index 1986c5be96389c52535a984ef7f134838872d343..3ed41f0e6fb77fa4b76884a1cd194273db5284aa 100644 (file)
@@ -24,3 +24,11 @@ historical IBM Public License 1.0, it is now also distributed with the
 more recent Eclipse Public License 2.0. Recipients can choose to take
 the software under the license of their choice. Those who are more
 comfortable with the IPL can continue with that license.
+
+Incompatibility with snapshot 20190427
+======================================
+
+Postfix now normalizes IP addresses received with XCLIENT, XFORWARD,
+or with the HaProxy protocol, for consistency with direct connections
+to Postfix. This may change the appearance of logging, and the way
+that check_client_access will match subnets of an IPv6 address.
index 0b0b8523251041be19f1ab386e0cfd655011e8cb..35ab5c7145373bed9eb58aa1879be7ac9a22eaee 100644 (file)
@@ -1,7 +1,5 @@
 Wish list:
 
-       XCLIENT/XFORWARD should canonicalize address forms.
-
        Things to do before the stable release:
 
        Spell-check, double-word check, HTML validator check,
index 512a82641f82a4972799b73f6574b7b4732b7139..8d55db4bfad21d8309f0ede7f8c9298729bcb962 100644 (file)
@@ -34,7 +34,8 @@ SRCS  = abounce.c anvil_clnt.c been_here.c bounce.c bounce_log.c \
        dict_memcache.c mail_version.c memcache_proto.c server_acl.c \
        mkmap_fail.c haproxy_srvr.c dsn_filter.c dynamicmaps.c uxtext.c \
        smtputf8.c mail_conf_over.c mail_parm_split.c midna_adomain.c \
-       mail_addr_form.c quote_flags.c maillog_client.c
+       mail_addr_form.c quote_flags.c maillog_client.c \
+       normalize_mailhost_addr.c
 OBJS   = abounce.o anvil_clnt.o been_here.o bounce.o bounce_log.o \
        canon_addr.o cfg_parser.o cleanup_strerror.o cleanup_strflags.o \
        clnt_stream.o conv_time.o db_common.o debug_peer.o debug_process.o \
@@ -70,7 +71,8 @@ OBJS  = abounce.o anvil_clnt.o been_here.o bounce.o bounce_log.o \
        dict_memcache.o mail_version.o memcache_proto.o server_acl.o \
        mkmap_fail.o haproxy_srvr.o dsn_filter.o dynamicmaps.o uxtext.o \
        smtputf8.o attr_override.o mail_parm_split.o midna_adomain.o \
-       $(NON_PLUGIN_MAP_OBJ) mail_addr_form.o quote_flags.o maillog_client.o
+       $(NON_PLUGIN_MAP_OBJ) mail_addr_form.o quote_flags.o maillog_client.o \
+       normalize_mailhost_addr.o
 # MAP_OBJ is for maps that may be dynamically loaded with dynamicmaps.cf.
 # When hard-linking these maps, makedefs sets NON_PLUGIN_MAP_OBJ=$(MAP_OBJ),
 # otherwise it sets the PLUGIN_* macros.
@@ -104,7 +106,7 @@ HDRS        = abounce.h anvil_clnt.h been_here.h bounce.h bounce_log.h \
        verify_sender_addr.h dict_memcache.h memcache_proto.h server_acl.h \
        haproxy_srvr.h dsn_filter.h dynamicmaps.h uxtext.h smtputf8.h \
        attr_override.h mail_parm_split.h midna_adomain.h mail_addr_form.h \
-       maillog_client.h
+       maillog_client.h normalize_mailhost_addr.h
 TESTSRC        = rec2stream.c stream2rec.c recdump.c
 DEFS   = -I. -I$(INC_DIR) -D$(SYSTYPE)
 CFLAGS = $(DEBUG) $(OPT) $(DEFS)
@@ -119,7 +121,8 @@ TESTPROG= domain_list dot_lockfile mail_addr_crunch mail_addr_find \
        valid_mailhost_addr own_inet_addr header_body_checks \
        data_redirect addr_match_list safe_ultostr verify_sender_addr \
        mail_version mail_dict server_acl uxtext mail_parm_split \
-       fold_addr smtp_reply_footer mail_addr_map
+       fold_addr smtp_reply_footer mail_addr_map normalize_mailhost_addr \
+       haproxy_srvr
 
 LIBS   = ../../lib/lib$(LIB_PREFIX)util$(LIB_SUFFIX)
 LIB_DIR        = ../../lib
@@ -378,13 +381,20 @@ fold_addr: fold_addr.c $(LIB) $(LIBS)
 smtp_reply_footer: smtp_reply_footer.c $(LIB) $(LIBS)
        $(CC) $(CFLAGS) -DTEST -o $@ $@.c $(LIB) $(LIBS) $(SYSLIBS)
 
+normalize_mailhost_addr: normalize_mailhost_addr.c $(LIB) $(LIBS)
+       $(CC) $(CFLAGS) -DTEST -o $@ $@.c $(LIB) $(LIBS) $(SYSLIBS)
+
+haproxy_srvr: haproxy_srvr.c $(LIB) $(LIBS)
+       $(CC) $(CFLAGS) -DTEST -o $@ $@.c $(LIB) $(LIBS) $(SYSLIBS)
+
 tests: tok822_test mime_tests strip_addr_test tok822_limit_test \
        xtext_test scache_multi_test ehlo_mask_test \
        namadr_list_test mail_conf_time_test header_body_checks_tests \
        mail_version_test server_acl_test resolve_local_test maps_test \
        safe_ultostr_test mail_parm_split_test fold_addr_test \
        smtp_reply_footer_test off_cvt_test mail_addr_crunch_test \
-       mail_addr_find_test mail_addr_map_test quote_822_local_test
+       mail_addr_find_test mail_addr_map_test quote_822_local_test \
+       normalize_mailhost_addr_test haproxy_srvr_test
 
 mime_tests: mime_test mime_nest mime_8bit mime_dom mime_trunc mime_cvt \
        mime_cvt2 mime_cvt3 mime_garb1 mime_garb2 mime_garb3 mime_garb4
@@ -697,6 +707,16 @@ quote_822_local_test: update quote_822_local quote_822_local.in quote_822_local.
        diff quote_822_local.ref quote_822_local.tmp
        rm -f quote_822_local.tmp
 
+normalize_mailhost_addr_test: update normalize_mailhost_addr
+       -$(SHLIB_ENV) $(VALGRIND) ./normalize_mailhost_addr >normalize_mailhost_addr.tmp 2>&1
+       diff /dev/null normalize_mailhost_addr.tmp
+       rm -f normalize_mailhost_addr.tmp
+
+haproxy_srvr_test: update haproxy_srvr
+       -$(SHLIB_ENV) $(VALGRIND) ./haproxy_srvr >haproxy_srvr.tmp 2>&1
+       diff /dev/null haproxy_srvr.tmp
+       rm -f haproxy_srvr.tmp
+
 printfck: $(OBJS) $(PROG)
        rm -rf printfck
        mkdir printfck
@@ -2224,6 +2244,18 @@ namadr_list.o: ../../include/vbuf.h
 namadr_list.o: ../../include/vstring.h
 namadr_list.o: namadr_list.c
 namadr_list.o: namadr_list.h
+normalize_mailhost_addr.o: ../../include/check_arg.h
+normalize_mailhost_addr.o: ../../include/inet_proto.h
+normalize_mailhost_addr.o: ../../include/myaddrinfo.h
+normalize_mailhost_addr.o: ../../include/mymalloc.h
+normalize_mailhost_addr.o: ../../include/stringops.h
+normalize_mailhost_addr.o: ../../include/sys_defs.h
+normalize_mailhost_addr.o: ../../include/valid_hostname.h
+normalize_mailhost_addr.o: ../../include/vbuf.h
+normalize_mailhost_addr.o: ../../include/vstring.h
+normalize_mailhost_addr.o: normalize_mailhost_addr.c
+normalize_mailhost_addr.o: normalize_mailhost_addr.h
+normalize_mailhost_addr.o: valid_mailhost_addr.h
 off_cvt.o: ../../include/check_arg.h
 off_cvt.o: ../../include/msg.h
 off_cvt.o: ../../include/sys_defs.h
index db3d0c1da5982700dd1148b1cb9ca82d77e6ffe6..87a660801545e6347c406555ad22fe5c5bec4177 100644 (file)
@@ -19,7 +19,8 @@
 /*     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.
+/*     converted to IPV4 syntax, provided that IPv4 support is
+/*     enabled.
 /* LICENSE
 /* .ad
 /* .fi
 /*     IBM T.J. Watson Research
 /*     P.O. Box 704
 /*     Yorktown Heights, NY 10598, USA
+/*
+/*     Wietse Venema
+/*     Google, Inc.
+/*     111 8th Avenue
+/*     New York, NY 10011, USA
 /*--*/
 
 /* System library. */
@@ -109,6 +115,9 @@ static int haproxy_srvr_parse_proto(const char *str, int *addr_family)
 static int haproxy_srvr_parse_addr(const char *str, MAI_HOSTADDR_STR *addr,
                                           int addr_family)
 {
+    struct addrinfo *res = 0;
+    int     err;
+
     if (msg_verbose)
        msg_info("haproxy_srvr_parse: addr=%s proto=%d", str, addr_family);
 
@@ -118,26 +127,28 @@ static int haproxy_srvr_parse_addr(const char *str, MAI_HOSTADDR_STR *addr,
     switch (addr_family) {
 #ifdef AF_INET6
     case AF_INET6:
-       if (!valid_ipv6_hostaddr(str, DONT_GRIPE))
-           return (-1);
-       if (strncasecmp("::ffff:", str, 7) == 0
-           && strchr((char *) proto_info->sa_family_list, AF_INET) != 0) {
-           memcpy(addr->buf, str + 7, strlen(str) + 1 - 7);
-           return (0);
-       } else {
-           memcpy(addr->buf, str, strlen(str) + 1);
-           return (0);
-       }
+       err = !valid_ipv6_hostaddr(str, DONT_GRIPE);
+       break;
 #endif
     case AF_INET:
-       if (!valid_ipv4_hostaddr(str, DONT_GRIPE))
-           return (-1);
-       memcpy(addr->buf, str, strlen(str) + 1);
-       return (0);
+       err = !valid_ipv4_hostaddr(str, DONT_GRIPE);
+       break;
     default:
        msg_panic("haproxy_srvr_parse: unexpected address family: %d",
                  addr_family);
     }
+    if (err == 0)
+       err = (hostaddr_to_sockaddr(str, (char *) 0, 0, &res)
+              || sockaddr_to_hostaddr(res->ai_addr, res->ai_addrlen,
+                                      addr, (MAI_SERVPORT_STR *) 0, 0));
+    if (res)
+       freeaddrinfo(res);
+    if (err)
+       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);
 }
 
 /* haproxy_srvr_parse_port - extract and validate TCP port */
@@ -195,3 +206,113 @@ const char *haproxy_srvr_parse(const char *str,
     myfree(saved_str);
     return (err);
 }
+
+ /*
+  * 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;
+
+    /* Actual results. */
+    const char *act_return;
+    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;
+
+    /* 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;
+           continue;
+       }
+       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;
+       }
+    }
+    if (tests_failed)
+       msg_info("tests failed: %d", tests_failed);
+    exit(tests_failed != 0);
+}
+
+#endif
index a7a5473f49f3b36886175cae4734324b7af07e82..1c8a231ce3d3135ecf92a283cec862617ba49a28 100644 (file)
@@ -20,7 +20,7 @@
   * Patches change both the patchlevel and the release date. Snapshots have no
   * patchlevel; they change the release date only.
   */
-#define MAIL_RELEASE_DATE      "20190418"
+#define MAIL_RELEASE_DATE      "20190427"
 #define MAIL_VERSION_NUMBER    "3.5"
 
 #ifdef SNAPSHOT
diff --git a/postfix/src/global/normalize_mailhost_addr.c b/postfix/src/global/normalize_mailhost_addr.c
new file mode 100644 (file)
index 0000000..d60135f
--- /dev/null
@@ -0,0 +1,259 @@
+/*++
+/* NAME
+/*     normalize_mailhost_addr 3
+/* SUMMARY
+/*     normalize mailhost address string representation
+/* SYNOPSIS
+/*     #include <normalize_mailhost_addr.h>
+/*
+/*     int     normalize_mailhost_addr(
+/*     const char *string,
+/*     char    **mailhost_addr,
+/*     char    **bare_addr,
+/*     int     *addr_family)
+/* DESCRIPTION
+/*     normalize_mailhost_addr() takes the RFC 2821 string
+/*     representation of an IPv4 or IPv6 network address, and
+/*     normalizes the "IPv6:" prefix and numeric form. An IPv6 or
+/*     IPv4 form is rejected if supposed for that protocol is
+/*     disabled or non-existent. If both IPv6 and IPv4 support are
+/*     enabled, a V4-in-V6 address is replaced with the IPv4 form.
+/*
+/*     Arguments:
+/* .IP string
+/*     Null-terminated string with the RFC 2821 string representation
+/*     of an IPv4 or IPv6 network address.
+/* .IP mailhost_addr
+/*     Null pointer, or pointer to null-terminated string with the
+/*     normalized RFC 2821 string representation of an IPv4 or
+/*     IPv6 network address. Storage must be freed with myfree().
+/* .IP bare_addr
+/*     Null pointer, or pointer to null-terminated string with the
+/*     numeric address without prefix, such as "IPv6:". Storage
+/*     must be freed with myfree().
+/* .IP addr_family
+/*     Null pointer, or pointer to integer for storing the address
+/*     family.
+/* DIAGNISTICS
+/*     normalize_mailhost_addr() returns -1 if the input is malformed,
+/*     zero otherwise.
+/* LICENSE
+/* .ad
+/* .fi
+/*     The Secure Mailer license must be distributed with this software.
+/* AUTHOR(S)
+/*     Wietse Venema
+/*     Google, Inc.
+/*     111 8th Avenue
+/*     New York, NY 10011, USA
+/*--*/
+
+ /*
+  * System library.
+  */
+#include <sys_defs.h>
+#include <string.h>
+
+#ifdef STRCASECMP_IN_STRINGS_H
+#include <strings.h>
+#endif
+
+ /*
+  * Utility library.
+  */
+#include <inet_proto.h>
+#include <msg.h>
+#include <myaddrinfo.h>
+#include <mymalloc.h>
+#include <stringops.h>
+
+ /*
+  * Global library.
+  */
+#include <normalize_mailhost_addr.h>
+#include <valid_mailhost_addr.h>
+
+/* normalize_mailhost_addr - parse and normalize mailhost IP address */
+
+int     normalize_mailhost_addr(const char *string, char **mailhost_addr,
+                                       char **bare_addr, int *addr_family)
+{
+    const char myname[] = "normalize_mailhost_addr";
+    INET_PROTO_INFO *proto_info = inet_proto_info();
+    struct addrinfo *res = 0;
+    MAI_HOSTADDR_STR hostaddr;
+    const char *valid_addr;            /* IPv6:fc00::1 */
+    const char *normal_addr;           /* 192.168.0.1 */
+    int     normal_family;
+
+#define UPDATE_BARE_ADDR(s, v) do { \
+       if (s) myfree(s); \
+       (s) = mystrdup(v); \
+    } while(0)
+#define UPDATE_MAILHOST_ADDR(s, prefix, addr) do { \
+       if (s) myfree(s); \
+       (s) = concatenate((prefix), (addr), (char *) 0); \
+    } while (0)
+
+    /*
+     * Parse and normalize the input.
+     */
+    if ((valid_addr = valid_mailhost_addr(string, DONT_GRIPE)) == 0
+       || hostaddr_to_sockaddr(valid_addr, (char *) 0, 0, &res) != 0
+       || sockaddr_to_hostaddr(res->ai_addr, res->ai_addrlen,
+                               &hostaddr, (MAI_SERVPORT_STR *) 0, 0) != 0) {
+       normal_addr = 0;
+#ifdef HAS_IPV6
+    } else if (res->ai_family == AF_INET6
+              && strncasecmp("::ffff:", hostaddr.buf, 7) == 0
+              && strchr((char *) proto_info->sa_family_list, AF_INET)) {
+       normal_addr = hostaddr.buf + 7;
+       normal_family = AF_INET;
+#endif
+    } else if (strchr((char *) proto_info->sa_family_list, res->ai_family)) {
+       normal_addr = hostaddr.buf;
+       normal_family = res->ai_family;
+    } else {
+       normal_addr = 0;
+    }
+    if (res)
+       freeaddrinfo(res);
+    if (normal_addr == 0)
+       return (-1);
+
+    /*
+     * Write the applicable outputs.
+     */
+    if (bare_addr) {
+       UPDATE_BARE_ADDR(*bare_addr, normal_addr);
+       if (msg_verbose)
+           msg_info("%s: bare_addr=%s", myname, *bare_addr);
+    }
+    if (mailhost_addr) {
+#ifdef HAS_IPV6
+       if (normal_family == AF_INET6)
+           UPDATE_MAILHOST_ADDR(*mailhost_addr, IPV6_COL, normal_addr);
+       else
+#endif
+           UPDATE_BARE_ADDR(*mailhost_addr, normal_addr);
+       if (msg_verbose)
+           msg_info("%s: mailhost_addr=%s", myname, *mailhost_addr);
+    }
+    if (addr_family) {
+       *addr_family = normal_family;
+       if (msg_verbose)
+           msg_info("%s: addr_family=%s", myname,
+                    *addr_family == AF_INET6 ? "AF_INET6"
+                    : *addr_family == AF_INET ? "AF_INET"
+                    : "unknown");
+    }
+    return (0);
+}
+
+ /*
+  * Test program.
+  */
+#ifdef TEST
+#include <stdlib.h>
+#include <mymalloc.h>
+#include <msg.h>
+
+ /*
+  * Main test program.
+  */
+int     main(int argc, char **argv)
+{
+    /* Test cases with inputs and expected outputs. */
+    typedef struct TEST_CASE {
+       const char *inet_protocols;
+       const char *mailhost_addr;
+       int     exp_return;
+       const char *exp_mailhost_addr;
+       char   *exp_bare_addr;
+       int     exp_addr_family;
+    } TEST_CASE;
+    static TEST_CASE test_cases[] = {
+       /* IPv4 in IPv6. */
+       {"ipv4, ipv6", "ipv6:::ffff:1.2.3.4", 0, "1.2.3.4", "1.2.3.4", AF_INET},
+       {"ipv6", "ipv6:::ffff:1.2.3.4", 0, "IPv6:::ffff:1.2.3.4", "::ffff:1.2.3.4", AF_INET6},
+       /* Pass IPv4 or IPV6. */
+       {"ipv4, ipv6", "ipv6:fc00::1", 0, "IPv6:fc00::1", "fc00::1", AF_INET6},
+       {"ipv4, ipv6", "1.2.3.4", 0, "1.2.3.4", "1.2.3.4", AF_INET},
+       /* Normalize IPv4 or IPV6. */
+       {"ipv4, ipv6", "ipv6:fc00::0", 0, "IPv6:fc00::", "fc00::", AF_INET6},
+       {"ipv4, ipv6", "01.02.03.04", 0, "1.2.3.4", "1.2.3.4", AF_INET},
+       /* Suppress specific outputs. */
+       {"ipv4, ipv6", "ipv6:fc00::1", 0, 0, "fc00::1", AF_INET6},
+       {"ipv4, ipv6", "ipv6:fc00::1", 0, "IPv6:fc00::1", 0, AF_INET6},
+       {"ipv4, ipv6", "ipv6:fc00::1", 0, "IPv6:fc00::1", "fc00::1", -1},
+       /* Address type mismatch. */
+       {"ipv4, ipv6", "::ffff:1.2.3.4", -1},
+       {"ipv4", "ipv6:fc00::1", -1},
+       {"ipv6", "1.2.3.4", -1},
+       0,
+    };
+    TEST_CASE *test_case;
+
+    /* Actual results. */
+    int     act_return;
+    char   *act_mailhost_addr = mystrdup("initial_mailhost_addr");
+    char   *act_bare_addr = mystrdup("initial_bare_addr");
+    int     act_addr_family = 0xdeadbeef;
+
+    /* Findings. */
+    int     tests_failed = 0;
+    int     test_failed;
+
+    for (tests_failed = 0, test_case = test_cases; test_case->inet_protocols;
+        tests_failed += test_failed, test_case++) {
+       test_failed = 0;
+       inet_proto_init(argv[0], test_case->inet_protocols);
+       act_return =
+           normalize_mailhost_addr(test_case->mailhost_addr,
+                                   test_case->exp_mailhost_addr ?
+                                   &act_mailhost_addr : (char **) 0,
+                                   test_case->exp_bare_addr ?
+                                   &act_bare_addr : (char **) 0,
+                                   test_case->exp_addr_family >= 0 ?
+                                   &act_addr_family : (int *) 0);
+       if (act_return != test_case->exp_return) {
+           msg_warn("test case %d return expected=%d actual=%d",
+                    (int) (test_case - test_cases),
+                    test_case->exp_return, act_return);
+           test_failed = 1;
+           continue;
+       }
+       if (test_case->exp_return != 0)
+           continue;
+       if (test_case->exp_mailhost_addr
+           && strcmp(test_case->exp_mailhost_addr, act_mailhost_addr)) {
+           msg_warn("test case %d mailhost_addr expected=%s actual=%s",
+                    (int) (test_case - test_cases),
+                    test_case->exp_mailhost_addr, act_mailhost_addr);
+           test_failed = 1;
+       }
+       if (test_case->exp_bare_addr
+           && strcmp(test_case->exp_bare_addr, act_bare_addr)) {
+           msg_warn("test case %d bare_addr expected=%s actual=%s",
+                    (int) (test_case - test_cases),
+                    test_case->exp_bare_addr, act_bare_addr);
+           test_failed = 1;
+       }
+       if (test_case->exp_addr_family >= 0
+           && test_case->exp_addr_family != act_addr_family) {
+           msg_warn("test case %d addr_family expected=0x%x actual=0x%x",
+                    (int) (test_case - test_cases),
+                    test_case->exp_addr_family, act_addr_family);
+           test_failed = 1;
+       }
+    }
+    if (act_mailhost_addr)
+       myfree(act_mailhost_addr);
+    if (act_bare_addr)
+       myfree(act_bare_addr);
+    if (tests_failed)
+       msg_info("tests failed: %d", tests_failed);
+    exit(tests_failed != 0);
+}
+
+#endif
diff --git a/postfix/src/global/normalize_mailhost_addr.h b/postfix/src/global/normalize_mailhost_addr.h
new file mode 100644 (file)
index 0000000..5ea4d3a
--- /dev/null
@@ -0,0 +1,30 @@
+#ifndef _NORMALIZE_MAILHOST_ADDR_H_INCLUDED_
+#define _NORMALIZE_MAILHOST_ADDR_H_INCLUDED_
+
+/*++
+/* NAME
+/*     normalize_mailhost_addr 3h
+/* SUMMARY
+/*     normalize mailhost address string representation
+/* SYNOPSIS
+/*     #include <normalize_mailhost_addr.h>
+/* DESCRIPTION
+/* .nf
+
+ /*
+  * External interface.
+  */
+extern int normalize_mailhost_addr(const char *, char **, char **, int *);
+
+/* LICENSE
+/* .ad
+/* .fi
+/*     The Secure Mailer license must be distributed with this software.
+/* AUTHOR(S)
+/*     Wietse Venema
+/*     Google, Inc.
+/*     111 8th Avenue
+/*     New York, NY 10011, USA
+/*--*/
+
+#endif
index 14540d370746402f7b948e444ed9a64fef98e456..e7d583e5c310befd4a518006b57b5d479d2f28b8 100644 (file)
@@ -221,6 +221,7 @@ smtpd.o: ../../include/mymalloc.h
 smtpd.o: ../../include/namadr_list.h
 smtpd.o: ../../include/name_code.h
 smtpd.o: ../../include/name_mask.h
+smtpd.o: ../../include/normalize_mailhost_addr.h
 smtpd.o: ../../include/nvtable.h
 smtpd.o: ../../include/off_cvt.h
 smtpd.o: ../../include/quote_822_local.h
index 124449819e760af4c079cc1f22de07ef3e392dbb..954d09ac6c4ac4e9dc3591cff26cc37f12bf96e1 100644 (file)
 #include <verify_sender_addr.h>
 #include <smtputf8.h>
 #include <match_parent_style.h>
+#include <normalize_mailhost_addr.h>
 
 /* Single-threaded server skeleton. */
 
@@ -4314,7 +4315,6 @@ static int xclient_cmd(SMTPD_STATE *state, int argc, SMTPD_TOKEN *argv)
     SMTPD_TOKEN *argp;
     char   *raw_value;
     char   *attr_value;
-    const char *bare_value;
     char   *attr_name;
     int     update_namaddr = 0;
     int     name_status;
@@ -4362,11 +4362,6 @@ static int xclient_cmd(SMTPD_STATE *state, int argc, SMTPD_TOKEN *argv)
        return (-1);
     }
 #define STREQ(x,y)     (strcasecmp((x), (y)) == 0)
-#define UPDATE_STR(s, v) do { \
-           const char *_v = (v); \
-           if (s) myfree(s); \
-           s = (_v) ? mystrdup(_v) : 0; \
-       } while(0)
 
     /*
      * Initialize.
@@ -4404,6 +4399,12 @@ static int xclient_cmd(SMTPD_STATE *state, int argc, SMTPD_TOKEN *argv)
         */
        printable(attr_value, '?');
 
+#define UPDATE_STR(s, v) do { \
+       const char *_v = (v); \
+       if (s) myfree(s); \
+       (s) = (_v) ? mystrdup(_v) : 0; \
+    } while(0)
+
        /*
         * NAME=substitute SMTP client hostname (and reverse/forward name, in
         * case of success). Also updates the client hostname lookup status
@@ -4458,24 +4459,18 @@ static int xclient_cmd(SMTPD_STATE *state, int argc, SMTPD_TOKEN *argv)
        else if (STREQ(attr_name, XCLIENT_ADDR)) {
            if (STREQ(attr_value, XCLIENT_UNAVAILABLE)) {
                attr_value = CLIENT_ADDR_UNKNOWN;
-               bare_value = attr_value;
+               UPDATE_STR(state->addr, attr_value);
+               UPDATE_STR(state->rfc_addr, attr_value);
            } else {
-               if ((bare_value = valid_mailhost_addr(attr_value, DONT_GRIPE)) == 0) {
+               neuter(attr_value, NEUTER_CHARACTERS, '?');
+               if (normalize_mailhost_addr(attr_value, &state->rfc_addr,
+                                  &state->addr, &state->addr_family) < 0) {
                    state->error_mask |= MAIL_ERROR_PROTOCOL;
                    smtpd_chat_reply(state, "501 5.5.4 Bad %s syntax: %s",
                                     XCLIENT_ADDR, attr_value);
                    return (-1);
                }
            }
-           UPDATE_STR(state->addr, bare_value);
-           UPDATE_STR(state->rfc_addr, attr_value);
-#ifdef HAS_IPV6
-           if (strncasecmp(attr_value, INET_PROTO_NAME_IPV6 ":",
-                           sizeof(INET_PROTO_NAME_IPV6 ":") - 1) == 0)
-               state->addr_family = AF_INET6;
-           else
-#endif
-               state->addr_family = AF_INET;
            update_namaddr = 1;
        }
 
@@ -4552,16 +4547,17 @@ static int xclient_cmd(SMTPD_STATE *state, int argc, SMTPD_TOKEN *argv)
        else if (STREQ(attr_name, XCLIENT_DESTADDR)) {
            if (STREQ(attr_value, XCLIENT_UNAVAILABLE)) {
                attr_value = SERVER_ADDR_UNKNOWN;
-               bare_value = attr_value;
+               UPDATE_STR(state->dest_addr, attr_value);
            } else {
-               if ((bare_value = valid_mailhost_addr(attr_value, DONT_GRIPE)) == 0) {
+               neuter(attr_value, NEUTER_CHARACTERS, '?');
+               if (normalize_mailhost_addr(attr_value, (char **) 0,
+                                       &state->dest_addr, (int *) 0) < 0) {
                    state->error_mask |= MAIL_ERROR_PROTOCOL;
                    smtpd_chat_reply(state, "501 5.5.4 Bad %s syntax: %s",
                                     XCLIENT_DESTADDR, attr_value);
                    return (-1);
                }
            }
-           UPDATE_STR(state->dest_addr, bare_value);
            /* XXX Require same address family as client address. */
        }
 
@@ -4671,7 +4667,6 @@ static int xforward_cmd(SMTPD_STATE *state, int argc, SMTPD_TOKEN *argv)
     SMTPD_TOKEN *argp;
     char   *raw_value;
     char   *attr_value;
-    const char *bare_value;
     char   *attr_name;
     int     updated = 0;
     static const NAME_CODE xforward_flags[] = {
@@ -4787,18 +4782,17 @@ static int xforward_cmd(SMTPD_STATE *state, int argc, SMTPD_TOKEN *argv)
        case SMTPD_STATE_XFORWARD_ADDR:
            if (STREQ(attr_value, XFORWARD_UNAVAILABLE)) {
                attr_value = CLIENT_ADDR_UNKNOWN;
-               bare_value = attr_value;
+               UPDATE_STR(state->xforward.addr, attr_value);
            } else {
                neuter(attr_value, NEUTER_CHARACTERS, '?');
-               if ((bare_value = valid_mailhost_addr(attr_value, DONT_GRIPE)) == 0) {
+               if (normalize_mailhost_addr(attr_value, &state->xforward.rfc_addr,
+                                   &state->xforward.addr, (int *) 0) < 0) {
                    state->error_mask |= MAIL_ERROR_PROTOCOL;
                    smtpd_chat_reply(state, "501 5.5.4 Bad %s syntax: %s",
                                     XFORWARD_ADDR, attr_value);
                    return (-1);
                }
            }
-           UPDATE_STR(state->xforward.addr, bare_value);
-           UPDATE_STR(state->xforward.rfc_addr, attr_value);
            break;
 
            /*
@@ -4900,7 +4894,8 @@ static int xforward_cmd(SMTPD_STATE *state, int argc, SMTPD_TOKEN *argv)
      * Update the combined name and address when either has changed. Use only
      * the name when no address is available.
      */
-    if (updated & (SMTPD_STATE_XFORWARD_NAME | SMTPD_STATE_XFORWARD_ADDR)) {
+    if (updated & (SMTPD_STATE_XFORWARD_NAME | SMTPD_STATE_XFORWARD_ADDR
+                  | SMTPD_STATE_XFORWARD_PORT)) {
        if (state->xforward.namaddr)
            myfree(state->xforward.namaddr);
        state->xforward.namaddr =