On Sun, Nov 23, 2014 at 05:43:44AM +0000, Viktor Dukhovni wrote: > It applies to Postfix > 2.12 snapshots. [ It conflicts with the IDNA for TLS patch I sent > off-list to Wietse in that a new header file #include is added to > src/smtp/smtp_addr.c in the same place by both patches. ]
Oops that conflict was imaginary, I added midna.h to tls_client.c not smtp_addr.c. So I edited the patch in error. Corrected patch attached. This applies to 2.12-20141119 with or without the off-list IDNA enhancements for TLS. -- Viktor.
>From 2d4b2558204e3bc409a8cd1b456807e0dcc3165c Mon Sep 17 00:00:00 2001 From: Viktor Dukhovni <postfix-us...@dukhovni.org> Date: Sun, 23 Nov 2014 00:30:13 -0500 Subject: [PATCH 1/1] smtp_inet_protocol_host_maps --- proto/postconf.proto | 26 +++++++++++++++++ src/global/mail_params.h | 6 ++++ src/smtp/lmtp_params.c | 1 + src/smtp/smtp.c | 13 +++++++++ src/smtp/smtp.h | 3 ++ src/smtp/smtp_addr.c | 67 ++++++++++++++++++++++++++++++++++++------ src/smtp/smtp_params.c | 1 + src/util/inet_proto.c | 75 ++++++++++++++++++++++++++++++++---------------- src/util/inet_proto.h | 5 ++++ 9 files changed, 163 insertions(+), 34 deletions(-) diff --git a/proto/postconf.proto b/proto/postconf.proto index 15f87f8..a4256ef 100644 --- a/proto/postconf.proto +++ b/proto/postconf.proto @@ -16272,3 +16272,29 @@ Names are matched in a case-insensitive manner. The list of supported header names is limited only by available memory. </p> <p> This feature is available in Postfix 2.12 and later. </p> + +%PARAM smtp_inet_protocol_host_maps + +<p> Optional lookup tables with Postfix SMTP client inet_protocols +settings by relay host. Specify zero or more "type:name" lookup +tables, separated by whitespace or comma. Tables will be searched +in the specified order until a match is found. The lookup value +can be any of the values acceptable in the inet_protocols configuration +parameter. See there for details. </p> + +<p> The tables are indexed by ASCII form of the hostname, and apply +for all connections to the host regardless of the TCP port. If no +entry is found for a host, the parent domain is tried recursively +up to and including any top-level domain. The parent domain +lookup key always starts with a leading period. Parent domain +lookups are not performed in regular expression, tcp-based +or socketmap tables. </p> + +<p> This feature is available in Postfix 2.12 and later. </p> + +%PARAM lmtp_inet_protocol_host_maps + +<p> The LMTP-specific version of the smtp_inet_protocol_host_maps +configuration parameter. See there for details. </p> + +<p> This feature is available in Postfix 2.12 and later. </p> diff --git a/src/global/mail_params.h b/src/global/mail_params.h index a7b0fdb..ce2e692 100644 --- a/src/global/mail_params.h +++ b/src/global/mail_params.h @@ -1140,6 +1140,12 @@ extern bool var_smtp_rand_addr; #define DEF_LMTP_LINE_LIMIT 998 extern int var_smtp_line_limit; +#define VAR_SMTP_PROTO_MAPS "smtp_inet_protocol_host_maps" +#define DEF_SMTP_PROTO_MAPS "" +#define VAR_LMTP_PROTO_MAPS "lmtp_inet_protocol_host_maps" +#define DEF_LMTP_PROTO_MAPS "" +extern char *var_smtp_proto_maps; + #define VAR_SMTP_PIX_THRESH "smtp_pix_workaround_threshold_time" #define DEF_SMTP_PIX_THRESH "500s" #define VAR_LMTP_PIX_THRESH "lmtp_pix_workaround_threshold_time" diff --git a/src/smtp/lmtp_params.c b/src/smtp/lmtp_params.c index 1861e5b..171b4d7 100644 --- a/src/smtp/lmtp_params.c +++ b/src/smtp/lmtp_params.c @@ -58,6 +58,7 @@ VAR_LMTP_ADDR_PREF, DEF_LMTP_ADDR_PREF, &var_smtp_addr_pref, 1, 0, VAR_LMTP_DNS_RES_OPT, DEF_LMTP_DNS_RES_OPT, &var_smtp_dns_res_opt, 0, 0, VAR_LMTP_DSN_FILTER, DEF_LMTP_DSN_FILTER, &var_smtp_dsn_filter, 0, 0, + VAR_LMTP_PROTO_MAPS, DEF_LMTP_PROTO_MAPS, &var_smtp_proto_maps, 0, 0, 0, }; static const CONFIG_TIME_TABLE lmtp_time_table[] = { diff --git a/src/smtp/smtp.c b/src/smtp/smtp.c index e7ea7aa..9cef36b 100644 --- a/src/smtp/smtp.c +++ b/src/smtp/smtp.c @@ -260,6 +260,9 @@ /* Optional filter for the \fBsmtp\fR(8) delivery agent to change the /* delivery status code or explanatory text of successful or unsuccessful /* deliveries. +/* .IP "\fBsmtp_inet_protocol_host_maps (empty)\fR" +/* Optional lookup tables with Postfix SMTP client inet_protocols +/* settings by relay host. /* MIME PROCESSING CONTROLS /* .ad /* .fi @@ -895,6 +898,7 @@ char *var_smtp_dns_support; bool var_smtp_rec_deadline; bool var_smtp_dummy_mail_auth; char *var_smtp_dsn_filter; +char *var_smtp_proto_maps; /* Special handling of 535 AUTH errors. */ char *var_smtp_sasl_auth_cache_name; @@ -916,6 +920,7 @@ unsigned smtp_dns_res_opt; MAPS *smtp_pix_bug_maps; HBC_CHECKS *smtp_header_checks; /* limited header checks */ HBC_CHECKS *smtp_body_checks; /* limited body checks */ +MAPS *smtp_proto_maps; #ifdef USE_TLS @@ -1202,6 +1207,14 @@ static void pre_init(char *unused_name, char **unused_argv) DICT_FLAG_LOCK | DICT_FLAG_FOLD_FIX); /* + * Inet protocol maps. + */ + if (*var_smtp_proto_maps) + smtp_proto_maps = + maps_create(VAR_LMTP_SMTP(PROTO_MAPS), var_smtp_proto_maps, + DICT_FLAG_LOCK | DICT_FLAG_FOLD_FIX); + + /* * Header/body checks. */ smtp_header_checks = hbc_header_checks_create( diff --git a/src/smtp/smtp.h b/src/smtp/smtp.h index 0582eae..0018d24 100644 --- a/src/smtp/smtp.h +++ b/src/smtp/smtp.h @@ -284,6 +284,9 @@ extern MAPS *smtp_ehlo_dis_maps; /* ehlo keyword filter */ extern MAPS *smtp_pix_bug_maps; /* PIX workarounds */ extern MAPS *smtp_generic_maps; /* make internal address valid */ + +extern MAPS *smtp_proto_maps; /* per MX inet_protocols override */ + extern int smtp_ext_prop_mask; /* address externsion propagation */ extern unsigned smtp_dns_res_opt; /* DNS query flags */ diff --git a/src/smtp/smtp_addr.c b/src/smtp/smtp_addr.c index 2294f1e..370c140 100644 --- a/src/smtp/smtp_addr.c +++ b/src/smtp/smtp_addr.c @@ -86,6 +86,7 @@ #include <myaddrinfo.h> #include <inet_proto.h> #include <midna.h> +#include <valid_hostname.h> /* Global library. */ @@ -102,6 +103,8 @@ #include "smtp.h" #include "smtp_addr.h" +#define PARTIAL DICT_FLAG_FIXED + /* smtp_print_addr - print address list */ static void smtp_print_addr(const char *what, DNS_RR *addr_list) @@ -122,6 +125,41 @@ static void smtp_print_addr(const char *what, DNS_RR *addr_list) msg_info("end %s address list", what); } +/* host_proto_info - protocol pref lookup by host */ + +static INET_PROTO_INFO *host_proto_info(const char *host) +{ + const char *myname = "host_proto_info"; + const char *name; + const char *next; + const char *value; + int maybe_numerical = 1; + int flags = 0; + + if (msg_verbose) + msg_info("%s: %s", myname, host); + + /* + * Try the name and its parent domains. Including top-level domains. + */ + for (name = host; *name != 0; name = next) { + if ((value = maps_find(smtp_proto_maps, name, flags)) != 0) + return inet_proto_lookup(VAR_LMTP_SMTP(PROTO_MAPS), value); + if (smtp_proto_maps->error != 0) { + msg_warn("%s: table lookup problem", smtp_proto_maps->title); + return (0); + } + /* Don't apply subdomain magic to numerical hostnames. */ + if (maybe_numerical + && (maybe_numerical = valid_hostaddr(host, DONT_GRIPE)) != 0) + break; + if ((next = strchr(name + 1, '.')) == 0) + break; + flags = PARTIAL; + } + return inet_proto_info(); +} + /* smtp_addr_one - address lookup for one host name */ static DNS_RR *smtp_addr_one(DNS_RR *addr_list, const char *host, int res_opt, @@ -133,24 +171,35 @@ static DNS_RR *smtp_addr_one(DNS_RR *addr_list, const char *host, int res_opt, int aierr; struct addrinfo *res0; struct addrinfo *res; - INET_PROTO_INFO *proto_info = inet_proto_info(); + INET_PROTO_INFO *proto_info; int found; if (msg_verbose) msg_info("%s: host %s", myname, host); + /* Skip host when lookup fails */ + if ((proto_info = host_proto_info(host)) == 0 + || proto_info->sa_family_list[0] == 0) { + msg_warn("%s: unusable %s value for host %s", + myname, VAR_LMTP_SMTP(PROTO_MAPS), host); + dsb_simple(why, "4.3.5", "Server configuration error"); + return (addr_list); + } + /* * Interpret a numerical name as an address. */ if (hostaddr_to_sockaddr(host, (char *) 0, 0, &res0) == 0) { - if (strchr((char *) proto_info->sa_family_list, res0->ai_family) != 0) { - if ((addr = dns_sa_to_rr(host, pref, res0->ai_addr)) == 0) - msg_fatal("host %s: conversion error for address family %d: %m", - host, ((struct sockaddr *) (res0->ai_addr))->sa_family); - addr_list = dns_rr_append(addr_list, addr); - freeaddrinfo(res0); - return (addr_list); - } + if (strchr((char *) proto_info->sa_family_list, + res0->ai_family) != 0) { + if ((addr = dns_sa_to_rr(host, pref, res0->ai_addr)) == 0) + msg_fatal("host %s: conversion error" + " for address family %d: %m", host, + ((struct sockaddr *) (res0->ai_addr))->sa_family); + addr_list = dns_rr_append(addr_list, addr); + freeaddrinfo(res0); + return (addr_list); + } freeaddrinfo(res0); } diff --git a/src/smtp/smtp_params.c b/src/smtp/smtp_params.c index 807215d..415a555 100644 --- a/src/smtp/smtp_params.c +++ b/src/smtp/smtp_params.c @@ -59,6 +59,7 @@ VAR_SMTP_ADDR_PREF, DEF_SMTP_ADDR_PREF, &var_smtp_addr_pref, 1, 0, VAR_SMTP_DNS_RES_OPT, DEF_SMTP_DNS_RES_OPT, &var_smtp_dns_res_opt, 0, 0, VAR_SMTP_DSN_FILTER, DEF_SMTP_DSN_FILTER, &var_smtp_dsn_filter, 0, 0, + VAR_SMTP_PROTO_MAPS, DEF_SMTP_PROTO_MAPS, &var_smtp_proto_maps, 0, 0, 0, }; static const CONFIG_TIME_TABLE smtp_time_table[] = { diff --git a/src/util/inet_proto.c b/src/util/inet_proto.c index 6b5cc4c..31b1ac1 100644 --- a/src/util/inet_proto.c +++ b/src/util/inet_proto.c @@ -115,6 +115,7 @@ INET_PROTO_INFO *inet_proto_table = 0; */ #define INET_PROTO_MASK_IPV4 (1<<0) #define INET_PROTO_MASK_IPV6 (1<<1) +#define INET_PROTO_MASK_LAST INET_PROTO_MASK_IPV6 static const NAME_MASK proto_table[] = { #ifdef HAS_IPV6 @@ -127,6 +128,11 @@ static const NAME_MASK proto_table[] = { 0, }; + /* + * Cache by mask value. + */ +static INET_PROTO_INFO *info_by_mask[1<<INET_PROTO_MASK_LAST]; + /* make_uchar_vector - create and initialize uchar vector */ static unsigned char *make_uchar_vector(int len,...) @@ -165,23 +171,11 @@ static unsigned *make_unsigned_vector(int len,...) return (vp); } -/* inet_proto_free - destroy data */ - -static void inet_proto_free(INET_PROTO_INFO *pf) -{ - myfree((char *) pf->ai_family_list); - myfree((char *) pf->dns_atype_list); - myfree((char *) pf->sa_family_list); - myfree((char *) pf); -} - /* inet_proto_init - convert protocol names to library inputs */ -INET_PROTO_INFO *inet_proto_init(const char *context, const char *protocols) +static INET_PROTO_INFO *init_mask(const char *context, int mask) { - const char *myname = "inet_proto"; INET_PROTO_INFO *pf; - int inet_proto_mask; int sock; /* @@ -189,25 +183,24 @@ INET_PROTO_INFO *inet_proto_init(const char *context, const char *protocols) * can't look up interface information, and we can't convert explicit * names or addresses. */ - inet_proto_mask = name_mask(context, proto_table, protocols); #ifdef HAS_IPV6 - if (inet_proto_mask & INET_PROTO_MASK_IPV6) { + if (mask & INET_PROTO_MASK_IPV6) { if ((sock = socket(PF_INET6, SOCK_STREAM, 0)) >= 0) { close(sock); } else if (errno == EAFNOSUPPORT || errno == EPROTONOSUPPORT) { - msg_warn("%s: disabling IPv6 name/address support: %m", context); - inet_proto_mask &= ~INET_PROTO_MASK_IPV6; + msg_warn("%s: IPv6 name/address support is disabled: %m", context); + mask &= ~INET_PROTO_MASK_IPV6; } else { msg_fatal("socket: %m"); } } #endif - if (inet_proto_mask & INET_PROTO_MASK_IPV4) { + if (mask & INET_PROTO_MASK_IPV4) { if ((sock = socket(PF_INET, SOCK_STREAM, 0)) >= 0) { close(sock); } else if (errno == EAFNOSUPPORT || errno == EPROTONOSUPPORT) { - msg_warn("%s: disabling IPv4 name/address support: %m", context); - inet_proto_mask &= ~INET_PROTO_MASK_IPV4; + msg_warn("%s: IPv4 name/address support is disabled: %m", context); + mask &= ~INET_PROTO_MASK_IPV4; } else { msg_fatal("socket: %m"); } @@ -221,7 +214,7 @@ INET_PROTO_INFO *inet_proto_init(const char *context, const char *protocols) * XXX Use compile-time initialized data templates instead of building the * reply on the fly. */ - switch (inet_proto_mask) { + switch (mask) { #ifdef HAS_IPV6 case INET_PROTO_MASK_IPV6: pf = (INET_PROTO_INFO *) mymalloc(sizeof(*pf)); @@ -253,11 +246,43 @@ INET_PROTO_INFO *inet_proto_init(const char *context, const char *protocols) pf->sa_family_list = make_uchar_vector(1, 0); break; default: - msg_panic("%s: bad inet_proto_mask 0x%x", myname, inet_proto_mask); + msg_panic("inet_proto: bad mask 0x%x", mask); } - if (inet_proto_table) - inet_proto_free(inet_proto_table); - return (inet_proto_table = pf); + return (info_by_mask[mask] = pf); +} + +/* inet_proto_lookup - ad-hoc mapping of protocol names to library inputs */ + +INET_PROTO_INFO *inet_proto_lookup(const char *context, const char *protocols) +{ + const char *myname = "inet_proto_lookup"; + int mask; + INET_PROTO_INFO *info; + + mask = name_mask_opt(context, proto_table, protocols, NAME_MASK_RETURN); + /* Warning logged by name_mask_opt */ + if (mask == 0) + return (0); + if ((info = info_by_mask[mask]) == 0) + info = init_mask(context, mask); + return info; +} + +/* inet_proto_init - default mapping of protocol names to library inputs */ + +INET_PROTO_INFO *inet_proto_init(const char *context, const char *protocols) +{ + const char *myname = "inet_proto_init"; + int mask; + INET_PROTO_INFO *info; + + mask = name_mask_opt(context, proto_table, protocols, NAME_MASK_RETURN); + if (mask == 0) + msg_fatal("%s: unsupported inet_protocols = %s", + myname, protocols); + if ((info = info_by_mask[mask]) == 0) + info = init_mask(context, mask); + return (inet_proto_table = info); } #ifdef TEST diff --git a/src/util/inet_proto.h b/src/util/inet_proto.h index 1fcc9db..e9d3783 100644 --- a/src/util/inet_proto.h +++ b/src/util/inet_proto.h @@ -33,6 +33,11 @@ typedef struct { extern INET_PROTO_INFO *inet_proto_init(const char *, const char *); extern INET_PROTO_INFO *inet_proto_table; + /* + * Ad-hoc lookups, that don't change the global default. + */ +extern INET_PROTO_INFO *inet_proto_lookup(const char *, const char *); + #define INET_PROTO_NAME_IPV6 "ipv6" #define INET_PROTO_NAME_IPV4 "ipv4" #define INET_PROTO_NAME_ALL "all" -- 1.9.3 (Apple Git-50)