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)

Reply via email to