Fix taint hybrid-checking on BSD
[users/heiko/exim.git] / src / src / ip.c
index 2a6a2492296f08401c47ba411fd9e7b3a4cc1a8b..bf332b160f25458ae43e71591d991cf858227c57 100644 (file)
@@ -14,6 +14,12 @@ different places in the code where sockets are used. */
 #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                    *
 *************************************************/
@@ -160,26 +166,6 @@ return bind(sock, (struct sockaddr *)&sin, s_len);
 
 
 
-/*************************************************
-*************************************************/
-
-#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           *
 *************************************************/
@@ -245,7 +231,7 @@ callout_address = string_sprintf("[%s]:%d", address, port);
 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
@@ -255,8 +241,7 @@ possibly use the data-on-syn, so support that too. */
 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)
@@ -264,7 +249,7 @@ if (fastopen_blob && f.tcp_fastopen_ok)
        /* 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;
@@ -276,7 +261,7 @@ if (fastopen_blob && f.tcp_fastopen_ok)
        /*   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)
       {
@@ -292,8 +277,26 @@ if (fastopen_blob && f.tcp_fastopen_ok)
       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,
@@ -306,7 +309,7 @@ if (fastopen_blob && f.tcp_fastopen_ok)
             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;
 
@@ -316,11 +319,11 @@ if (fastopen_blob && f.tcp_fastopen_ok)
     }
   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)
       {
-      tcp_out_fastopen = TFO_ATTEMPTED;                /* we tried; unknown if useful yet */
+      tcp_out_fastopen = TFO_ATTEMPTED_NODATA;         /* we tried; unknown if useful yet */
       rc = 0;
       }
     else       /* assume that no data was queued; block in send */
@@ -329,11 +332,14 @@ if (fastopen_blob && f.tcp_fastopen_ok)
 # 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
@@ -394,9 +400,8 @@ int
 ip_connectedsocket(int type, const uschar * hostname, int portlo, int porthi,
       int timeout, host_item * connhost, uschar ** errstr, const blob * fastopen_blob)
 {
-int namelen, port;
+int namelen;
 host_item shost;
-host_item *h;
 int af = 0, fd, fd4 = -1, fd6 = -1;
 
 shost.next = NULL;
@@ -440,7 +445,7 @@ else
 
 /* Try to connect to the server - test each IP till one works */
 
-for (h = &shost; h; h = h->next)
+for (host_item * h = &shost; h; h = h->next)
   {
   fd = Ustrchr(h->address, ':') != 0
     ? fd6 < 0 ? (fd6 = ip_socket(type, af = AF_INET6)) : fd6
@@ -452,7 +457,7 @@ for (h = &shost; h; h = h->next)
     goto bad;
     }
 
-  for(port = portlo; port <= porthi; port++)
+  for (int port = portlo; port <= porthi; port++)
     if (ip_connect(fd, af, h->address, port, timeout, fastopen_blob) == 0)
       {
       if (fd != fd6) close(fd6);
@@ -513,7 +518,7 @@ if ((sock = socket(AF_UNIX, SOCK_STREAM, 0)) < 0)
 
 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)
   {
@@ -526,6 +531,10 @@ 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)
 {
@@ -566,16 +575,15 @@ if (setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE,
 /*
 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)
@@ -609,8 +617,7 @@ do
     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)
@@ -634,24 +641,24 @@ Arguments:
   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 */
@@ -835,8 +842,7 @@ return FALSE;
 void
 dscp_list_to_stream(FILE *stream)
 {
-int i;
-for (i=0; i < dscp_table_size; ++i)
+for (int i = 0; i < dscp_table_size; ++i)
   fprintf(stream, "%s\n", dscp_table[i].name);
 }