-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
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.
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.
Wish list:
- XCLIENT/XFORWARD should canonicalize address forms.
-
Things to do before the stable release:
Spell-check, double-word check, HTML validator check,
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 \
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.
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)
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
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
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
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
/* 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. */
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);
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 */
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
* 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
--- /dev/null
+/*++
+/* 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
--- /dev/null
+#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
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
#include <verify_sender_addr.h>
#include <smtputf8.h>
#include <match_parent_style.h>
+#include <normalize_mailhost_addr.h>
/* Single-threaded server skeleton. */
SMTPD_TOKEN *argp;
char *raw_value;
char *attr_value;
- const char *bare_value;
char *attr_name;
int update_namaddr = 0;
int name_status;
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.
*/
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
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;
}
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. */
}
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[] = {
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;
/*
* 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 =