Prereq: "3.3.14"
diff -ur --new-file /var/tmp/postfix-3.3.14/src/global/mail_version.h ./src/global/mail_version.h
--- /var/tmp/postfix-3.3.14/src/global/mail_version.h	2020-07-26 14:12:19.000000000 -0400
+++ ./src/global/mail_version.h	2020-11-07 16:31:48.000000000 -0500
@@ -20,8 +20,8 @@
   * Patches change both the patchlevel and the release date. Snapshots have no
   * patchlevel; they change the release date only.
   */
-#define MAIL_RELEASE_DATE	"20200726"
-#define MAIL_VERSION_NUMBER	"3.3.14"
+#define MAIL_RELEASE_DATE	"20201107"
+#define MAIL_VERSION_NUMBER	"3.3.15"
 
 #ifdef SNAPSHOT
 #define MAIL_VERSION_DATE	"-" MAIL_RELEASE_DATE
diff -ur --new-file /var/tmp/postfix-3.3.14/HISTORY ./HISTORY
--- /var/tmp/postfix-3.3.14/HISTORY	2020-07-26 13:57:51.000000000 -0400
+++ ./HISTORY	2020-11-04 18:06:52.000000000 -0500
@@ -23656,3 +23656,43 @@
 
 	Bugfix (introduced: Postfix 3.3.13): part of a memory leak
 	fix was backported to the wrong place. File: tls/tls_misc.c.
+
+20200830
+
+	Bugfix (introduced: Postfix 2.0): smtp_sasl_mechanism_filter
+	ignored table lookup errors, treating them as 'not found'.
+	Found during Postfix 3.6 development. File: smtp/smtp_sasl_proto.c.
+
+202000920
+
+	Bugfix (introduced: Postfix 2.3): when deleting a recipient
+	with a milter, delete the recipient from the duplicate
+	filter, so that the recipient can be added back. Backported
+	from Postfix 3.6. Files: global/been_here.[hc],
+	cleanup/cleanup_milter.c.
+
+20200925
+
+	Bugfix (introduced: before Postfix alpha): the code that
+	looks for Delivered-To: headers ignored headers longer than
+	$line_length_limit. Backported from Postfix 3.6. File:
+	global/delivered_hdr.c.
+
+20201011
+
+	Bugfix (introduced: Postfix 2.8): save a copy of the
+	postscreen_dnsbl_reply_map lookup result. This has no effect
+	when the recommended texthash: look table is used, but it
+	may avoid stale data with other lookup tables. File:
+	postscreen/postscreen_dnsbl.c.
+
+20201022
+
+	Bugfix (introduced: Postfix 2.2): after processing an
+	XCCLIENT command, the smtps service was waiting for a TLS
+	handshake. Found by Aki Tuomi. File: smtpd/smtpd.c.
+
+20201025
+
+	Bugfix (introduced: Postfix 2.3): static maps did not free
+	their casefolding buffer. File: util/dict_static.c.
diff -ur --new-file /var/tmp/postfix-3.3.14/src/cleanup/cleanup_milter.c ./src/cleanup/cleanup_milter.c
--- /var/tmp/postfix-3.3.14/src/cleanup/cleanup_milter.c	2017-12-23 19:13:13.000000000 -0500
+++ ./src/cleanup/cleanup_milter.c	2020-11-04 17:27:33.000000000 -0500
@@ -1800,6 +1800,10 @@
 		}
 		count++;
 	    }
+	    if (var_enable_orcpt)
+		been_here_drop(state->dups, "%s\n%d\n%s\n%s",
+			       dsn_orcpt ? dsn_orcpt : "", dsn_notify,
+			     orig_rcpt ? orig_rcpt : "", STR(int_rcpt_buf));
 	    /* FALLTHROUGH */
 	case REC_TYPE_DRCP:			/* canceled recipient */
 	case REC_TYPE_DONE:			/* can't happen */
@@ -1815,6 +1819,8 @@
 	    break;
 	}
     }
+    if (var_enable_orcpt == 0 && count > 0)
+	been_here_drop_fixed(state->dups, STR(int_rcpt_buf));
 
     if (msg_verbose)
 	msg_info("%s: deleted %d records for recipient \"%s\"",
diff -ur --new-file /var/tmp/postfix-3.3.14/src/global/been_here.c ./src/global/been_here.c
--- /var/tmp/postfix-3.3.14/src/global/been_here.c	2015-01-27 14:13:22.000000000 -0500
+++ ./src/global/been_here.c	2020-11-04 17:27:33.000000000 -0500
@@ -26,6 +26,14 @@
 /*	BH_TABLE *dup_filter;
 /*	char	*format;
 /*
+/*	int	been_here_drop_fixed(dup_filter, string)
+/*	BH_TABLE *dup_filter;
+/*	char	*string;
+/*
+/*	int	been_here_drop(dup_filter, format, ...)
+/*	BH_TABLE *dup_filter;
+/*	char	*format;
+/*
 /*	void	been_here_free(dup_filter)
 /*	BH_TABLE *dup_filter;
 /* DESCRIPTION
@@ -46,6 +54,16 @@
 /*	been_here_check_fixed() and been_here_check() are similar
 /*	but do not update the duplicate filter.
 /*
+/*	been_here_drop_fixed() looks up a fixed string in the given
+/*	table, and deletes the entry if the string was found. The
+/*	result is non-zero (true) if the string was found, zero
+/*	(false) otherwise.
+/*
+/*	been_here_drop() formats its arguments, looks up the result
+/*	in the given table, and removes the entry if the formatted
+/*	result was found. The result is non-zero (true) if the
+/*	formatted result was found, zero (false) otherwise.
+/*
 /*	been_here_free() releases storage for a duplicate filter.
 /*
 /*	Arguments:
@@ -249,3 +267,64 @@
 
     return (status);
 }
+
+/* been_here_drop - remove filter entry with finer control */
+
+int     been_here_drop(BH_TABLE *dup_filter, const char *fmt,...)
+{
+    VSTRING *buf = vstring_alloc(100);
+    int     status;
+    va_list ap;
+
+    /*
+     * Construct the string to be dropped.
+     */
+    va_start(ap, fmt);
+    vstring_vsprintf(buf, fmt, ap);
+    va_end(ap);
+
+    /*
+     * Drop the filter entry.
+     */
+    status = been_here_drop_fixed(dup_filter, vstring_str(buf));
+
+    /*
+     * Cleanup.
+     */
+    vstring_free(buf);
+    return (status);
+}
+
+/* been_here_drop_fixed - remove filter entry */
+
+int     been_here_drop_fixed(BH_TABLE *dup_filter, const char *string)
+{
+    VSTRING *folded_string;
+    const char *lookup_key;
+    int     status;
+
+    /*
+     * Special processing: case insensitive lookup.
+     */
+    if (dup_filter->flags & BH_FLAG_FOLD) {
+	folded_string = vstring_alloc(100);
+	lookup_key = casefold(folded_string, string);
+    } else {
+	folded_string = 0;
+	lookup_key = string;
+    }
+
+    /*
+     * Drop the filter entry.
+     */
+    if ((status = been_here_check_fixed(dup_filter, lookup_key)) != 0)
+	htable_delete(dup_filter->table, lookup_key, (void (*) (void *)) 0);
+
+    /*
+     * Cleanup.
+     */
+    if (folded_string)
+	vstring_free(folded_string);
+
+    return (status);
+}
diff -ur --new-file /var/tmp/postfix-3.3.14/src/global/been_here.h ./src/global/been_here.h
--- /var/tmp/postfix-3.3.14/src/global/been_here.h	2000-07-15 13:47:42.000000000 -0400
+++ ./src/global/been_here.h	2020-11-04 17:27:33.000000000 -0500
@@ -34,6 +34,8 @@
 extern int PRINTFLIKE(2, 3) been_here(BH_TABLE *, const char *,...);
 extern int been_here_check_fixed(BH_TABLE *, const char *);
 extern int PRINTFLIKE(2, 3) been_here_check(BH_TABLE *, const char *,...);
+extern int been_here_drop_fixed(BH_TABLE *, const char *);
+extern int PRINTFLIKE(2, 3) been_here_drop(BH_TABLE *, const char *,...);
 
 /* LICENSE
 /* .ad
diff -ur --new-file /var/tmp/postfix-3.3.14/src/global/delivered_hdr.c ./src/global/delivered_hdr.c
--- /var/tmp/postfix-3.3.14/src/global/delivered_hdr.c	2015-01-27 10:22:33.000000000 -0500
+++ ./src/global/delivered_hdr.c	2020-11-04 17:29:00.000000000 -0500
@@ -115,6 +115,8 @@
     char   *cp;
     DELIVERED_HDR_INFO *info;
     const HEADER_OPTS *hdr;
+    int     curr_type;
+    int     prev_type;
 
     /*
      * Sanity check.
@@ -130,15 +132,20 @@
 
     /*
      * XXX Assume that mail_copy() produces delivered-to headers that fit in
-     * a REC_TYPE_NORM record. Lowercase the delivered-to addresses for
-     * consistency.
+     * a REC_TYPE_NORM or REC_TYPE_CONT record. Lowercase the delivered-to
+     * addresses for consistency.
      * 
      * XXX Don't get bogged down by gazillions of delivered-to headers.
      */
 #define DELIVERED_HDR_LIMIT	1000
 
-    while (rec_get(fp, info->buf, 0) == REC_TYPE_NORM
-	   && info->table->used < DELIVERED_HDR_LIMIT) {
+    for (prev_type = REC_TYPE_NORM;
+	 info->table->used < DELIVERED_HDR_LIMIT
+	 && ((curr_type = rec_get(fp, info->buf, 0)) == REC_TYPE_NORM
+	     || curr_type == REC_TYPE_CONT);
+	 prev_type = curr_type) {
+	if (prev_type != REC_TYPE_NORM)
+	    continue;
 	if (is_header(STR(info->buf))) {
 	    if ((hdr = header_opts_find(STR(info->buf))) != 0
 		&& hdr->type == HDR_DELIVERED_TO) {
diff -ur --new-file /var/tmp/postfix-3.3.14/src/postscreen/postscreen_dnsbl.c ./src/postscreen/postscreen_dnsbl.c
--- /var/tmp/postfix-3.3.14/src/postscreen/postscreen_dnsbl.c	2017-02-18 20:58:20.000000000 -0500
+++ ./src/postscreen/postscreen_dnsbl.c	2020-11-04 17:31:23.000000000 -0500
@@ -231,6 +231,7 @@
     int     weight;
     HTABLE_INFO *ht;
     char   *parse_err;
+    const char  *safe_dnsbl;
 
     /*
      * Parse the required DNSBL domain name, the optional reply filter and
@@ -271,8 +272,9 @@
 	ht = htable_enter(dnsbl_site_cache, saved_site, (void *) head);
 	/* Translate the DNSBL name into a safe name if available. */
 	if (psc_dnsbl_reply == 0
-	 || (head->safe_dnsbl = dict_get(psc_dnsbl_reply, saved_site)) == 0)
-	    head->safe_dnsbl = ht->key;
+	    || (safe_dnsbl = dict_get(psc_dnsbl_reply, saved_site)) == 0)
+	    safe_dnsbl = ht->key;
+	head->safe_dnsbl = mystrdup(safe_dnsbl);
 	if (psc_dnsbl_reply && psc_dnsbl_reply->error)
 	    msg_fatal("%s:%s lookup error", psc_dnsbl_reply->type,
 		      psc_dnsbl_reply->name);
diff -ur --new-file /var/tmp/postfix-3.3.14/src/smtp/smtp_sasl_proto.c ./src/smtp/smtp_sasl_proto.c
--- /var/tmp/postfix-3.3.14/src/smtp/smtp_sasl_proto.c	2014-12-12 14:33:27.000000000 -0500
+++ ./src/smtp/smtp_sasl_proto.c	2020-11-04 18:36:14.000000000 -0500
@@ -102,6 +102,8 @@
 	    if (VSTRING_LEN(buf) > 0)
 		VSTRING_ADDCH(buf, ' ');
 	    vstring_strcat(buf, mech);
+	} else if (smtp_sasl_mechs->error) {
+	    msg_fatal("SASL mechanism filter failed for: '%s'", mech);
 	}
     }
     myfree(save_mech);
diff -ur --new-file /var/tmp/postfix-3.3.14/src/smtpd/smtpd.c ./src/smtpd/smtpd.c
--- /var/tmp/postfix-3.3.14/src/smtpd/smtpd.c	2020-03-12 10:53:37.000000000 -0400
+++ ./src/smtpd/smtpd.c	2020-11-04 17:32:23.000000000 -0500
@@ -5022,7 +5022,8 @@
 	 * obsolete, so we don't have to provide perfect support.
 	 */
 #ifdef USE_TLS
-	if (SMTPD_STAND_ALONE(state) == 0 && var_smtpd_tls_wrappermode) {
+	if (SMTPD_STAND_ALONE(state) == 0 && var_smtpd_tls_wrappermode
+	    && state->tls_context == 0) {
 #ifdef USE_TLSPROXY
 	    /* We garbage-collect the VSTREAM in smtpd_state_reset() */
 	    state->tlsproxy = tls_proxy_open(var_tlsproxy_service,
diff -ur --new-file /var/tmp/postfix-3.3.14/src/util/dict_static.c ./src/util/dict_static.c
--- /var/tmp/postfix-3.3.14/src/util/dict_static.c	2014-12-11 14:02:29.000000000 -0500
+++ ./src/util/dict_static.c	2020-11-04 17:36:57.000000000 -0500
@@ -52,6 +52,8 @@
 
 static void dict_static_close(DICT *dict)
 {
+    if (dict->fold_buf)
+	vstring_free(dict->fold_buf);
     dict_free(dict);
 }