*************************************************/
/* Copyright (c) University of Cambridge 1995 - 2018 */
+/* Copyright (c) The Exim Maintainers 2020 */
/* See the file NOTICE for conditions of use and distribution. */
/* A number of functions for driving outgoing SMTP calls. */
return FALSE;
}
-while (isspace(*expint)) expint++;
-if (*expint == 0) return TRUE;
+if (is_tainted(expint))
+ {
+ log_write(0, LOG_MAIN|LOG_PANIC,
+ "attempt to use tainted value '%s' from '%s' for interface",
+ expint, istring);
+ addr->transport_return = PANIC;
+ addr->message = string_sprintf("failed to expand \"interface\" "
+ "option for %s: configuration error", msg);
+ return FALSE;
+ }
+
+Uskip_whitespace(&expint);
+if (!*expint) return TRUE;
while ((iface = string_nextinlist(&expint, &sep, big_buffer,
big_buffer_size)))
static void
tfo_out_check(int sock)
{
-# if defined(TCP_INFO) && defined(EXIM_HAVE_TCPI_UNACKED)
+# ifdef __FreeBSD__
+struct tcp_info tinfo;
+int val;
+socklen_t len = sizeof(val);
+
+/* The observability as of 12.1 is not useful as a client, only telling us that
+a TFO option was used on SYN. It could have been a TFO-R, or ignored by the
+server. */
+
+/*
+if (tcp_out_fastopen == TFO_ATTEMPTED_NODATA || tcp_out_fastopen == TFO_ATTEMPTED_DATA)
+ if (getsockopt(sock, IPPROTO_TCP, TCP_FASTOPEN, &val, &len) == 0 && val != 0) {}
+*/
+switch (tcp_out_fastopen)
+ {
+ case TFO_ATTEMPTED_NODATA: tcp_out_fastopen = TFO_USED_NODATA; break;
+ case TFO_ATTEMPTED_DATA: tcp_out_fastopen = TFO_USED_DATA; break;
+ default: break; /* compiler quietening */
+ }
+
+# else /* Linux & Apple */
+# if defined(TCP_INFO) && defined(EXIM_HAVE_TCPI_UNACKED)
struct tcp_info tinfo;
socklen_t len = sizeof(tinfo);
tcp_out_fastopen = TFO_NOT_USED;
}
break;
+
+ default: break; /* compiler quietening */
}
-# endif
+# endif
+# endif /* Linux & Apple */
}
#endif
{
HDEBUG(D_transport|D_acl|D_v)
{
- debug_printf_indent("failed: %s", CUstrerror(save_errno));
+ debug_printf_indent(" failed: %s", CUstrerror(save_errno));
if (save_errno == ETIMEDOUT)
debug_printf(" (timeout=%s)", readconf_printtime(timeout));
debug_printf("\n");
union sockaddr_46 interface_sock;
EXIM_SOCKLEN_T size = sizeof(interface_sock);
- HDEBUG(D_transport|D_acl|D_v) debug_printf_indent("connected\n");
+ HDEBUG(D_transport|D_acl|D_v) debug_printf_indent(" connected\n");
if (getsockname(sock, (struct sockaddr *)(&interface_sock), &size) == 0)
sending_ip_address = host_ntoa(-1, &interface_sock, NULL, &sending_port);
else
HDEBUG(D_transport|D_acl) debug_printf_indent("cmd buf flush %d bytes%s\n", n,
more ? " (more expected)" : "");
-#ifdef SUPPORT_TLS
+#ifndef DISABLE_TLS
if (outblock->cctx->tls_ctx)
rc = tls_write(outblock->cctx->tls_ctx, outblock->buffer, n, more);
else
rc = n;
}
else
-
+ {
rc = send(outblock->cctx->sock, outblock->buffer, n,
#ifdef MSG_MORE
more ? MSG_MORE : 0
0
#endif
);
+
+#if defined(__linux__)
+ /* This is a workaround for a current linux kernel bug: as of
+ 5.6.8-200.fc31.x86_64 small (<MSS) writes get delayed by about 200ms,
+ This is despite NODELAY being active.
+ https://bugzilla.redhat.com/show_bug.cgi?id=1803806 */
+
+ if (!more)
+ setsockopt(outblock->cctx->sock, IPPROTO_TCP, TCP_CORK, &off, sizeof(off));
+#endif
+ }
}
if (rc <= 0)
smtp_write_command(void * sx, int mode, const char *format, ...)
{
smtp_outblock * outblock = &((smtp_context *)sx)->outblock;
-int count;
int rc = 0;
-va_list ap;
if (format)
{
+ gstring gs = { .size = big_buffer_size, .ptr = 0, .s = big_buffer };
+ va_list ap;
+
+ /* Use taint-unchecked routines for writing into big_buffer, trusting that
+ we'll never expand the results. Actually, the error-message use - leaving
+ the results in big_buffer for potential later use - is uncomfortably distant.
+ XXX Would be better to assume all smtp commands are short, use normal pool
+ alloc rather than big_buffer, and another global for the data-for-error. */
+
va_start(ap, format);
- if (!string_vformat(big_buffer, big_buffer_size, CS format, ap))
+ if (!string_vformat(&gs, SVFMT_TAINT_NOCHK, CS format, ap))
log_write(0, LOG_MAIN|LOG_PANIC_DIE, "overlong write_command in outgoing "
"SMTP");
va_end(ap);
- count = Ustrlen(big_buffer);
+ string_from_gstring(&gs);
- if (count > outblock->buffersize)
+ if (gs.ptr > outblock->buffersize)
log_write(0, LOG_MAIN|LOG_PANIC_DIE, "overlong write_command in outgoing "
"SMTP");
- if (count > outblock->buffersize - (outblock->ptr - outblock->buffer))
+ if (gs.ptr > outblock->buffersize - (outblock->ptr - outblock->buffer))
{
rc = outblock->cmd_count; /* flush resets */
if (!flush_buffer(outblock, SCMD_FLUSH)) return -1;
}
- Ustrncpy(CS outblock->ptr, big_buffer, count);
- outblock->ptr += count;
+ Ustrncpy(outblock->ptr, gs.s, gs.ptr);
+ outblock->ptr += gs.ptr;
outblock->cmd_count++;
- count -= 2;
- big_buffer[count] = 0; /* remove \r\n for error message */
+ gs.ptr -= 2; string_from_gstring(&gs); /* remove \r\n for error message */
/* We want to hide the actual data sent in AUTH transactions from reflections
and logs. While authenticating, a flag is set in the outblock to enable this.
inblock the SMTP input block (contains holding buffer, socket, etc.)
buffer where to put the line
size space available for the line
- timeout the timeout to use when reading a packet
+ timelimit deadline for reading the lime, seconds past epoch
Returns: length of a line that has been put in the buffer
-1 otherwise, with errno set
*/
static int
-read_response_line(smtp_inblock *inblock, uschar *buffer, int size, int timeout)
+read_response_line(smtp_inblock *inblock, uschar *buffer, int size, time_t timelimit)
{
uschar *p = buffer;
uschar *ptr = inblock->ptr;
/* Need to read a new input packet. */
- if((rc = ip_recv(cctx, inblock->buffer, inblock->buffersize, timeout)) <= 0)
+ if((rc = ip_recv(cctx, inblock->buffer, inblock->buffersize, timelimit)) <= 0)
{
- DEBUG(D_deliver|D_transport|D_acl)
+ DEBUG(D_deliver|D_transport|D_acl|D_v)
debug_printf_indent(errno ? " SMTP(%s)<<\n" : " SMTP(closed)<<\n",
strerror(errno));
break;
/*XXX could move to smtp transport; no other users */
BOOL
-smtp_read_response(void * sx0, uschar *buffer, int size, int okdigit,
+smtp_read_response(void * sx0, uschar * buffer, int size, int okdigit,
int timeout)
{
smtp_context * sx = sx0;
-uschar *ptr = buffer;
+uschar * ptr = buffer;
int count = 0;
+time_t timelimit = time(NULL) + timeout;
errno = 0; /* Ensure errno starts out zero */
-#ifdef EXPERIMENTAL_PIPE_CONNECT
+#ifndef DISABLE_PIPE_CONNECT
if (sx->pending_BANNER || sx->pending_EHLO)
- if (smtp_reap_early_pipe(sx, &count) != OK)
+ {
+ int rc;
+ if ((rc = smtp_reap_early_pipe(sx, &count)) != OK)
{
DEBUG(D_transport) debug_printf("failed reaping pipelined cmd responsess\n");
+ buffer[0] = '\0';
+ if (rc == DEFER) errno = ERRNO_TLSFAILURE;
return FALSE;
}
+ }
#endif
/* This is a loop to read and concatenate the lines that make up a multi-line
for (;;)
{
- if ((count = read_response_line(&sx->inblock, ptr, size, timeout)) < 0)
+ if ((count = read_response_line(&sx->inblock, ptr, size, timelimit)) < 0)
return FALSE;
HDEBUG(D_transport|D_acl|D_v)