"Fossies" - the Fresh Open Source Software Archive  

Source code changes of the file "src/smtp_out.c" between
exim-4.91.tar.xz and exim-4.92.tar.xz

About: Exim is a message transfer agent (MTA).

smtp_out.c  (exim-4.91.tar.xz):smtp_out.c  (exim-4.92.tar.xz)
skipping to change at line 45 skipping to change at line 45
uschar **interface, uschar *msg) uschar **interface, uschar *msg)
{ {
const uschar * expint; const uschar * expint;
uschar *iface; uschar *iface;
int sep = 0; int sep = 0;
if (!istring) return TRUE; if (!istring) return TRUE;
if (!(expint = expand_string(istring))) if (!(expint = expand_string(istring)))
{ {
if (expand_string_forcedfail) return TRUE; if (f.expand_string_forcedfail) return TRUE;
addr->transport_return = PANIC; addr->transport_return = PANIC;
addr->message = string_sprintf("failed to expand \"interface\" " addr->message = string_sprintf("failed to expand \"interface\" "
"option for %s: %s", msg, expand_string_message); "option for %s: %s", msg, expand_string_message);
return FALSE; return FALSE;
} }
while (isspace(*expint)) expint++; while (isspace(*expint)) expint++;
if (*expint == 0) return TRUE; if (*expint == 0) return TRUE;
while ((iface = string_nextinlist(&expint, &sep, big_buffer, while ((iface = string_nextinlist(&expint, &sep, big_buffer,
skipping to change at line 143 skipping to change at line 143
} }
#ifdef TCP_FASTOPEN #ifdef TCP_FASTOPEN
static void static void
tfo_out_check(int sock) tfo_out_check(int sock)
{ {
# if defined(TCP_INFO) && defined(EXIM_HAVE_TCPI_UNACKED) # if defined(TCP_INFO) && defined(EXIM_HAVE_TCPI_UNACKED)
struct tcp_info tinfo; struct tcp_info tinfo;
socklen_t len = sizeof(tinfo); socklen_t len = sizeof(tinfo);
if (getsockopt(sock, IPPROTO_TCP, TCP_INFO, &tinfo, &len) == 0) switch (tcp_out_fastopen)
{ {
switch (tcp_out_fastopen) /* This is a somewhat dubious detection method; totally undocumented so like
{ ly
/* This is a somewhat dubious detection method; totally undocumented so li to fail in future kernels. There seems to be no documented way. What we re
kely ally
to fail in future kernels. There seems to be no documented way. What we want to know is if the server sent smtp-banner data before our ACK of his SY
really N,ACK
want to know is if the server sent smtp-banner data before our ACK of his hit him. What this (possibly?) detects is whether we sent a TFO cookie with
SYN,ACK our
hit him. What this (possibly?) detects is whether we sent a TFO cookie wi SYN, as distinct from a TFO request. This gets a false-positive when the se
th our rver
SYN, as distinct from a TFO request. This gets a false-positive when the key is rotated; we send the old one (which this test sees) but the server re
server turns
key is rotated; we send the old one (which this test sees) but the server the new one and does not send its SMTP banner before we ACK his SYN,ACK.
returns To force that rotation case:
the new one and does not send its SMTP banner before we ACK his SYN,ACK. '# echo -n "00000000-00000000-00000000-0000000" >/proc/sys/net/ipv4/tcp_fas
To force that rotation case: topen_key'
'# echo -n "00000000-00000000-00000000-0000000" >/proc/sys/net/ipv4/tcp_f The kernel seems to be counting unack'd packets. */
astopen_key'
The kernel seems to be counting unack'd packets. */ case TFO_ATTEMPTED_NODATA:
if ( getsockopt(sock, IPPROTO_TCP, TCP_INFO, &tinfo, &len) == 0
&& tinfo.tcpi_state == TCP_SYN_SENT
&& tinfo.tcpi_unacked > 1
)
{
DEBUG(D_transport|D_v)
debug_printf("TCP_FASTOPEN tcpi_unacked %d\n", tinfo.tcpi_unacked);
tcp_out_fastopen = TFO_USED_NODATA;
}
break;
case 1: /* When called after waiting for received data we should be able
if (tinfo.tcpi_unacked > 1) to tell if data we sent was accepted. */
case TFO_ATTEMPTED_DATA:
if ( getsockopt(sock, IPPROTO_TCP, TCP_INFO, &tinfo, &len) == 0
&& tinfo.tcpi_state == TCP_ESTABLISHED
)
if (tinfo.tcpi_options & TCPI_OPT_SYN_DATA)
{ {
DEBUG(D_transport|D_v) DEBUG(D_transport|D_v) debug_printf("TFO: data was acked\n");
debug_printf("TCP_FASTOPEN tcpi_unacked %d\n", tinfo.tcpi_unacked); tcp_out_fastopen = TFO_USED_DATA;
tcp_out_fastopen = 2;
} }
break; else
#ifdef notdef /* This seems to always fire, meaning that we cannot tell
whether the server accepted data we sent. For now assume
that it did. */
/* If there was data-on-SYN but we had to retrasnmit it, declare no TFO */
case 2:
if (!(tinfo.tcpi_options & TCPI_OPT_SYN_DATA))
{ {
DEBUG(D_transport|D_v) debug_printf("TFO: had to retransmit\n"); DEBUG(D_transport|D_v) debug_printf("TFO: had to retransmit\n");
tcp_out_fastopen = 0; tcp_out_fastopen = TFO_NOT_USED;
} }
break; break;
#endif
}
} }
# endif # endif
} }
#endif #endif
/* Arguments as for smtp_connect(), plus /* Arguments as for smtp_connect(), plus
early_data if non-NULL, data to be sent - preferably in the TCP SYN segment early_data if non-NULL, idenmpotent data to be sent -
preferably in the TCP SYN segment
Returns: connected socket number, or -1 with errno set Returns: connected socket number, or -1 with errno set
*/ */
int int
smtp_sock_connect(host_item * host, int host_af, int port, uschar * interface, smtp_sock_connect(host_item * host, int host_af, int port, uschar * interface,
transport_instance * tb, int timeout, const blob * early_data) transport_instance * tb, int timeout, const blob * early_data)
{ {
smtp_transport_options_block * ob = smtp_transport_options_block * ob =
(smtp_transport_options_block *)tb->options_block; (smtp_transport_options_block *)tb->options_block;
skipping to change at line 252 skipping to change at line 256
if (interface && ip_bind(sock, host_af, interface, 0) < 0) if (interface && ip_bind(sock, host_af, interface, 0) < 0)
{ {
save_errno = errno; save_errno = errno;
HDEBUG(D_transport|D_acl|D_v) HDEBUG(D_transport|D_acl|D_v)
debug_printf_indent("unable to bind outgoing SMTP call to %s: %s", interface , debug_printf_indent("unable to bind outgoing SMTP call to %s: %s", interface ,
strerror(errno)); strerror(errno));
} }
/* Connect to the remote host, and add keepalive to the socket before returning /* Connect to the remote host, and add keepalive to the socket before returning
it, if requested. If the build supports TFO, request it - and if the caller it, if requested. If the build supports TFO, request it - and if the caller
requested some early-data then include that in the TFO request. */ requested some early-data then include that in the TFO request. If there is
early-data but no TFO support, send it after connecting. */
else else
{ {
#ifdef TCP_FASTOPEN #ifdef TCP_FASTOPEN
if (verify_check_given_host(&ob->hosts_try_fastopen, host) == OK) if (verify_check_given_host(CUSS &ob->hosts_try_fastopen, host) == OK)
fastopen_blob = early_data ? early_data : &tcp_fastopen_nodata; fastopen_blob = early_data ? early_data : &tcp_fastopen_nodata;
#endif #endif
if (ip_connect(sock, host_af, host->address, port, timeout, fastopen_blob) < 0 ) if (ip_connect(sock, host_af, host->address, port, timeout, fastopen_blob) < 0 )
save_errno = errno; save_errno = errno;
else if (early_data && !fastopen_blob && early_data->data && early_data->len) else if (early_data && !fastopen_blob && early_data->data && early_data->len)
{
HDEBUG(D_transport|D_acl|D_v)
debug_printf("sending %ld nonTFO early-data\n", (long)early_data->len);
#ifdef TCP_QUICKACK
(void) setsockopt(sock, IPPROTO_TCP, TCP_QUICKACK, US &off, sizeof(off));
#endif
if (send(sock, early_data->data, early_data->len, 0) < 0) if (send(sock, early_data->data, early_data->len, 0) < 0)
save_errno = errno; save_errno = errno;
}
} }
/* Either bind() or connect() failed */ /* Either bind() or connect() failed */
if (save_errno != 0) if (save_errno != 0)
{ {
HDEBUG(D_transport|D_acl|D_v) 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) if (save_errno == ETIMEDOUT)
debug_printf(" (timeout=%s)", readconf_printtime(timeout)); debug_printf(" (timeout=%s)", readconf_printtime(timeout));
debug_printf("\n"); debug_printf("\n");
} }
(void)close(sock); (void)close(sock);
errno = save_errno; errno = save_errno;
return -1; return -1;
} }
/* Both bind() and connect() succeeded */ /* Both bind() and connect() succeeded, and any early-data */
else else
{ {
union sockaddr_46 interface_sock; union sockaddr_46 interface_sock;
EXIM_SOCKLEN_T size = sizeof(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) if (getsockname(sock, (struct sockaddr *)(&interface_sock), &size) == 0)
sending_ip_address = host_ntoa(-1, &interface_sock, NULL, &sending_port); sending_ip_address = host_ntoa(-1, &interface_sock, NULL, &sending_port);
else else
{ {
log_write(0, LOG_MAIN | ((errno == ECONNRESET)? 0 : LOG_PANIC), log_write(0, LOG_MAIN | ((errno == ECONNRESET)? 0 : LOG_PANIC),
"getsockname() failed: %s", strerror(errno)); "getsockname() failed: %s", strerror(errno));
close(sock); close(sock);
return -1; return -1;
} }
if (ob->keepalive) ip_keepalive(sock, host->address, TRUE); if (ob->keepalive) ip_keepalive(sock, host->address, TRUE);
#ifdef TCP_FASTOPEN #ifdef TCP_FASTOPEN
if (fastopen_blob) tfo_out_check(sock); tfo_out_check(sock);
#endif #endif
return sock; return sock;
} }
} }
void void
smtp_port_for_connect(host_item * host, int port) smtp_port_for_connect(host_item * host, int port)
{ {
if (host->port != PORT_NONE) if (host->port != PORT_NONE)
{ {
skipping to change at line 331 skipping to change at line 346
/************************************************* /*************************************************
* Connect to remote host * * Connect to remote host *
*************************************************/ *************************************************/
/* Create a socket, and connect it to a remote host. IPv6 addresses are /* Create a socket, and connect it to a remote host. IPv6 addresses are
detected by checking for a colon in the address. AF_INET6 is defined even on detected by checking for a colon in the address. AF_INET6 is defined even on
non-IPv6 systems, to enable the code to be less messy. However, on such systems non-IPv6 systems, to enable the code to be less messy. However, on such systems
host->address will always be an IPv4 address. host->address will always be an IPv4 address.
Arguments: Arguments:
host host item containing name and address and port sc details for making connection: host, af, interface, transport
host_af AF_INET or AF_INET6 early_data if non-NULL, data to be sent - preferably in the TCP SYN segment
interface outgoing interface address or NULL
timeout timeout value or 0
tb transport
Returns: connected socket number, or -1 with errno set Returns: connected socket number, or -1 with errno set
*/ */
int int
smtp_connect(host_item *host, int host_af, uschar *interface, smtp_connect(smtp_connect_args * sc, const blob * early_data)
int timeout, transport_instance * tb)
{ {
int port = host->port; int port = sc->host->port;
#ifdef SUPPORT_SOCKS smtp_transport_options_block * ob = sc->ob;
smtp_transport_options_block * ob =
(smtp_transport_options_block *)tb->options_block;
#endif
callout_address = string_sprintf("[%s]:%d", host->address, port); callout_address = string_sprintf("[%s]:%d", sc->host->address, port);
HDEBUG(D_transport|D_acl|D_v) HDEBUG(D_transport|D_acl|D_v)
{ {
uschar * s = US" "; uschar * s = US" ";
if (interface) s = string_sprintf(" from %s ", interface); if (sc->interface) s = string_sprintf(" from %s ", sc->interface);
#ifdef SUPPORT_SOCKS #ifdef SUPPORT_SOCKS
if (ob->socks_proxy) s = string_sprintf("%svia proxy ", s); if (ob->socks_proxy) s = string_sprintf("%svia proxy ", s);
#endif #endif
debug_printf_indent("Connecting to %s %s%s... ", host->name, callout_address, s); debug_printf_indent("Connecting to %s %s%s... ", sc->host->name, callout_addre ss, s);
} }
/* Create and connect the socket */ /* Create and connect the socket */
#ifdef SUPPORT_SOCKS #ifdef SUPPORT_SOCKS
if (ob->socks_proxy) if (ob->socks_proxy)
return socks_sock_connect(host, host_af, port, interface, tb, timeout); {
int sock = socks_sock_connect(sc->host, sc->host_af, port, sc->interface,
sc->tblock, ob->connect_timeout);
if (sock >= 0)
{
if (early_data && early_data->data && early_data->len)
if (send(sock, early_data->data, early_data->len, 0) < 0)
{
int save_errno = errno;
HDEBUG(D_transport|D_acl|D_v)
{
debug_printf_indent("failed: %s", CUstrerror(save_errno));
if (save_errno == ETIMEDOUT)
debug_printf(" (timeout=%s)", readconf_printtime(ob->connect_timeout)
);
debug_printf("\n");
}
(void)close(sock);
sock = -1;
errno = save_errno;
}
}
return sock;
}
#endif #endif
return smtp_sock_connect(host, host_af, port, interface, tb, timeout, NULL); return smtp_sock_connect(sc->host, sc->host_af, port, sc->interface,
sc->tblock, ob->connect_timeout, early_data);
} }
/************************************************* /*************************************************
* Flush outgoing command buffer * * Flush outgoing command buffer *
*************************************************/ *************************************************/
/* This function is called only from smtp_write_command() below. It flushes /* This function is called only from smtp_write_command() below. It flushes
the buffer of outgoing commands. There is more than one in the buffer only when the buffer of outgoing commands. There is more than one in the buffer only when
pipelining. pipelining.
skipping to change at line 398 skipping to change at line 430
flush_buffer(smtp_outblock * outblock, int mode) flush_buffer(smtp_outblock * outblock, int mode)
{ {
int rc; int rc;
int n = outblock->ptr - outblock->buffer; int n = outblock->ptr - outblock->buffer;
BOOL more = mode == SCMD_MORE; BOOL more = mode == SCMD_MORE;
HDEBUG(D_transport|D_acl) debug_printf_indent("cmd buf flush %d bytes%s\n", n, HDEBUG(D_transport|D_acl) debug_printf_indent("cmd buf flush %d bytes%s\n", n,
more ? " (more expected)" : ""); more ? " (more expected)" : "");
#ifdef SUPPORT_TLS #ifdef SUPPORT_TLS
if (tls_out.active == outblock->sock) if (outblock->cctx->tls_ctx)
rc = tls_write(FALSE, outblock->buffer, n, more); rc = tls_write(outblock->cctx->tls_ctx, outblock->buffer, n, more);
else else
#endif #endif
rc = send(outblock->sock, outblock->buffer, n,
{
if (outblock->conn_args)
{
blob early_data = { .data = outblock->buffer, .len = n };
/* We ignore the more-flag if we're doing a connect with early-data, which
means we won't get BDAT+data. A pity, but wise due to the idempotency
requirement: TFO with data can, in rare cases, replay the data to the
receiver. */
if ( (outblock->cctx->sock = smtp_connect(outblock->conn_args, &early_data)
)
< 0)
return FALSE;
outblock->conn_args = NULL;
rc = n;
}
else
rc = send(outblock->cctx->sock, outblock->buffer, n,
#ifdef MSG_MORE #ifdef MSG_MORE
more ? MSG_MORE : 0 more ? MSG_MORE : 0
#else #else
0 0
#endif #endif
); );
}
if (rc <= 0) if (rc <= 0)
{ {
HDEBUG(D_transport|D_acl) debug_printf_indent("send failed: %s\n", strerror(er rno)); HDEBUG(D_transport|D_acl) debug_printf_indent("send failed: %s\n", strerror(er rno));
return FALSE; return FALSE;
} }
outblock->ptr = outblock->buffer; outblock->ptr = outblock->buffer;
outblock->cmd_count = 0; outblock->cmd_count = 0;
return TRUE; return TRUE;
} }
/************************************************* /*************************************************
* Write SMTP command * * Write SMTP command *
*************************************************/ *************************************************/
/* The formatted command is left in big_buffer so that it can be reflected in /* The formatted command is left in big_buffer so that it can be reflected in
any error message. any error message.
Arguments: Arguments:
outblock contains buffer for pipelining, and socket sx SMTP connection, contains buffer for pipelining, and socket
mode buffer, write-with-more-likely, write mode buffer, write-with-more-likely, write
format a format, starting with one of format a format, starting with one of
of HELO, MAIL FROM, RCPT TO, DATA, ".", or QUIT. of HELO, MAIL FROM, RCPT TO, DATA, ".", or QUIT.
If NULL, flush pipeline buffer only. If NULL, flush pipeline buffer only.
... data for the format ... data for the format
Returns: 0 if command added to pipelining buffer, with nothing transmitted Returns: 0 if command added to pipelining buffer, with nothing transmitted
+n if n commands transmitted (may still have buffered the new one) +n if n commands transmitted (may still have buffered the new one)
-1 on error, with errno set -1 on error, with errno set
*/ */
int int
smtp_write_command(smtp_outblock * outblock, int mode, const char *format, ...) smtp_write_command(void * sx, int mode, const char *format, ...)
{ {
int count; smtp_outblock * outblock = &((smtp_context *)sx)->outblock;
int rc = 0; int rc = 0;
va_list ap;
if (format) if (format)
{ {
gstring gs = { .size = big_buffer_size, .ptr = 0, .s = big_buffer };
va_list ap;
va_start(ap, format); va_start(ap, format);
if (!string_vformat(big_buffer, big_buffer_size, CS format, ap)) if (!string_vformat(&gs, FALSE, CS format, ap))
log_write(0, LOG_MAIN|LOG_PANIC_DIE, "overlong write_command in outgoing " log_write(0, LOG_MAIN|LOG_PANIC_DIE, "overlong write_command in outgoing "
"SMTP"); "SMTP");
va_end(ap); 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 " log_write(0, LOG_MAIN|LOG_PANIC_DIE, "overlong write_command in outgoing "
"SMTP"); "SMTP");
if (count > outblock->buffersize - (outblock->ptr - outblock->buffer)) if (gs.ptr > outblock->buffersize - (outblock->ptr - outblock->buffer))
{ {
rc = outblock->cmd_count; /* flush resets */ rc = outblock->cmd_count; /* flush resets */
if (!flush_buffer(outblock, SCMD_FLUSH)) return -1; if (!flush_buffer(outblock, SCMD_FLUSH)) return -1;
} }
Ustrncpy(CS outblock->ptr, big_buffer, count); Ustrncpy(CS outblock->ptr, gs.s, gs.ptr);
outblock->ptr += count; outblock->ptr += gs.ptr;
outblock->cmd_count++; outblock->cmd_count++;
count -= 2; gs.ptr -= 2; string_from_gstring(&gs); /* remove \r\n for error message */
big_buffer[count] = 0; /* remove \r\n for error message */
/* We want to hide the actual data sent in AUTH transactions from reflections /* 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. and logs. While authenticating, a flag is set in the outblock to enable this.
The AUTH command itself gets any data flattened. Other lines are flattened The AUTH command itself gets any data flattened. Other lines are flattened
completely. */ completely. */
if (outblock->authenticating) if (outblock->authenticating)
{ {
uschar *p = big_buffer; uschar *p = big_buffer;
if (Ustrncmp(big_buffer, "AUTH ", 5) == 0) if (Ustrncmp(big_buffer, "AUTH ", 5) == 0)
skipping to change at line 529 skipping to change at line 582
Returns: length of a line that has been put in the buffer Returns: length of a line that has been put in the buffer
-1 otherwise, with errno set -1 otherwise, with errno set
*/ */
static int static int
read_response_line(smtp_inblock *inblock, uschar *buffer, int size, int timeout) read_response_line(smtp_inblock *inblock, uschar *buffer, int size, int timeout)
{ {
uschar *p = buffer; uschar *p = buffer;
uschar *ptr = inblock->ptr; uschar *ptr = inblock->ptr;
uschar *ptrend = inblock->ptrend; uschar *ptrend = inblock->ptrend;
int sock = inblock->sock; client_conn_ctx * cctx = inblock->cctx;
/* Loop for reading multiple packets or reading another packet after emptying /* Loop for reading multiple packets or reading another packet after emptying
a previously-read one. */ a previously-read one. */
for (;;) for (;;)
{ {
int rc; int rc;
/* If there is data in the input buffer left over from last time, copy /* If there is data in the input buffer left over from last time, copy
characters from it until the end of a line, at which point we can return, characters from it until the end of a line, at which point we can return,
skipping to change at line 567 skipping to change at line 620
if (--size < 4) if (--size < 4)
{ {
*p = 0; /* Leave malformed line for error message */ *p = 0; /* Leave malformed line for error message */
errno = ERRNO_SMTPFORMAT; errno = ERRNO_SMTPFORMAT;
return -1; return -1;
} }
} }
/* Need to read a new input packet. */ /* Need to read a new input packet. */
if((rc = ip_recv(sock, inblock->buffer, inblock->buffersize, timeout)) <= 0) if((rc = ip_recv(cctx, inblock->buffer, inblock->buffersize, timeout)) <= 0)
{ {
DEBUG(D_deliver|D_transport|D_acl) DEBUG(D_deliver|D_transport|D_acl)
debug_printf_indent(errno ? " SMTP(%s)<<\n" : " SMTP(closed)<<\n", debug_printf_indent(errno ? " SMTP(%s)<<\n" : " SMTP(closed)<<\n",
strerror(errno)); strerror(errno));
break; break;
} }
/* Another block of data has been successfully read. Set up the pointers /* Another block of data has been successfully read. Set up the pointers
and let the loop continue. */ and let the loop continue. */
skipping to change at line 602 skipping to change at line 655
*************************************************/ *************************************************/
/* This function reads an SMTP response with a timeout, and returns the /* This function reads an SMTP response with a timeout, and returns the
response in the given buffer, as a string. A multiline response will contain response in the given buffer, as a string. A multiline response will contain
newline characters between the lines. The function also analyzes the first newline characters between the lines. The function also analyzes the first
digit of the reply code and returns FALSE if it is not acceptable. FALSE is digit of the reply code and returns FALSE if it is not acceptable. FALSE is
also returned after a reading error. In this case buffer[0] will be zero, and also returned after a reading error. In this case buffer[0] will be zero, and
the error code will be in errno. the error code will be in errno.
Arguments: Arguments:
inblock the SMTP input block (contains holding buffer, socket, etc.) sx the SMTP connection (contains input block with holding buffer,
socket, etc.)
buffer where to put the response buffer where to put the response
size the size of the buffer size the size of the buffer
okdigit the expected first digit of the response okdigit the expected first digit of the response
timeout the timeout to use, in seconds timeout the timeout to use, in seconds
Returns: TRUE if a valid, non-error response was received; else FALSE Returns: TRUE if a valid, non-error response was received; else FALSE
*/ */
/*XXX could move to smtp transport; no other users */
BOOL BOOL
smtp_read_response(smtp_inblock *inblock, uschar *buffer, int size, int okdigit, smtp_read_response(void * sx0, uschar *buffer, int size, int okdigit,
int timeout) int timeout)
{ {
smtp_context * sx = sx0;
uschar *ptr = buffer; uschar *ptr = buffer;
int count; int count = 0;
errno = 0; /* Ensure errno starts out zero */ errno = 0; /* Ensure errno starts out zero */
#ifdef EXPERIMENTAL_PIPE_CONNECT
if (sx->pending_BANNER || sx->pending_EHLO)
if (smtp_reap_early_pipe(sx, &count) != OK)
{
DEBUG(D_transport) debug_printf("failed reaping pipelined cmd responsess\n")
;
return FALSE;
}
#endif
/* This is a loop to read and concatenate the lines that make up a multi-line /* This is a loop to read and concatenate the lines that make up a multi-line
response. */ response. */
for (;;) for (;;)
{ {
if ((count = read_response_line(inblock, ptr, size, timeout)) < 0) if ((count = read_response_line(&sx->inblock, ptr, size, timeout)) < 0)
return FALSE; return FALSE;
HDEBUG(D_transport|D_acl|D_v) HDEBUG(D_transport|D_acl|D_v)
debug_printf_indent(" %s %s\n", (ptr == buffer)? "SMTP<<" : " ", ptr); debug_printf_indent(" %s %s\n", ptr == buffer ? "SMTP<<" : " ", ptr);
/* Check the format of the response: it must start with three digits; if /* Check the format of the response: it must start with three digits; if
these are followed by a space or end of line, the response is complete. If these are followed by a space or end of line, the response is complete. If
they are followed by '-' this is a multi-line response and we must look for they are followed by '-' this is a multi-line response and we must look for
another line until the final line is reached. The only use made of multi-line another line until the final line is reached. The only use made of multi-line
responses is to pass them back as error messages. We therefore just responses is to pass them back as error messages. We therefore just
concatenate them all within the buffer, which should be large enough to concatenate them all within the buffer, which should be large enough to
accept any reasonable number of lines. */ accept any reasonable number of lines. */
if (count < 3 || if (count < 3 ||
skipping to change at line 663 skipping to change at line 728
/* Move the reading pointer upwards in the buffer and insert \n between the /* Move the reading pointer upwards in the buffer and insert \n between the
components of a multiline response. Space is left for this by read_response_ components of a multiline response. Space is left for this by read_response_
line(). */ line(). */
ptr += count; ptr += count;
*ptr++ = '\n'; *ptr++ = '\n';
size -= count + 1; size -= count + 1;
} }
#ifdef TCP_FASTOPEN
tfo_out_check(sx->cctx.sock);
#endif
/* Return a value that depends on the SMTP return code. On some systems a /* Return a value that depends on the SMTP return code. On some systems a
non-zero value of errno has been seen at this point, so ensure it is zero, non-zero value of errno has been seen at this point, so ensure it is zero,
because the caller of this function looks at errno when FALSE is returned, to because the caller of this function looks at errno when FALSE is returned, to
distinguish between an unexpected return code and other errors such as distinguish between an unexpected return code and other errors such as
timeouts, lost connections, etc. */ timeouts, lost connections, etc. */
errno = 0; errno = 0;
return buffer[0] == okdigit; return buffer[0] == okdigit;
} }
 End of changes. 52 change blocks. 
88 lines changed or deleted 160 lines changed or added

Home  |  About  |  Features  |  All  |  Newest  |  Dox  |  Diffs  |  RSS Feeds  |  Screenshots  |  Comments  |  Imprint  |  Privacy  |  HTTP(S)