Hi guys,
I created a new version of this feature which does not include
changes in connection management code.
Furthermore this new version adds configuration parameter
"-o smtp_srv_resolution_allowed=yes" which enables it, so
only users interested in SRV resolution can use it.

Thanks for any review or help that you can provide.

On Tue, Aug 9, 2022 at 1:53 PM Tomas Korbar <tkor...@redhat.com> wrote:
>
> Hi guys,
> Thanks for your opinions and hints. I will try to come up with
> a implementation that does not involve changes in
> Postfix connection management code. smtp process was
> the first place where I thought that this feature could be
> implemented.
>
> On Tue, Aug 9, 2022 at 11:56 AM Wietse Venema <wie...@porcupine.org> wrote:
> >
> > Viktor Dukhovni:
> > > On Mon, Aug 08, 2022 at 05:06:22PM -0400, Viktor Dukhovni wrote:
> > >
> > > > > We're discussing support for an MUA-specific feature, not high-volime
> > > > > MTA-to-MTA support. Connection reuse is less important, as long as
> > > > > Postfix does not mix traffic with different authentication properties,
> > > > > and that is what SMTP_HOST_KEY is for. So if sharing is a consern,
> > > > > just add a "comes from SRV lookup" flag to the connection cache
> > > > > lookup key.
> > > > >
> > > > > > Are keys along the lines of "domain:submission+srv" too clumsy?
> > > >
> > > > I meant TLS policy lookup keys (smtp_tls_policy_maps).  The session and
> > > > connection caches are already fine, since transport name is part of the
> > > > cache key.
> > >
> > > Also, for the caches, in addition to not getting false positives from
> > > imprecise keys, we presumably actually want to get cache hits on the
> > > logical destination for connection reuse, which is less likely to happen
> > > if it splits into multiple separate nexthop values.
> >
> > Seriously, this is MUA submission, we don't need to optimize
> > connection reuse for that.
> >
> > > And perhaps reuse may not be appropriate when the logical nexthop
> > > destinations have different TLS policies, or different SASL settings,
> > > ... and yet share underlying submission servers.
> >
> > Some kind of grouping metadata can take care of that.
> >
> >         Wietse
> >
commit 222deebf825e12f7620abfbe84e94f47831e34cd
Author: Tomas Korbar <tkor...@redhat.com>
Date:   Thu Sep 1 15:32:02 2022 +0200

    Implement SRV resolution feature

diff --git a/src/dns/dns.h b/src/dns/dns.h
index 5f53dbc..84f1f6b 100644
--- a/src/dns/dns.h
+++ b/src/dns/dns.h
@@ -158,7 +158,9 @@ typedef struct DNS_RR {
     unsigned short class;		/* C_IN, etc. */
     unsigned int ttl;			/* always */
     unsigned int dnssec_valid;		/* DNSSEC validated */
-    unsigned short pref;		/* T_MX only */
+    unsigned short pref;		/* T_MX and T_SRV record related */
+    unsigned short weight;	/* T_SRV related, defined in rfc2782 */
+    unsigned short port;		/* T_SRV related, defined in rfc2782 */
     struct DNS_RR *next;		/* linkage */
     size_t  data_len;			/* actual data size */
     char    data[1];			/* actually a bunch of data */
@@ -186,11 +188,13 @@ extern char *dns_strrecord(VSTRING *, DNS_RR *);
 extern DNS_RR *dns_rr_create(const char *, const char *,
 			             ushort, ushort,
 			             unsigned, unsigned,
+			             unsigned, unsigned,
 			             const char *, size_t);
 extern void dns_rr_free(DNS_RR *);
 extern DNS_RR *dns_rr_copy(DNS_RR *);
 extern DNS_RR *dns_rr_append(DNS_RR *, DNS_RR *);
 extern DNS_RR *dns_rr_sort(DNS_RR *, int (*) (DNS_RR *, DNS_RR *));
+extern DNS_RR *dns_srv_rr_sort(DNS_RR *);
 extern int dns_rr_compare_pref_ipv6(DNS_RR *, DNS_RR *);
 extern int dns_rr_compare_pref_ipv4(DNS_RR *, DNS_RR *);
 extern int dns_rr_compare_pref_any(DNS_RR *, DNS_RR *);
@@ -295,6 +299,7 @@ extern int dns_get_h_errno(void);
   * Below is the precedence order. The order between DNS_RETRY and DNS_NOTFOUND
   * is arbitrary.
   */
+#define DNS_NULLSRV	(-8)		/* query ok, submission service unavailable */
 #define DNS_RECURSE	(-7)		/* internal only: recursion needed */
 #define DNS_NOTFOUND	(-6)		/* query ok, data not found */
 #define DNS_NULLMX	(-5)		/* query ok, service unavailable */
diff --git a/src/dns/dns_lookup.c b/src/dns/dns_lookup.c
index 1c12a88..b7029a8 100644
--- a/src/dns/dns_lookup.c
+++ b/src/dns/dns_lookup.c
@@ -740,6 +740,8 @@ static int dns_get_rr(DNS_RR **list, const char *orig_name, DNS_REPLY *reply,
     int     comp_len;
     ssize_t data_len;
     unsigned pref = 0;
+    unsigned weight = 0;
+    unsigned port = 0;
     unsigned char *src;
     unsigned char *dst;
     int     ch;
@@ -765,6 +767,18 @@ static int dns_get_rr(DNS_RR **list, const char *orig_name, DNS_REPLY *reply,
 	    return (DNS_INVAL);
 	data_len = strlen(temp) + 1;
 	break;
+	case T_SRV:
+	GETSHORT(pref, pos);
+	GETSHORT(weight, pos);
+	GETSHORT(port, pos);
+	if (dn_expand(reply->buf, reply->end, pos, temp, sizeof(temp)) < 0)
+	    return (DNS_RETRY);
+	if (*temp == 0)
+	    return (DNS_NULLSRV);
+	if (!valid_rr_name(temp, "resource data", fixed->type, reply))
+	    return (DNS_INVAL);
+	data_len = strlen(temp) + 1;
+	break;
     case T_MX:
 	GETSHORT(pref, pos);
 	if (dn_expand(reply->buf, reply->end, pos, temp, sizeof(temp)) < 0)
@@ -860,7 +874,7 @@ static int dns_get_rr(DNS_RR **list, const char *orig_name, DNS_REPLY *reply,
 	break;
     }
     *list = dns_rr_create(orig_name, rr_name, fixed->type, fixed->class,
-			  fixed->ttl, pref, tempbuf, data_len);
+			  fixed->ttl, pref, weight, port, tempbuf, data_len);
     return (DNS_OK);
 }
 
@@ -960,7 +974,7 @@ static int dns_get_answer(const char *orig_name, DNS_REPLY *reply, int type,
 		    resource_found++;
 		    rr->dnssec_valid = *maybe_secure ? reply->dnssec_ad : 0;
 		    *rrlist = dns_rr_append(*rrlist, rr);
-		} else if (status == DNS_NULLMX) {
+		} else if (status == DNS_NULLMX || status == DNS_NULLSRV) {
 		    CORRUPT(status);		/* TODO: use better name */
 		} else if (not_found_status != DNS_RETRY)
 		    not_found_status = status;
@@ -1094,6 +1108,12 @@ int     dns_lookup_x(const char *name, unsigned type, unsigned flags,
 				name);
 	    DNS_SET_H_ERRNO(&dns_res_state, NO_DATA);
 	    return (status);
+	case DNS_NULLSRV:
+		if (why)
+		vstring_sprintf(why, "Domain %s does not accept mail submission (Service not supported)",
+				name);
+	    DNS_SET_H_ERRNO(&dns_res_state, NO_DATA);
+	    return (status);
 	case DNS_OK:
 	    if (rrlist && dns_rr_filter_maps) {
 		if (dns_rr_filter_execute(rrlist) < 0) {
diff --git a/src/dns/dns_rr.c b/src/dns/dns_rr.c
index b550788..cf51b8f 100644
--- a/src/dns/dns_rr.c
+++ b/src/dns/dns_rr.c
@@ -49,6 +49,14 @@
 /*	DNS_RR	*dns_rr_remove(list, record)
 /*	DNS_RR	*list;
 /*	DNS_RR	*record;
+/*
+/*	static void weight_order(array, count)
+/*	DNS_RR **array;
+/*	int count;
+/*
+/*	DNS_RR *dns_srv_rr_sort(list)
+/*	DNS_RR *list;
+/*
 /* DESCRIPTION
 /*	The routines in this module maintain memory for DNS resource record
 /*	information, and maintain lists of DNS resource records.
@@ -81,6 +89,15 @@
 /*	dns_rr_remove() removes the specified record from the specified list.
 /*	The updated list is the result value.
 /*	The record MUST be a list member.
+/*
+/*	weight_order() sorts the members of the array of dns records according
+/*	to their weight as described in RFC2782. Function sorts n members
+/*	according to the count argument. It is used internally by the
+/*	dns_srv_rr_sort function.
+/*
+/*	dns_srv_rr_sort() Sorts list of dns SRV records according to their
+/*	priority and weight.
+/*
 /* LICENSE
 /* .ad
 /* .fi
@@ -113,6 +130,7 @@
 DNS_RR *dns_rr_create(const char *qname, const char *rname,
 		              ushort type, ushort class,
 		              unsigned int ttl, unsigned pref,
+		              unsigned weight, unsigned port,
 		              const char *data, size_t data_len)
 {
     DNS_RR *rr;
@@ -125,6 +143,8 @@ DNS_RR *dns_rr_create(const char *qname, const char *rname,
     rr->ttl = ttl;
     rr->dnssec_valid = 0;
     rr->pref = pref;
+    rr->weight = weight;
+    rr->port = port;
     if (data && data_len > 0)
 	memcpy(rr->data, data, data_len);
     rr->data_len = data_len;
@@ -345,3 +365,127 @@ DNS_RR *dns_rr_remove(DNS_RR *list, DNS_RR *record)
     }
     return (list);
 }
+
+static void weight_order(DNS_RR **array, int count) {
+    // compute sum of weights
+    int weight_sum = 0;
+    for (int i = 0; i < count; i++)
+        weight_sum += array[i]->weight;
+
+    // if weights are not supplied then we do not have to order records
+    if (weight_sum == 0)
+        return;
+
+    // first move records with weight 0 to the beginning
+    int swap_place = 0;
+    DNS_RR *temp;
+    for (int i = 0; i < count; i++) {
+        if (array[i]->weight == 0) {
+            temp = array[swap_place];
+            array[swap_place] = array[i];
+            array[i] = temp;
+            swap_place++;
+        }
+    }
+
+    int random;
+
+    unsigned int running_sums[count];
+    for (int i = 0; i < count-1; i++) {
+        running_sums[i] = array[i]->weight;
+        // calculate running sums of records
+        for (int x = i+1; x < count; x++)
+            running_sums[x] = array[x]->weight + running_sums[x-1];
+
+        random = myrand() % (weight_sum + 1);
+
+        // find first record that has running sum greater or equal to
+        // the random number
+        for (int k = i; k < count; k++) {
+            if (running_sums[k] >= random) {
+                weight_sum -= array[k]->weight;
+                temp = array[i];
+                array[i] = array[k];
+                array[k] = temp;
+                break;
+            }
+        }
+    }
+}
+
+/* dns_srv_rr_sort - sort resource record list */
+
+DNS_RR *dns_srv_rr_sort(DNS_RR *list) {
+    int     (*saved_user) (DNS_RR *, DNS_RR *);
+    DNS_RR **rr_array;
+    DNS_RR *rr;
+    int     len;
+    int     i;
+    int     r;
+
+    /*
+     * Save state and initialize.
+     */
+    saved_user = dns_rr_sort_user;
+    dns_rr_sort_user = dns_rr_compare_pref_any;
+
+    /*
+     * Build linear array with pointers to each list element.
+     */
+    for (len = 0, rr = list; rr != 0; len++, rr = rr->next)
+    /* void */ ;
+    rr_array = (DNS_RR **) mymalloc(len * sizeof(*rr_array));
+    for (len = 0, rr = list; rr != 0; len++, rr = rr->next)
+    rr_array[len] = rr;
+
+    /*
+     * Shuffle resource records. Every element has an equal chance of landing
+     * in slot 0.  After that every remaining element has an equal chance of
+     * landing in slot 1, ...  This is exactly n! states for n! permutations.
+     */
+    for (i = 0; i < len - 1; i++) {
+    r = i + (myrand() % (len - i));		/* Victor&Son */
+    rr = rr_array[i];
+    rr_array[i] = rr_array[r];
+    rr_array[r] = rr;
+    }
+
+    // first order the records by preference
+    qsort((void *) rr_array, len, sizeof(*rr_array), dns_rr_sort_callback);
+
+    int cur_pref=rr_array[0]->pref;
+    int left_bound = 0;
+    int right_bound = 0;
+
+    // walk through records and sort every partition with the same preference,
+    // according to their weight
+    for (int k = 1; k < len; k++) {
+        if (rr_array[k]->pref != cur_pref) {
+            if (left_bound != right_bound) {
+                weight_order(&rr_array[left_bound], right_bound - left_bound + 1);
+            }
+            cur_pref = rr_array[k]->pref;
+            left_bound = k;
+        }
+        right_bound = k;
+    }
+    // we need to check whether there is not one more partition to sort
+    if (left_bound != right_bound) {
+        weight_order(&rr_array[left_bound], right_bound - left_bound + 1);
+    }
+
+    /*
+     * Fix the links.
+     */
+    for (i = 0; i < len - 1; i++)
+    rr_array[i]->next = rr_array[i + 1];
+    rr_array[i]->next = 0;
+    list = rr_array[0];
+
+    /*
+     * Cleanup.
+     */
+    myfree((void *) rr_array);
+    dns_rr_sort_user = saved_user;
+    return (list);
+}
\ No newline at end of file
diff --git a/src/dns/dns_sa_to_rr.c b/src/dns/dns_sa_to_rr.c
index 6b9efcc..d7e6fee 100644
--- a/src/dns/dns_sa_to_rr.c
+++ b/src/dns/dns_sa_to_rr.c
@@ -55,12 +55,12 @@ DNS_RR *dns_sa_to_rr(const char *hostname, unsigned pref, struct sockaddr *sa)
 #define DUMMY_TTL	0
 
     if (sa->sa_family == AF_INET) {
-	return (dns_rr_create(hostname, hostname, T_A, C_IN, DUMMY_TTL, pref,
+	return (dns_rr_create(hostname, hostname, T_A, C_IN, DUMMY_TTL, pref, 0, 0,
 			      (char *) &SOCK_ADDR_IN_ADDR(sa),
 			      sizeof(SOCK_ADDR_IN_ADDR(sa))));
 #ifdef HAS_IPV6
     } else if (sa->sa_family == AF_INET6) {
-	return (dns_rr_create(hostname, hostname, T_AAAA, C_IN, DUMMY_TTL, pref,
+	return (dns_rr_create(hostname, hostname, T_AAAA, C_IN, DUMMY_TTL, pref, 0, 0,
 			      (char *) &SOCK_ADDR_IN6_ADDR(sa),
 			      sizeof(SOCK_ADDR_IN6_ADDR(sa))));
 #endif
diff --git a/src/dns/dns_strtype.c b/src/dns/dns_strtype.c
index 70e59ac..7eebe3c 100644
--- a/src/dns/dns_strtype.c
+++ b/src/dns/dns_strtype.c
@@ -180,6 +180,9 @@ static struct dns_type_map dns_type_map[] = {
 #ifdef T_ANY
     T_ANY, "ANY",
 #endif
+#ifdef T_SRV
+    T_SRV, "SRV",
+#endif
 };
 
 /* dns_strtype - translate DNS query type to string */
diff --git a/src/global/mail_params.h b/src/global/mail_params.h
index e728006..d819a4b 100644
--- a/src/global/mail_params.h
+++ b/src/global/mail_params.h
@@ -1172,6 +1172,12 @@ extern char *var_smtp_bind_addr6;
 #define DEF_LMTP_BIND_ADDR_ENFORCE	0
 extern bool var_smtp_bind_addr_enforce;
 
+#define VAR_SMTP_SRV_RESOLUTION_ALLOWED	"smtp_srv_resolution_allowed"
+#define DEF_SMTP_SRV_RESOLUTION_ALLOWED	0
+#define VAR_LMTP_SRV_RESOLUTION_ALLOWED	"lmtp_srv_resolution_allowed"
+#define DEF_LMTP_SRV_RESOLUTION_ALLOWED	0
+extern bool var_smtp_srv_resolution_allowed;
+
 #define VAR_SMTP_HELO_NAME	"smtp_helo_name"
 #define DEF_SMTP_HELO_NAME	"$myhostname"
 #define VAR_LMTP_HELO_NAME	"lmtp_lhlo_name"
diff --git a/src/smtp/smtp.c b/src/smtp/smtp.c
index 3ccbfe8..023a0cb 100644
--- a/src/smtp/smtp.c
+++ b/src/smtp/smtp.c
@@ -1091,6 +1091,7 @@ bool    var_smtp_sasl_auth_soft_bounce;
 
 char   *var_hfrom_format;
 bool var_smtp_bind_addr_enforce;
+bool var_smtp_srv_resolution_allowed;
 
  /*
   * Global variables.
diff --git a/src/smtp/smtp_addr.c b/src/smtp/smtp_addr.c
index 2b5c126..9a7f07b 100644
--- a/src/smtp/smtp_addr.c
+++ b/src/smtp/smtp_addr.c
@@ -17,6 +17,12 @@
 /*	char	*name;
 /*	int	misc_flags;
 /*	DSN_BUF	*why;
+/*
+/*	DNS_RR *smtp_submission_addr(host, misc_flags, why)
+/*	const char *host;
+/*	int misc_flags;
+/*	DSN_BUF *why;
+/*
 /* DESCRIPTION
 /*	This module implements Internet address lookups. By default,
 /*	lookups are done via the Internet domain name service (DNS).
@@ -44,6 +50,9 @@
 /*	host.  The host can be specified as a numerical Internet network
 /*	address, or as a symbolic host name.
 /*
+/*	smtp_submission_addr() performs lookup for SRV records of domain
+/*	dedicated to submission service.
+/*
 /*	Results from smtp_domain_addr() or smtp_host_addr() are
 /*	destroyed by dns_rr_free(), including null lists.
 /* DIAGNOSTICS
@@ -130,7 +139,7 @@ static void smtp_print_addr(const char *what, DNS_RR *addr_list)
 /* 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,
-			             unsigned pref, DSN_BUF *why)
+			             unsigned pref, unsigned port, DSN_BUF *why)
 {
     const char *myname = "smtp_addr_one";
     DNS_RR *addr = 0;
@@ -174,8 +183,10 @@ static DNS_RR *smtp_addr_one(DNS_RR *addr_list, const char *host, int res_opt,
 			     why->reason, DNS_REQ_FLAG_NONE,
 			     proto_info->dns_atype_list)) {
 	case DNS_OK:
-	    for (rr = addr; rr; rr = rr->next)
-		rr->pref = pref;
+	    for (rr = addr; rr; rr = rr->next) {
+			rr->pref = pref;
+			rr->port = port;
+		}
 	    addr_list = dns_rr_append(addr_list, addr);
 	    return (addr_list);
 	default:
@@ -293,10 +304,10 @@ static DNS_RR *smtp_addr_list(DNS_RR *mx_names, DSN_BUF *why)
      * tweaking the in-process resolver flags.
      */
     for (rr = mx_names; rr; rr = rr->next) {
-	if (rr->type != T_MX)
+	if (rr->type != T_MX && rr->type != T_SRV)
 	    msg_panic("smtp_addr_list: bad resource type: %d", rr->type);
 	addr_list = smtp_addr_one(addr_list, (char *) rr->data, res_opt,
-				  rr->pref, why);
+				  rr->pref, rr->port, why);
     }
     return (addr_list);
 }
@@ -678,7 +689,7 @@ DNS_RR *smtp_host_addr(const char *host, int misc_flags, DSN_BUF *why)
      * address to internal form. Otherwise, the host is specified by name.
      */
 #define PREF0	0
-    addr_list = smtp_addr_one((DNS_RR *) 0, ahost, res_opt, PREF0, why);
+    addr_list = smtp_addr_one((DNS_RR *) 0, ahost, res_opt, PREF0, 0, why);
     if (addr_list
 	&& (misc_flags & SMTP_MISC_FLAG_LOOP_DETECT)
 	&& smtp_find_self(addr_list) != 0) {
@@ -700,3 +711,52 @@ DNS_RR *smtp_host_addr(const char *host, int misc_flags, DSN_BUF *why)
 	smtp_print_addr(host, addr_list);
     return (addr_list);
 }
+
+/* smtp_submission_address - submission service lookup */
+
+DNS_RR *smtp_submission_addr(const char *host, int misc_flags, DSN_BUF *why) {
+  DNS_RR *srv_names = 0;
+  DNS_RR *addr_list = 0;
+  int r = 0;
+
+  dsb_reset(why);
+
+  if (smtp_dns_support == SMTP_DNS_DNSSEC) {
+    r |= RES_USE_DNSSEC;
+  }
+
+  switch (dns_lookup(host, T_SRV, r, &srv_names, (VSTRING *)0, why->reason)) {
+    case DNS_INVAL:
+      dsb_status(why, "5.4.4");
+      break;
+    case DNS_POLICY:
+      dsb_status(why, "4.7.0");
+      break;
+    case DNS_FAIL:
+      dsb_status(why, "5.4.3");
+      break;
+    case DNS_NULLSRV:
+      dsb_status(why, "5.1.0");
+      break;
+    case DNS_OK:
+      /* At first we must sort the rr records according to their priority */
+      srv_names = dns_srv_rr_sort(srv_names);
+      addr_list = smtp_addr_list(srv_names, why);
+      dns_rr_free(srv_names);
+      if (addr_list == 0) {
+        msg_warn("no SRV host for %s has a valid address record", host);
+        break;
+      }
+      if (misc_flags & SMTP_MISC_FLAG_LOOP_DETECT && smtp_find_self(addr_list) != 0) {
+        dns_rr_free(addr_list);
+        dsb_simple(why, "5.4.6", "mail for %s loops back to myself", host);
+        return (0);
+      }
+      break;
+    default:
+      dsb_status(why, "4.4.3");
+      break;
+  }
+
+  return (addr_list);
+}
\ No newline at end of file
diff --git a/src/smtp/smtp_addr.h b/src/smtp/smtp_addr.h
index 8f20961..751aca2 100644
--- a/src/smtp/smtp_addr.h
+++ b/src/smtp/smtp_addr.h
@@ -18,6 +18,7 @@
   */
 extern DNS_RR *smtp_host_addr(const char *, int, DSN_BUF *);
 extern DNS_RR *smtp_domain_addr(const char *, DNS_RR **, int, DSN_BUF *, int *);
+extern DNS_RR *smtp_submission_addr(const char *, int, DSN_BUF *);
 
 /* LICENSE
 /* .ad
diff --git a/src/smtp/smtp_connect.c b/src/smtp/smtp_connect.c
index ed58180..6cacddf 100644
--- a/src/smtp/smtp_connect.c
+++ b/src/smtp/smtp_connect.c
@@ -30,7 +30,11 @@
 /*
 /*	With SMTP, the Internet domain name service is queried for mail
 /*	exchanger hosts. Quote the domain name with `[' and `]' to
-/*	suppress mail exchanger lookups.
+/*	suppress mail exchanger lookups. Another feature is that
+/*	_submission._tcp prefix in `[]' brackets triggers lookup for
+/*	submission service SRV records and picking appropriate submission
+/*	server according to SRV records priority and weight
+/*	(eg. [_submission._tcp.domain]).
 /*
 /*	Numerical address information should always be quoted with `[]'.
 /* DIAGNOSTICS
@@ -785,6 +789,39 @@ static int smtp_reuse_session(SMTP_STATE *state, DNS_RR **addr_list,
     return (session_count);
 }
 
+/* transform_srv_sites - identify SRV sites and transform them*/
+
+static ARGV *transform_srv_sites(ARGV *sites, SMTP_STATE *state) {
+    ARGV *new_sites = argv_alloc(sites->argc);
+    for (int i = 0; i < sites->argc; i++) {
+        if (strncmp("[_submission._tcp", sites->argv[i], 17) == 0) {
+            char *site = mystrdup(sites->argv[i]);
+            // truncate the second ]
+            site[strlen(site) - 1] = 0;
+            DNS_RR *srv_list = smtp_submission_addr(site + 1, state->misc_flags,
+                                                    state->why);
+            myfree(site);
+            // if this site has no srv record then treat it as normal
+            // domain name
+            if (srv_list) {
+                MAI_HOSTADDR_STR printable_address;
+                char translated_host[55];
+                for (DNS_RR *rec = srv_list; rec; rec = rec->next) {
+                    dns_rr_to_pa(rec, &printable_address);
+                    sprintf(translated_host, "[%s]:%hu",
+                            printable_address.buf, rec->port);
+                    argv_split_append(new_sites, translated_host, CHARS_COMMA_SP);
+                }
+                dns_rr_free(srv_list);
+                continue;
+            }
+        }
+        argv_split_append(new_sites, sites->argv[i], CHARS_COMMA_SP);
+    }
+    argv_free(sites);
+    return new_sites;
+}
+
 /* smtp_connect_inet - establish network connection */
 
 static void smtp_connect_inet(SMTP_STATE *state, const char *nexthop,
@@ -816,6 +853,11 @@ static void smtp_connect_inet(SMTP_STATE *state, const char *nexthop,
     sites = argv_split(nexthop, CHARS_COMMA_SP);
     if (sites->argc == 0)
 	msg_panic("null destination: \"%s\"", nexthop);
+
+    if (var_smtp_srv_resolution_allowed) {
+        sites = transform_srv_sites(sites, state);
+    }
+
     non_fallback_sites = sites->argc;
     argv_split_append(sites, var_fallback_relay, CHARS_COMMA_SP);
 
diff --git a/src/smtp/smtp_params.c b/src/smtp/smtp_params.c
index cd54f8f..16e1bae 100644
--- a/src/smtp/smtp_params.c
+++ b/src/smtp/smtp_params.c
@@ -132,6 +132,7 @@
 	VAR_SMTP_DUMMY_MAIL_AUTH, DEF_SMTP_DUMMY_MAIL_AUTH, &var_smtp_dummy_mail_auth,
 	VAR_SMTP_BALANCE_INET_PROTO, DEF_SMTP_BALANCE_INET_PROTO, &var_smtp_balance_inet_proto,
 	VAR_SMTP_BIND_ADDR_ENFORCE, DEF_SMTP_BIND_ADDR_ENFORCE, &var_smtp_bind_addr_enforce,
+	VAR_SMTP_SRV_RESOLUTION_ALLOWED, DEF_SMTP_SRV_RESOLUTION_ALLOWED, &var_smtp_srv_resolution_allowed,
 	0,
     };
     static const CONFIG_NBOOL_TABLE smtp_nbool_table[] = {
diff --git a/src/smtpd/smtpd_check.c b/src/smtpd/smtpd_check.c
index 2785ce1..274cbb2 100644
--- a/src/smtpd/smtpd_check.c
+++ b/src/smtpd/smtpd_check.c
@@ -3064,7 +3064,7 @@ static int check_server_access(SMTPD_STATE *state, const char *table,
 	|| type == T_AAAA
 #endif
 	) {
-	server_list = dns_rr_create(domain, domain, T_MX, C_IN, 0, 0,
+	server_list = dns_rr_create(domain, domain, T_MX, C_IN, 0, 0, 0, 0,
 				    domain, strlen(domain) + 1);
     } else {
 	dns_status = dns_lookup(domain, type, 0, &server_list,
@@ -3073,7 +3073,7 @@ static int check_server_access(SMTPD_STATE *state, const char *table,
 	    return (SMTPD_CHECK_DUNNO);
 	if (dns_status == DNS_NOTFOUND /* Not: h_errno == NO_DATA */ ) {
 	    if (type == T_MX) {
-		server_list = dns_rr_create(domain, domain, type, C_IN, 0, 0,
+		server_list = dns_rr_create(domain, domain, type, C_IN, 0, 0, 0, 0,
 					    domain, strlen(domain) + 1);
 		dns_status = DNS_OK;
 	    } else if (type == T_NS /* && h_errno == NO_DATA */ ) {

Reply via email to