transport dynamic modules
[exim.git] / src / src / ip.c
index bf332b160f25458ae43e71591d991cf858227c57..d5343e005003aa7c403f4b06ab26d293f41cfacc 100644 (file)
@@ -2,8 +2,10 @@
 *     Exim - an Internet mail transport agent    *
 *************************************************/
 
+/* Copyright (c) The Exim Maintainers 2020 - 2023 */
 /* Copyright (c) University of Cambridge 1995 - 2018 */
 /* See the file NOTICE for conditions of use and distribution. */
+/* SPDX-License-Identifier: GPL-2.0-or-later */
 
 /* Functions for doing things with sockets. With the advent of IPv6 this has
 got messier, so that it's worth pulling out the code into separate functions
@@ -126,8 +128,6 @@ if (af == AF_INET6)
   return sizeof(sin->v6);
   }
 else
-#else     /* HAVE_IPv6 */
-af = af;  /* Avoid compiler warning */
 #endif    /* HAVE_IPV6 */
 
 /* Setup code when using IPv4 socket. The wildcard address is "". */
@@ -161,7 +161,10 @@ ip_bind(int sock, int af, uschar *address, int port)
 {
 union sockaddr_46 sin;
 int s_len = ip_addr(&sin, af, address, port);
-return bind(sock, (struct sockaddr *)&sin, s_len);
+int rc = bind(sock, (struct sockaddr *)&sin, s_len);
+if (rc < 0)
+  log_write(0, LOG_MAIN, "bind of [%s]:%d failed", address, port);
+return rc;
 }
 
 
@@ -208,8 +211,6 @@ if (af == AF_INET6)
   s_len = sizeof(s_in6);
   }
 else
-#else     /* HAVE_IPV6 */
-af = af;  /* Avoid compiler warning */
 #endif    /* HAVE_IPV6 */
 
 /* For an IPv4 address, use an IPv4 sockaddr structure, even on a system with
@@ -254,28 +255,34 @@ if (fastopen_blob && f.tcp_fastopen_ok)
     /*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);
-    }
-  else if(errno == EOPNOTSUPP)
-    {
-    DEBUG(D_transport)
-      debug_printf("Tried TCP Fast Open but apparently not enabled by sysctl\n");
-    goto legacy_connect;
+      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;
     }
 
 # elif defined(EXIM_TFO_FREEBSD)
@@ -460,8 +467,8 @@ for (host_item * h = &shost; h; h = h->next)
   for (int port = portlo; port <= porthi; port++)
     if (ip_connect(fd, af, h->address, port, timeout, fastopen_blob) == 0)
       {
-      if (fd != fd6) close(fd6);
-      if (fd != fd4) close(fd4);
+      if (fd6 >= 0 && fd != fd6) close(fd6);
+      if (fd4 >= 0 && fd != fd4) close(fd4);
       if (connhost)
        {
        h->port = port;
@@ -482,7 +489,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];
@@ -501,7 +509,7 @@ if (scan != 3)
   }
 
 return ip_connectedsocket(SOCK_STREAM, hostname, portlow, porthigh,
-                         tmo, NULL, errstr, NULL);
+                         tmo, connhost, errstr, NULL);
 }
 
 int
@@ -534,12 +542,15 @@ 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);
 }
 
 /*************************************************
@@ -582,9 +593,7 @@ Returns:      TRUE => ready for i/o
 BOOL
 fd_ready(int fd, time_t timelimit)
 {
-fd_set select_inset;
-int time_left = timelimit - time(NULL);
-int rc;
+int rc, time_left = timelimit - time(NULL);
 
 if (time_left <= 0)
   {
@@ -595,12 +604,8 @@ if (time_left <= 0)
 
 do
   {
-  struct timeval tv = { .tv_sec = time_left, .tv_usec = 0 };
-  FD_ZERO (&select_inset);
-  FD_SET (fd, &select_inset);
-
   /*DEBUG(D_transport) debug_printf("waiting for data on fd\n");*/
-  rc = select(fd + 1, (SELECT_ARG2_TYPE *)&select_inset, NULL, NULL, &tv);
+  rc = poll_one_fd(fd, POLLIN, time_left * 1000);
 
   /* If some interrupt arrived, just retry. We presume this to be rare,
   but it can happen (e.g. the SIGUSR1 signal sent by exiwhat causes
@@ -629,7 +634,7 @@ do
   /* Checking the FD_ISSET is not enough, if we're interrupted, the
   select_inset may still contain the 'input'. */
   }
-while (rc < 0 || !FD_ISSET(fd, &select_inset));
+while (rc < 0);
 return TRUE;
 }