diff --git a/pflogsumm-1.1.5-datecalc.patch b/pflogsumm-1.1.5-datecalc.patch new file mode 100644 index 0000000..77ca806 --- /dev/null +++ b/pflogsumm-1.1.5-datecalc.patch @@ -0,0 +1,15 @@ +diff --git pflogsumm-1.1.5/pflogsumm.pl pflogsumm-1.1.5/pflogsumm.pl +index 31de5bd..36384dd 100755 +--- pflogsumm-1.1.5/pflogsumm.pl ++++ pflogsumm-1.1.5/pflogsumm.pl +@@ -398,8 +398,8 @@ Copyright (C) 1998-2010 by James S. Seymour, Release 1.1.5 + use strict; + use locale; + use Getopt::Long; +-eval { require Date::Calc }; +-my $hasDateCalc = $@ ? 0 : 1; ++require Date::Calc; ++my $hasDateCalc = 1; + + my $mailqCmd = "mailq"; + my $release = "1.1.5"; diff --git a/pflogsumm-1.1.5-ipv6-warnings-fix.patch b/pflogsumm-1.1.5-ipv6-warnings-fix.patch new file mode 100644 index 0000000..51e759e --- /dev/null +++ b/pflogsumm-1.1.5-ipv6-warnings-fix.patch @@ -0,0 +1,13 @@ +diff --git pflogsumm-1.1.5/pflogsumm.pl pflogsumm-1.1.5/pflogsumm.pl +index 36384dd..eb527d0 100755 +--- pflogsumm-1.1.5/pflogsumm.pl ++++ pflogsumm-1.1.5/pflogsumm.pl +@@ -1536,7 +1536,7 @@ sub gimme_domain { + # split domain/ipaddr into separates + # newer versions of Postfix have them "dom.ain[i.p.add.ress]" + # older versions of Postfix have them "dom.ain/i.p.add.ress" +- unless((($domain, $ipAddr) = /^([^\[]+)\[((?:\d{1,3}\.){3}\d{1,3})\]/) == 2 || ++ unless((($domain, $ipAddr) = /^([^\[]+)\[((?:\d{1,3}\.){3}\d{1,3}|[0-9a-f:]+)\]/) == 2 || + (($domain, $ipAddr) = /^([^\/]+)\/([0-9a-f.:]+)/i) == 2) { + # more exhaustive method + ($domain, $ipAddr) = /^([^\[\(\/]+)[\[\(\/]([^\]\)]+)[\]\)]?:?\s*$/; diff --git a/pflogsumm-1.1.6-syslog-name-underscore-fix.patch b/pflogsumm-1.1.5-syslog-name-underscore-fix.patch similarity index 65% rename from pflogsumm-1.1.6-syslog-name-underscore-fix.patch rename to pflogsumm-1.1.5-syslog-name-underscore-fix.patch index d5e461d..9279bcc 100644 --- a/pflogsumm-1.1.6-syslog-name-underscore-fix.patch +++ b/pflogsumm-1.1.5-syslog-name-underscore-fix.patch @@ -1,7 +1,7 @@ -diff --git a/pflogsumm b/pflogsumm -index cb3b8a9..35309f0 100755 ---- a/pflogsumm -+++ b/pflogsumm +diff --git a/pflogsumm-1.1.5/pflogsumm.pl b/pflogsumm-1.1.5/pflogsumm.pl +index eb527d0..7e26206 100755 +--- a/pflogsumm-1.1.5/pflogsumm.pl ++++ b/pflogsumm-1.1.5/pflogsumm.pl @@ -503,7 +503,12 @@ $usageMsg = # Accept either "_"s or "-"s in --switches foreach (@ARGV) { diff --git a/postfix-3.9.0-alternatives.patch b/postfix-3.3.3-alternatives.patch similarity index 85% rename from postfix-3.9.0-alternatives.patch rename to postfix-3.3.3-alternatives.patch index d384239..2c3ba18 100644 --- a/postfix-3.9.0-alternatives.patch +++ b/postfix-3.3.3-alternatives.patch @@ -1,8 +1,8 @@ diff --git a/conf/post-install b/conf/post-install -index ed7c79a..c647f81 100644 +index 25ef7e6..4fd6434 100644 --- a/conf/post-install +++ b/conf/post-install -@@ -541,6 +541,18 @@ test -n "$create" && { +@@ -537,6 +537,17 @@ test -n "$create" && { case $path in no|no/*) continue;; esac @@ -15,7 +15,6 @@ index ed7c79a..c647f81 100644 + /usr/share/man/man1/mailq.1.gz) path=/usr/share/man/man1/mailq.postfix.1.gz ;; + /usr/share/man/man1/newaliases.1.gz) path=/usr/share/man/man1/newaliases.postfix.1.gz ;; + /usr/share/man/man5/aliases.5.gz) path=/usr/share/man/man5/aliases.postfix.5.gz ;; -+ /usr/share/man/man8/smtp.8.gz) path=/usr/share/man/man8/smtp.postfix.8.gz ;; + /usr/share/man/man8/smtpd.8.gz) path=/usr/share/man/man8/smtpd.postfix.8.gz ;; + esac # Pick up the flags. diff --git a/postfix-3.9.0-files.patch b/postfix-3.4.0-files.patch similarity index 84% rename from postfix-3.9.0-files.patch rename to postfix-3.4.0-files.patch index 03ae645..ad73553 100644 --- a/postfix-3.9.0-files.patch +++ b/postfix-3.4.0-files.patch @@ -1,8 +1,8 @@ diff --git a/conf/postfix-files b/conf/postfix-files -index 5a93982..b843fec 100644 +index 4ed9d1f..19711d2 100644 --- a/conf/postfix-files +++ b/conf/postfix-files -@@ -84,7 +84,6 @@ $shlib_directory/${LIB_PREFIX}sqlite${LIB_SUFFIX}:f:root:-:755 +@@ -83,7 +83,6 @@ $shlib_directory/${LIB_PREFIX}sqlite${LIB_SUFFIX}:f:root:-:755 $meta_directory/dynamicmaps.cf.d:d:root:-:755 $meta_directory/dynamicmaps.cf:f:root:-:644 $meta_directory/main.cf.proto:f:root:-:644 @@ -10,7 +10,7 @@ index 5a93982..b843fec 100644 $meta_directory/master.cf.proto:f:root:-:644 $meta_directory/postfix-files.d:d:root:-:755 $meta_directory/postfix-files:f:root:-:644 -@@ -142,18 +141,13 @@ $command_directory/postqueue:f:root:$setgid_group:2755:u +@@ -141,18 +140,13 @@ $command_directory/postqueue:f:root:$setgid_group:2755:u $sendmail_path:f:root:-:755 $newaliases_path:l:$sendmail_path $mailq_path:l:$sendmail_path @@ -29,7 +29,7 @@ index 5a93982..b843fec 100644 $config_directory/main.cf:f:root:-:644:p $config_directory/master.cf:f:root:-:644:p $config_directory/pcre_table:f:root:-:644:o -@@ -166,8 +160,8 @@ $config_directory/postfix-script:f:root:-:755:o +@@ -165,8 +159,8 @@ $config_directory/postfix-script:f:root:-:755:o $config_directory/postfix-script-sgid:f:root:-:755:o $config_directory/postfix-script-nosgid:f:root:-:755:o $config_directory/post-install:f:root:-:755:o @@ -40,7 +40,7 @@ index 5a93982..b843fec 100644 $manpage_directory/man1/postalias.1:f:root:-:644 $manpage_directory/man1/postcat.1:f:root:-:644 $manpage_directory/man1/postconf.1:f:root:-:644 -@@ -181,9 +175,9 @@ $manpage_directory/man1/postmap.1:f:root:-:644 +@@ -180,9 +174,9 @@ $manpage_directory/man1/postmap.1:f:root:-:644 $manpage_directory/man1/postmulti.1:f:root:-:644 $manpage_directory/man1/postqueue.1:f:root:-:644 $manpage_directory/man1/postsuper.1:f:root:-:644 @@ -52,13 +52,11 @@ index 5a93982..b843fec 100644 $manpage_directory/man5/body_checks.5:f:root:-:644 $manpage_directory/man5/bounce.5:f:root:-:644 $manpage_directory/man5/canonical.5:f:root:-:644 -@@ -231,8 +225,8 @@ $manpage_directory/man8/qmgr.8:f:root:-:644 - $manpage_directory/man8/qmqpd.8:f:root:-:644 +@@ -230,7 +224,7 @@ $manpage_directory/man8/qmqpd.8:f:root:-:644 $manpage_directory/man8/scache.8:f:root:-:644 $manpage_directory/man8/showq.8:f:root:-:644 --$manpage_directory/man8/smtp.8:f:root:-:644 + $manpage_directory/man8/smtp.8:f:root:-:644 -$manpage_directory/man8/smtpd.8:f:root:-:644 -+$manpage_directory/man8/smtp.postfix.8:f:root:-:644 +$manpage_directory/man8/smtpd.postfix.8:f:root:-:644 $manpage_directory/man8/spawn.8:f:root:-:644 $manpage_directory/man8/tlsproxy.8:f:root:-:644 diff --git a/postfix-3.10.5-config.patch b/postfix-3.7.0-config.patch similarity index 91% rename from postfix-3.10.5-config.patch rename to postfix-3.7.0-config.patch index c09a76d..d8be512 100644 --- a/postfix-3.10.5-config.patch +++ b/postfix-3.7.0-config.patch @@ -1,5 +1,5 @@ diff --git a/conf/main.cf b/conf/main.cf -index 3687c85..ae3af9e 100644 +index 47de434..112c1f1 100644 --- a/conf/main.cf +++ b/conf/main.cf @@ -136,6 +136,10 @@ mail_owner = postfix @@ -40,13 +40,11 @@ index 3687c85..ae3af9e 100644 #alias_database = hash:/etc/aliases, hash:/opt/majordomo/aliases # ADDRESS EXTENSIONS (e.g., user+foo) -@@ -487,8 +491,28 @@ unknown_local_recipient_reject_code = 550 - # non-UNIX accounts with "User unknown in local recipient table". +@@ -488,7 +492,27 @@ unknown_local_recipient_reject_code = 550 # # Cyrus IMAP over LMTP. Specify ``lmtpunix cmd="lmtpd" --# listen="/var/imap/socket/lmtp" prefork=0'' in cyrus.conf. + # listen="/var/imap/socket/lmtp" prefork=0'' in cyrus.conf. -#mailbox_transport = lmtp:unix:/var/imap/socket/lmtp -+# listen="/var/lib/imap/socket/lmtp" prefork=0'' in cyrus.conf. +#mailbox_transport = lmtp:unix:/var/lib/imap/socket/lmtp + +# If using the cyrus-imapd IMAP server deliver local mail to the IMAP @@ -81,13 +79,11 @@ index 3687c85..ae3af9e 100644 #fallback_transport = # The luser_relay parameter specifies an optional destination address -@@ -682,6 +705,43 @@ sample_directory = +@@ -682,4 +705,41 @@ sample_directory = # readme_directory: The location of the Postfix README files. # readme_directory = -inet_protocols = ipv4 - shlib_directory = /usr/lib/postfix/${mail_version} - meta_directory = /etc/postfix + +# TLS CONFIGURATION +# @@ -127,10 +123,10 @@ index 3687c85..ae3af9e 100644 +# +smtp_tls_security_level = may diff --git a/conf/master.cf b/conf/master.cf -index 5abe06c..81ae929 100644 +index 83fc6fd..bb0eae9 100644 --- a/conf/master.cf +++ b/conf/master.cf -@@ -116,14 +116,14 @@ postlog unix-dgram n - n - 1 postlogd +@@ -110,14 +110,14 @@ postlog unix-dgram n - n - 1 postlogd # Also specify in main.cf: cyrus_destination_recipient_limit=1 # #cyrus unix - n n - - pipe diff --git a/postfix-3.7.0-whitespace-name-fix.patch b/postfix-3.7.0-whitespace-name-fix.patch new file mode 100644 index 0000000..e6a1db4 --- /dev/null +++ b/postfix-3.7.0-whitespace-name-fix.patch @@ -0,0 +1,13 @@ +diff --git a/src/cleanup/cleanup_message.c b/src/cleanup/cleanup_message.c +index f2b86d0..018e39e 100644 +--- a/src/cleanup/cleanup_message.c ++++ b/src/cleanup/cleanup_message.c +@@ -772,6 +772,8 @@ static void cleanup_header_done_callback(void *context) + /* Normalize whitespace. */ + token = tok822_scan_limit(state->fullname, &dummy_token, + var_token_limit); ++ if (!token) ++ token = tok822_alloc(TOK822_QSTRING, state->fullname); + } else { + token = tok822_alloc(TOK822_QSTRING, state->fullname); + } diff --git a/postfix-3.7.9-SRV-resolve.patch b/postfix-3.7.9-SRV-resolve.patch new file mode 100644 index 0000000..48171c3 --- /dev/null +++ b/postfix-3.7.9-SRV-resolve.patch @@ -0,0 +1,1584 @@ +diff --git a/mantools/postlink b/mantools/postlink +index 15306d9..70ff038 100755 +--- a/mantools/postlink ++++ b/mantools/postlink +@@ -1161,6 +1161,10 @@ while (<>) { + s;\blocal_login_sender_maps\b;$&;g; + s;\bempty_address_local_login_sender_maps_lookup_key\b;$&;g; + ++ s;\buse_srv_lookup\b;$&;g; ++ s;\ballow_srv_lookup_fallback\b;$&;g; ++ s;\bignore_srv_lookup_error\b;$&;g; ++ + # Service-defined parameters... + + s;\bpolicy_time_limit\b;$&;g; +diff --git a/proto/postconf.proto b/proto/postconf.proto +index 787e01c..6c214e7 100644 +--- a/proto/postconf.proto ++++ b/proto/postconf.proto +@@ -18599,3 +18599,110 @@ to enable. This feature is enabled by default with Postfix ≥ + +

This feature is available in Postfix ≥ 3.9, 3.8.1, 3.7.6, + 3.6.10, and 3.5.20.

++ ++%PARAM use_srv_lookup ++ ++

Enables discovery for the specified service(s) using DNS SRV ++records. For example, with "use_srv_lookup = submission" and ++"relayhost = example.com:submission", the Postfix SMTP client will ++look up DNS SRV records for _submission._tcp.example.com, and will ++relay email through the hosts and ports that are specified with ++those records. See RFC 2782 for details of the host selection ++process.

++ ++

Specify zero or more service names separated by comma and/or ++whitespace. Any name in the services(5) database may be specified, ++though in practice only submission, submissions, and smtp make ++sense.

++ ++

When SRV record lookup is enabled with use_srv_lookup, you can ++enclose a domain name in "[]" to force IP address lookup instead ++of SRV record lookup.

++ ++

Example 1: MUA-to-MTA submission using SRV record lookup for ++the "submission" service for domain "example.com". This uses the ++default SMTP delivery agent with STARTTLS, and looks up SRV records ++for "_submission._tcp.example.com".

++ ++
++/etc/postfix/main.cf:
++    use_srv_lookup = submission
++    relayhost = example.com:submission
++    smtp_tls_security_level = may
++    ...see SASL_README for sasl configuration...
++
++ ++

Example 2: MUA-to-MTA submission using SRV record lookup for ++the "submissions" service for domain "example.org". This uses a ++dedicated SMTP delivery agent (smtp-wraptls) with tls_wrappermode ++turned on, and looks up SRV records for "_submissions._tcp.example.org". ++

++ ++

Note: specify the older name "smtps" instead of "submissions" ++when a provider has DNS SRV records like "_smtps._tcp.example.org" ++instead of "_submissions._tcp.example.org".

++ ++
++/etc/postfix/main.cf:
++    use_srv_lookup = submissions
++    default_transport = smtp-wraptls:example.org:submissions
++    ...see SASL_README for sasl configuration...
++
++ ++
++/etc/postfix/master.cf:
++    smtp-wraptls   unix   ...   ...   ...   ...   ...   smtp
++        -o { smtp_tls_wrappermode = yes }
++        -o { smtp_tls_security_level = encrypt }
++
++ ++

Example 3: Sender-dependent selection for a combination of ++MUA-to-MTA submission services. This combines examples 1 and 2 with ++examples of how to disable SRV and look up IP address records for ++"smtp-relay.example.net" and "smtp-relay.other.example". Again, ++specify the older name "smtps" instead of "submissions" when a ++provider has DNS SRV records like "_smtps._tcp.example.org" instead ++of "_submissions._tcp.example.org".

++ ++
++/etc/postfix/main.cf:
++    use_srv_lookup = submission, submissions
++    sender_dependent_default_transport_maps = inline:{
++        # Destinations that support SRV record lookup.
++        { user1@example.com = smtp:example.com:submission }
++        { user2@example.org = smtp-wraptls:example.org:submissions }
++        # Use [destination] to force IP address lookups.
++        { user3@example.net = smtp:[smtp-relay.example.net]:submission }
++        { user4@other.example =
++              smtp-wraptls:[smtp-relay.other.example]:submissions } }
++    ...see SASL_README for sasl configuration...
++
++ ++

Example 4: MTA-to-MTA traffic, using SRV record lookup for the ++SMTP service. This is useful for Postfix tests, and may be useful ++in environments where ports are dynamically assigned to servers. ++

++ ++
++/etc/postfix/main.cf:
++    use_srv_lookup = smtp
++    # Fall back to MX record lookup when SRV records are unavailable.
++    #allow_srv_lookup_fallback = yes
++    #ignore_srv_lookup_error = yes
++
++ ++

This feature was backported from Postfix 3.8.

++ ++%PARAM ignore_srv_lookup_error no ++ ++

When SRV record lookup fails, fall back to MX or IP address ++lookup as if SRV record lookup was not enabled.

++ ++

This feature was backported from Postfix 3.8.

++ ++%PARAM allow_srv_lookup_fallback no ++ ++

When SRV record lookup fails or no SRV record exists, fall back ++to MX or IP address lookup as if SRV record lookup was not enabled.

++ ++

This feature was backported from Postfix 3.8.

+diff --git a/src/dns/dns.h b/src/dns/dns.h +index 5f53dbc..0c1dcb3 100644 +--- a/src/dns/dns.h ++++ b/src/dns/dns.h +@@ -158,10 +158,12 @@ 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 */ ++ char *data; /* a bunch of data */ + } DNS_RR; + + /* +@@ -183,14 +185,29 @@ extern char *dns_strrecord(VSTRING *, DNS_RR *); + /* + * dns_rr.c + */ ++#define DNS_RR_NOPREF (0) ++#define DNS_RR_NOWEIGHT (0) ++#define DNS_RR_NOPORT (0) ++ ++#define dns_rr_create_noport(qname, rname, type, class, ttl, pref, data, \ ++ data_len) \ ++ dns_rr_create((qname), (rname), (type), (class), (ttl), \ ++ (pref), DNS_RR_NOWEIGHT, DNS_RR_NOPORT, (data), (data_len)) ++ ++#define dns_rr_create_nopref(qname, rname, type, class, ttl, data, data_len) \ ++ dns_rr_create_noport((qname), (rname), (type), (class), (ttl), \ ++ DNS_RR_NOPREF, (data), (data_len)) ++ + 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,8 +312,9 @@ extern int dns_get_h_errno(void); + * Below is the precedence order. The order between DNS_RETRY and DNS_NOTFOUND + * is arbitrary. + */ +-#define DNS_RECURSE (-7) /* internal only: recursion needed */ +-#define DNS_NOTFOUND (-6) /* query ok, data not found */ ++#define DNS_RECURSE (-8) /* internal only: recursion needed */ ++#define DNS_NOTFOUND (-7) /* query ok, data not found */ ++#define DNS_NULLSRV (-6) /* query ok, service unavailable */ + #define DNS_NULLMX (-5) /* query ok, service unavailable */ + #define DNS_FAIL (-4) /* query failed, don't retry */ + #define DNS_INVAL (-3) /* query ok, malformed reply */ +diff --git a/src/dns/dns_lookup.c b/src/dns/dns_lookup.c +index 615902d..18d6916 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 support SRV requests", ++ 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..15b5dee 100644 +--- a/src/dns/dns_rr.c ++++ b/src/dns/dns_rr.c +@@ -7,13 +7,15 @@ + /* #include + /* + /* DNS_RR *dns_rr_create(qname, rname, type, class, ttl, preference, +-/* data, data_len) ++/* weight, port, data, data_len) + /* const char *qname; + /* const char *rname; + /* unsigned short type; + /* unsigned short class; + /* unsigned int ttl; + /* unsigned preference; ++/* unsigned weight; ++/* unsigned port; + /* const char *data; + /* size_t data_len; + /* +@@ -49,6 +51,30 @@ + /* DNS_RR *dns_rr_remove(list, record) + /* DNS_RR *list; + /* DNS_RR *record; ++/* ++/* DNS_RR *dns_srv_rr_sort(list) ++/* DNS_RR *list; ++/* AUXILIARY FUNCTIONS ++/* DNS_RR *dns_rr_create_nopref(qname, rname, type, class, ttl, ++/* data, data_len) ++/* const char *qname; ++/* const char *rname; ++/* unsigned short type; ++/* unsigned short class; ++/* unsigned int ttl; ++/* const char *data; ++/* size_t data_len; ++/* ++/* DNS_RR *dns_rr_create_noport(qname, rname, type, class, ttl, ++/* preference, data, data_len) ++/* const char *qname; ++/* const char *rname; ++/* unsigned short type; ++/* unsigned short class; ++/* unsigned int ttl; ++/* unsigned preference; ++/* const char *data; ++/* size_t data_len; + /* DESCRIPTION + /* The routines in this module maintain memory for DNS resource record + /* information, and maintain lists of DNS resource records. +@@ -56,10 +82,14 @@ + /* dns_rr_create() creates and initializes one resource record. + /* The \fIqname\fR field specifies the query name. + /* The \fIrname\fR field specifies the reply name. +-/* \fIpreference\fR is used for MX records; \fIdata\fR is a null ++/* \fIpreference\fR is used for MX and SRV records; \fIweight\fR ++/* and \fIport\fR are used for SRV records; \fIdata\fR is a null + /* pointer or specifies optional resource-specific data; + /* \fIdata_len\fR is the amount of resource-specific data. + /* ++/* dns_rr_create_nopref() and dns_rr_create_noport() are convenience ++/* wrappers around dns_rr_create() that take fewer arguments. ++/* + /* dns_rr_free() releases the resource used by of zero or more + /* resource records. + /* +@@ -81,6 +111,9 @@ + /* 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. ++/* ++/* dns_srv_rr_sort() sorts a list of SRV records according to ++/* their priority and weight as described in RFC 2782. + /* LICENSE + /* .ad + /* .fi +@@ -113,11 +146,15 @@ + 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; + +- rr = (DNS_RR *) mymalloc(sizeof(*rr) + data_len - 1); ++ /* ++ * Note: if this function is changed, update dns_rr_copy(). ++ */ ++ rr = (DNS_RR *) mymalloc(sizeof(*rr)); + rr->qname = mystrdup(qname); + rr->rname = mystrdup(rname); + rr->type = type; +@@ -125,8 +162,14 @@ DNS_RR *dns_rr_create(const char *qname, const char *rname, + rr->ttl = ttl; + rr->dnssec_valid = 0; + rr->pref = pref; +- if (data && data_len > 0) ++ rr->weight = weight; ++ rr->port = port; ++ if (data_len != 0) { ++ rr->data = mymalloc(data_len); + memcpy(rr->data, data, data_len); ++ } else { ++ rr->data = 0; ++ } + rr->data_len = data_len; + rr->next = 0; + return (rr); +@@ -141,6 +184,8 @@ void dns_rr_free(DNS_RR *rr) + dns_rr_free(rr->next); + myfree(rr->qname); + myfree(rr->rname); ++ if (rr->data) ++ myfree(rr->data); + myfree((void *) rr); + } + } +@@ -149,16 +194,17 @@ void dns_rr_free(DNS_RR *rr) + + DNS_RR *dns_rr_copy(DNS_RR *src) + { +- ssize_t len = sizeof(*src) + src->data_len - 1; + DNS_RR *dst; + + /* +- * Combine struct assignment and data copy in one block copy operation. ++ * Note: struct copy, because dns_rr_create() would not copy all fields. + */ +- dst = (DNS_RR *) mymalloc(len); +- memcpy((void *) dst, (void *) src, len); ++ dst = (DNS_RR *) mymalloc(sizeof(*dst)); ++ *dst = *src; + dst->qname = mystrdup(src->qname); + dst->rname = mystrdup(src->rname); ++ if (dst->data) ++ dst->data = mymemdup(src->data, src->data_len); + dst->next = 0; + return (dst); + } +@@ -247,6 +293,12 @@ DNS_RR *dns_rr_sort(DNS_RR *list, int (*compar) (DNS_RR *, DNS_RR *)) + int len; + int i; + ++ /* ++ * Avoid mymalloc() panic. ++ */ ++ if (list == 0) ++ return (list); ++ + /* + * Save state and initialize. + */ +@@ -293,6 +345,12 @@ DNS_RR *dns_rr_shuffle(DNS_RR *list) + int i; + int r; + ++ /* ++ * Avoid mymalloc() panic. ++ */ ++ if (list == 0) ++ return (list); ++ + /* + * Build linear array with pointers to each list element. + */ +@@ -345,3 +403,141 @@ DNS_RR *dns_rr_remove(DNS_RR *list, DNS_RR *record) + } + return (list); + } ++ ++/* weight_order - sort equal-priority records by weight */ ++ ++static void weight_order(DNS_RR **array, int count) ++{ ++ int unordered_weights; ++ int i; ++ ++ /* ++ * Compute the sum of record weights. If weights are not supplied then ++ * this function would be a noop. In fact this would be a noop when all ++ * weights have the same value, whether that weight is zero or not. There ++ * is no need to give special treatment to zero weights. ++ */ ++ for (unordered_weights = 0, i = 0; i < count; i++) ++ unordered_weights += array[i]->weight; ++ if (unordered_weights == 0) ++ return; ++ ++ /* ++ * The record ordering code below differs from RFC 2782 when the input ++ * contains a mix of zero and non-zero weights: the code below does not ++ * give special treatment to zero weights. Instead, it treats a zero ++ * weight just like any other small weight. Fewer special cases make for ++ * code that is simpler and more robust. ++ */ ++ for (i = 0; i < count - 1; i++) { ++ int running_sum; ++ int threshold; ++ int k; ++ DNS_RR *temp; ++ ++ /* ++ * Choose a random threshold [0..unordered_weights] inclusive. ++ */ ++ threshold = myrand() % (unordered_weights + 1); ++ ++ /* ++ * Move the first record with running_sum >= threshold to the ordered ++ * list, and update unordered_weights. ++ */ ++ for (running_sum = 0, k = i; k < count; k++) { ++ running_sum += array[k]->weight; ++ if (running_sum >= threshold) { ++ unordered_weights -= 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; ++ int cur_pref; ++ int left_bound; /* inclusive */ ++ int right_bound; /* non-inclusive */ ++ ++ /* ++ * Avoid mymalloc() panic, or rr_array[0] fence-post error. ++ */ ++ if (list == 0) ++ return (list); ++ ++ /* ++ * 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); ++ ++ /* ++ * Walk through records and sort the records in every same-preference ++ * partition according to their weight. Note that left_bound is ++ * inclusive, and that right-bound is non-inclusive. ++ */ ++ left_bound = 0; ++ cur_pref = rr_array[left_bound]->pref; /* assumes len > 0 */ ++ ++ for (right_bound = 1; /* see below */ ; right_bound++) { ++ if (right_bound == len || rr_array[right_bound]->pref != cur_pref) { ++ if (right_bound - left_bound > 1) ++ weight_order(rr_array + left_bound, right_bound - left_bound); ++ if (right_bound == len) ++ break; ++ left_bound = right_bound; ++ cur_pref = rr_array[left_bound]->pref; ++ } ++ } ++ ++ /* ++ * 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); ++} +diff --git a/src/dns/dns_sa_to_rr.c b/src/dns/dns_sa_to_rr.c +index 6b9efcc..b5dee20 100644 +--- a/src/dns/dns_sa_to_rr.c ++++ b/src/dns/dns_sa_to_rr.c +@@ -55,14 +55,14 @@ 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, +- (char *) &SOCK_ADDR_IN_ADDR(sa), +- sizeof(SOCK_ADDR_IN_ADDR(sa)))); ++ return (dns_rr_create_noport(hostname, hostname, T_A, C_IN, DUMMY_TTL, ++ pref, (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, +- (char *) &SOCK_ADDR_IN6_ADDR(sa), +- sizeof(SOCK_ADDR_IN6_ADDR(sa)))); ++ return (dns_rr_create_noport(hostname, hostname, T_AAAA, C_IN, DUMMY_TTL, ++ pref, (char *) &SOCK_ADDR_IN6_ADDR(sa), ++ sizeof(SOCK_ADDR_IN6_ADDR(sa)))); + #endif + } else { + errno = EAFNOSUPPORT; +@@ -121,7 +121,7 @@ int main(int argc, char **argv) + resv[len++] = res; + qsort((void *) resv, len, sizeof(*resv), compare_family); + for (n = 0; n < len; n++) { +- if ((rr = dns_sa_to_rr(argv[0], 0, resv[n]->ai_addr)) == 0) ++ if ((rr = dns_sa_to_rr(argv[0], DNS_RR_NOPREF, resv[n]->ai_addr)) == 0) + msg_fatal("dns_sa_to_rr: %m"); + if (dns_rr_to_pa(rr, &hostaddr) == 0) + msg_fatal("dns_rr_to_pa: %m"); +diff --git a/src/dns/dns_strrecord.c b/src/dns/dns_strrecord.c +index 6b8e989..1e3b743 100644 +--- a/src/dns/dns_strrecord.c ++++ b/src/dns/dns_strrecord.c +@@ -80,6 +80,10 @@ char *dns_strrecord(VSTRING *buf, DNS_RR *rr) + case T_MX: + vstring_sprintf_append(buf, "%u %s.", rr->pref, rr->data); + break; ++ case T_SRV: ++ vstring_sprintf_append(buf, "%u %u %u %s.", rr->pref, rr->weight, ++ rr->port, rr->data); ++ break; + case T_TLSA: + if (rr->data_len >= 3) { + uint8_t *ip = (uint8_t *) rr->data; +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 25df5fc..781264b 100644 +--- a/src/global/mail_params.h ++++ b/src/global/mail_params.h +@@ -4370,6 +4370,21 @@ extern char *var_dnssec_probe; + "lmtp=24, smtp=25, smtps=submissions=465, submission=587" + extern char *var_known_tcp_ports; + ++ /* ++ * SRV lookup support. ++ */ ++#define VAR_USE_SRV_LOOKUP "use_srv_lookup" ++#define DEF_USE_SRV_LOOKUP "" ++extern char *var_use_srv_lookup; ++ ++#define VAR_IGN_SRV_LOOKUP_ERR "ignore_srv_lookup_error" ++#define DEF_IGN_SRV_LOOKUP_ERR 0 ++extern bool var_ign_srv_lookup_err; ++ ++#define VAR_ALLOW_SRV_FALLBACK "allow_srv_lookup_fallback" ++#define DEF_ALLOW_SRV_FALLBACK 0 ++extern bool var_allow_srv_fallback; ++ + /* LICENSE + /* .ad + /* .fi +diff --git a/src/posttls-finger/posttls-finger.c b/src/posttls-finger/posttls-finger.c +index 502645c..7a62afe 100644 +--- a/src/posttls-finger/posttls-finger.c ++++ b/src/posttls-finger/posttls-finger.c +@@ -237,6 +237,8 @@ + /* is encountered, up to 5 times or as specified with the \fB-m\fR option. + /* By default reconnection is disabled, specify a positive delay to + /* enable this behavior. ++/* .IP "\fB-R\fR" ++/* Use SRV lookup instead of MX. + /* .IP "\fB-s \fIservername\fR" + /* The server name to send with the TLS Server Name Indication (SNI) + /* extension. When the server has DANE TLSA records, this parameter +@@ -468,6 +470,7 @@ typedef struct STATE { + DNS_RR *mx; /* MX RRset qname, rname, valid */ + int pass; /* Pass number, 2 for reconnect */ + int nochat; /* disable chat logging */ ++ int dosrv; /* look up SRV records instead of MX */ + char *helo; /* Server name from EHLO reply */ + DSN_BUF *why; /* SMTP-style error message */ + VSTRING *buffer; /* Response buffer */ +@@ -1158,7 +1161,7 @@ static VSTREAM *connect_addr(STATE *state, DNS_RR *addr) + /* addr_one - address lookup for one host name */ + + static DNS_RR *addr_one(STATE *state, DNS_RR *addr_list, const char *host, +- int res_opt, unsigned pref) ++ int res_opt, unsigned pref, unsigned port) + { + static const char *myname = "addr_one"; + DSN_BUF *why = state->why; +@@ -1181,6 +1184,8 @@ static DNS_RR *addr_one(STATE *state, DNS_RR *addr_list, const char *host, + 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->pref = pref; ++ addr->port = port; + addr_list = dns_rr_append(addr_list, addr); + freeaddrinfo(res0); + return (addr_list); +@@ -1197,8 +1202,10 @@ static DNS_RR *addr_one(STATE *state, DNS_RR *addr_list, const char *host, + why->reason, DNS_REQ_FLAG_NONE, + proto_info->dns_atype_list)) { + case DNS_OK: +- for (rr = addr; rr; rr = rr->next) ++ 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: +@@ -1285,15 +1292,15 @@ static DNS_RR *mx_addr_list(STATE *state, DNS_RR *mx_names) + #endif + + for (rr = mx_names; rr; rr = rr->next) { +- if (rr->type != T_MX) ++ if (rr->type != T_MX && rr->type != T_SRV) + msg_panic("%s: bad resource type: %d", myname, rr->type); + addr_list = addr_one(state, addr_list, (char *) rr->data, res_opt, +- rr->pref); ++ rr->pref, rr->port); + } + return (addr_list); + } + +-/* smtp_domain_addr - mail exchanger address lookup */ ++/* domain_addr - mail exchanger address lookup */ + + static DNS_RR *domain_addr(STATE *state, char *domain) + { +@@ -1358,6 +1365,74 @@ static DNS_RR *domain_addr(STATE *state, char *domain) + return (addr_list); + } + ++/* service_addr - mail exchanger address lookup */ ++ ++static DNS_RR *service_addr(STATE *state, const char *domain, ++ const char *service) ++{ ++ VSTRING *srv_qname = vstring_alloc(100); ++ char *str_srv_qname; ++ DNS_RR *srv_names; ++ DNS_RR *addr_list = 0; ++ int r = 0; /* Resolver flags */ ++ const char *aname; ++ ++ dsb_reset(state->why); ++ ++#if (RES_USE_DNSSEC != 0) && (RES_USE_EDNS0 != 0) ++ r |= RES_USE_DNSSEC; ++#endif ++ ++ vstring_sprintf(srv_qname, "_%s._tcp.%s", service, domain); ++ str_srv_qname = STR(srv_qname); ++ ++ /* ++ * IDNA support. ++ */ ++#ifndef NO_EAI ++ if (!allascii(str_srv_qname) ++ && (aname = midna_domain_to_ascii(str_srv_qname)) != 0) { ++ msg_info("%s asciified to %s", str_srv_qname, aname); ++ } else ++#endif ++ aname = str_srv_qname; ++ ++ switch (dns_lookup(aname, T_SRV, r, &srv_names, (VSTRING *) 0, ++ state->why->reason)) { ++ default: ++ dsb_status(state->why, "4.4.3"); ++ break; ++ case DNS_INVAL: ++ dsb_status(state->why, "5.4.4"); ++ break; ++ case DNS_NULLMX: ++ dsb_status(state->why, "5.1.0"); ++ break; ++ case DNS_FAIL: ++ dsb_status(state->why, "5.4.3"); ++ break; ++ case DNS_OK: ++ /* Shuffle then sort the SRV rr records by priority and weight. */ ++ srv_names = dns_srv_rr_sort(srv_names); ++ addr_list = mx_addr_list(state, srv_names); ++ state->mx = dns_rr_copy(srv_names); ++ dns_rr_free(srv_names); ++ if (addr_list == 0) { ++ msg_warn("no SRV host for %s has a valid address record", ++ str_srv_qname); ++ break; ++ } ++ /* TODO: sort by priority, weight, and address family preference. */ ++ break; ++ case DNS_NOTFOUND: ++ dsb_status(state->why, "5.4.4"); ++ break; ++ } ++ ++ vstring_free(srv_qname); ++ return (addr_list); ++} ++ + /* host_addr - direct host lookup */ + + static DNS_RR *host_addr(STATE *state, const char *host) +@@ -1384,7 +1459,8 @@ static DNS_RR *host_addr(STATE *state, const char *host) + ahost = host; + + #define PREF0 0 +- addr_list = addr_one(state, (DNS_RR *) 0, ahost, res_opt, PREF0); ++#define NOPORT 0 ++ addr_list = addr_one(state, (DNS_RR *) 0, ahost, res_opt, PREF0, NOPORT); + if (addr_list && addr_list->next) { + addr_list = dns_rr_shuffle(addr_list); + if (inet_proto_info()->ai_family_list[1] != 0) +@@ -1468,7 +1544,8 @@ static int dane_host_level(STATE *state, DNS_RR *addr) + /* parse_destination - parse host/port destination */ + + static char *parse_destination(char *destination, char *def_service, +- char **hostp, unsigned *portp) ++ char **hostp, char **servicep, ++ unsigned *portp) + { + char *buf = mystrdup(destination); + char *service; +@@ -1484,13 +1561,13 @@ static char *parse_destination(char *destination, char *def_service, + * Parse the host/port information. We're working with a copy of the + * destination argument so the parsing can be destructive. + */ +- if ((err = host_port(buf, hostp, (char *) 0, &service, def_service)) != 0) ++ if ((err = host_port(buf, hostp, (char *) 0, servicep, def_service)) != 0) + msg_fatal("%s in server description: %s", err, destination); + + /* + * Convert service to port number, network byte order. + */ +- service = (char *) filter_known_tcp_port(service); ++ service = (char *) filter_known_tcp_port(*servicep); + if (alldig(service)) { + if ((port = atoi(service)) >= 65536 || port == 0) + msg_fatal("bad network port: %s for destination: %s", +@@ -1512,17 +1589,21 @@ static char *parse_destination(char *destination, char *def_service, + static void connect_remote(STATE *state, char *dest) + { + DNS_RR *addr; +- char *buf; +- char *domain; + + /* When reconnecting use IP address of previous session */ + if (state->addr == 0) { ++ char *buf; ++ char *domain; ++ char *service; ++ + buf = parse_destination(dest, state->smtp ? "smtp" : "24", +- &domain, &state->port); ++ &domain, &service, &state->port); + if (!state->nexthop) + state->nexthop = mystrdup(domain); + if (state->smtp == 0 || *dest == '[') + state->addr = host_addr(state, domain); ++ else if (state->dosrv) ++ state->addr = service_addr(state, domain, service); + else + state->addr = domain_addr(state, domain); + myfree(buf); +@@ -1536,10 +1617,14 @@ static void connect_remote(STATE *state, char *dest) + for (addr = state->addr; addr; addr = addr->next) { + int level = dane_host_level(state, addr); + ++ if (addr->port) /* SRV port override */ ++ state->port = htons(addr->port); ++ + if (level == TLS_LEV_INVALID + || (state->stream = connect_addr(state, addr)) == 0) { +- msg_info("Failed to establish session to %s via %s: %s", +- dest, HNAME(addr), vstring_str(state->why->reason)); ++ msg_info("Failed to establish session to %s via %s:%u: %s", ++ dest, HNAME(addr), addr->port, ++ vstring_str(state->why->reason)); + continue; + } + /* We have a connection */ +@@ -1807,6 +1892,7 @@ static void parse_options(STATE *state, int argc, char *argv[]) + + state->smtp = 1; + state->pass = 1; ++ state->dosrv = 0; + state->reconnect = -1; + state->max_reconnect = 5; + state->wrapper_mode = 0; +@@ -1817,7 +1903,7 @@ static void parse_options(STATE *state, int argc, char *argv[]) + memset((void *) &state->options, 0, sizeof(state->options)); + state->options.host_lookup = mystrdup("dns"); + +-#define OPTS "a:ch:o:St:T:v" ++#define OPTS "a:ch:o:RSt:T:v" + #ifdef USE_TLS + #define TLSOPTS "A:Cd:fF:g:H:k:K:l:L:m:M:p:P:r:s:wX" + +@@ -1856,6 +1942,9 @@ static void parse_options(STATE *state, int argc, char *argv[]) + case 'o': + override(optarg); + break; ++ case 'R': ++ state->dosrv = 1; ++ break; + case 'S': + state->smtp = 0; + break; +diff --git a/src/smtp/lmtp_params.c b/src/smtp/lmtp_params.c +index cc33646..bca7cd4 100644 +--- a/src/smtp/lmtp_params.c ++++ b/src/smtp/lmtp_params.c +@@ -65,6 +65,7 @@ + VAR_LMTP_DNS_RE_FILTER, DEF_LMTP_DNS_RE_FILTER, &var_smtp_dns_re_filter, 0, 0, + VAR_TLSPROXY_SERVICE, DEF_TLSPROXY_SERVICE, &var_tlsproxy_service, 1, 0, + VAR_HFROM_FORMAT, DEF_HFROM_FORMAT, &var_hfrom_format, 1, 0, ++ VAR_USE_SRV_LOOKUP, DEF_USE_SRV_LOOKUP, &var_use_srv_lookup, 0, 0, + 0, + }; + static const CONFIG_TIME_TABLE lmtp_time_table[] = { +@@ -128,6 +129,8 @@ + VAR_LMTP_DUMMY_MAIL_AUTH, DEF_LMTP_DUMMY_MAIL_AUTH, &var_smtp_dummy_mail_auth, + VAR_LMTP_BALANCE_INET_PROTO, DEF_LMTP_BALANCE_INET_PROTO, &var_smtp_balance_inet_proto, + VAR_LMTP_BIND_ADDR_ENFORCE, DEF_LMTP_BIND_ADDR_ENFORCE, &var_smtp_bind_addr_enforce, ++ VAR_IGN_SRV_LOOKUP_ERR, DEF_IGN_SRV_LOOKUP_ERR, &var_ign_srv_lookup_err, ++ VAR_ALLOW_SRV_FALLBACK, DEF_ALLOW_SRV_FALLBACK, &var_allow_srv_fallback, + 0, + }; + static const CONFIG_NBOOL_TABLE lmtp_nbool_table[] = { +diff --git a/src/smtp/smtp.c b/src/smtp/smtp.c +index 791ec89..816095b 100644 +--- a/src/smtp/smtp.c ++++ b/src/smtp/smtp.c +@@ -146,6 +146,7 @@ + /* RFC 2046 (MIME: Media Types) + /* RFC 2554 (AUTH command) + /* RFC 2821 (SMTP protocol) ++/* RFC 2782 (SRV resource records) + /* RFC 2920 (SMTP Pipelining) + /* RFC 3207 (STARTTLS command) + /* RFC 3461 (SMTP DSN Extension) +@@ -352,6 +353,17 @@ + /* DATA requests, when deadlines are enabled with smtp_per_request_deadline. + /* .IP "\fBheader_from_format (standard)\fR" + /* The format of the Postfix-generated \fBFrom:\fR header. ++/* .PP ++/* Backported from Postfix version 3.8: ++/* .IP "\fBuse_srv_lookup (empty)\fR" ++/* Enables discovery for the specified service(s) using DNS SRV ++/* records. ++/* .IP "\fBignore_srv_lookup_error (no)\fR" ++/* When SRV record lookup fails, fall back to MX or IP address ++/* lookup as if SRV record lookup was not enabled. ++/* .IP "\fBallow_srv_lookup_fallback (no)\fR" ++/* When SRV record lookup fails or no SRV record exists, fall back ++/* to MX or IP address lookup as if SRV record lookup was not enabled. + /* MIME PROCESSING CONTROLS + /* .ad + /* .fi +@@ -1092,6 +1104,9 @@ char *var_smtp_dns_re_filter; + bool var_smtp_balance_inet_proto; + bool var_smtp_req_deadline; + int var_smtp_min_data_rate; ++char *var_use_srv_lookup; ++bool var_ign_srv_lookup_err; ++bool var_allow_srv_fallback; + + /* Special handling of 535 AUTH errors. */ + char *var_smtp_sasl_auth_cache_name; +@@ -1118,6 +1133,7 @@ HBC_CHECKS *smtp_header_checks; /* limited header checks */ + HBC_CHECKS *smtp_body_checks; /* limited body checks */ + SMTP_CLI_ATTR smtp_cli_attr; /* parsed command-line */ + int smtp_hfrom_format; /* postmaster notifications */ ++STRING_LIST *smtp_use_srv_lookup; + + #ifdef USE_TLS + +@@ -1408,6 +1424,15 @@ static void post_init(char *unused_name, char **argv) + * header_from format, for postmaster notifications. + */ + smtp_hfrom_format = hfrom_format_parse(VAR_HFROM_FORMAT, var_hfrom_format); ++ ++ ++ /* ++ * Service discovery with SRV record lookup. ++ */ ++ if (*var_use_srv_lookup) ++ smtp_use_srv_lookup = string_list_init(VAR_USE_SRV_LOOKUP, ++ MATCH_FLAG_RETURN, ++ var_use_srv_lookup); + } + + /* pre_init - pre-jail initialization */ +diff --git a/src/smtp/smtp.h b/src/smtp/smtp.h +index 0d5c80a..2818d6e 100644 +--- a/src/smtp/smtp.h ++++ b/src/smtp/smtp.h +@@ -84,6 +84,14 @@ typedef struct SMTP_ITERATOR { + vstring_strcpy((iter)->dest, STR((iter)->saved_dest)); \ + } while (0) + ++#define SMTP_ITER_UPDATE_HOST(iter, _host, _addr, _rr) do { \ ++ vstring_strcpy((iter)->host, (_host)); \ ++ vstring_strcpy((iter)->addr, (_addr)); \ ++ (iter)->rr = (_rr); \ ++ if ((_rr)->port) \ ++ (iter)->port = htons((_rr)->port); /* SRV port override */ \ ++ } while (0) ++ + /* + * TLS Policy support. + */ +@@ -273,6 +281,7 @@ typedef struct SMTP_STATE { + #define SMTP_MISC_FLAG_COMPLETE_SESSION (1<<7) + #define SMTP_MISC_FLAG_PREF_IPV6 (1<<8) + #define SMTP_MISC_FLAG_PREF_IPV4 (1<<9) ++#define SMTP_MISC_FLAG_FALLBACK_SRV_TO_MX (1<<10) + + #define SMTP_MISC_FLAG_CONN_CACHE_MASK \ + (SMTP_MISC_FLAG_CONN_LOAD | SMTP_MISC_FLAG_CONN_STORE) +@@ -312,6 +321,8 @@ extern MAPS *smtp_generic_maps; /* make internal address valid */ + extern int smtp_ext_prop_mask; /* address extension propagation */ + extern unsigned smtp_dns_res_opt; /* DNS query flags */ + ++extern STRING_LIST *smtp_use_srv_lookup;/* services with SRV record lookup */ ++ + #ifdef USE_TLS + + extern TLS_APPL_STATE *smtp_tls_ctx; /* client-side TLS engine */ +diff --git a/src/smtp/smtp_addr.c b/src/smtp/smtp_addr.c +index 2b5c126..94adaa1 100644 +--- a/src/smtp/smtp_addr.c ++++ b/src/smtp/smtp_addr.c +@@ -17,6 +17,15 @@ + /* char *name; + /* int misc_flags; + /* DSN_BUF *why; ++/* ++/* DNS_RR *smtp_service_addr(name, service, mxrr, misc_flags, why, ++/* found_myself) ++/* const char *name; ++/* const char *service; ++/* DNS_RR **mxrr; ++/* int misc_flags; ++/* DSN_BUF *why; ++/* int *found_myself; + /* DESCRIPTION + /* This module implements Internet address lookups. By default, + /* lookups are done via the Internet domain name service (DNS). +@@ -33,6 +42,8 @@ + /* destination. If MX records were found, the rname, qname, + /* and dnssec validation status of the MX RRset are returned + /* via mxrr, which the caller must free with dns_rr_free(). ++/* Fallback from MX to address lookups is governed by RFC 2821, ++/* and by local policy (var_ign_mx_lookup_err). + /* + /* When no mail exchanger is listed in the DNS for \fIname\fR, the + /* request is passed to smtp_host_addr(). +@@ -44,8 +55,18 @@ + /* host. The host can be specified as a numerical Internet network + /* address, or as a symbolic host name. + /* +-/* Results from smtp_domain_addr() or smtp_host_addr() are +-/* destroyed by dns_rr_free(), including null lists. ++/* smtp_service_addr() looks up addresses for hosts specified ++/* in SRV records for the specified domain and service. This ++/* supports the features of smtp_domain_addr() except that ++/* the order of SRV records is determined by RFC 2782, and ++/* that address records are not sorted by IP address family ++/* preference. Fallback from SRV to MX or address lookups is ++/* governed by local policy (var_ign_mx_lookup_err and ++/* var_allow_srv_fallback). ++/* ++/* Results from smtp_domain_addr(), smtp_host_addr(), and ++/* smtp_service_addr() are destroyed by dns_rr_free(), including ++/* null lists. + /* DIAGNOSTICS + /* Panics: interface violations. For example, calling smtp_domain_addr() + /* when DNS lookups are explicitly disabled. +@@ -130,7 +151,8 @@ 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; +@@ -153,6 +175,8 @@ static DNS_RR *smtp_addr_one(DNS_RR *addr_list, const char *host, int res_opt, + if ((addr = dns_sa_to_rr(host, pref, res0->ai_addr)) == 0) + msg_fatal("host %s: conversion error for address family " + "%d: %m", host, res0->ai_addr->sa_family); ++ addr->pref = pref; ++ addr->port = port; + addr_list = dns_rr_append(addr_list, addr); + if (msg_verbose) + msg_info("%s: using numerical host %s", myname, host); +@@ -174,8 +198,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) ++ 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 +319,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 +704,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 +726,135 @@ DNS_RR *smtp_host_addr(const char *host, int misc_flags, DSN_BUF *why) + smtp_print_addr(host, addr_list); + return (addr_list); + } ++ ++/* smtp_service_addr - service address lookup */ ++ ++DNS_RR *smtp_service_addr(const char *name, const char *service, DNS_RR **mxrr, ++ int misc_flags, DSN_BUF *why, ++ int *found_myself) ++{ ++ static VSTRING *srv_qname = 0; ++ const char *str_srv_qname; ++ DNS_RR *srv_names = 0; ++ DNS_RR *addr_list = 0; ++ DNS_RR *self = 0; ++ unsigned best_pref; ++ unsigned best_found; ++ int r = 0; ++ const char *aname; ++ int allow_non_srv_fallback = var_allow_srv_fallback; ++ ++ dsb_reset(why); ++ ++ /* ++ * Sanity check. ++ */ ++ if (smtp_dns_support == SMTP_DNS_DISABLED) ++ msg_panic("smtp_service_addr: DNS lookup is disabled"); ++ ++ if (smtp_dns_support == SMTP_DNS_DNSSEC) { ++ r |= RES_USE_DNSSEC; ++ } ++ if (srv_qname == 0) ++ srv_qname = vstring_alloc(100); ++ vstring_sprintf(srv_qname, "_%s._tcp.%s", service, name); ++ str_srv_qname = STR(srv_qname); ++ ++ /* ++ * IDNA support. ++ */ ++#ifndef NO_EAI ++ if (!allascii(str_srv_qname) ++ && (aname = midna_domain_to_ascii(str_srv_qname)) != 0) { ++ if (msg_verbose) ++ msg_info("%s asciified to %s", str_srv_qname, aname); ++ } else ++#endif ++ aname = str_srv_qname; ++ ++ switch (dns_lookup(aname, T_SRV, r, &srv_names, (VSTRING *) 0, ++ why->reason)) { ++ default: ++ dsb_status(why, "4.4.3"); ++ allow_non_srv_fallback |= var_ign_srv_lookup_err; ++ break; ++ case DNS_INVAL: ++ dsb_status(why, "5.4.4"); ++ allow_non_srv_fallback |= var_ign_srv_lookup_err; ++ break; ++ case DNS_POLICY: ++ dsb_status(why, "4.7.0"); ++ break; ++ case DNS_FAIL: ++ dsb_status(why, "5.4.3"); ++ allow_non_srv_fallback |= var_ign_srv_lookup_err; ++ break; ++ case DNS_NULLSRV: ++ dsb_status(why, "5.1.0"); ++ break; ++ case DNS_OK: ++ /* Shuffle then sort the SRV rr records by priority and weight. */ ++ srv_names = dns_srv_rr_sort(srv_names); ++ best_pref = (srv_names ? srv_names->pref : IMPOSSIBLE_PREFERENCE); ++ addr_list = smtp_addr_list(srv_names, why); ++ if (mxrr) ++ *mxrr = dns_rr_copy(srv_names); /* copies one record! */ ++ dns_rr_free(srv_names); ++ if (addr_list == 0) { ++ msg_warn("no SRV host for %s has a valid address record", ++ str_srv_qname); ++ break; ++ } ++ /* Optional loop prevention, similar to smtp_domain_addr(). */ ++ best_found = (addr_list ? addr_list->pref : IMPOSSIBLE_PREFERENCE); ++ if (msg_verbose) ++ smtp_print_addr(aname, addr_list); ++ if ((misc_flags & SMTP_MISC_FLAG_LOOP_DETECT) ++ && (self = smtp_find_self(addr_list)) != 0) { ++ addr_list = smtp_truncate_self(addr_list, self->pref); ++ if (addr_list == 0) { ++ if (best_pref != best_found) { ++ dsb_simple(why, "4.4.4", ++ "unable to find primary relay for %s", ++ str_srv_qname); ++ } else { ++ dsb_simple(why, "5.4.6", "mail for %s loops back to myself", ++ str_srv_qname); ++ } ++ } ++ } ++ /* TODO: sort by priority, weight, and address family preference. */ ++ ++ /* Optional address family balancing, as in smtp_domain_addr(). */ ++ if (addr_list && addr_list->next) { ++ if (var_smtp_mxaddr_limit > 0 && var_smtp_balance_inet_proto) ++ addr_list = smtp_balance_inet_proto(addr_list, misc_flags, ++ var_smtp_mxaddr_limit); ++ } ++ break; ++ case DNS_NOTFOUND: ++ dsb_status(why, "5.4.4"); ++ break; ++ } ++ ++ /* ++ * If permitted, fall back to non-SRV record lookups. ++ */ ++ if (addr_list == 0 && allow_non_srv_fallback) { ++ msg_info("skipping SRV lookup for %s: %s", ++ str_srv_qname, STR(why->reason)); ++ if (misc_flags & SMTP_MISC_FLAG_FALLBACK_SRV_TO_MX) ++ addr_list = smtp_domain_addr(name, mxrr, misc_flags, why, ++ found_myself); ++ else ++ addr_list = smtp_host_addr(name, misc_flags, why); ++ } ++ ++ /* ++ * Only if we're not falling back. ++ */ ++ else { ++ *found_myself |= (self != 0); ++ } ++ return (addr_list); ++} +diff --git a/src/smtp/smtp_addr.h b/src/smtp/smtp_addr.h +index 8f20961..3d70413 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_service_addr(const char *, const char *, DNS_RR **, int, DSN_BUF *, int *); + + /* LICENSE + /* .ad +diff --git a/src/smtp/smtp_connect.c b/src/smtp/smtp_connect.c +index ed58180..68faca1 100644 +--- a/src/smtp/smtp_connect.c ++++ b/src/smtp/smtp_connect.c +@@ -28,7 +28,8 @@ + /* destinations may be specified as "unix:pathname", "inet:host" + /* or "inet:host:port". + /* +-/* With SMTP, the Internet domain name service is queried for mail ++/* With SMTP, or with SRV record lookup enabled, the Internet ++/* domain name service is queried for mail + /* exchanger hosts. Quote the domain name with `[' and `]' to + /* suppress mail exchanger lookups. + /* +@@ -357,7 +358,8 @@ static SMTP_SESSION *smtp_connect_sock(int sock, struct sockaddr *sa, + /* smtp_parse_destination - parse host/port destination */ + + static char *smtp_parse_destination(char *destination, char *def_service, +- char **hostp, unsigned *portp) ++ char **hostp, char **servicep, ++ unsigned *portp) + { + char *buf = mystrdup(destination); + char *service; +@@ -373,13 +375,13 @@ static char *smtp_parse_destination(char *destination, char *def_service, + * Parse the host/port information. We're working with a copy of the + * destination argument so the parsing can be destructive. + */ +- if ((err = host_port(buf, hostp, (char *) 0, &service, def_service)) != 0) ++ if ((err = host_port(buf, hostp, (char *) 0, servicep, def_service)) != 0) + msg_fatal("%s in server description: %s", err, destination); + + /* + * Convert service to port number, network byte order. + */ +- service = (char *) filter_known_tcp_port(service); ++ service = (char *) filter_known_tcp_port(*servicep); + if (alldig(service)) { + if ((port = atoi(service)) >= 65536 || port == 0) + msg_fatal("bad network port: %s for destination: %s", +@@ -661,6 +663,9 @@ static void smtp_update_addr_list(DNS_RR **addr_list, const char *server_addr, + * XXX Extend the SMTP_SESSION structure with sockaddr information so that + * we can avoid repeated string->binary transformations for the same + * address. ++ * ++ * XXX SRV support: this should match the port, too, otherwise we may ++ * eliminate too many list entries. + */ + if ((aierr = hostaddr_to_sockaddr(server_addr, (char *) 0, 0, &res0)) != 0) { + msg_warn("hostaddr_to_sockaddr %s: %s", +@@ -691,6 +696,18 @@ static int smtp_reuse_session(SMTP_STATE *state, DNS_RR **addr_list, + DSN_BUF *why = state->why; + + /* ++ * This code is called after server address/port lookup, before ++ * iter->host, iter->addr, iter->rr and iter->mx are assigned concrete ++ * values, and while iter->port still corresponds to the nexthop service, ++ * or the default service configured with smtp_tcp_port or lmtp_tcp_port. ++ * ++ * When a connection is reused by nexthop/service or by server address/port, ++ * iter->host, iter->addr and iter->port are updated with actual values ++ * from the cached session. Additionally, when a connection is searched ++ * by nexthop/service, iter->rr remains null, and when a connection is ++ * searched by server address/port, iter->rr is updated with an actual ++ * server address/port before the search is made. ++ * + * First, search the cache by delivery request nexthop. We truncate the + * server address list when all the sessions for this destination are + * used up, to reduce the number of variables that need to be checked +@@ -757,9 +774,7 @@ static int smtp_reuse_session(SMTP_STATE *state, DNS_RR **addr_list, + /* XXX Assume there is no code at the end of this loop. */ + continue; + } +- vstring_strcpy(iter->addr, hostaddr.buf); +- vstring_strcpy(iter->host, SMTP_HNAME(addr)); +- iter->rr = addr; ++ SMTP_ITER_UPDATE_HOST(iter, SMTP_HNAME(addr), hostaddr.buf, addr); + #ifdef USE_TLS + if (!smtp_tls_policy_cache_query(why, state->tls, iter)) { + msg_warn("TLS policy lookup error for %s/%s: %s", +@@ -844,6 +859,7 @@ static void smtp_connect_inet(SMTP_STATE *state, const char *nexthop, + char *dest_buf; + char *domain; + unsigned port; ++ char *service; + DNS_RR *addr_list; + DNS_RR *addr; + DNS_RR *next; +@@ -851,6 +867,8 @@ static void smtp_connect_inet(SMTP_STATE *state, const char *nexthop, + int sess_count; + SMTP_SESSION *session; + int lookup_mx; ++ int non_dns_or_literal; ++ int i_am_mx; + unsigned domain_best_pref; + MAI_HOSTADDR_STR hostaddr; + +@@ -860,8 +878,28 @@ static void smtp_connect_inet(SMTP_STATE *state, const char *nexthop, + /* + * Parse the destination. If no TCP port is specified, use the port + * that is reserved for the protocol (SMTP or LMTP). ++ * ++ * The 'service' variable corresponds to the remote service specified ++ * with the nexthop, or the default service configured with ++ * smtp_tcp_port or lmtp_tcp_port. The 'port' variable and ++ * SMTP_ITERATOR.port initially correspond to that service. This ++ * determines what loop prevention will be in effect. ++ * ++ * The SMTP_ITERATOR.port will be overwritten after SRV record lookup. ++ * This guarantees that the connection cache key contains the correct ++ * port value when caching and retrieving a connection by its server ++ * address (and port). ++ * ++ * By design, the connection cache key contains NO port information when ++ * caching or retrieving a connection by its nexthop destination. ++ * Instead, the cache key contains the master.cf service name (a ++ * proxy for all the parameter settings including the default service ++ * from smtp_tcp_port or lmtp_tcp_port), together with the nexthop ++ * destination and sender-dependent info. This should be sufficient ++ * to avoid cross talk between mail streams that should be separated. + */ +- dest_buf = smtp_parse_destination(dest, def_service, &domain, &port); ++ dest_buf = smtp_parse_destination(dest, def_service, &domain, ++ &service, &port); + if (var_helpful_warnings && var_smtp_tls_wrappermode == 0 + && ntohs(port) == 465) { + msg_info("SMTPS wrappermode (TCP port 465) requires setting " +@@ -874,32 +912,48 @@ static void smtp_connect_inet(SMTP_STATE *state, const char *nexthop, + SMTP_ITER_INIT(iter, dest, NO_HOST, NO_ADDR, port, state); + + /* +- * Resolve an SMTP or LMTP server. In the case of SMTP, skip mail +- * exchanger lookups when a quoted host is specified or when DNS +- * lookups are disabled. ++ * Resolve an SMTP or LMTP server. Skip MX or SRV lookups when a ++ * quoted domain is specified or when DNS lookups are disabled. + */ + if (msg_verbose) +- msg_info("connecting to %s port %d", domain, ntohs(port)); ++ msg_info("connecting to %s service %s", domain, service); ++ non_dns_or_literal = (smtp_dns_support == SMTP_DNS_DISABLED ++ || *dest == '['); + if (smtp_mode) { + if (ntohs(port) == IPPORT_SMTP) + state->misc_flags |= SMTP_MISC_FLAG_LOOP_DETECT; + else + state->misc_flags &= ~SMTP_MISC_FLAG_LOOP_DETECT; +- lookup_mx = (smtp_dns_support != SMTP_DNS_DISABLED && *dest != '['); ++ lookup_mx = !non_dns_or_literal; + } else + lookup_mx = 0; +- if (!lookup_mx) { ++ ++ /* ++ * Look up SRV and address records and fall back to non-SRV lookups ++ * if permitted by configuration settings, or look up MX and address ++ * records, or look up address records only. ++ */ ++ i_am_mx = 0; ++ addr_list = 0; ++ if (!non_dns_or_literal && smtp_use_srv_lookup ++ && string_list_match(smtp_use_srv_lookup, service)) { ++ if (lookup_mx) ++ state->misc_flags |= SMTP_MISC_FLAG_FALLBACK_SRV_TO_MX; ++ else ++ state->misc_flags &= ~SMTP_MISC_FLAG_FALLBACK_SRV_TO_MX; ++ addr_list = smtp_service_addr(domain, service, &iter->mx, ++ state->misc_flags, why, &i_am_mx); ++ } else if (!lookup_mx) { ++ /* Non-DNS, literal, or non-SMTP service */ + addr_list = smtp_host_addr(domain, state->misc_flags, why); + /* XXX We could be an MX host for this destination... */ + } else { +- int i_am_mx = 0; +- + addr_list = smtp_domain_addr(domain, &iter->mx, state->misc_flags, + why, &i_am_mx); +- /* If we're MX host, don't connect to non-MX backups. */ +- if (i_am_mx) +- state->misc_flags |= SMTP_MISC_FLAG_FINAL_NEXTHOP; + } ++ /* If we're MX host, don't connect to non-MX backups. */ ++ if (i_am_mx) ++ state->misc_flags |= SMTP_MISC_FLAG_FINAL_NEXTHOP; + + /* + * Don't try fall-back hosts if mail loops to myself. That would just +@@ -992,9 +1046,7 @@ static void smtp_connect_inet(SMTP_STATE *state, const char *nexthop, + /* XXX Assume there is no code at the end of this loop. */ + continue; + } +- vstring_strcpy(iter->addr, hostaddr.buf); +- vstring_strcpy(iter->host, SMTP_HNAME(addr)); +- iter->rr = addr; ++ SMTP_ITER_UPDATE_HOST(iter, SMTP_HNAME(addr), hostaddr.buf, addr); + #ifdef USE_TLS + if (!smtp_tls_policy_cache_query(why, state->tls, iter)) { + msg_warn("TLS policy lookup for %s/%s: %s", +diff --git a/src/smtp/smtp_params.c b/src/smtp/smtp_params.c +index cd54f8f..22f4709 100644 +--- a/src/smtp/smtp_params.c ++++ b/src/smtp/smtp_params.c +@@ -66,6 +66,7 @@ + VAR_SMTP_DNS_RE_FILTER, DEF_SMTP_DNS_RE_FILTER, &var_smtp_dns_re_filter, 0, 0, + VAR_TLSPROXY_SERVICE, DEF_TLSPROXY_SERVICE, &var_tlsproxy_service, 1, 0, + VAR_HFROM_FORMAT, DEF_HFROM_FORMAT, &var_hfrom_format, 1, 0, ++ VAR_USE_SRV_LOOKUP, DEF_USE_SRV_LOOKUP, &var_use_srv_lookup, 0, 0, + 0, + }; + static const CONFIG_TIME_TABLE smtp_time_table[] = { +@@ -132,6 +133,8 @@ + 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_IGN_SRV_LOOKUP_ERR, DEF_IGN_SRV_LOOKUP_ERR, &var_ign_srv_lookup_err, ++ VAR_ALLOW_SRV_FALLBACK, DEF_ALLOW_SRV_FALLBACK, &var_allow_srv_fallback, + 0, + }; + static const CONFIG_NBOOL_TABLE smtp_nbool_table[] = { +diff --git a/src/smtp/smtp_session.c b/src/smtp/smtp_session.c +index 9f13978..90a0ff1 100644 +--- a/src/smtp/smtp_session.c ++++ b/src/smtp/smtp_session.c +@@ -129,6 +129,7 @@ + #define SESS_ATTR_DEST "destination" + #define SESS_ATTR_HOST "host_name" + #define SESS_ATTR_ADDR "host_addr" ++#define SESS_ATTR_PORT "host_port" + #define SESS_ATTR_DEST_FEATURES "destination_features" + + #define SESS_ATTR_TLS_LEVEL "tls_level" +@@ -259,6 +260,7 @@ int smtp_session_passivate(SMTP_SESSION *session, VSTRING *dest_prop, + SEND_ATTR_STR(SESS_ATTR_DEST, STR(iter->dest)), + SEND_ATTR_STR(SESS_ATTR_HOST, STR(iter->host)), + SEND_ATTR_STR(SESS_ATTR_ADDR, STR(iter->addr)), ++ SEND_ATTR_UINT(SESS_ATTR_PORT, iter->port), + SEND_ATTR_INT(SESS_ATTR_DEST_FEATURES, + session->features & SMTP_FEATURE_DESTINATION_MASK), + ATTR_TYPE_END) != 0 +@@ -398,9 +400,10 @@ SMTP_SESSION *smtp_session_activate(int fd, SMTP_ITERATOR *iter, + RECV_ATTR_STR(SESS_ATTR_DEST, iter->dest), + RECV_ATTR_STR(SESS_ATTR_HOST, iter->host), + RECV_ATTR_STR(SESS_ATTR_ADDR, iter->addr), ++ RECV_ATTR_UINT(SESS_ATTR_PORT, &iter->port), + RECV_ATTR_INT(SESS_ATTR_DEST_FEATURES, + &dest_features), +- ATTR_TYPE_END) != 4 ++ ATTR_TYPE_END) != 5 + || vstream_fclose(mp) != 0) { + msg_warn("smtp_session_passivate: bad cached dest properties"); + SMTP_SESSION_ACTIVATE_ERR_RETURN(); +diff --git a/src/smtpd/smtpd_check.c b/src/smtpd/smtpd_check.c +index a4a6af0..aa6200f 100644 +--- a/src/smtpd/smtpd_check.c ++++ b/src/smtpd/smtpd_check.c +@@ -3064,8 +3064,8 @@ 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, +- domain, strlen(domain) + 1); ++ server_list = dns_rr_create_nopref(domain, domain, T_MX, C_IN, 0, ++ domain, strlen(domain) + 1); + } else { + dns_status = dns_lookup(domain, type, 0, &server_list, + (VSTRING *) 0, (VSTRING *) 0); +@@ -3073,8 +3073,8 @@ 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, +- domain, strlen(domain) + 1); ++ server_list = dns_rr_create_nopref(domain, domain, type, C_IN, ++ 0, domain, strlen(domain) + 1); + dns_status = DNS_OK; + } else if (type == T_NS /* && h_errno == NO_DATA */ ) { + while ((domain = strchr(domain, '.')) != 0 && domain[1]) { +diff --git a/src/util/attr.h b/src/util/attr.h +index 067405f..7cd0cf2 100644 +--- a/src/util/attr.h ++++ b/src/util/attr.h +@@ -62,6 +62,7 @@ typedef int (*ATTR_PRINT_CUSTOM_FN) (ATTR_PRINT_COMMON_FN, VSTREAM *, int, const + * for documentation. + */ + #define SEND_ATTR_INT(name, val) ATTR_TYPE_INT, CHECK_CPTR(ATTR, char, (name)), CHECK_VAL(ATTR, int, (val)) ++#define SEND_ATTR_UINT(name, val) ATTR_TYPE_INT, CHECK_CPTR(ATTR, char, (name)), CHECK_VAL(ATTR, unsigned, (val)) + #define SEND_ATTR_STR(name, val) ATTR_TYPE_STR, CHECK_CPTR(ATTR, char, (name)), CHECK_CPTR(ATTR, char, (val)) + #define SEND_ATTR_HASH(val) ATTR_TYPE_HASH, CHECK_CPTR(ATTR, HTABLE, (val)) + #define SEND_ATTR_NV(val) ATTR_TYPE_NV, CHECK_CPTR(ATTR, NVTABLE, (val)) +@@ -70,6 +71,7 @@ typedef int (*ATTR_PRINT_CUSTOM_FN) (ATTR_PRINT_COMMON_FN, VSTREAM *, int, const + #define SEND_ATTR_FUNC(func, val) ATTR_TYPE_FUNC, CHECK_VAL(ATTR, ATTR_PRINT_CUSTOM_FN, (func)), CHECK_CPTR(ATTR, void, (val)) + + #define RECV_ATTR_INT(name, val) ATTR_TYPE_INT, CHECK_CPTR(ATTR, char, (name)), CHECK_PTR(ATTR, int, (val)) ++#define RECV_ATTR_UINT(name, val) ATTR_TYPE_INT, CHECK_CPTR(ATTR, char, (name)), CHECK_PTR(ATTR, unsigned, (val)) + #define RECV_ATTR_STR(name, val) ATTR_TYPE_STR, CHECK_CPTR(ATTR, char, (name)), CHECK_PTR(ATTR, VSTRING, (val)) + #define RECV_ATTR_STREQ(name, val) ATTR_TYPE_STREQ, CHECK_CPTR(ATTR, char, (name)), CHECK_CPTR(ATTR, char, (val)) + #define RECV_ATTR_HASH(val) ATTR_TYPE_HASH, CHECK_PTR(ATTR, HTABLE, (val)) +@@ -81,9 +83,11 @@ typedef int (*ATTR_PRINT_CUSTOM_FN) (ATTR_PRINT_COMMON_FN, VSTREAM *, int, const + CHECK_VAL_HELPER_DCL(ATTR, ssize_t); + CHECK_VAL_HELPER_DCL(ATTR, long); + CHECK_VAL_HELPER_DCL(ATTR, int); ++CHECK_VAL_HELPER_DCL(ATTR, unsigned); + CHECK_PTR_HELPER_DCL(ATTR, void); + CHECK_PTR_HELPER_DCL(ATTR, long); + CHECK_PTR_HELPER_DCL(ATTR, int); ++CHECK_PTR_HELPER_DCL(ATTR, unsigned); + CHECK_PTR_HELPER_DCL(ATTR, VSTRING); + CHECK_PTR_HELPER_DCL(ATTR, NVTABLE); + CHECK_PTR_HELPER_DCL(ATTR, HTABLE); diff --git a/postfix-3.8.0-large-fs.patch b/postfix-3.7.9-large-fs.patch similarity index 92% rename from postfix-3.8.0-large-fs.patch rename to postfix-3.7.9-large-fs.patch index b0c7933..28c1eab 100644 --- a/postfix-3.8.0-large-fs.patch +++ b/postfix-3.7.9-large-fs.patch @@ -21,10 +21,10 @@ index 50a4aa7..beef3db 100644 if (msg_verbose) msg_info("%s: %s: block size %lu, blocks free %lu", diff --git a/src/util/sys_defs.h b/src/util/sys_defs.h -index 1fb449d..bcaac27 100644 +index aec8d8a..aff8d44 100644 --- a/src/util/sys_defs.h +++ b/src/util/sys_defs.h -@@ -783,8 +783,8 @@ extern int initgroups(const char *, int); +@@ -772,8 +772,8 @@ extern int initgroups(const char *, int); #define GETTIMEOFDAY(t) gettimeofday(t,(struct timezone *) 0) #define ROOT_PATH "/bin:/usr/bin:/sbin:/usr/sbin" #define FIONREAD_IN_TERMIOS_H diff --git a/postfix-etc-init.d-postfix b/postfix-etc-init.d-postfix index 52bd7bd..c609b0a 100644 --- a/postfix-etc-init.d-postfix +++ b/postfix-etc-init.d-postfix @@ -51,20 +51,15 @@ conf_check() { } make_aliasesdb() { - local MAP="" - local ALIASESDB="$(/usr/sbin/postconf -h alias_database)" - [ "$ALIASESDB" = "hash:/etc/aliases" ] && MAP="db" - [ "$ALIASESDB" = "lmdb:/etc/aliases" ] && MAP="lmdb" - - if [ "$MAP" = "db" -o "$MAP" = "lmdb" ] + if [ "$(/usr/sbin/postconf -h alias_database)" == "hash:/etc/aliases" ] then - # /etc/aliases.db|lmdb may be used by other MTA, make sure nothing + # /etc/aliases.db may be used by other MTA, make sure nothing # has touched it since our last newaliases call - [ /etc/aliases -nt /etc/aliases.$MAP ] || - [ "$ALIASESDB_STAMP" -nt /etc/aliases.$MAP ] || - [ "$ALIASESDB_STAMP" -ot /etc/aliases.$MAP ] || return + [ /etc/aliases -nt /etc/aliases.db ] || + [ "$ALIASESDB_STAMP" -nt /etc/aliases.db ] || + [ "$ALIASESDB_STAMP" -ot /etc/aliases.db ] || return /usr/bin/newaliases - touch -r /etc/aliases.$MAP "$ALIASESDB_STAMP" + touch -r /etc/aliases.db "$ALIASESDB_STAMP" else /usr/bin/newaliases fi diff --git a/postfix.aliasesdb b/postfix.aliasesdb index 863b405..8c0156e 100644 --- a/postfix.aliasesdb +++ b/postfix.aliasesdb @@ -3,20 +3,15 @@ ALIASESDB_STAMP=/var/lib/misc/postfix.aliasesdb-stamp make_aliasesdb() { - local MAP="" - local ALIASESDB="$(/usr/sbin/postconf -h alias_database)" - [ "$ALIASESDB" = "hash:/etc/aliases" ] && MAP="db" - [ "$ALIASESDB" = "lmdb:/etc/aliases" ] && MAP="lmdb" - - if [ "$MAP" = "db" -o "$MAP" = "lmdb" ] + if [ "$(/usr/sbin/postconf -h alias_database)" == "hash:/etc/aliases" ] then - # /etc/aliases.db|lmdb may be used by other MTA, make sure nothing + # /etc/aliases.db may be used by other MTA, make sure nothing # has touched it since our last newaliases call - [ /etc/aliases -nt /etc/aliases.$MAP ] || - [ "$ALIASESDB_STAMP" -nt /etc/aliases.$MAP ] || - [ "$ALIASESDB_STAMP" -ot /etc/aliases.$MAP ] || return 0 + [ /etc/aliases -nt /etc/aliases.db ] || + [ "$ALIASESDB_STAMP" -nt /etc/aliases.db ] || + [ "$ALIASESDB_STAMP" -ot /etc/aliases.db ] || return 0 /usr/bin/newaliases - touch -r /etc/aliases.$MAP "$ALIASESDB_STAMP" + touch -r /etc/aliases.db "$ALIASESDB_STAMP" else /usr/bin/newaliases fi diff --git a/postfix.spec b/postfix.spec index 30811de..499df4f 100644 --- a/postfix.spec +++ b/postfix.spec @@ -1,11 +1,6 @@ # plugins have unresolvable symbols in compile time %undefine _strict_symbol_defs_build -%if 0%{?rhel} < 10 -%bcond_without db -%else -%bcond_with db -%endif %bcond_without mysql %bcond_without pgsql %bcond_without sqlite @@ -18,12 +13,6 @@ %bcond_without ipv6 %bcond_without pflogsumm -%if %{without db} && %{with lmdb} -%global defmap_lmdb 1 -%else -%global defmap_lmdb 0 -%endif - %global sysv2systemdnvr 2.8.12-2 # hardened build if not overrided @@ -32,9 +21,12 @@ # Postfix requires one exlusive uid/gid and a 2nd exclusive gid for its own # use. Let me know if the second gid collides with another package. # Be careful: Redhat's 'mail' user & group isn't unique! -# It's now handled by systemd-sysusers. +%define postfix_uid 89 %define postfix_user postfix +%define postfix_gid 89 +%define postfix_group postfix %define maildrop_group postdrop +%define maildrop_gid 90 %define postfix_config_dir %{_sysconfdir}/postfix %define postfix_daemon_dir %{_libexecdir}/postfix @@ -56,14 +48,16 @@ Name: postfix Summary: Postfix Mail Transport Agent -Version: 3.10.7 +Version: 3.7.9 Release: 1%{?dist} Epoch: 2 URL: http://www.postfix.org -License: (IPL-1.0 OR EPL-2.0) AND GPL-2.0-or-later AND BSD-4-Clause-UC +License: (IBM and GPLv2+) or (EPL-2.0 and GPLv2+) Requires(post): systemd systemd-sysv hostname Requires(post): %{_sbindir}/alternatives Requires(post): %{_bindir}/openssl +Requires(pre): %{_sbindir}/groupadd +Requires(pre): %{_sbindir}/useradd Requires(preun): %{_sbindir}/alternatives Requires(preun): systemd Requires(postun): systemd @@ -74,17 +68,16 @@ Requires: findutils Requires: policycoreutils Provides: MTA smtpd smtpdaemon server(smtp) -Source0: http://ftp.porcupine.org/mirrors/postfix-release/official/%{name}-%{version}.tar.gz +Source0: ftp://ftp.porcupine.org/mirrors/postfix-release/official/%{name}-%{version}.tar.gz Source1: postfix-etc-init.d-postfix Source2: postfix.service Source3: README-Postfix-SASL-RedHat.txt Source4: postfix.aliasesdb Source5: postfix-chroot-update -Source6: postfix.sysusers # Sources 50-99 are upstream [patch] contributions -%define pflogsumm_ver 1.1.6 +%define pflogsumm_ver 1.1.5 # Postfix Log Entry Summarizer: http://jimsun.linxnet.com/postfix_contrib.html Source53: http://jimsun.linxnet.com/downloads/pflogsumm-%{pflogsumm_ver}.tar.gz @@ -96,14 +89,20 @@ Source101: postfix-pam.conf # Patches -Patch1: postfix-3.10.5-config.patch -Patch2: postfix-3.9.0-files.patch -Patch3: postfix-3.9.0-alternatives.patch -# probably rhbz#428996 -Patch4: postfix-3.8.0-large-fs.patch -# rhbz#1931403, sent upstream -Patch9: pflogsumm-1.1.6-syslog-name-underscore-fix.patch +Patch1: postfix-3.7.0-config.patch +Patch2: postfix-3.4.0-files.patch +Patch3: postfix-3.3.3-alternatives.patch +Patch4: postfix-3.7.9-large-fs.patch +Patch9: pflogsumm-1.1.5-datecalc.patch +# rhbz#1384871, sent upstream +Patch10: pflogsumm-1.1.5-ipv6-warnings-fix.patch Patch11: postfix-3.4.4-chroot-example-fix.patch +# sent upstream +Patch12: postfix-3.7.0-whitespace-name-fix.patch +# rhbz#1931403, sent upstream +Patch13: pflogsumm-1.1.5-syslog-name-underscore-fix.patch +# backported feature from upstream +Patch14: postfix-3.7.9-SRV-resolve.patch # Optional patches - set the appropriate environment variables to include # them when building the package/spec file @@ -111,21 +110,13 @@ Patch11: postfix-3.4.4-chroot-example-fix.patch # Determine the different packages required for building postfix BuildRequires: make -BuildRequires: perl-generators -BuildRequires: pkgconfig -BuildRequires: zlib-devel -BuildRequires: systemd-units -BuildRequires: libicu-devel -BuildRequires: gcc -BuildRequires: m4 -BuildRequires: findutils -BuildRequires: systemd-rpm-macros -BuildRequires: sed +BuildRequires: libdb-devel, perl-generators, pkgconfig, zlib-devel +BuildRequires: systemd-units, libicu-devel +BuildRequires: gcc, m4, findutils %if 0%{?rhel} < 9 BuildRequires: libnsl2-devel %endif -%{?with_db:BuildRequires: libdb-devel} %{?with_ldap:BuildRequires: openldap-devel} %{?with_lmdb:BuildRequires: lmdb-devel} %{?with_sasl:BuildRequires: cyrus-sasl-devel} @@ -136,18 +127,6 @@ BuildRequires: libnsl2-devel %{?with_cdb:BuildRequires: tinycdb-devel} %{?with_tls:BuildRequires: openssl-devel} -%if 0%{?defmap_lmdb} -Requires: %{name}-lmdb%{?_isa} = %{epoch}:%{version}-%{release} -%endif - -%if "%{_sbindir}" == "%{_bindir}" -# Compat symlinks for Requires in other packages. -# We rely on filesystem to create the symlinks for us. -Requires: filesystem(unmerged-sbin-symlinks) -Provides: /usr/sbin/sendmail -Provides: /usr/sbin/smtp-sink -%endif - %description Postfix is a Mail Transport Agent (MTA). @@ -256,10 +235,10 @@ maps with Postfix, you need this. %prep %setup -q # Apply obligatory patches -%patch -P1 -p1 -b .config -%patch -P2 -p1 -b .files -%patch -P3 -p1 -b .alternatives -%patch -P4 -p1 -b .large-fs +%patch1 -p1 -b .config +%patch2 -p1 -b .files +%patch3 -p1 -b .alternatives +%patch4 -p1 -b .large-fs # Change DEF_SHLIB_DIR according to build host sed -i \ @@ -269,10 +248,14 @@ src/global/mail_params.h %if %{with pflogsumm} gzip -dc %{SOURCE53} | tar xf - pushd pflogsumm-%{pflogsumm_ver} -%patch -P9 -p1 -b .pflogsumm-1.1.6-syslog-name-underscore-fix +%patch9 -p1 -b .datecalc +%patch10 -p1 -b .ipv6-warnings-fix popd %endif -%patch -P11 -p1 -b .chroot-example-fix +%patch11 -p1 -b .chroot-example-fix +%patch12 -p1 -b .whitespace-name-fix +%patch13 -p1 -b .pflogsumm-1.1.5-syslog-name-underscore-fix +%patch14 -p1 -b .SRV # Backport 3.8-20221006 fix for uname -r detection sed -i makedefs -e '\@Linux\.@s|345|3456|' @@ -283,28 +266,16 @@ for f in README_FILES/TLS_{LEGACY_,}README TLS_ACKNOWLEDGEMENTS; do touch -r ${f}{,_} && mv -f ${f}{_,} done -# fix default maps -%if 0%{?defmap_lmdb} - sed -i '/^\s*alias_maps\s*=\s*hash:\/etc\/aliases/ s|hash:|lmdb:|g' conf/main.cf - sed -i '/^\s*alias_database\s*=\s*hash:\/etc\/aliases/ s|hash:|lmdb:|g' conf/main.cf - echo >> conf/main.cf - echo "default_database_type = lmdb" >> conf/main.cf -%endif - %build %set_build_flags unset AUXLIBS AUXLIBS_LDAP AUXLIBS_LMDB AUXLIBS_PCRE AUXLIBS_MYSQL AUXLIBS_PGSQL AUXLIBS_SQLITE AUXLIBS_CDB -CCARGS="-fPIC -fcommon -std=gnu17" +CCARGS="-fPIC -fcommon" %if 0%{?rhel} >= 9 AUXLIBS="" %else AUXLIBS="-lnsl" %endif -%if %{without db} - CCARGS="${CCARGS} -DNO_DB" -%endif - %ifarch s390 s390x ppc CCARGS="${CCARGS} -fsigned-char" %endif @@ -382,7 +353,7 @@ make -f Makefile.init makefiles shared=yes dynamicmaps=yes \ # install postfix into $RPM_BUILD_ROOT # Move stuff around so we don't conflict with sendmail -for i in man1/mailq.1 man1/newaliases.1 man1/sendmail.1 man5/aliases.5 man8/smtp{,d}.8; do +for i in man1/mailq.1 man1/newaliases.1 man1/sendmail.1 man5/aliases.5 man8/smtpd.8; do dest=$(echo $i | sed 's|\.[1-9]$|.postfix\0|') mv man/$i man/$dest sed -i "s|^\.so $i|\.so $dest|" man/man?/*.[1-9] @@ -418,9 +389,6 @@ install -m 644 %{SOURCE2} %{buildroot}%{_unitdir} install -m 755 %{SOURCE4} %{buildroot}%{postfix_daemon_dir}/aliasesdb install -m 755 %{SOURCE5} %{buildroot}%{postfix_daemon_dir}/chroot-update -# systemd-sysusers -install -p -D -m 0644 %{SOURCE6} %{buildroot}%{_sysusersdir}/postfix.conf - install -c auxiliary/rmail/rmail $RPM_BUILD_ROOT%{_bindir}/rmail.postfix for i in active bounce corrupt defer deferred flush incoming private saved maildrop public pid saved trace; do @@ -466,7 +434,7 @@ find $RPM_BUILD_ROOT%{postfix_doc_dir} -type d | xargs chmod 755 %if %{with pflogsumm} install -c -m 644 pflogsumm-%{pflogsumm_ver}/pflogsumm-faq.txt $RPM_BUILD_ROOT%{postfix_doc_dir}/pflogsumm-faq.txt install -c -m 644 pflogsumm-%{pflogsumm_ver}/pflogsumm.1 $RPM_BUILD_ROOT%{_mandir}/man1/pflogsumm.1 -install -c pflogsumm-%{pflogsumm_ver}/pflogsumm $RPM_BUILD_ROOT%{postfix_command_dir}/pflogsumm +install -c pflogsumm-%{pflogsumm_ver}/pflogsumm.pl $RPM_BUILD_ROOT%{postfix_command_dir}/pflogsumm %endif # install qshape @@ -479,7 +447,9 @@ rm -f $RPM_BUILD_ROOT%{postfix_config_dir}/aliases # create /usr/lib/sendmail mkdir -p $RPM_BUILD_ROOT%{_prefix}/lib -ln -sf --relative $RPM_BUILD_ROOT%{_sbindir}/sendmail.postfix $RPM_BUILD_ROOT%{_prefix}/lib/ +pushd $RPM_BUILD_ROOT%{_prefix}/lib +ln -sf ../sbin/sendmail.postfix . +popd mkdir -p $RPM_BUILD_ROOT%{_var}/lib/misc touch $RPM_BUILD_ROOT%{_var}/lib/misc/postfix.aliasesdb-stamp @@ -487,7 +457,7 @@ touch $RPM_BUILD_ROOT%{_var}/lib/misc/postfix.aliasesdb-stamp # prepare alternatives ghosts for i in %{postfix_command_dir}/sendmail %{_bindir}/{mailq,newaliases,rmail} \ %{_sysconfdir}/pam.d/smtp %{_prefix}/lib/sendmail \ - %{_mandir}/{man1/{mailq.1,newaliases.1},man5/aliases.5,man8/{sendmail.8,smtp{,d}.8}} + %{_mandir}/{man1/{mailq.1,newaliases.1},man5/aliases.5,man8/{sendmail.8,smtpd.8}} do touch $RPM_BUILD_ROOT$i done @@ -528,27 +498,21 @@ popd readme_directory=%{postfix_readme_dir} &> /dev/null ALTERNATIVES_DOCS="" -[ "%%{_excludedocs}" = 1 ] || ALTERNATIVES_DOCS='--follower %{_mandir}/man1/mailq.1.gz mta-mailqman %{_mandir}/man1/mailq.postfix.1.gz - --follower %{_mandir}/man1/newaliases.1.gz mta-newaliasesman %{_mandir}/man1/newaliases.postfix.1.gz - --follower %{_mandir}/man8/sendmail.8.gz mta-sendmailman %{_mandir}/man1/sendmail.postfix.1.gz - --follower %{_mandir}/man5/aliases.5.gz mta-aliasesman %{_mandir}/man5/aliases.postfix.5.gz - --follower %{_mandir}/man8/smtp.8.gz mta-smtpman %{_mandir}/man8/smtp.postfix.8.gz - --follower %{_mandir}/man8/smtpd.8.gz mta-smtpdman %{_mandir}/man8/smtpd.postfix.8.gz' +[ "%%{_excludedocs}" = 1 ] || ALTERNATIVES_DOCS='--slave %{_mandir}/man1/mailq.1.gz mta-mailqman %{_mandir}/man1/mailq.postfix.1.gz + --slave %{_mandir}/man1/newaliases.1.gz mta-newaliasesman %{_mandir}/man1/newaliases.postfix.1.gz + --slave %{_mandir}/man8/sendmail.8.gz mta-sendmailman %{_mandir}/man1/sendmail.postfix.1.gz + --slave %{_mandir}/man5/aliases.5.gz mta-aliasesman %{_mandir}/man5/aliases.postfix.5.gz + --slave %{_mandir}/man8/smtpd.8.gz mta-smtpdman %{_mandir}/man8/smtpd.postfix.8.gz' -alternatives --install %{postfix_command_dir}/sendmail mta %{postfix_command_dir}/sendmail.postfix 60 \ - --follower %{_bindir}/mailq mta-mailq %{_bindir}/mailq.postfix \ - --follower %{_bindir}/newaliases mta-newaliases %{_bindir}/newaliases.postfix \ - --follower %{_sysconfdir}/pam.d/smtp mta-pam %{_sysconfdir}/pam.d/smtp.postfix \ - --follower %{_bindir}/rmail mta-rmail %{_bindir}/rmail.postfix \ - --follower %{_prefix}/lib/sendmail mta-sendmail %{_prefix}/lib/sendmail.postfix \ +%{_sbindir}/alternatives --install %{postfix_command_dir}/sendmail mta %{postfix_command_dir}/sendmail.postfix 60 \ + --slave %{_bindir}/mailq mta-mailq %{_bindir}/mailq.postfix \ + --slave %{_bindir}/newaliases mta-newaliases %{_bindir}/newaliases.postfix \ + --slave %{_sysconfdir}/pam.d/smtp mta-pam %{_sysconfdir}/pam.d/smtp.postfix \ + --slave %{_bindir}/rmail mta-rmail %{_bindir}/rmail.postfix \ + --slave %{_prefix}/lib/sendmail mta-sendmail %{_prefix}/lib/sendmail.postfix \ $ALTERNATIVES_DOCS \ --initscript postfix -# Make sure that /usr/sbin/sendmail is not missing, if /usr/sbin is a -# directory. The symlink will only be created if there is no symlink -# or file already. -test -h /usr/sbin || ln -s ../bin/sendmail /usr/sbin/sendmail 2>/dev/null || : - %if %{with sasl} # Move sasl config to new location if [ -f %{_libdir}/sasl2/smtpd.conf ]; then @@ -580,12 +544,15 @@ exit 0 %pre # Add user and groups if necessary -%sysusers_create_compat %{SOURCE6} +%{_sbindir}/groupadd -g %{maildrop_gid} -r %{maildrop_group} 2>/dev/null +%{_sbindir}/groupadd -g %{postfix_gid} -r %{postfix_group} 2>/dev/null +%{_sbindir}/groupadd -g 12 -r mail 2>/dev/null +%{_sbindir}/useradd -d %{postfix_queue_dir} -s /sbin/nologin -g %{postfix_group} -G mail -M -r -u %{postfix_uid} %{postfix_user} 2>/dev/null -# hack, to turn man8/smtp.8.gz into alternatives symlink (part of the rhbz#2274402 fix) -# this could be probably dropped in f44+ -if [ -e %{_mandir}/man8/smtp.8.gz ]; then - [ -h %{_mandir}/man8/smtp.8.gz ] || rm -f %{_mandir}/man8/smtp.8.gz +# hack, to turn man8/smtpd.8.gz into alternatives symlink (part of the rhbz#1051180 fix) +# this could be probably dropped in f23+ +if [ -e %{_mandir}/man8/smtpd.8.gz ]; then + [ -h %{_mandir}/man8/smtpd.8.gz ] || rm -f %{_mandir}/man8/smtpd.8.gz fi exit 0 @@ -594,7 +561,7 @@ exit 0 %systemd_preun %{name}.service if [ "$1" = 0 ]; then - alternatives --remove mta %{postfix_command_dir}/sendmail.postfix + %{_sbindir}/alternatives --remove mta %{postfix_command_dir}/sendmail.postfix fi exit 0 @@ -697,7 +664,7 @@ fi %attr(0644, root, root) %{_mandir}/man5/*.postfix.5* %attr(0644, root, root) %{_mandir}/man8/[a-qt-v]*.8* %attr(0644, root, root) %{_mandir}/man8/s[ch-lnp]*.8* -%attr(0644, root, root) %{_mandir}/man8/smtp.postfix.8* +%attr(0644, root, root) %{_mandir}/man8/smtp.8* %attr(0644, root, root) %{_mandir}/man8/smtpd.postfix.8* %attr(0755, root, root) %{postfix_command_dir}/smtp-sink @@ -711,7 +678,7 @@ fi %attr(0755, root, root) %{postfix_command_dir}/postfix %attr(0755, root, root) %{postfix_command_dir}/postkick %attr(0755, root, root) %{postfix_command_dir}/postlock -%attr(2755, root, %{maildrop_group}) %{postfix_command_dir}/postlog +%attr(0755, root, root) %{postfix_command_dir}/postlog %attr(0755, root, root) %{postfix_command_dir}/postmap %attr(0755, root, root) %{postfix_command_dir}/postmulti %attr(2755, root, %{maildrop_group}) %{postfix_command_dir}/postqueue @@ -754,7 +721,6 @@ fi %ghost %{_mandir}/man1/newaliases.1.gz %ghost %{_mandir}/man5/aliases.5.gz %ghost %{_mandir}/man8/sendmail.8.gz -%ghost %{_mandir}/man8/smtp.8.gz %ghost %{_mandir}/man8/smtpd.8.gz %ghost %attr(0755, root, root) %{_bindir}/mailq @@ -765,9 +731,6 @@ fi %ghost %attr(0644, root, root) %{_var}/lib/misc/postfix.aliasesdb-stamp -# systemd-sysusers -%{_sysusersdir}/postfix.conf - %if 0%{?fedora} < 23 && 0%{?rhel} < 9 %files sysvinit %{_initrddir}/postfix @@ -846,157 +809,18 @@ fi %endif %changelog -* Fri Jan 02 2026 Jaroslav Škarvada - 2:3.10.7-1 -- New version - Resolves: rhbz#2417154 - -* Thu Oct 30 2025 Jaroslav Škarvada - 2:3.10.5-1 -- New version - Resolves: rhbz#2406450 -- Changed protocol for downloading sources from FTP to HTTP (HTTPS isn't supported) - -* Mon Aug 25 2025 Jaroslav Škarvada - 2:3.10.4-1 -- New version - Resolves: rhbz#2389310 - -* Wed Aug 06 2025 František Zatloukal - 2:3.10.3-3 -- Rebuilt for icu 77.1 - -* Fri Jul 25 2025 Fedora Release Engineering - 2:3.10.3-2 -- Rebuilt for https://fedoraproject.org/wiki/Fedora_43_Mass_Rebuild - -* Thu Jul 10 2025 Jaroslav Škarvada - 2:3.10.3-1 -- New version - Resolves: rhbz#2379297 -- Updated cyrus-imapd comment in config to point to the correct location - Related: RHEL-63089 - -* Thu Jun 5 2025 Jaroslav Škarvada - 2:3.10.2-3 -- Updated pflogsumm to 1.1.6 - Resolves: rhbz#2368396 - -* Thu May 08 2025 Zbigniew Jędrzejewski-Szmek - 2:3.10.2-2 -- Make sure the /usr/sbin/sendmail symlink is created on unmerged systems - Resolves: rhbz#2360491 - -* Thu Apr 24 2025 Jaroslav Škarvada - 2:3.10.2-1 -- New version - Resolves: rhbz#2361704 - -* Thu Mar 6 2025 Jaroslav Škarvada - 2:3.10.1-1 -- New version - Resolves: rhbz#2346039 - -* Sat Jan 18 2025 Fedora Release Engineering - 2:3.9.1-4 -- Rebuilt for https://fedoraproject.org/wiki/Fedora_42_Mass_Rebuild - -* Sun Jan 12 2025 Zbigniew Jędrzejewski-Szmek - 2:3.9.1-3 -- Rebuilt for the bin-sbin merge (2nd attempt) - -* Sun Dec 08 2024 Pete Walter - 2:3.9.1-2 -- Rebuild for ICU 76 - -* Thu Dec 5 2024 Jaroslav Škarvada - 2:3.9.1-1 -- New version - Resolves: rhbz#2330454 - -* Thu Jul 25 2024 Jaroslav Škarvada - 2:3.9.0-8 -- Fixed postlog RPM verification - -* Thu Jul 25 2024 Jaroslav Škarvada - 2:3.9.0-7 -- Explicitly set default_database_type if lmdb map is used - -* Fri Jul 19 2024 Fedora Release Engineering - 2:3.9.0-6 -- Rebuilt for https://fedoraproject.org/wiki/Fedora_41_Mass_Rebuild - -* Tue Jul 09 2024 Zbigniew Jędrzejewski-Szmek - 2:3.9.0-5 -- Rebuilt for the bin-sbin merge - -* Fri Apr 12 2024 Jaroslav Škarvada - 2:3.9.0-4 -- Fixed closing quote in alternatives - Related: rhbz#2274402 - -* Thu Apr 11 2024 Jaroslav Škarvada - 2:3.9.0-3 -- Fixed typo in alternatives - Related: rhbz#2274402 - -* Thu Apr 11 2024 Jaroslav Škarvada - 2:3.9.0-2 -- Added man8/smtp.8.gz to alternatives - Resolves: rhbz#2274402 - -* Thu Mar 7 2024 Jaroslav Škarvada - 2:3.9.0-1 -- New version - Resolves: rhbz#2268245 - -* Tue Mar 5 2024 Jaroslav Škarvada - 2:3.8.6-1 -- New version - Resolves: rhbz#2267836 - -* Tue Feb 27 2024 Jaroslav Škarvada - 2:3.8.5-4 -- Dropped mail group configuration / creation, it is provided by setup - Resolves: rhbz#2244744 - -* Thu Feb 01 2024 Pete Walter - 2:3.8.5-3 -- Rebuild for ICU 74 - -* Thu Jan 25 2024 Fedora Release Engineering - 2:3.8.5-2 -- Rebuilt for https://fedoraproject.org/wiki/Fedora_40_Mass_Rebuild - -* Mon Jan 22 2024 Jaroslav Škarvada - 2:3.8.5-1 -- New version - Resolves: rhbz#2259469 - -* Sun Jan 21 2024 Fedora Release Engineering - 2:3.8.4-2 -- Rebuilt for https://fedoraproject.org/wiki/Fedora_40_Mass_Rebuild - -* Tue Jan 2 2024 Jaroslav Škarvada - 2:3.8.4-1 +* Tue Jan 2 2024 Jaroslav Škarvada - 2:3.7.9-1 - New version Resolves: rhbz#2255641 - Fixed SMTP smuggling vulnerability Resolves: CVE-2023-51764 -* Tue Dec 12 2023 Jaroslav Škarvada - 2:3.8.3-2 -- Converted license tag to SPDX - -* Thu Nov 2 2023 Jaroslav Škarvada - 2:3.8.3-1 -- New version - Resolves: rhbz#2247553 - -* Mon Oct 9 2023 Jaroslav Škarvada - 2:3.8.2-2 -- Drop libdb for RHEL>9 - Related: rhbz#1788480 - -* Tue Sep 5 2023 Jaroslav Škarvada - 2:3.8.2-1 -- New version - Resolves: rhbz#2236828 - -* Mon Aug 14 2023 Jaroslav Škarvada - 2:3.8.1-5 -- Use systemd-sysusers, original patch by - Jonathan Wright - -* Fri Jul 21 2023 Fedora Release Engineering - 2:3.8.1-4 -- Rebuilt for https://fedoraproject.org/wiki/Fedora_39_Mass_Rebuild - -* Tue Jul 11 2023 František Zatloukal - 2:3.8.1-3 -- Rebuilt for ICU 73.2 - -* Tue Jul 11 2023 Jaroslav Škarvada - 2:3.8.1-2 +* Tue Jul 11 2023 Jaroslav Škarvada - 2:3.7.4-3 - Fixed possible warning when postfix is restarted Resolves: rhbz#2218058 -* Tue Jun 6 2023 Jaroslav Škarvada - 2:3.8.1-1 -- New version - Resolves: rhbz#2212596 - -* Thu May 25 2023 Tomas Korbar - 2:3.8.0-3 -- Fix freed memory access - -* Wed Apr 26 2023 Jaroslav Škarvada - 2:3.8.0-2 -- Dropped whitespace-name-fix patch, not needed - -* Wed Apr 26 2023 Jaroslav Škarvada - 2:3.8.0-1 -- New version - Resolves: rhbz#2187121 +* Thu Jun 08 2023 Tomas Korbar - 2:3.7.4-2 +- Backport SRV resolution from 3.8.0 release * Wed Jan 25 2023 Jaroslav Škarvada - 2:3.7.4-1 - New version diff --git a/postfix.sysusers b/postfix.sysusers deleted file mode 100644 index 7778edf..0000000 --- a/postfix.sysusers +++ /dev/null @@ -1,3 +0,0 @@ -u postfix 89 - /var/spool/postfix /sbin/nologin -g postdrop 90 -m postfix mail diff --git a/sources b/sources index 195284e..648cef3 100644 --- a/sources +++ b/sources @@ -1,2 +1,2 @@ -SHA512 (pflogsumm-1.1.6.tar.gz) = 4581d2120bc17cfb322fda8d293989a2754eb9fa2696e3a1cea9032e85ed6a566e75aea1f628a6c7bbc7b9d6bf47ffeb873f95d71d7cb7f0ee46e27e492cf630 -SHA512 (postfix-3.10.7.tar.gz) = 28d7c05bfd6fb15f906c67cca989c4b47640ec631b640943deb66d91e44a6684deb79af95f0d4ba4dd362d9ba59179e5ed268942384e5e35ba472c91cf366e4c +SHA512 (postfix-3.7.9.tar.gz) = a529a2ee3e7623e258ffb7b5d8708253ec55b616a87a83f8e3200e37fff939f8cb23ee65e7c685d71aeacd3deb3f19d343a616be2fec2eaee912dd8cf0cd6a7a +SHA512 (pflogsumm-1.1.5.tar.gz) = 994d660692dfea38a1dd9866d15f15035657e85131c1f5a2cd82baa5bd4ad987a00939cb5233f316d2090014c52ae68ef20db0c893f8634969484e0e74678f4d