*************************************************/
/* Copyright (c) University of Cambridge 1995 - 2018 */
+/* Copyright (c) The Exim Maintainers 2020 */
/* See the file NOTICE for conditions of use and distribution. */
/* Functions for handling an incoming SMTP call. */
static struct {
BOOL auth_advertised :1;
-#ifdef SUPPORT_TLS
+#ifndef DISABLE_TLS
BOOL tls_advertised :1;
-# ifdef EXPERIMENTAL_REQUIRETLS
- BOOL requiretls_advertised :1;
-# endif
#endif
BOOL dsn_advertised :1;
BOOL esmtp :1;
BOOL helo_verify :1;
BOOL helo_seen :1;
BOOL helo_accept_junk :1;
-#ifdef EXPERIMENTAL_PIPE_CONNECT
+#ifndef DISABLE_PIPE_CONNECT
BOOL pipe_connect_acceptable :1;
#endif
BOOL rcpt_smtp_response_same :1;
{ "helo", sizeof("helo")-1, HELO_CMD, TRUE, FALSE },
{ "ehlo", sizeof("ehlo")-1, EHLO_CMD, TRUE, FALSE },
{ "auth", sizeof("auth")-1, AUTH_CMD, TRUE, TRUE },
-#ifdef SUPPORT_TLS
+#ifndef DISABLE_TLS
{ "starttls", sizeof("starttls")-1, STARTTLS_CMD, FALSE, FALSE },
{ "tls_auth", 0, TLS_AUTH_CMD, FALSE, FALSE },
#endif
ENV_MAIL_OPT_RET, ENV_MAIL_OPT_ENVID,
#ifdef SUPPORT_I18N
ENV_MAIL_OPT_UTF8,
-#endif
-#ifdef EXPERIMENTAL_REQUIRETLS
- ENV_MAIL_OPT_REQTLS,
#endif
};
typedef struct {
{ US"ENVID", ENV_MAIL_OPT_ENVID, TRUE },
#ifdef SUPPORT_I18N
{ US"SMTPUTF8",ENV_MAIL_OPT_UTF8, FALSE }, /* rfc6531 */
-#endif
-#ifdef EXPERIMENTAL_REQUIRETLS
- /* https://tools.ietf.org/html/draft-ietf-uta-smtp-require-tls-03 */
- { US"REQUIRETLS",ENV_MAIL_OPT_REQTLS, FALSE },
#endif
/* keep this the last entry */
{ US"NULL", ENV_MAIL_OPT_NULL, FALSE },
fd_set fds;
struct timeval tzero;
-#ifdef SUPPORT_TLS
+#ifndef DISABLE_TLS
if (tls_in.active.sock >= 0)
return !tls_could_read();
#endif
}
-#ifdef EXPERIMENTAL_PIPE_CONNECT
+#ifndef DISABLE_PIPE_CONNECT
static BOOL
pipeline_connect_sends(void)
{
if (recipients_count > 0)
{
- raw_recipients = store_get(recipients_count * sizeof(uschar *));
+ raw_recipients = store_get(recipients_count * sizeof(uschar *), FALSE);
for (int i = 0; i < recipients_count; i++)
raw_recipients[i] = recipients_list[i].address;
raw_recipients_count = recipients_count;
smtp_notquit_exit(US"command-timeout", US"421",
US"%s: SMTP command timeout - closing connection",
smtp_active_hostname);
-exim_exit(EXIT_FAILURE, US"receiving");
+exim_exit(EXIT_FAILURE);
}
void
moan_smtp_batch(NULL, "421 SIGTERM received"); /* Does not return */
smtp_notquit_exit(US"signal-exit", US"421",
US"%s: Service not available - closing connection", smtp_active_hostname);
-exim_exit(EXIT_FAILURE, US"receiving");
+exim_exit(EXIT_FAILURE);
}
void
smtp_data_sigint_exit();
smtp_had_error = save_errno;
- smtp_read_error = string_copy_malloc(
- string_sprintf(" (error: %s)", strerror(save_errno)));
+ smtp_read_error = string_copy_perm(
+ string_sprintf(" (error: %s)", strerror(save_errno)), FALSE);
}
else
smtp_had_eof = 1;
}
+/* Forward declarations */
+static inline void bdat_push_receive_functions(void);
+static inline void bdat_pop_receive_functions(void);
+
+
/* Get a byte from the smtp input, in CHUNKING mode. Handle ack of the
previous BDAT chunk and getting new ones when we run out. Uses the
underlying smtp_getc or tls_getc both for that and for getting the
if (chunking_data_left > 0)
return lwr_receive_getc(chunking_data_left--);
- receive_getc = lwr_receive_getc;
- receive_getbuf = lwr_receive_getbuf;
- receive_ungetc = lwr_receive_ungetc;
+ bdat_pop_receive_functions();
#ifndef DISABLE_DKIM
dkim_save = dkim_collect_input;
dkim_collect_input = 0;
goto repeat_until_rset;
}
- receive_getc = bdat_getc;
- receive_getbuf = bdat_getbuf; /* r~getbuf is never actually used */
- receive_ungetc = bdat_ungetc;
+ bdat_push_receive_functions();
#ifndef DISABLE_DKIM
dkim_collect_input = dkim_save;
#endif
if (!bdat_getbuf(&n)) break;
}
-receive_getc = lwr_receive_getc;
-receive_getbuf = lwr_receive_getbuf;
-receive_ungetc = lwr_receive_ungetc;
+bdat_pop_receive_functions();
if (chunking_state != CHUNKING_LAST)
{
}
+static inline void
+bdat_push_receive_functions(void)
+{
+/* push the current receive_* function on the "stack", and
+replace them by bdat_getc(), which in turn will use the lwr_receive_*
+functions to do the dirty work. */
+if (lwr_receive_getc == NULL)
+ {
+ lwr_receive_getc = receive_getc;
+ lwr_receive_getbuf = receive_getbuf;
+ lwr_receive_ungetc = receive_ungetc;
+ }
+else
+ {
+ DEBUG(D_receive) debug_printf("chunking double-push receive functions\n");
+ }
+
+receive_getc = bdat_getc;
+receive_getbuf = bdat_getbuf;
+receive_ungetc = bdat_ungetc;
+}
+
+static inline void
+bdat_pop_receive_functions(void)
+{
+if (lwr_receive_getc == NULL)
+ {
+ DEBUG(D_receive) debug_printf("chunking double-pop receive functions\n");
+ return;
+ }
+receive_getc = lwr_receive_getc;
+receive_getbuf = lwr_receive_getbuf;
+receive_ungetc = lwr_receive_ungetc;
+lwr_receive_getc = NULL;
+lwr_receive_getbuf = NULL;
+lwr_receive_ungetc = NULL;
+}
/*************************************************
* SMTP version of ungetc() *
int
smtp_ungetc(int ch)
{
+if (smtp_inptr <= smtp_inbuffer)
+ log_write(0, LOG_MAIN|LOG_PANIC_DIE, "buffer underflow in smtp_ungetc");
+
*--smtp_inptr = ch;
return ch;
}
checking that: for convenience, TLS output errors are remembered here so that
they are also picked up later by smtp_fflush().
+This function is exposed to the local_scan API; do not change the signature.
+
Arguments:
format format string
more further data expected
/* This is split off so that verify.c:respond_printf() can, in effect, call
smtp_printf(), bearing in mind that in C a vararg function can't directly
-call another vararg function, only a function which accepts a va_list. */
+call another vararg function, only a function which accepts a va_list.
+
+This function is exposed to the local_scan API; do not change the signature.
+*/
+/*XXX consider passing caller-info in, for string_vformat-onward */
void
smtp_vprintf(const char *format, BOOL more, va_list ap)
gstring gs = { .size = big_buffer_size, .ptr = 0, .s = big_buffer };
BOOL yield;
-yield = !! string_vformat(&gs, FALSE, format, ap);
+/* Use taint-unchecked routines for writing into big_buffer, trusting
+that we'll never expand it. */
+
+yield = !! string_vformat(&gs, SVFMT_TAINT_NOCHK, format, ap);
string_from_gstring(&gs);
DEBUG(D_receive)
{
- void *reset_point = store_get(0);
uschar *msg_copy, *cr, *end;
msg_copy = string_copy(gs.s);
end = msg_copy + gs.ptr;
while ((cr = Ustrchr(msg_copy, '\r')) != NULL) /* lose CRs */
memmove(cr, cr + 1, (end--) - cr);
debug_printf("SMTP>> %s", msg_copy);
- store_reset(reset_point);
}
if (!yield)
{
log_write(0, LOG_MAIN|LOG_PANIC, "string too large in smtp_printf()");
smtp_closedown(US"Unexpected error");
- exim_exit(EXIT_FAILURE, NULL);
+ exim_exit(EXIT_FAILURE);
}
/* If this is the first output for a (non-batch) RCPT command, see if all RCPTs
/* Now write the string */
-#ifdef SUPPORT_TLS
-if (tls_in.active.sock >= 0)
- {
- if (tls_write(NULL, gs.s, gs.ptr, more) < 0)
- smtp_write_error = -1;
- }
-else
+if (
+#ifndef DISABLE_TLS
+ tls_in.active.sock >= 0 ? (tls_write(NULL, gs.s, gs.ptr, more) < 0) :
#endif
-
-if (fprintf(smtp_out, "%s", gs.s) < 0) smtp_write_error = -1;
+ (fwrite(gs.s, gs.ptr, 1, smtp_out) == 0)
+ )
+ smtp_write_error = -1;
}
/* This function isn't currently used within Exim (it detects errors when it
tries to read the next SMTP input), but is available for use in local_scan().
-For non-TLS connections, it flushes the output and checks for errors. For
-TLS-connections, it checks for a previously-detected TLS write error.
+It flushes the output and checks for errors.
Arguments: none
Returns: 0 for no error; -1 after an error
smtp_fflush(void)
{
if (tls_in.active.sock < 0 && fflush(smtp_out) != 0) smtp_write_error = -1;
+
+if (
+#ifndef DISABLE_TLS
+ tls_in.active.sock >= 0 ? (tls_write(NULL, NULL, 0, FALSE) < 0) :
+#endif
+ (fflush(smtp_out) != 0)
+ )
+ smtp_write_error = -1;
+
return smtp_write_error;
}
-#ifdef SUPPORT_TLS
+#ifndef DISABLE_TLS
/* Append TLS-related information to a log line
Arguments:
s_tlslog(gstring * g)
{
if (LOGGING(tls_cipher) && tls_in.cipher)
+ {
g = string_append(g, 2, US" X=", tls_in.cipher);
+#ifdef EXPERIMENTAL_TLS_RESUME
+ if (LOGGING(tls_resumption) && tls_in.resumption & RESUME_USED)
+ g = string_catn(g, US"*", 1);
+#endif
+ }
if (LOGGING(tls_certificate_verified) && tls_in.cipher)
g = string_append(g, 2, US" CV=", tls_in.certificate_verified? "yes":"no");
if (LOGGING(tls_peerdn) && tls_in.peerdn)
g = string_append(g, 3, US" DN=\"", string_printing(tls_in.peerdn), US"\"");
if (LOGGING(tls_sni) && tls_in.sni)
- g = string_append(g, 3, US" SNI=\"", string_printing(tls_in.sni), US"\"");
+ g = string_append(g, 2, US" SNI=", string_printing2(tls_in.sni, SP_TAB|SP_SPACE));
return g;
}
#endif
+
+
+static gstring *
+s_connhad_log(gstring * g)
+{
+uschar * sep = smtp_connection_had[SMTP_HBUFF_SIZE-1] != SCH_NONE
+ ? US" C=..." : US" C=";
+
+for (int i = smtp_ch_index; i < SMTP_HBUFF_SIZE; i++)
+ if (smtp_connection_had[i] != SCH_NONE)
+ {
+ g = string_append(g, 2, sep, smtp_names[smtp_connection_had[i]]);
+ sep = US",";
+ }
+for (int i = 0; i < smtp_ch_index; i++)
+ {
+ g = string_append(g, 2, sep, smtp_names[smtp_connection_had[i]]);
+ sep = US",";
+ }
+return g;
+}
+
+
/*************************************************
* Log lack of MAIL if so configured *
*************************************************/
void
smtp_log_no_mail(void)
{
-uschar * sep, * s;
+uschar * s;
gstring * g = NULL;
if (smtp_mailcmd_count > 0 || !LOGGING(smtp_no_mail))
if (authenticated_id) g = string_append(g, 2, US":", authenticated_id);
}
-#ifdef SUPPORT_TLS
+#ifndef DISABLE_TLS
g = s_tlslog(g);
#endif
-sep = smtp_connection_had[SMTP_HBUFF_SIZE-1] != SCH_NONE ? US" C=..." : US" C=";
-
-for (int i = smtp_ch_index; i < SMTP_HBUFF_SIZE; i++)
- if (smtp_connection_had[i] != SCH_NONE)
- {
- g = string_append(g, 2, sep, smtp_names[smtp_connection_had[i]]);
- sep = US",";
- }
-
-for (int i = 0; i < smtp_ch_index; i++)
- {
- g = string_append(g, 2, sep, smtp_names[smtp_connection_had[i]]);
- sep = US",";
- }
+g = s_connhad_log(g);
if (!(s = string_from_gstring(g))) s = US"";
/* Discard any previous helo name */
-if (sender_helo_name)
- {
- store_free(sender_helo_name);
- sender_helo_name = NULL;
- }
+sender_helo_name = NULL;
/* Skip tests if junk is permitted. */
/* Save argument if OK */
-if (yield) sender_helo_name = string_copy_malloc(start);
+if (yield) sender_helo_name = string_copy_perm(start, TRUE);
return yield;
}
extract_option(uschar **name, uschar **value)
{
uschar *n;
-uschar *v = smtp_cmd_data + Ustrlen(smtp_cmd_data) - 1;
-while (isspace(*v)) v--;
+uschar *v;
+if (Ustrlen(smtp_cmd_data) <= 0) return FALSE;
+v = smtp_cmd_data + Ustrlen(smtp_cmd_data) - 1;
+while (v > smtp_cmd_data && isspace(*v)) v--;
v[1] = 0;
+
while (v > smtp_cmd_data && *v != '=' && !isspace(*v))
{
/* Take care to not stop at a space embedded in a quoted local-part */
-
- if (*v == '"') do v--; while (*v != '"' && v > smtp_cmd_data+1);
+ if (*v == '"')
+ {
+ do v--; while (v > smtp_cmd_data && *v != '"');
+ if (v <= smtp_cmd_data) return FALSE;
+ }
v--;
}
+if (v <= smtp_cmd_data) return FALSE;
n = v;
if (*v == '=')
{
- while(isalpha(n[-1])) n--;
+ while (n > smtp_cmd_data && isalpha(n[-1])) n--;
/* RFC says SP, but TAB seen in wild and other major MTAs accept it */
- if (!isspace(n[-1])) return FALSE;
+ if (n <= smtp_cmd_data || !isspace(n[-1])) return FALSE;
n[-1] = 0;
}
else
{
n++;
- if (v == smtp_cmd_data) return FALSE;
}
*v++ = 0;
*name = n;
Returns: nothing
*/
-void
+void *
smtp_reset(void *reset_point)
{
recipients_list = NULL;
f.active_local_sender_retain = local_sender_retain; /* Can be set by ACL */
sending_ip_address = NULL;
return_path = sender_address = NULL;
-sender_data = NULL; /* Can be set by ACL */
+deliver_localpart_data = deliver_domain_data =
+recipient_data = sender_data = NULL; /* Can be set by ACL */
deliver_localpart_parent = deliver_localpart_orig = NULL;
deliver_domain_parent = deliver_domain_orig = NULL;
callout_address = NULL;
dkim_verify_overall = dkim_verify_status = dkim_verify_reason = NULL;
dkim_key_length = 0;
#endif
-#ifdef EXPERIMENTAL_DMARC
+#ifdef SUPPORT_DMARC
f.dmarc_has_been_checked = f.dmarc_disable_verify = f.dmarc_enable_forensic = FALSE;
dmarc_domain_policy = dmarc_status = dmarc_status_text =
dmarc_used_domain = NULL;
#endif
#ifdef EXPERIMENTAL_ARC
arc_state = arc_state_reason = NULL;
+arc_received_instance = 0;
#endif
dsn_ret = 0;
dsn_envid = NULL;
store_free(this);
}
store_reset(reset_point);
+return store_mark();
}
smtp_setup_batch_msg(void)
{
int done = 0;
-void *reset_point = store_get(0);
+rmark reset_point = store_mark();
/* Save the line count at the start of each transaction - single commands
like HELO and RSET count as whole transactions. */
if ((receive_feof)()) return 0; /* Treat EOF as QUIT */
cancel_cutthrough_connection(TRUE, US"smtp_setup_batch_msg");
-smtp_reset(reset_point); /* Reset for start of message */
+reset_point = smtp_reset(reset_point); /* Reset for start of message */
/* Deal with SMTP commands. This loop is exited by setting done to a POSITIVE
value. The values are 2 larger than the required yield of the function. */
case RSET_CMD:
cancel_cutthrough_connection(TRUE, US"RSET received");
- smtp_reset(reset_point);
+ reset_point = smtp_reset(reset_point);
bsmtp_transaction_linecount = receive_linecount;
break;
/* Reset to start of message */
cancel_cutthrough_connection(TRUE, US"MAIL received");
- smtp_reset(reset_point);
+ reset_point = smtp_reset(reset_point);
/* Apply SMTP rewrite */
- raw_sender = ((rewrite_existflags & rewrite_smtp) != 0)?
- rewrite_one(smtp_cmd_data, rewrite_smtp|rewrite_smtp_sender, NULL, FALSE,
- US"", global_rewrite_rules) : smtp_cmd_data;
+ raw_sender = rewrite_existflags & rewrite_smtp
+ /* deconst ok as smtp_cmd_data was not const */
+ ? US rewrite_one(smtp_cmd_data, rewrite_smtp|rewrite_smtp_sender, NULL,
+ FALSE, US"", global_rewrite_rules)
+ : smtp_cmd_data;
/* Extract the address; the TRUE flag allows <> as valid */
&& sender_address[0] != 0 && sender_address[0] != '@')
if (f.allow_unqualified_sender)
{
- sender_address = rewrite_address_qualify(sender_address, FALSE);
+ /* deconst ok as sender_address was not const */
+ sender_address = US rewrite_address_qualify(sender_address, FALSE);
DEBUG(D_receive) debug_printf("unqualified address %s accepted "
"and rewritten\n", raw_sender);
}
recipient address */
recipient = rewrite_existflags & rewrite_smtp
- ? rewrite_one(smtp_cmd_data, rewrite_smtp, NULL, FALSE, US"",
+ /* deconst ok as smtp_cmd_data was not const */
+ ? US rewrite_one(smtp_cmd_data, rewrite_smtp, NULL, FALSE, US"",
global_rewrite_rules)
: smtp_cmd_data;
{
DEBUG(D_receive) debug_printf("unqualified address %s accepted\n",
recipient);
- recipient = rewrite_address_qualify(recipient, TRUE);
+ /* deconst ok as recipient was not const */
+ recipient = US rewrite_address_qualify(recipient, TRUE);
}
/* The function moan_smtp_batch() does not return. */
else
-#ifdef SUPPORT_TLS
+#ifndef DISABLE_TLS
static BOOL
smtp_log_tls_fail(uschar * errstr)
{
static void
tfo_in_check(void)
{
-# ifdef TCP_INFO
+# ifdef __FreeBSD__
+int is_fastopen;
+socklen_t len = sizeof(is_fastopen);
+
+/* The tinfo TCPOPT_FAST_OPEN bit seems unreliable, and we don't see state
+TCP_SYN_RCV (as of 12.1) so no idea about data-use. */
+
+if (getsockopt(fileno(smtp_out), IPPROTO_TCP, TCP_FASTOPEN, &is_fastopen, &len) == 0)
+ {
+ if (is_fastopen)
+ {
+ DEBUG(D_receive)
+ debug_printf("TFO mode connection (TCP_FASTOPEN getsockopt)\n");
+ f.tcp_in_fastopen = TRUE;
+ }
+ }
+else DEBUG(D_receive)
+ debug_printf("TCP_INFO getsockopt: %s\n", strerror(errno));
+
+# elif defined(TCP_INFO)
struct tcp_info tinfo;
socklen_t len = sizeof(tinfo);
if (getsockopt(fileno(smtp_out), IPPROTO_TCP, TCP_INFO, &tinfo, &len) == 0)
-#ifdef TCPI_OPT_SYN_DATA /* FreeBSD 11 does not seem to have this yet */
+# ifdef TCPI_OPT_SYN_DATA /* FreeBSD 11,12 do not seem to have this yet */
if (tinfo.tcpi_options & TCPI_OPT_SYN_DATA)
{
- DEBUG(D_receive) debug_printf("TCP_FASTOPEN mode connection (ACKd data-on-SYN)\n");
+ DEBUG(D_receive)
+ debug_printf("TFO mode connection (ACKd data-on-SYN)\n");
f.tcp_in_fastopen_data = f.tcp_in_fastopen = TRUE;
}
else
-#endif
- if (tinfo.tcpi_state == TCP_SYN_RECV)
+# endif
+ if (tinfo.tcpi_state == TCP_SYN_RECV) /* Not seen on FreeBSD 12.1 */
{
- DEBUG(D_receive) debug_printf("TCP_FASTOPEN mode connection (state TCP_SYN_RECV)\n");
+ DEBUG(D_receive)
+ debug_printf("TFO mode connection (state TCP_SYN_RECV)\n");
f.tcp_in_fastopen = TRUE;
}
+else DEBUG(D_receive)
+ debug_printf("TCP_INFO getsockopt: %s\n", strerror(errno));
# endif
}
#endif
sender_host_auth_pubname = sender_host_authenticated = NULL;
authenticated_by = NULL;
-#ifdef SUPPORT_TLS
-tls_in.cipher = tls_in.peerdn = NULL;
+#ifndef DISABLE_TLS
+tls_in.ver = tls_in.cipher = tls_in.peerdn = NULL;
tls_in.ourcert = tls_in.peercert = NULL;
tls_in.sni = NULL;
tls_in.ocsp = OCSP_NOT_REQ;
fl.tls_advertised = FALSE;
-# ifdef EXPERIMENTAL_REQUIRETLS
-fl.requiretls_advertised = FALSE;
-# endif
#endif
fl.dsn_advertised = FALSE;
#ifdef SUPPORT_I18N
acl_var_c = NULL;
-/* Allow for trailing 0 in the command and data buffers. */
+/* Allow for trailing 0 in the command and data buffers. Tainted. */
-if (!(smtp_cmd_buffer = US malloc(2*SMTP_CMD_BUFFER_SIZE + 2)))
- log_write(0, LOG_MAIN|LOG_PANIC_DIE,
- "malloc() failed for SMTP command buffer");
+smtp_cmd_buffer = store_get_perm(2*SMTP_CMD_BUFFER_SIZE + 2, TRUE);
smtp_cmd_buffer[0] = 0;
smtp_data_buffer = smtp_cmd_buffer + SMTP_CMD_BUFFER_SIZE + 1;
receive_feof = smtp_feof;
receive_ferror = smtp_ferror;
receive_smtp_buffered = smtp_buffered;
+lwr_receive_getc = NULL;
+lwr_receive_getbuf = NULL;
+lwr_receive_ungetc = NULL;
smtp_inptr = smtp_inend = smtp_inbuffer;
smtp_had_eof = smtp_had_error = 0;
{
#if OPTSTYLE == 1
EXIM_SOCKLEN_T optlen = sizeof(struct ip_options) + MAX_IPOPTLEN;
- struct ip_options *ipopt = store_get(optlen);
+ struct ip_options *ipopt = store_get(optlen, FALSE);
#elif OPTSTYLE == 2
struct ip_opts ipoptblock;
struct ip_opts *ipopt = &ipoptblock;
/* Start up TLS if tls_on_connect is set. This is for supporting the legacy
smtps port for use with older style SSL MTAs. */
-#ifdef SUPPORT_TLS
+#ifndef DISABLE_TLS
if (tls_in.on_connect)
{
if (tls_server_start(tls_require_ciphers, &user_msg) != OK)
/* Before we write the banner, check that there is no input pending, unless
this synchronisation check is disabled. */
-#ifdef EXPERIMENTAL_PIPE_CONNECT
+#ifndef DISABLE_PIPE_CONNECT
fl.pipe_connect_acceptable =
sender_host_address && verify_check_host(&pipe_connect_advertise_hosts) == OK;
#endif
{
unsigned n = smtp_inend - smtp_inptr;
- if (n > 32) n = 32;
+ if (n > 128) n = 128;
log_write(0, LOG_MAIN|LOG_REJECT, "SMTP protocol "
"synchronization error (input sent without waiting for greeting): "
/*XXX the ehlo-resp code does its own tls/nontls bit. Maybe subroutine that? */
smtp_printf("%s",
-#ifdef EXPERIMENTAL_PIPE_CONNECT
+#ifndef DISABLE_PIPE_CONNECT
fl.pipe_connect_acceptable && pipeline_connect_sends(),
#else
FALSE,
handshake arrived. If so we must have managed a TFO. */
#ifdef TCP_FASTOPEN
-tfo_in_check();
+if (sender_host_address && !f.sender_host_notsocket) tfo_in_check();
#endif
return TRUE;
int yield = -1;
log_write(type, LOG_MAIN, "SMTP %s error in \"%s\" %s %s",
- (type == L_smtp_syntax_error)? "syntax" : "protocol",
+ type == L_smtp_syntax_error ? "syntax" : "protocol",
string_printing(smtp_cmd_buffer), host_and_ident(TRUE), errmess);
if (++synprot_error_count > smtp_max_synprot_errors)
{
yield = 1;
log_write(0, LOG_MAIN|LOG_REJECT, "SMTP call from %s dropped: too many "
- "syntax or protocol errors (last command was \"%s\")",
- host_and_ident(FALSE), string_printing(smtp_cmd_buffer));
+ "syntax or protocol errors (last command was \"%s\", %s)",
+ host_and_ident(FALSE), string_printing(smtp_cmd_buffer),
+ string_from_gstring(s_connhad_log(NULL))
+ );
}
if (code > 0)
{
smtp_printf("%.3s-%.*s%.*s\r\n", TRUE, code, esclen, esc, (int)(nl - msg), msg);
msg = nl + 1;
- while (isspace(*msg)) msg++;
+ Uskip_whitespace(&msg);
}
}
}
if (log_reject_target != 0)
{
-#ifdef SUPPORT_TLS
+#ifndef DISABLE_TLS
gstring * g = s_tlslog(NULL);
uschar * tls = string_from_gstring(g);
if (!tls) tls = US"";
va_list ap;
va_start(ap, defaultrespond);
- g = string_vformat(NULL, TRUE, CS defaultrespond, ap);
+ g = string_vformat(NULL, SVFMT_EXTEND|SVFMT_REBUFFER, CS defaultrespond, ap);
va_end(ap);
smtp_printf("%s %s\r\n", FALSE, code, string_from_gstring(g));
}
switch(rc)
{
case OK:
- if (!au->set_id || set_id) /* Complete success */
- {
- if (set_id) authenticated_id = string_copy_malloc(set_id);
- sender_host_authenticated = au->name;
- sender_host_auth_pubname = au->public_name;
- authentication_failed = FALSE;
- authenticated_fail_id = NULL; /* Impossible to already be set? */
-
- received_protocol =
- (sender_host_address ? protocols : protocols_local)
- [pextend + pauthed + (tls_in.active.sock >= 0 ? pcrpted:0)];
- *s = *ss = US"235 Authentication succeeded";
- authenticated_by = au;
- break;
- }
+ if (!au->set_id || set_id) /* Complete success */
+ {
+ if (set_id) authenticated_id = string_copy_perm(set_id, TRUE);
+ sender_host_authenticated = au->name;
+ sender_host_auth_pubname = au->public_name;
+ authentication_failed = FALSE;
+ authenticated_fail_id = NULL; /* Impossible to already be set? */
+
+ received_protocol =
+ (sender_host_address ? protocols : protocols_local)
+ [pextend + pauthed + (tls_in.active.sock >= 0 ? pcrpted:0)];
+ *s = *ss = US"235 Authentication succeeded";
+ authenticated_by = au;
+ break;
+ }
- /* Authentication succeeded, but we failed to expand the set_id string.
- Treat this as a temporary error. */
+ /* Authentication succeeded, but we failed to expand the set_id string.
+ Treat this as a temporary error. */
- auth_defer_msg = expand_string_message;
- /* Fall through */
+ auth_defer_msg = expand_string_message;
+ /* Fall through */
case DEFER:
- if (set_id) authenticated_fail_id = string_copy_malloc(set_id);
- *s = string_sprintf("435 Unable to authenticate at present%s",
- auth_defer_user_msg);
- *ss = string_sprintf("435 Unable to authenticate at present%s: %s",
- set_id, auth_defer_msg);
- break;
+ if (set_id) authenticated_fail_id = string_copy_perm(set_id, TRUE);
+ *s = string_sprintf("435 Unable to authenticate at present%s",
+ auth_defer_user_msg);
+ *ss = string_sprintf("435 Unable to authenticate at present%s: %s",
+ set_id, auth_defer_msg);
+ break;
case BAD64:
- *s = *ss = US"501 Invalid base64 data";
- break;
+ *s = *ss = US"501 Invalid base64 data";
+ break;
case CANCELLED:
- *s = *ss = US"501 Authentication cancelled";
- break;
+ *s = *ss = US"501 Authentication cancelled";
+ break;
case UNEXPECTED:
- *s = *ss = US"553 Initial data not expected";
- break;
+ *s = *ss = US"553 Initial data not expected";
+ break;
case FAIL:
- if (set_id) authenticated_fail_id = string_copy_malloc(set_id);
- *s = US"535 Incorrect authentication data";
- *ss = string_sprintf("535 Incorrect authentication data%s", set_id);
- break;
+ if (set_id) authenticated_fail_id = string_copy_perm(set_id, TRUE);
+ *s = US"535 Incorrect authentication data";
+ *ss = string_sprintf("535 Incorrect authentication data%s", set_id);
+ break;
default:
- if (set_id) authenticated_fail_id = string_copy_malloc(set_id);
- *s = US"435 Internal error";
- *ss = string_sprintf("435 Internal error%s: return %d from authentication "
- "check", set_id, rc);
- break;
+ if (set_id) authenticated_fail_id = string_copy_perm(set_id, TRUE);
+ *s = US"435 Internal error";
+ *ss = string_sprintf("435 Internal error%s: return %d from authentication "
+ "check", set_id, rc);
+ break;
}
return rc;
DEBUG(D_receive) debug_printf("unqualified address %s accepted\n",
*recipient);
rd = Ustrlen(recipient) + 1;
- *recipient = rewrite_address_qualify(*recipient, TRUE);
+ /* deconst ok as *recipient was not const */
+ *recipient = US rewrite_address_qualify(*recipient, TRUE);
return rd;
}
smtp_printf("501 %s: recipient address must contain a domain\r\n", FALSE,
log_write(0, LOG_MAIN|LOG_PANIC, "ACL for QUIT returned ERROR: %s",
*log_msgp);
}
+
+#ifdef TCP_CORK
+(void) setsockopt(fileno(smtp_out), IPPROTO_TCP, TCP_CORK, US &on, sizeof(on));
+#endif
+
if (*user_msgp)
smtp_respond(US"221", 3, TRUE, *user_msgp);
else
smtp_printf("221 %s closing connection\r\n", FALSE, smtp_active_hostname);
-#ifdef SUPPORT_TLS
+#ifndef DISABLE_TLS
tls_close(NULL, TLS_SHUTDOWN_NOWAIT);
#endif
BOOL discarded = FALSE;
BOOL last_was_rej_mail = FALSE;
BOOL last_was_rcpt = FALSE;
-void *reset_point = store_get(0);
+rmark reset_point = store_mark();
DEBUG(D_receive) debug_printf("smtp_setup_msg entered\n");
TLS between messages (an Exim client may do this if it has messages queued up
for the host). Note: we do NOT reset AUTH at this point. */
-smtp_reset(reset_point);
+reset_point = smtp_reset(reset_point);
message_ended = END_NOTSTARTED;
chunking_state = f.chunking_offered ? CHUNKING_OFFERED : CHUNKING_NOT_OFFERED;
cmd_list[CMD_LIST_RSET].is_mail_cmd = TRUE;
cmd_list[CMD_LIST_HELO].is_mail_cmd = TRUE;
cmd_list[CMD_LIST_EHLO].is_mail_cmd = TRUE;
-#ifdef SUPPORT_TLS
+#ifndef DISABLE_TLS
cmd_list[CMD_LIST_STARTTLS].is_mail_cmd = TRUE;
#endif
+if (lwr_receive_getc != NULL)
+ {
+ /* This should have already happened, but if we've gotten confused,
+ force a reset here. */
+ DEBUG(D_receive) debug_printf("WARNING: smtp_setup_msg had to restore receive functions to lowers\n");
+ bdat_pop_receive_functions();
+ }
+
/* Set the local signal handler for SIGTERM - it tries to end off tidily */
had_command_sigterm = 0;
#endif
switch(smtp_read_command(
-#ifdef EXPERIMENTAL_PIPE_CONNECT
+#ifndef DISABLE_PIPE_CONNECT
!fl.pipe_connect_acceptable,
#else
TRUE,
if (++synprot_error_count > smtp_max_synprot_errors)
{
log_write(0, LOG_MAIN|LOG_REJECT, "SMTP call from %s dropped: too many "
- "syntax or protocol errors (last command was \"%s\")",
- host_and_ident(FALSE), string_printing(smtp_cmd_buffer));
+ "syntax or protocol errors (last command was \"%s\", %s)",
+ host_and_ident(FALSE), string_printing(smtp_cmd_buffer),
+ string_from_gstring(s_connhad_log(NULL))
+ );
done = 1;
}
#ifdef SUPPORT_SPF
/* set up SPF context */
- spf_init(sender_helo_name, sender_host_address);
+ spf_conn_init(sender_helo_name, sender_host_address);
#endif
/* Apply an ACL check if one is defined; afterwards, recheck
&user_msg, &log_msg)) != OK)
{
done = smtp_handle_acl_fail(ACL_WHERE_HELO, rc, user_msg, log_msg);
- if (sender_helo_name)
- {
- store_free(sender_helo_name);
- sender_helo_name = NULL;
- }
+ sender_helo_name = NULL;
host_build_sender_fullhost(); /* Rebuild */
break;
}
-#ifdef EXPERIMENTAL_PIPE_CONNECT
+#ifndef DISABLE_PIPE_CONNECT
else if (!fl.pipe_connect_acceptable && !check_sync())
#else
else if (!check_sync())
fl.auth_advertised = FALSE;
f.smtp_in_pipelining_advertised = FALSE;
-#ifdef SUPPORT_TLS
+#ifndef DISABLE_TLS
fl.tls_advertised = FALSE;
-# ifdef EXPERIMENTAL_REQUIRETLS
- fl.requiretls_advertised = FALSE;
-# endif
#endif
fl.dsn_advertised = FALSE;
#ifdef SUPPORT_I18N
smtp_code = US"250 "; /* Default response code plus space*/
if (!user_msg)
{
- g = string_fmt_append(NULL, "%.3s %s Hello %s%s%s",
+ /* sender_host_name below will be tainted, so save on copy when we hit it */
+ g = string_get_tainted(24, TRUE);
+ g = string_fmt_append(g, "%.3s %s Hello %s%s%s",
smtp_code,
smtp_active_hostname,
sender_ident ? sender_ident : US"",
sync_cmd_limit = NON_SYNC_CMD_PIPELINING;
f.smtp_in_pipelining_advertised = TRUE;
-#ifdef EXPERIMENTAL_PIPE_CONNECT
+#ifndef DISABLE_PIPE_CONNECT
if (fl.pipe_connect_acceptable)
{
f.smtp_in_early_pipe_advertised = TRUE;
if (au->server)
{
DEBUG(D_auth+D_expand) debug_printf_indent(
- "Evaluating advertise_condition for %s athenticator\n",
- au->public_name);
+ "Evaluating advertise_condition for %s %s athenticator\n",
+ au->name, au->public_name);
if ( !au->advertise_condition
|| expand_check_condition(au->advertise_condition, au->name,
US"authenticator")
tls_advertise_hosts. We must *not* advertise if we are already in a
secure connection. */
-#ifdef SUPPORT_TLS
+#ifndef DISABLE_TLS
if (tls_in.active.sock < 0 &&
verify_check_host(&tls_advertise_hosts) != FAIL)
{
g = string_catn(g, US"-STARTTLS\r\n", 11);
fl.tls_advertised = TRUE;
}
-
-# ifdef EXPERIMENTAL_REQUIRETLS
- /* Advertise REQUIRETLS only once we are in a secure connection */
- if ( tls_in.active.sock >= 0
- && verify_check_host(&tls_advertise_requiretls) != FAIL)
- {
- g = string_catn(g, smtp_code, 3);
- g = string_catn(g, US"-REQUIRETLS\r\n", 13);
- fl.requiretls_advertised = TRUE;
- }
-# endif
#endif
#ifndef DISABLE_PRDR
/* Terminate the string (for debug), write it, and note that HELO/EHLO
has been seen. */
-#ifdef SUPPORT_TLS
+#ifndef DISABLE_TLS
if (tls_in.active.sock >= 0)
(void)tls_write(NULL, g->s, g->ptr,
-# ifdef EXPERIMENTAL_PIPE_CONNECT
+# ifndef DISABLE_PIPE_CONNECT
fl.pipe_connect_acceptable && pipeline_connect_sends());
# else
FALSE);
+ (tls_in.active.sock >= 0 ? pcrpted : 0)
];
cancel_cutthrough_connection(TRUE, US"sent EHLO response");
- smtp_reset(reset_point);
+ reset_point = smtp_reset(reset_point);
toomany = FALSE;
break; /* HELO/EHLO */
obviously need to throw away any previous data. */
cancel_cutthrough_connection(TRUE, US"MAIL received");
- smtp_reset(reset_point);
+ reset_point = smtp_reset(reset_point);
toomany = FALSE;
sender_data = recipient_data = NULL;
break;
#endif
-#if defined(SUPPORT_TLS) && defined(EXPERIMENTAL_REQUIRETLS)
- case ENV_MAIL_OPT_REQTLS:
- {
- uschar * r, * t;
-
- if (!fl.requiretls_advertised)
- {
- done = synprot_error(L_smtp_syntax_error, 555, NULL,
- US"unadvertised MAIL option: REQUIRETLS");
- goto COMMAND_LOOP;
- }
-
- DEBUG(D_receive) debug_printf("requiretls requested\n");
- tls_requiretls = REQUIRETLS_MSG;
-
- r = string_copy_malloc(received_protocol);
- if ((t = Ustrrchr(r, 's'))) *t = 'S';
- received_protocol = r;
- }
- break;
-#endif
-
/* No valid option. Stick back the terminator characters and break
the loop. Do the name-terminator second as extract_option sets
value==name when it found no equal-sign.
if (arg_error) break;
}
-#if defined(SUPPORT_TLS) && defined(EXPERIMENTAL_REQUIRETLS)
- if (tls_requiretls & REQUIRETLS_MSG)
- {
- /* Ensure headers-only bounces whether a RET option was given or not. */
-
- DEBUG(D_receive) if (dsn_ret == dsn_ret_full)
- debug_printf("requiretls override: dsn_ret_full -> dsn_ret_hdrs\n");
- dsn_ret = dsn_ret_hdrs;
- }
-#endif
-
/* If we have passed the threshold for rate limiting, apply the current
delay, and update it for next time, provided this is a limited host. */
TRUE flag allows "<>" as a sender address. */
raw_sender = rewrite_existflags & rewrite_smtp
- ? rewrite_one(smtp_cmd_data, rewrite_smtp, NULL, FALSE, US"",
+ /* deconst ok as smtp_cmd_data was not const */
+ ? US rewrite_one(smtp_cmd_data, rewrite_smtp, NULL, FALSE, US"",
global_rewrite_rules)
: smtp_cmd_data;
and EXPN etc. to be used when space is short. */
if (!receive_check_fs(
- (smtp_check_spool_space && message_size >= 0)?
- message_size + 5000 : 0))
+ smtp_check_spool_space && message_size >= 0
+ ? message_size + 5000 : 0))
{
smtp_printf("452 Space shortage, please try later\r\n", FALSE);
sender_address = NULL;
if (f.allow_unqualified_sender)
{
sender_domain = Ustrlen(sender_address) + 1;
- sender_address = rewrite_address_qualify(sender_address, FALSE);
+ /* deconst ok as sender_address was not const */
+ sender_address = US rewrite_address_qualify(sender_address, FALSE);
DEBUG(D_receive) debug_printf("unqualified address %s accepted\n",
raw_sender);
}
case RCPT_CMD:
HAD(SCH_RCPT);
+ /* We got really to many recipients. A check against configured
+ limits is done later */
+ if (rcpt_count < 0 || rcpt_count >= INT_MAX/2)
+ log_write(0, LOG_MAIN|LOG_PANIC_DIE, "Too many recipients: %d", rcpt_count);
rcpt_count++;
was_rcpt = fl.rcpt_in_progress = TRUE;
as a recipient address */
recipient = rewrite_existflags & rewrite_smtp
- ? rewrite_one(smtp_cmd_data, rewrite_smtp, NULL, FALSE, US"",
+ /* deconst ok as smtp_cmd_data was not const */
+ ? US rewrite_one(smtp_cmd_data, rewrite_smtp, NULL, FALSE, US"",
global_rewrite_rules)
: smtp_cmd_data;
/* Check maximum allowed */
- if (rcpt_count > recipients_max && recipients_max > 0)
+ if (rcpt_count+1 < 0 || rcpt_count > recipients_max && recipients_max > 0)
{
if (recipients_max_reject)
{
recipients_list[recipients_count-1].orcpt = orcpt;
recipients_list[recipients_count-1].dsn_flags = dsn_flags;
- DEBUG(D_receive) debug_printf("DSN: orcpt: %s flags: %d\n",
+ /* DEBUG(D_receive) debug_printf("DSN: orcpt: %s flags: %d\n",
recipients_list[recipients_count-1].orcpt,
- recipients_list[recipients_count-1].dsn_flags);
+ recipients_list[recipients_count-1].dsn_flags); */
}
/* The recipient was discarded */
discarded = TRUE;
log_write(0, LOG_MAIN|LOG_REJECT, "%s F=<%s> RCPT %s: "
"discarded by %s ACL%s%s", host_and_ident(TRUE),
- sender_address_unrewritten? sender_address_unrewritten : sender_address,
- smtp_cmd_argument, f.recipients_discarded? "MAIL" : "RCPT",
+ sender_address_unrewritten ? sender_address_unrewritten : sender_address,
+ smtp_cmd_argument, f.recipients_discarded ? "MAIL" : "RCPT",
log_msg ? US": " : US"", log_msg ? log_msg : US"");
}
DEBUG(D_receive) debug_printf("chunking state %d, %d bytes\n",
(int)chunking_state, chunking_data_left);
- /* push the current receive_* function on the "stack", and
- replace them by bdat_getc(), which in turn will use the lwr_receive_*
- functions to do the dirty work. */
- lwr_receive_getc = receive_getc;
- lwr_receive_getbuf = receive_getbuf;
- lwr_receive_ungetc = receive_ungetc;
-
- receive_getc = bdat_getc;
- receive_ungetc = bdat_ungetc;
-
+ f.bdat_readers_wanted = TRUE; /* FIXME: redundant vs chunking_state? */
f.dot_ends = FALSE;
goto DATA_BDAT;
case DATA_CMD:
HAD(SCH_DATA);
f.dot_ends = TRUE;
+ f.bdat_readers_wanted = FALSE;
DATA_BDAT: /* Common code for DATA and BDAT */
-#ifdef EXPERIMENTAL_PIPE_CONNECT
+#ifndef DISABLE_PIPE_CONNECT
fl.pipe_connect_acceptable = FALSE;
#endif
if (!discarded && recipients_count <= 0)
}
if (f.smtp_in_pipelining_advertised && last_was_rcpt)
smtp_printf("503 Valid RCPT command must precede %s\r\n", FALSE,
- smtp_names[smtp_connection_had[smtp_ch_index-1]]);
+ smtp_names[smtp_connection_had[SMTP_HBUFF_PREV(smtp_ch_index)]]);
else
done = synprot_error(L_smtp_protocol_error, 503, NULL,
- smtp_connection_had[smtp_ch_index-1] == SCH_DATA
+ smtp_connection_had[SMTP_HBUFF_PREV(smtp_ch_index)] == SCH_DATA
? US"valid RCPT command must precede DATA"
: US"valid RCPT command must precede BDAT");
if (chunking_state > CHUNKING_OFFERED)
+ {
+ bdat_push_receive_functions();
bdat_flush_data();
+ }
break;
}
sender_address = NULL; /* This will allow a new MAIL without RSET */
sender_address_unrewritten = NULL;
smtp_printf("554 Too many recipients\r\n", FALSE);
+
+ if (chunking_state > CHUNKING_OFFERED)
+ {
+ bdat_push_receive_functions();
+ bdat_flush_data();
+ }
break;
}
"354 Enter message, ending with \".\" on a line by itself\r\n", FALSE);
}
+ if (f.bdat_readers_wanted)
+ bdat_push_receive_functions();
+
#ifdef TCP_QUICKACK
if (smtp_in) /* all ACKs needed to ramp window up for bulk data */
(void) setsockopt(fileno(smtp_in), IPPROTO_TCP, TCP_QUICKACK,
break;
- #ifdef SUPPORT_TLS
+ #ifndef DISABLE_TLS
case STARTTLS_CMD:
HAD(SCH_STARTTLS);
incomplete_transaction_log(US"STARTTLS");
cancel_cutthrough_connection(TRUE, US"STARTTLS received");
- smtp_reset(reset_point);
+ reset_point = smtp_reset(reset_point);
toomany = FALSE;
cmd_list[CMD_LIST_STARTTLS].is_mail_cmd = FALSE;
cmd_list[CMD_LIST_TLS_AUTH].is_mail_cmd = TRUE;
if (sender_helo_name)
{
- store_free(sender_helo_name);
sender_helo_name = NULL;
host_build_sender_fullhost(); /* Rebuild */
set_process_info("handling incoming TLS connection from %s",
case RSET_CMD:
smtp_rset_handler();
cancel_cutthrough_connection(TRUE, US"RSET received");
- smtp_reset(reset_point);
+ reset_point = smtp_reset(reset_point);
toomany = FALSE;
break;
{
uschar buffer[256];
buffer[0] = 0;
- Ustrcat(buffer, " AUTH");
- #ifdef SUPPORT_TLS
+ Ustrcat(buffer, US" AUTH");
+ #ifndef DISABLE_TLS
if (tls_in.active.sock < 0 &&
verify_check_host(&tls_advertise_hosts) != FAIL)
- Ustrcat(buffer, " STARTTLS");
+ Ustrcat(buffer, US" STARTTLS");
#endif
- Ustrcat(buffer, " HELO EHLO MAIL RCPT DATA BDAT");
- Ustrcat(buffer, " NOOP QUIT RSET HELP");
- if (acl_smtp_etrn != NULL) Ustrcat(buffer, " ETRN");
- if (acl_smtp_expn != NULL) Ustrcat(buffer, " EXPN");
- if (acl_smtp_vrfy != NULL) Ustrcat(buffer, " VRFY");
+ Ustrcat(buffer, US" HELO EHLO MAIL RCPT DATA BDAT");
+ Ustrcat(buffer, US" NOOP QUIT RSET HELP");
+ if (acl_smtp_etrn) Ustrcat(buffer, US" ETRN");
+ if (acl_smtp_expn) Ustrcat(buffer, US" EXPN");
+ if (acl_smtp_vrfy) Ustrcat(buffer, US" VRFY");
smtp_printf("214%s\r\n", FALSE, buffer);
}
break;
oldsignal = signal(SIGCHLD, SIG_IGN);
- if ((pid = fork()) == 0)
+ if ((pid = exim_fork(US"etrn-command")) == 0)
{
smtp_input = FALSE; /* This process is not associated with the */
(void)fclose(smtp_in); /* SMTP call any more. */
/* If not serializing, do the exec right away. Otherwise, fork down
into another process. */
- if (!smtp_etrn_serialize || (pid = fork()) == 0)
+ if ( !smtp_etrn_serialize
+ || (pid = exim_fork(US"etrn-serialised-command")) == 0)
{
DEBUG(D_exec) debug_print_argv(argv);
exim_nullstd(); /* Ensure std{in,out,err} exist */
}
enq_end(etrn_serialize_key);
- _exit(EXIT_SUCCESS);
+ exim_underbar_exit(EXIT_SUCCESS);
}
/* Back in the top level SMTP process. Check that we started a subprocess
if (smtp_etrn_serialize) enq_end(etrn_serialize_key);
}
else
- {
- if (user_msg == NULL) smtp_printf("250 OK\r\n", FALSE);
- else smtp_user_msg(US"250", user_msg);
- }
+ if (!user_msg)
+ smtp_printf("250 OK\r\n", FALSE);
+ else
+ smtp_user_msg(US"250", user_msg);
signal(SIGCHLD, oldsignal);
break;
g = string_append(g, 2, US";\n\tauth=pass (", sender_host_auth_pubname);
-if (Ustrcmp(sender_host_auth_pubname, "tls") != 0)
- g = string_append(g, 2, US") smtp.auth=", authenticated_id);
-else if (authenticated_id)
- g = string_append(g, 2, US") x509.auth=", authenticated_id);
+if (Ustrcmp(sender_host_auth_pubname, "tls") == 0)
+ g = authenticated_id
+ ? string_append(g, 2, US") x509.auth=", authenticated_id)
+ : string_cat(g, US") reason=x509.auth");
else
- g = string_catn(g, US") reason=x509.auth", 17);
+ g = authenticated_id
+ ? string_append(g, 2, US") smtp.auth=", authenticated_id)
+ : string_cat(g, US", no id saved)");
if (authenticated_sender)
g = string_append(g, 2, US" smtp.mailfrom=", authenticated_sender);