#include "exim.h"
+#if defined(TCP_FASTOPEN)
+# if defined(MSG_FASTOPEN) || defined(EXIM_TFO_CONNECTX) || defined(EXIM_TFO_FREEBSD)
+# define EXIM_SUPPORT_TFO
+# endif
+#endif
+
/*************************************************
* Create a socket *
*************************************************/
-/*************************************************
-*************************************************/
-
-#ifdef EXIM_TFO_PROBE
-void
-tfo_probe(void)
-{
-# ifdef TCP_FASTOPEN
-int sock, backlog = 5;
-
-if ( (sock = socket(SOCK_STREAM, AF_INET, 0)) < 0
- && setsockopt(sock, IPPROTO_TCP, TCP_FASTOPEN, &backlog, sizeof(backlog))
- )
- f.tcp_fastopen_ok = TRUE;
-close(sock);
-# endif
-}
-#endif
-
-
/*************************************************
* Connect socket to remote host *
*************************************************/
sigalrm_seen = FALSE;
if (timeout > 0) ALARM(timeout);
-#ifdef TCP_FASTOPEN
+#ifdef EXIM_SUPPORT_TFO
/* TCP Fast Open, if the system has a cookie from a previous call to
this peer, can send data in the SYN packet. The peer can send data
before it gets our ACK of its SYN,ACK - the latter is useful for
if (fastopen_blob && f.tcp_fastopen_ok)
{
# ifdef MSG_FASTOPEN
- /* This is a Linux implementation. It might be useable on FreeBSD; I have
- not checked. */
+ /* This is a Linux implementation. */
if ((rc = sendto(sock, fastopen_blob->data, fastopen_blob->len,
MSG_FASTOPEN | MSG_DONTWAIT, s_ptr, s_len)) >= 0)
/* seen for with-data, proper TFO opt, with-cookie case */
{
DEBUG(D_transport|D_v)
- debug_printf("TFO mode connection attempt to %s, %lu data\n",
+ debug_printf(" TFO mode connection attempt to %s, %lu data\n",
address, (unsigned long)fastopen_blob->len);
/*XXX also seen on successful TFO, sigh */
tcp_out_fastopen = fastopen_blob->len > 0 ? TFO_ATTEMPTED_DATA : TFO_ATTEMPTED_NODATA;
/* with netwk delay, post-conn tcp_info sees unacked 1 for R, 2 for C; code in smtp_out.c */
/* ? older Experimental TFO option behaviour ? */
{ /* queue unsent data */
- DEBUG(D_transport|D_v) debug_printf("TFO mode sendto, %s data: EINPROGRESS\n",
+ DEBUG(D_transport|D_v) debug_printf(" TFO mode sendto, %s data: EINPROGRESS\n",
fastopen_blob->len > 0 ? "with" : "no");
if (!fastopen_blob->data)
{
debug_printf("Tried TCP Fast Open but apparently not enabled by sysctl\n");
goto legacy_connect;
}
-# endif
-# ifdef EXIM_TFO_CONNECTX
+
+# elif defined(EXIM_TFO_FREEBSD)
+ /* Re: https://people.freebsd.org/~pkelsey/tfo-tools/tfo-client.c */
+
+ if (setsockopt(sock, IPPROTO_TCP, TCP_FASTOPEN, &on, sizeof(on)) < 0)
+ {
+ DEBUG(D_transport)
+ debug_printf("Tried TCP Fast Open but apparently not enabled by sysctl\n");
+ goto legacy_connect;
+ }
+ if ((rc = sendto(sock, fastopen_blob->data, fastopen_blob->len, 0,
+ s_ptr, s_len)) >= 0)
+ {
+ DEBUG(D_transport|D_v)
+ debug_printf(" TFO mode connection attempt to %s, %lu data\n",
+ address, (unsigned long)fastopen_blob->len);
+ tcp_out_fastopen = fastopen_blob->len > 0 ? TFO_ATTEMPTED_DATA : TFO_ATTEMPTED_NODATA;
+ }
+
+# elif defined(EXIM_TFO_CONNECTX)
/* MacOS */
sa_endpoints_t ends = {
.sae_srcif = 0, .sae_srcaddr = NULL, .sae_srcaddrlen = 0,
CONNECT_DATA_IDEMPOTENT, &iov, 1, &len, NULL)) == 0)
{
DEBUG(D_transport|D_v)
- debug_printf("TFO mode connection attempt to %s, %lu data\n",
+ debug_printf(" TFO mode connection attempt to %s, %lu data\n",
address, (unsigned long)fastopen_blob->len);
tcp_out_fastopen = fastopen_blob->len > 0 ? TFO_ATTEMPTED_DATA : TFO_ATTEMPTED_NODATA;
}
else if (errno == EINPROGRESS)
{
- DEBUG(D_transport|D_v) debug_printf("TFO mode sendto, %s data: EINPROGRESS\n",
+ DEBUG(D_transport|D_v) debug_printf(" TFO mode connectx, %s data: EINPROGRESS\n",
fastopen_blob->len > 0 ? "with" : "no");
if (!fastopen_blob->data)
{
# endif
}
else
-#endif /*TCP_FASTOPEN*/
+#endif /*EXIM_SUPPORT_TFO*/
{
+#if defined(EXIM_SUPPORT_TFO) && !defined(EXIM_TFO_CONNECTX)
legacy_connect:
+#endif
+
DEBUG(D_transport|D_v) if (fastopen_blob)
- debug_printf("non-TFO mode connection attempt to %s, %lu data\n",
+ debug_printf(" non-TFO mode connection attempt to %s, %lu data\n",
address, (unsigned long)fastopen_blob->len);
if ((rc = connect(sock, s_ptr, s_len)) >= 0)
if ( fastopen_blob && fastopen_blob->data && fastopen_blob->len
callout_address = string_copy(path);
server.sun_family = AF_UNIX;
-Ustrncpy(server.sun_path, path, sizeof(server.sun_path)-1);
+Ustrncpy(US server.sun_path, path, sizeof(server.sun_path)-1);
server.sun_path[sizeof(server.sun_path)-1] = '\0';
if (connect(sock, (struct sockaddr *) &server, sizeof(server)) < 0)
{
return sock;
}
+/* spec is either an absolute path (with a leading /), or
+a host (name or IP) and port (whitespace-separated).
+The port can be a range, dash-separated, or a single number.
+*/
int
ip_streamsocket(const uschar * spec, uschar ** errstr, int tmo)
{
/*
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.
close down of the connection), set errno to zero; otherwise leave it alone. */
-#ifdef SUPPORT_TLS
+#ifndef DISABLE_TLS
if (cctx->tls_ctx) /* client TLS */
rc = tls_read(cctx->tls_ctx, buffer, buffsize);
else if (tls_in.active.sock == cctx->sock) /* server TLS */