2
0
mirror of https://github.com/vdukhovni/postfix synced 2025-08-29 13:18:12 +00:00

postfix-3.5.25

This commit is contained in:
Wietse Z Venema 2024-03-04 00:00:00 -05:00 committed by Viktor Dukhovni
parent 785f1727c1
commit 090083cc2f
17 changed files with 287 additions and 40 deletions

View File

@ -25433,3 +25433,83 @@ Apologies for any names omitted.
Files: mantools/postlink, proto/postconf.proto,
global/mail_params.h, global/smtp_stream.c, global/smtp_stream.h,
smtpd/smtpd.c, smtpd/smtpd_check.[hc].
20231102
Bugfix (defect introduced: Postfix 2.3, date 20051222): the
Dovecot auth client did not reset the 'reason' from a
previous Dovecot auth service response, before parsing the
next Dovecot auth server response in the same SMTP session.
Reported by Stephan Bosch, File: xsasl/xsasl_dovecot_server.c.
20231105
Cleanup: Postfix SMTP server response with an empty
authentication failure reason. File: smtpd/smtpd_sasl_glue.c.
20231208
Bugfix (defect introduced: Postfix 3.1, date: 20151128):
"postqueue -j" produced broken JSON when escaping a control
character as \uXXXX. Found during code maintenance. File:
postqueue/showq_json.c.
20231211
Cleanup: posttls-finger certificate match expectations for
all TLS security levels, including warnings for levels that
don't implement certificate matching. Viktor Dukhovni.
File: posttls-finger.c.
20231213
Bugfix (defect introduced: Postfix 2.3): after prepending
a message header with a Postfix access table PREPEND action,
a Milter request to delete or update an existing header
could have no effect, or it could target the wrong instance
of an existing header. Root cause: the fix dated 20141018
for the Postfix Milter client was incomplete. The client
did correctly hide the first, Postfix-generated, Received:
header when sending message header information to a Milter
with the smfi_header() application callback function, but
it was still hiding the first header (instead of the first
Received: header) when handling requests from a Milter to
delete or update an existing header. Problem report by
Carlos Velasco. This change was verified to have no effect
on requests from a Milter to add or insert a header. File:
cleanup/cleanup_milter.c.
20240124
Workaround: tlsmgr logfile spam. Some OS lies under load:
it says that a socket is readable, then it says that the
socket has unread data, and then it says that read returns
EOF, causing Postfix to spam the log with a warning message.
File: tlsmgr/tlsmgr.c.
Bugfix (defect introduced: Postfix 3.4): the SMTP server's
BDAT command handler could be tricked to read $message_size_limit
bytes into memory. Found during code maintenance. File:
smtpd/smtpd.c.
20240209
Performance: eliminate worst-case behavior where the queue
manager defers delivery to all destinations over a specific
delivery transport, after only a single delivery agent
failure. The scheduler now throttles one destination, and
allows deliveries to other destinations to keep making
progress. Files: *qmgr/qmgr_deliver.c.
20240226
Safety: drop and log over-size DNS responses resulting in
more than 100 records. This 20x larger than the number of
server addresses that the Postfix SMTP client is willing
to consider when delivering mail, and is well below the
number of records that could cause a tail recursion crash
in dns_rr_append() as reported by Toshifumi Sakaguchi. This
also limits the number of DNS requests from check_*_*_access
restrictions. Files: dns/dns.h, dns/dns_lookup.c, dns/dns_rr.c,
dns/test_dns_lookup.c, posttls-finger/posttls-finger.c,
smtp/smtp_addr.c, smtpd/smtpd_check.c.

View File

@ -119,6 +119,7 @@
#include <dsn_util.h>
#include <xtext.h>
#include <info_log_addr_form.h>
#include <header_opts.h>
/* Application-specific. */
@ -754,14 +755,26 @@ static const char *cleanup_add_header(void *context, const char *name,
*/
}
/* hidden_header - respect milter header hiding protocol */
static int hidden_header(VSTRING *buf, ARGV *auto_hdrs, int *hide_done)
{
char **cpp;
int mask;
for (cpp = auto_hdrs->argv, mask = 1; *cpp; cpp++, mask <<= 1)
if ((*hide_done & mask) == 0 && strncmp(*cpp, STR(buf), LEN(buf)) == 0)
return (*hide_done |= mask);
return (0);
}
/* cleanup_find_header_start - find specific header instance */
static off_t cleanup_find_header_start(CLEANUP_STATE *state, ssize_t index,
const char *header_label,
VSTRING *buf,
int *prec_type,
int allow_ptr_backup,
int skip_headers)
int allow_ptr_backup)
{
const char *myname = "cleanup_find_header_start";
off_t curr_offset; /* offset after found record */
@ -770,7 +783,7 @@ static off_t cleanup_find_header_start(CLEANUP_STATE *state, ssize_t index,
int rec_type = REC_TYPE_ERROR;
int last_type;
ssize_t len;
int hdr_count = 0;
int hide_done = 0;
if (msg_verbose)
msg_info("%s: index %ld name \"%s\"",
@ -912,11 +925,10 @@ static off_t cleanup_find_header_start(CLEANUP_STATE *state, ssize_t index,
break;
}
/* This the start of a message header. */
else if (hdr_count++ < skip_headers)
/* Reset the saved PTR record and update last_type. */ ;
else if ((header_label == 0
|| (strncasecmp(header_label, STR(buf), len) == 0
&& (strlen(header_label) == len)))
&& strlen(header_label) == len
&& !hidden_header(buf, state->auto_hdrs, &hide_done)))
&& --index == 0) {
/* If we have a saved PTR record, it points to start of header. */
break;
@ -1182,15 +1194,12 @@ static const char *cleanup_ins_header(void *context, ssize_t index,
*/
#define NO_HEADER_NAME ((char *) 0)
#define ALLOW_PTR_BACKUP 1
#define SKIP_ONE_HEADER 1
#define DONT_SKIP_HEADERS 0
if (index < 1)
index = 1;
old_rec_offset = cleanup_find_header_start(state, index, NO_HEADER_NAME,
old_rec_buf, &old_rec_type,
ALLOW_PTR_BACKUP,
DONT_SKIP_HEADERS);
ALLOW_PTR_BACKUP);
if (old_rec_offset == CLEANUP_FIND_HEADER_IOERROR)
/* Warning and errno->error mapping are done elsewhere. */
CLEANUP_INS_HEADER_RETURN(cleanup_milter_error(state, 0));
@ -1270,8 +1279,7 @@ static const char *cleanup_upd_header(void *context, ssize_t index,
rec_buf = vstring_alloc(100);
old_rec_offset = cleanup_find_header_start(state, index, new_hdr_name,
rec_buf, &last_type,
NO_PTR_BACKUP,
SKIP_ONE_HEADER);
NO_PTR_BACKUP);
if (old_rec_offset == CLEANUP_FIND_HEADER_IOERROR)
/* Warning and errno->error mapping are done elsewhere. */
CLEANUP_UPD_HEADER_RETURN(cleanup_milter_error(state, 0));
@ -1333,8 +1341,7 @@ static const char *cleanup_del_header(void *context, ssize_t index,
rec_buf = vstring_alloc(100);
header_offset = cleanup_find_header_start(state, index, hdr_name, rec_buf,
&last_type, NO_PTR_BACKUP,
SKIP_ONE_HEADER);
&last_type, NO_PTR_BACKUP);
if (header_offset == CLEANUP_FIND_HEADER_IOERROR)
/* Warning and errno->error mapping are done elsewhere. */
CLEANUP_DEL_HEADER_RETURN(cleanup_milter_error(state, 0));

View File

@ -148,11 +148,17 @@ typedef struct DNS_RR {
unsigned int ttl; /* always */
unsigned int dnssec_valid; /* DNSSEC validated */
unsigned short pref; /* T_MX only */
/* Assume that flags lives in what was previously padding */
unsigned short flags; /* DNS_RR_FLAG_XX, see below */
struct DNS_RR *next; /* linkage */
size_t data_len; /* actual data size */
char data[1]; /* actually a bunch of data */
} DNS_RR;
#define DNS_RR_FLAG_TRUNCATED (1<<0)
#define DNS_RR_IS_TRUNCATED(rr) ((rr)->flags & DNS_RR_FLAG_TRUNCATED)
/*
* dns_strerror.c
*/
@ -186,6 +192,7 @@ extern int dns_rr_compare_pref_any(DNS_RR *, DNS_RR *);
extern int dns_rr_compare_pref(DNS_RR *, DNS_RR *);
extern DNS_RR *dns_rr_shuffle(DNS_RR *);
extern DNS_RR *dns_rr_remove(DNS_RR *, DNS_RR *);
extern int var_dns_rr_list_limit;
/*
* dns_rr_to_pa.c

View File

@ -911,6 +911,8 @@ 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);
if (DNS_RR_IS_TRUNCATED(*rrlist))
break;
} else if (status == DNS_NULLMX) {
CORRUPT(status); /* TODO: use better name */
} else if (not_found_status != DNS_RETRY)
@ -1135,8 +1137,11 @@ int dns_lookup_rl(const char *name, unsigned flags, DNS_RR **rrlist,
name, dns_strtype(type), dns_str_resflags(flags));
status = dns_lookup_x(name, type, flags, rrlist ? &rr : (DNS_RR **) 0,
fqdn, why, rcode, lflags);
if (rrlist && rr)
if (rrlist && rr) {
*rrlist = dns_rr_append(*rrlist, rr);
if (DNS_RR_IS_TRUNCATED(*rrlist))
break;
}
if (status == DNS_OK) {
if (lflags & DNS_REQ_FLAG_STOP_OK)
break;
@ -1187,8 +1192,11 @@ int dns_lookup_rv(const char *name, unsigned flags, DNS_RR **rrlist,
name, dns_strtype(type), dns_str_resflags(flags));
status = dns_lookup_x(name, type, flags, rrlist ? &rr : (DNS_RR **) 0,
fqdn, why, rcode, lflags);
if (rrlist && rr)
if (rrlist && rr) {
*rrlist = dns_rr_append(*rrlist, rr);
if (DNS_RR_IS_TRUNCATED(*rrlist))
break;
}
if (status == DNS_OK) {
if (lflags & DNS_REQ_FLAG_STOP_OK)
break;

View File

@ -49,6 +49,8 @@
/* DNS_RR *dns_rr_remove(list, record)
/* DNS_RR *list;
/* DNS_RR *record;
/*
/* int var_dns_rr_list_limit;
/* DESCRIPTION
/* The routines in this module maintain memory for DNS resource record
/* information, and maintain lists of DNS resource records.
@ -65,9 +67,17 @@
/*
/* dns_rr_copy() makes a copy of a resource record.
/*
/* dns_rr_append() appends a resource record to a (list of) resource
/* record(s).
/* A null input list is explicitly allowed.
/* dns_rr_append() appends an input resource record list to
/* an output list. Null arguments are explicitly allowed.
/* When the result would be longer than var_dns_rr_list_limit
/* (default: 100), dns_rr_append() logs a warning, flags the
/* output list as truncated, and discards the excess elements.
/* Once an output list is flagged as truncated (test with
/* DNS_RR_IS_TRUNCATED()), the caller is expected to stop
/* trying to append records to that list. Note: the 'truncated'
/* flag is transitive, i.e. when appending a input list that
/* was flagged as truncated to an output list, the output list
/* will also be flagged as truncated.
/*
/* dns_rr_sort() sorts a list of resource records into ascending
/* order according to a user-specified criterion. The result is the
@ -108,6 +118,16 @@
#include "dns.h"
/*
* A generous safety limit for the number of DNS resource records that the
* Postfix DNS client library will admit into a list. The default value 100
* is 20x the default limit on the number address records that the Postfix
* SMTP client is willing to consider.
*
* Mutable, to make code testable.
*/
int var_dns_rr_list_limit = 100;
/* dns_rr_create - fill in resource record structure */
DNS_RR *dns_rr_create(const char *qname, const char *rname,
@ -129,6 +149,7 @@ DNS_RR *dns_rr_create(const char *qname, const char *rname,
memcpy(rr->data, data, data_len);
rr->data_len = data_len;
rr->next = 0;
rr->flags = 0;
return (rr);
}
@ -163,14 +184,58 @@ DNS_RR *dns_rr_copy(DNS_RR *src)
return (dst);
}
/* dns_rr_append - append resource record to list */
/* dns_rr_append_with_limit - append resource record to limited list */
static void dns_rr_append_with_limit(DNS_RR *list, DNS_RR *rr, int limit)
{
/*
* Pre: list != 0, all lists are concatenated with dns_rr_append().
*
* Post: all elements have the DNS_RR_FLAG_TRUNCATED flag value set, or all
* elements have it cleared, so that there is no need to update code in
* legacy stable releases that deletes or reorders elements.
*/
if (limit <= 1) {
if (list->next || rr) {
msg_warn("DNS record count limit (%d) exceeded -- dropping"
" excess record(s) after qname=%s qtype=%s",
var_dns_rr_list_limit, list->qname,
dns_strtype(list->type));
list->flags |= DNS_RR_FLAG_TRUNCATED;
dns_rr_free(list->next);
dns_rr_free(rr);
list->next = 0;
}
} else {
if (list->next == 0 && rr) {
list->next = rr;
rr = 0;
}
if (list->next) {
dns_rr_append_with_limit(list->next, rr, limit - 1);
list->flags |= list->next->flags;
}
}
}
/* dns_rr_append - append resource record(s) to list, or discard */
DNS_RR *dns_rr_append(DNS_RR *list, DNS_RR *rr)
{
if (list == 0) {
list = rr;
/*
* Note: rr is not length checked; when multiple lists are concatenated,
* the output length may be a small multiple of var_dns_rr_list_limit.
*/
if (rr == 0)
return (list);
if (list == 0)
return (rr);
if (!DNS_RR_IS_TRUNCATED(list)) {
dns_rr_append_with_limit(list, rr, var_dns_rr_list_limit);
} else {
list->next = dns_rr_append(list->next, rr);
dns_rr_free(rr);
}
return (list);
}

View File

@ -119,9 +119,11 @@ int main(int argc, char **argv)
vstream_printf("%s: fqdn: %s\n", name, vstring_str(fqdn));
buf = vstring_alloc(100);
print_rr(buf, rr);
vstream_fflush(VSTREAM_OUT);
if (DNS_RR_IS_TRUNCATED(rr))
msg_warn("one or more excess DNS_RR records were dropped");
dns_rr_free(rr);
vstring_free(buf);
vstream_fflush(VSTREAM_OUT);
}
}
myfree((void *) types);

View File

@ -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 "20240121"
#define MAIL_VERSION_NUMBER "3.5.24"
#define MAIL_RELEASE_DATE "20240304"
#define MAIL_VERSION_NUMBER "3.5.25"
#ifdef SNAPSHOT
#define MAIL_VERSION_DATE "-" MAIL_RELEASE_DATE

View File

@ -285,6 +285,7 @@ static void qmgr_deliver_update(int unused_event, void *context)
* The queue itself won't go away before we dispose of the current queue
* entry.
*/
#if 0
if (status == DELIVER_STAT_CRASH) {
message->flags |= DELIVER_STAT_DEFER;
#if 0
@ -319,6 +320,7 @@ static void qmgr_deliver_update(int unused_event, void *context)
qmgr_defer_transport(transport, &dsb->dsn);
return;
}
#endif
/*
* This message must be tried again.
@ -333,7 +335,9 @@ static void qmgr_deliver_update(int unused_event, void *context)
*/
#define SUSPENDED "delivery temporarily suspended: "
if (status == DELIVER_STAT_DEFER) {
if (status == DELIVER_STAT_CRASH)
DSN_SIMPLE(&dsb->dsn, "4.3.0", "unknown mail transport error");
if (status == DELIVER_STAT_CRASH || status == DELIVER_STAT_DEFER) {
message->flags |= DELIVER_STAT_DEFER;
if (VSTRING_LEN(dsb->status)) {
/* Sanitize the DSN status/reason from the delivery agent. */

View File

@ -96,7 +96,7 @@ static char *json_quote(VSTRING *result, const char *text)
VSTRING_ADDCH(result, 't');
break;
default:
vstring_sprintf(result, "\\u%04X", ch);
vstring_sprintf_append(result, "\\u%04X", ch);
break;
}
} else {

View File

@ -1244,6 +1244,8 @@ static DNS_RR *addr_one(STATE *state, DNS_RR *addr_list, const char *host,
msg_fatal("host %s: conversion error for address family %d: %m",
host, ((struct sockaddr *) (res0->ai_addr))->sa_family);
addr_list = dns_rr_append(addr_list, addr);
if (DNS_RR_IS_TRUNCATED(addr_list))
break;
}
freeaddrinfo(res0);
if (found == 0) {
@ -1281,6 +1283,8 @@ static DNS_RR *mx_addr_list(STATE *state, DNS_RR *mx_names)
msg_panic("%s: bad resource type: %d", myname, rr->type);
addr_list = addr_one(state, addr_list, (char *) rr->data, res_opt,
rr->pref);
if (addr_list && DNS_RR_IS_TRUNCATED(addr_list))
break;
}
return (addr_list);
}
@ -2048,8 +2052,20 @@ static void parse_match(STATE *state, int argc, char *argv[])
{
#ifdef USE_TLS
/*
* DANE match names are configured late, once the TLSA records are in
* hand. For now, prepare to fall back to "secure".
*/
switch (state->level) {
case TLS_LEV_SECURE:
default:
state->match = 0;
if (*argv)
msg_warn("TLS level '%s' does not implement certificate matching",
str_tls_level(state->level));
break;
case TLS_LEV_DANE:
case TLS_LEV_DANE_ONLY:
case TLS_LEV_SECURE:
state->match = argv_alloc(2);
while (*argv)
argv_split_append(state->match, *argv++, "");
@ -2069,11 +2085,6 @@ static void parse_match(STATE *state, int argc, char *argv[])
tls_dane_add_ee_digests((TLS_DANE *) state->dane,
state->mdalg, *argv++, "");
break;
case TLS_LEV_DANE:
case TLS_LEV_DANE_ONLY:
state->match = argv_alloc(2);
argv_add(state->match, "nexthop", "hostname", ARGV_END);
break;
}
#endif
}

View File

@ -290,6 +290,7 @@ static void qmgr_deliver_update(int unused_event, void *context)
* The queue itself won't go away before we dispose of the current queue
* entry.
*/
#if 0
if (status == DELIVER_STAT_CRASH) {
message->flags |= DELIVER_STAT_DEFER;
#if 0
@ -324,6 +325,7 @@ static void qmgr_deliver_update(int unused_event, void *context)
qmgr_defer_transport(transport, &dsb->dsn);
return;
}
#endif
/*
* This message must be tried again.
@ -338,7 +340,9 @@ static void qmgr_deliver_update(int unused_event, void *context)
*/
#define SUSPENDED "delivery temporarily suspended: "
if (status == DELIVER_STAT_DEFER) {
if (status == DELIVER_STAT_CRASH)
DSN_SIMPLE(&dsb->dsn, "4.3.0", "unknown mail transport error");
if (status == DELIVER_STAT_CRASH || status == DELIVER_STAT_DEFER) {
message->flags |= DELIVER_STAT_DEFER;
if (VSTRING_LEN(dsb->status)) {
/* Sanitize the DSN status/reason from the delivery agent. */

View File

@ -230,6 +230,8 @@ static DNS_RR *smtp_addr_one(DNS_RR *addr_list, const char *host, int res_opt,
msg_fatal("host %s: conversion error for address family "
"%d: %m", host, res0->ai_addr->sa_family);
addr_list = dns_rr_append(addr_list, addr);
if (DNS_RR_IS_TRUNCATED(addr_list))
break;
}
freeaddrinfo(res0);
if (found == 0) {
@ -287,6 +289,8 @@ static DNS_RR *smtp_addr_list(DNS_RR *mx_names, DSN_BUF *why)
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);
if (addr_list && DNS_RR_IS_TRUNCATED(addr_list))
break;
}
return (addr_list);
}
@ -381,6 +385,13 @@ static DNS_RR *smtp_balance_inet_proto(DNS_RR *addr_list, int misc_flags,
* relative list order is unchanged, but some elements are removed.
*/
/*
* Ensure that dns_rr_append() won't interfere with the protocol
* balancing goals.
*/
if (addr_limit > var_dns_rr_list_limit)
addr_limit = var_dns_rr_list_limit;
/*
* Count the number of IPv6 and IPv4 addresses.
*/

View File

@ -4053,14 +4053,31 @@ static int bdat_cmd(SMTPD_STATE *state, int argc, SMTPD_TOKEN *argv)
/*
* Read lines from the fragment. The last line may continue in the
* next fragment, or in the next chunk.
*
* If smtp_get_noexcept() stopped after var_line_limit bytes and did not
* emit a queue file record, then that means smtp_get_noexcept()
* stopped after CR and hit EOF as it tried to find out if the next
* byte is LF. In that case, read the first byte from the next
* fragment or chunk, and if that first byte is LF, then
* smtp_get_noexcept() strips off the trailing CRLF and returns '\n'
* as it always does after reading a complete line.
*/
do {
int can_read = var_line_limit - LEN(state->bdat_get_buffer);
if (smtp_get_noexcept(state->bdat_get_buffer,
state->bdat_get_stream,
var_line_limit,
can_read > 0 ? can_read : 1, /* Peek one */
SMTP_GET_FLAG_APPEND) == '\n') {
/* Stopped at end-of-line. */
curr_rec_type = REC_TYPE_NORM;
} else if (LEN(state->bdat_get_buffer) > var_line_limit) {
/* Undo peeking, and output the buffer as REC_TYPE_CONT. */
vstream_ungetc(state->bdat_get_stream,
vstring_end(state->bdat_get_buffer)[-1]);
vstring_truncate(state->bdat_get_buffer,
LEN(state->bdat_get_buffer) - 1);
curr_rec_type = REC_TYPE_CONT;
} else if (!vstream_feof(state->bdat_get_stream)) {
/* Stopped at var_line_limit. */
curr_rec_type = REC_TYPE_CONT;

View File

@ -2985,6 +2985,7 @@ static int check_server_access(SMTPD_STATE *state, const char *table,
struct addrinfo *res;
int status;
INET_PROTO_INFO *proto_info;
int server_addr_count = 0;
/*
* Sanity check.
@ -3134,6 +3135,15 @@ static int check_server_access(SMTPD_STATE *state, const char *table,
msg_info("%s: %s host address check: %s",
myname, dns_strtype(type), (char *) server->data);
for (res = res0; res != 0; res = res->ai_next) {
server_addr_count += 1;
if (server_addr_count > var_dns_rr_list_limit) {
msg_warn("%s: %s server address count limit (%d) exceeded"
" for %s %s -- ignoring the remainder", myname,
dns_strtype(type), var_dns_rr_list_limit,
reply_class, reply_name);
freeaddrinfo(res0);
CHECK_SERVER_RETURN(SMTPD_CHECK_DUNNO);
}
if (strchr((char *) proto_info->sa_family_list, res->ai_family) == 0) {
if (msg_verbose)
msg_info("skipping address family %d for host %s",

View File

@ -321,18 +321,20 @@ int smtpd_sasl_authenticate(SMTPD_STATE *state,
}
}
if (status != XSASL_AUTH_DONE) {
const char *reason = (*STR(state->sasl_reply) ? STR(state->sasl_reply) :
"(reason unavailable)");
sasl_username = xsasl_server_get_username(state->sasl_server);
msg_warn("%s: SASL %.100s authentication failed: %s, sasl_username=%.100s",
state->namaddr, sasl_method, *STR(state->sasl_reply) ?
STR(state->sasl_reply) : "(reason unavailable)",
state->namaddr, sasl_method, reason,
sasl_username ? sasl_username : "(unavailable)");
/* RFC 4954 Section 6. */
if (status == XSASL_AUTH_TEMP)
smtpd_chat_reply(state, "454 4.7.0 Temporary authentication failure: %s",
STR(state->sasl_reply));
reason);
else
smtpd_chat_reply(state, "535 5.7.8 Error: authentication failed: %s",
STR(state->sasl_reply));
reason);
return (-1);
}
/* RFC 4954 Section 6. */

View File

@ -818,6 +818,23 @@ static void tlsmgr_service(VSTREAM *client_stream, char *unused_service,
}
}
/*
* Workaround: some OS lies under load. It tells the Postfix event
* handler that a server socket is readable, then it tells peekfd() that
* the socket has unread data, and then it tells vstring_get_null() that
* there is none, causing Postfix to spam the log with warning messages.
* Close the stream to stop such nonsense; the client can reconnect if it
* still wants to talk to us.
*
* XXX Why is this problem not reported for the other five
* multi_server-based Postfix services?
*/
else if (vstream_ferror(client_stream) || vstream_feof(client_stream)) {
multi_server_disconnect(client_stream);
return;
/* Note: client_stream is now a dangling pointer. */
}
/*
* Protocol error.
*/

View File

@ -542,6 +542,8 @@ static void xsasl_dovecot_parse_reply_args(XSASL_DOVECOT_SERVER *server,
myfree(server->username);
server->username = 0;
}
VSTRING_RESET(reply);
VSTRING_TERMINATE(reply);
/*
* Note: TAB is part of the Dovecot protocol and must not appear in