Taint: check on supplied buffer vs. list when extracting elements
[exim.git] / src / src / ip.c
index bf13d825c5ef52680cbf117cd38a6603967ffd3a..4c543566dd55b0547548d1c732dd2af03eb87cbc 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,36 +249,60 @@ 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;
     }
-  else if (errno == EINPROGRESS)       /* expected if we had no cookie for peer */
+  else switch (errno)
+    {
+    case EINPROGRESS:  /* expected if we had no cookie for peer */
        /* seen for no-data, proper TFO option, both cookie-request and with-cookie cases */
        /*  apparently no visibility of the diffference at this point */
        /* seen for with-data, proper TFO opt, cookie-req */
        /*   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",
-      fastopen_blob->len > 0 ? "with"  : "no");
-    if (!fastopen_blob->data)
-      {
-      tcp_out_fastopen = TFO_ATTEMPTED_NODATA;         /* we tried; unknown if useful yet */
-      rc = 0;
-      }
-    else
-      rc = send(sock, fastopen_blob->data, fastopen_blob->len, 0);
+      DEBUG(D_transport|D_v) debug_printf(" TFO mode sendto, %s data: EINPROGRESS\n",
+       fastopen_blob->len > 0 ? "with"  : "no");
+      if (!fastopen_blob->data)
+       {
+       tcp_out_fastopen = TFO_ATTEMPTED_NODATA;                /* we tried; unknown if useful yet */
+       rc = 0;
+       }
+      else                                     /* queue unsent data */
+       rc = send(sock, fastopen_blob->data, fastopen_blob->len, 0);
+      break;
+
+    case EOPNOTSUPP:
+      DEBUG(D_transport)
+       debug_printf("Tried TCP Fast Open but apparently not enabled by sysctl\n");
+      goto legacy_connect;
+
+    case EPIPE:
+      DEBUG(D_transport)
+       debug_printf("Tried TCP Fast Open but kernel too old to support it\n");
+      goto legacy_connect;
     }
-  else if(errno == EOPNOTSUPP)
+
+# 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;
     }
-# endif
-# ifdef EXIM_TFO_CONNECTX
+  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 +315,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,7 +325,7 @@ 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)
       {
@@ -329,14 +338,14 @@ if (fastopen_blob && f.tcp_fastopen_ok)
 # endif
   }
 else
-#endif /*TCP_FASTOPEN*/
+#endif /*EXIM_SUPPORT_TFO*/
   {
-#if defined(TCP_FASTOPEN) && defined(MSG_FASTOPEN)
+#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
@@ -479,7 +488,8 @@ bad:
 
 /*XXX TFO? */
 int
-ip_tcpsocket(const uschar * hostport, uschar ** errstr, int tmo)
+ip_tcpsocket(const uschar * hostport, uschar ** errstr, int tmo,
+  host_item * connhost)
 {
 int scan;
 uschar hostname[256];
@@ -498,7 +508,7 @@ if (scan != 3)
   }
 
 return ip_connectedsocket(SOCK_STREAM, hostname, portlow, porthigh,
-                         tmo, NULL, errstr, NULL);
+                         tmo, connhost, errstr, NULL);
 }
 
 int
@@ -515,7 +525,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)
   {
@@ -528,11 +538,18 @@ 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.
+
+For a TCP socket, optionally fill in a  host_item.
+*/
 int
-ip_streamsocket(const uschar * spec, uschar ** errstr, int tmo)
+ip_streamsocket(const uschar * spec, uschar ** errstr, int tmo,
+  host_item * connhost)
 {
 return *spec == '/'
-  ? ip_unixsocket(spec, errstr) : ip_tcpsocket(spec, errstr, tmo);
+  ? ip_unixsocket(spec, errstr) : ip_tcpsocket(spec, errstr, tmo, connhost);
 }
 
 /*************************************************