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 <[email protected]>
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)