controlled by the build-time option SUPPORT_PIPE_CONNECT.
PP/01 Unbreak heimdal_gssapi, broken in 4.92.
+
JH/27 Bug 2404: Use the main-section configuration option "dsn_from" for
success-DSN messages. Previously the From: header was always the default
one for these; the option was ignored.
+JH/28 Fix the timeout on smtp response to apply to the whole response.
+ Previously it was reset for every read, so a teergrubing peer sending
+ single bytes within the time limit could extend the connection for a
+ long time. Credit to Qualsys Security Advisory Team for the discovery.
+
Exim version 4.92
-----------------
extern int_eximarith_t expand_string_integer(uschar *, BOOL);
extern void modify_variable(uschar *, void *);
-extern BOOL fd_ready(int, int);
+extern BOOL fd_ready(int, time_t);
extern int filter_interpret(uschar *, int, address_item **, uschar **);
extern BOOL filter_personal(string_item *, BOOL);
int, host_item *, uschar **, const blob *);
extern int ip_get_address_family(int);
extern void ip_keepalive(int, const uschar *, BOOL);
-extern int ip_recv(client_conn_ctx *, uschar *, int, int);
+extern int ip_recv(client_conn_ctx *, uschar *, int, time_t);
extern int ip_socket(int, int);
extern int ip_tcpsocket(const uschar *, uschar **, int);
/*
Arguments:
fd the file descriptor
- timeout the timeout, seconds
+ timelimit the timeout endpoint, seconds-since-epoch
Returns: TRUE => ready for i/o
FALSE => timed out, or other error
*/
BOOL
-fd_ready(int fd, int timeout)
+fd_ready(int fd, time_t timelimit)
{
fd_set select_inset;
-time_t start_recv = time(NULL);
-int time_left = timeout;
+int time_left = timelimit - time(NULL);
int rc;
if (time_left <= 0)
DEBUG(D_transport) debug_printf("EINTR while waiting for socket data\n");
/* Watch out, 'continue' jumps to the condition, not to the loops top */
- time_left = timeout - (time(NULL) - start_recv);
- if (time_left > 0) continue;
+ if ((time_left = timelimit - time(NULL)) > 0) continue;
}
if (rc <= 0)
cctx the connection context (socket fd, possibly TLS context)
buffer to read into
bufsize the buffer size
- timeout the timeout
+ timelimit the timeout endpoint, seconds-since-epoch
Returns: > 0 => that much data read
<= 0 on error or EOF; errno set - zero for EOF
*/
int
-ip_recv(client_conn_ctx * cctx, uschar * buffer, int buffsize, int timeout)
+ip_recv(client_conn_ctx * cctx, uschar * buffer, int buffsize, time_t timelimit)
{
int rc;
-if (!fd_ready(cctx->sock, timeout))
+if (!fd_ready(cctx->sock, timelimit))
return -1;
/* The socket is ready, read from it (via TLS if it's active). On EOF (i.e.
-2 on timeout or error
*/
static int
-recv_line(int fd, uschar * buffer, int bsize, int tmo)
+recv_line(int fd, uschar * buffer, int bsize, time_t tmo)
{
uschar * p = buffer;
ssize_t rcv;
BOOL ok = FALSE;
-if (!fd_ready(fd, tmo-time(NULL)))
+if (!fd_ready(fd, tmo))
return -2;
/*XXX tmo handling assumes we always get a whole line */
/* return TRUE iff size as requested */
static BOOL
-recv_len(int sock, void * buf, int size, int tmo)
+recv_len(int sock, void * buf, int size, time_t tmo)
{
-return fd_ready(sock, tmo-time(NULL))
+return fd_ready(sock, tmo)
? recv(sock, buf, size, 0) == size
: FALSE;
}
}
static inline int
-mksd_read_lines (int sock, uschar *av_buffer, int av_buffer_size, int tmo)
+mksd_read_lines (int sock, uschar *av_buffer, int av_buffer_size, time_t tmo)
{
client_conn_ctx cctx = {.sock = sock};
int offset = 0;
do
{
- i = ip_recv(&cctx, av_buffer+offset, av_buffer_size-offset, tmo-time(NULL));
+ i = ip_recv(&cctx, av_buffer+offset, av_buffer_size-offset, tmo);
if (i <= 0)
{
(void) malware_panic_defer(US"unable to read from mksd UNIX socket (/var/run/mksd/socket)");
static int
mksd_scan_packed(struct scan * scanent, int sock, const uschar * scan_filename,
- int tmo)
+ time_t tmo)
{
struct iovec iov[3];
const char *cmd = "MSQ\n";
if (m_sock_send(malware_daemon_ctx.sock, scanrequest, Ustrlen(scanrequest), &errstr) < 0)
return m_panic_defer(scanent, CUS callout_address, errstr);
- bread = ip_recv(&malware_daemon_ctx, av_buffer, sizeof(av_buffer), tmo-time(NULL));
+ bread = ip_recv(&malware_daemon_ctx, av_buffer, sizeof(av_buffer), tmo);
if (bread <= 0)
return m_panic_defer_3(scanent, CUS callout_address,
if (m_sock_send(malware_daemon_ctx.sock, cmdopt[i], Ustrlen(cmdopt[i]), &errstr) < 0)
return m_panic_defer(scanent, CUS callout_address, errstr);
- bread = ip_recv(&malware_daemon_ctx, av_buffer, sizeof(av_buffer), tmo-time(NULL));
+ bread = ip_recv(&malware_daemon_ctx, av_buffer, sizeof(av_buffer), tmo);
if (bread > 0) av_buffer[bread]='\0';
if (bread < 0)
return m_panic_defer_3(scanent, CUS callout_address,
{
errno = ETIMEDOUT;
i = av_buffer+sizeof(av_buffer)-p;
- if ((bread= ip_recv(&malware_daemon_ctx, p, i-1, tmo-time(NULL))) < 0)
+ if ((bread= ip_recv(&malware_daemon_ctx, p, i-1, tmo)) < 0)
return m_panic_defer_3(scanent, CUS callout_address,
string_sprintf("unable to read result (%s)", strerror(errno)),
malware_daemon_ctx.sock);
/* wait for result */
memset(av_buffer, 0, sizeof(av_buffer));
- if ((bread = ip_recv(&malware_daemon_ctx, av_buffer, sizeof(av_buffer), tmo-time(NULL))) <= 0)
+ if ((bread = ip_recv(&malware_daemon_ctx, av_buffer, sizeof(av_buffer), tmo)) <= 0)
return m_panic_defer_3(scanent, CUS callout_address,
string_sprintf("unable to read from UNIX socket (%s)", scanner_options),
malware_daemon_ctx.sock);
/* Read the result */
memset(av_buffer, 0, sizeof(av_buffer));
- bread = ip_recv(&malware_daemon_ctx, av_buffer, sizeof(av_buffer), tmo-time(NULL));
+ bread = ip_recv(&malware_daemon_ctx, av_buffer, sizeof(av_buffer), tmo);
(void)close(malware_daemon_ctx.sock);
malware_daemon_ctx.sock = -1;
malware_daemon_ctx.tls_ctx = NULL;
return m_panic_defer(scanent, CUS callout_address, errstr);
/* Read the result */
- bread = ip_recv(&malware_daemon_ctx, av_buffer, sizeof(av_buffer), tmo-time(NULL));
+ bread = ip_recv(&malware_daemon_ctx, av_buffer, sizeof(av_buffer), tmo);
if (bread <= 0)
return m_panic_defer_3(scanent, CUS callout_address,
/* Read the response and close the socket. If the read fails, try the
next IP address. */
- count = ip_recv(&query_cctx, reply, sizeof(reply) - 1, ob->timeout);
+ count = ip_recv(&query_cctx, reply, sizeof(reply) - 1, time(NULL) + ob->timeout);
(void)close(query_cctx.sock);
if (count <= 0)
{
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|D_v)
debug_printf_indent(errno ? " SMTP(%s)<<\n" : " SMTP(closed)<<\n",
smtp_context * sx = sx0;
uschar * ptr = buffer;
int count = 0;
+time_t timelimit = time(NULL) + timeout;
errno = 0; /* Ensure errno starts out zero */
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)
while ((i = ip_recv(&spamd_cctx,
spamd_buffer + offset,
sizeof(spamd_buffer) - offset - 1,
- sd->timeout - time(NULL) + start)) > 0)
+ sd->timeout + start)) > 0)
offset += i;
spamd_buffer[offset] = '\0'; /* guard byte */
#ifdef TCP_QUICKACK
(void) setsockopt(fd, IPPROTO_TCP, TCP_QUICKACK, US &off, sizeof(off));
#endif
- if (!fd_ready(fd, tmo-time(NULL)) || read(fd, s, 2) != 2)
+ if (!fd_ready(fd, tmo) || read(fd, s, 2) != 2)
return FAIL;
HDEBUG(D_transport|D_acl|D_v)
debug_printf_indent(" SOCKS<< %02x %02x\n", s[0], s[1]);
(void) setsockopt(fd, IPPROTO_TCP, TCP_QUICKACK, US &off, sizeof(off));
#endif
-if ( !fd_ready(fd, tmo-time(NULL))
+if ( !fd_ready(fd, tmo)
|| read(fd, buf, 2) != 2
)
goto rcv_err;
/* expect conn-reply (success, local(ipver, addr, port))
of same length as conn-request, or non-success fail code */
-if ( !fd_ready(fd, tmo-time(NULL))
+if ( !fd_ready(fd, tmo)
|| (size = read(fd, buf, size)) < 2
)
goto rcv_err;
int size = sizeof(buffer) - (p - buffer);
if (size <= 0) goto END_OFF; /* Buffer filled without seeing \n. */
- count = ip_recv(&ident_conn_ctx, p, size, rfc1413_query_timeout);
+ count = ip_recv(&ident_conn_ctx, p, size, time(NULL) + rfc1413_query_timeout);
if (count <= 0) goto END_OFF; /* Read error or EOF */
/* Scan what we just read, to see if we have reached the terminating \r\n. Be