TLS: rework client-side use with an explicit context rather than a global
authorJeremy Harris <jgh146exb@wizmail.org>
Thu, 21 Jun 2018 18:16:29 +0000 (19:16 +0100)
committerJeremy Harris <jgh146exb@wizmail.org>
Sun, 24 Jun 2018 17:00:40 +0000 (18:00 +0100)
24 files changed:
doc/doc-txt/ChangeLog
src/exim_monitor/em_globals.c
src/src/daemon.c
src/src/deliver.c
src/src/dkim_transport.c
src/src/exim.c
src/src/functions.h
src/src/globals.c
src/src/globals.h
src/src/ip.c
src/src/macros.h
src/src/malware.c
src/src/receive.c
src/src/routers/iplookup.c
src/src/smtp_in.c
src/src/smtp_out.c
src/src/spam.c
src/src/structs.h
src/src/tls-gnu.c
src/src/tls-openssl.c
src/src/transport.c
src/src/transports/smtp.c
src/src/transports/smtp.h
src/src/verify.c

index 96508ff3f2c6abeefc0827ab582c17bbd3b845cb..db8804904f712b9adcfcdfcd1f4f76307d5b0639 100644 (file)
@@ -68,6 +68,11 @@ JH/13 For receent Openssl versions (1.1 onward) use modern generic protocol
 
 JH/14 Bug 2284: Fix DKIM signing for body lines starting with a pair of dots.
 
 
 JH/14 Bug 2284: Fix DKIM signing for body lines starting with a pair of dots.
 
+JH/15 Rework TLS client-side context management.  Stop using a global, and
+      explicitly pass a context around.  This enables future use of TLS for
+      connections to service-daemons (eg. malware scanning) while a client smtp
+      connection is using TLS; with cutthrough connections this is quite likely.
+
 
 Exim version 4.91
 -----------------
 
 Exim version 4.91
 -----------------
index e0ed0244e5afee5fbfc4333a34ba0910a60a1a30..6ed57ef1731e4675c43764c22fcc7cc2afe5b96e 100644 (file)
@@ -217,7 +217,7 @@ int     string_datestamp_type  = -1;
 
 BOOL    timestamps_utc         = FALSE;
 tls_support tls_in = {
 
 BOOL    timestamps_utc         = FALSE;
 tls_support tls_in = {
-1,   /* tls_active */
{-1}, /* tls_active */
  0,    /* bits */
  FALSE,        /* tls_certificate_verified */
 #ifdef SUPPORT_DANE
  0,    /* bits */
  FALSE,        /* tls_certificate_verified */
 #ifdef SUPPORT_DANE
index 4a89afe742b0ba8005ad19950ec22239f53d6496..a382a734ecc6e62ce529579fbf18543cb8718995 100644 (file)
@@ -648,7 +648,7 @@ if (pid == 0)
         the data structures if necessary. */
 
 #ifdef SUPPORT_TLS
         the data structures if necessary. */
 
 #ifdef SUPPORT_TLS
-        tls_close(TRUE, TLS_NO_SHUTDOWN);
+        tls_close(NULL, TLS_NO_SHUTDOWN);
 #endif
 
         /* Reset SIGHUP and SIGCHLD in the child in both cases. */
 #endif
 
         /* Reset SIGHUP and SIGCHLD in the child in both cases. */
index c35f3fa5bbe19661d94fed49220dfdcf31b42034..7127518e2c1df84af330f04d819279d6b5f8f258 100644 (file)
@@ -4478,7 +4478,7 @@ for (delivery_count = 0; addr_remote; delivery_count++)
   treat it as if it is a continued connection (apart from the counter used
   for the log line mark). */
 
   treat it as if it is a continued connection (apart from the counter used
   for the log line mark). */
 
-  if (cutthrough.fd >= 0 && cutthrough.callout_hold_only)
+  if (cutthrough.cctx.sock >= 0 && cutthrough.callout_hold_only)
     {
     DEBUG(D_deliver)
       debug_printf("lazy-callout-close: have conn still open from verification\n");
     {
     DEBUG(D_deliver)
       debug_printf("lazy-callout-close: have conn still open from verification\n");
@@ -4985,12 +4985,13 @@ all pipes, so I do not see a reason to use non-blocking IO here
   release its TLS library context (if any) as responsibility was passed to
   the delivery child process. */
 
   release its TLS library context (if any) as responsibility was passed to
   the delivery child process. */
 
-  if (cutthrough.fd >= 0 && cutthrough.callout_hold_only)
+  if (cutthrough.cctx.sock >= 0 && cutthrough.callout_hold_only)
     {
 #ifdef SUPPORT_TLS
     {
 #ifdef SUPPORT_TLS
-    tls_close(FALSE, TLS_NO_SHUTDOWN);
+    if (cutthrough.is_tls)
+      tls_close(cutthrough.cctx.tls_ctx, TLS_NO_SHUTDOWN);
 #endif
 #endif
-    (void) close(cutthrough.fd);
+    (void) close(cutthrough.cctx.sock);
     release_cutthrough_connection(US"passed to transport proc");
     }
 
     release_cutthrough_connection(US"passed to transport proc");
     }
 
@@ -8528,9 +8529,9 @@ delivery_re_exec(int exec_type)
 {
 uschar * where;
 
 {
 uschar * where;
 
-if (cutthrough.fd >= 0 && cutthrough.callout_hold_only)
+if (cutthrough.cctx.sock >= 0 && cutthrough.callout_hold_only)
   {
   {
-  int pfd[2], channel_fd = cutthrough.fd, pid;
+  int pfd[2], channel_fd = cutthrough.cctx.sock, pid;
 
   smtp_peer_options = cutthrough.peer_options;
   continue_sequence = 0;
 
   smtp_peer_options = cutthrough.peer_options;
   continue_sequence = 0;
@@ -8554,7 +8555,8 @@ if (cutthrough.fd >= 0 && cutthrough.callout_hold_only)
       {
       if (running_in_test_harness) millisleep(100); /* let parent debug out */
       /* does not return */
       {
       if (running_in_test_harness) millisleep(100); /* let parent debug out */
       /* does not return */
-      smtp_proxy_tls(big_buffer, big_buffer_size, pfd, 5*60);
+      smtp_proxy_tls(cutthrough.cctx.tls_ctx, big_buffer, big_buffer_size,
+                     pfd, 5*60);
       }
 
     DEBUG(D_transport) debug_printf("proxy-proc inter-pid %d\n", pid);
       }
 
     DEBUG(D_transport) debug_printf("proxy-proc inter-pid %d\n", pid);
index 0e9c3818cddbcdbf63e44c665a5ee73f96fa1e13..db3db91809b6d7a55e0f0cedae82a4456bfe8024 100644 (file)
@@ -56,7 +56,7 @@ DEBUG(D_transport) debug_printf("send file fd=%d\n", out_fd);
    to the socket. However only if we don't use TLS,
    as then there's another layer of indirection
    before the data finally hits the socket. */
    to the socket. However only if we don't use TLS,
    as then there's another layer of indirection
    before the data finally hits the socket. */
-if (tls_out.active != out_fd)
+if (tls_out.active.sock != out_fd)
   {
   ssize_t copied = 0;
 
   {
   ssize_t copied = 0;
 
@@ -84,8 +84,8 @@ else
     while (sread)
       {
 #ifdef SUPPORT_TLS
     while (sread)
       {
 #ifdef SUPPORT_TLS
-      wwritten = tls_out.active == out_fd
-       ? tls_write(FALSE, p, sread, FALSE)
+      wwritten = tls_out.active.sock == out_fd
+       ? tls_write(tls_out.active.tls_ctx, p, sread, FALSE)
        : write(out_fd, CS p, sread);
 #else
       wwritten = write(out_fd, CS p, sread);
        : write(out_fd, CS p, sread);
 #else
       wwritten = write(out_fd, CS p, sread);
index 58c2dc9af95ca08f2b3c0275a4e29007affede49..2b4ecbc66516ad4cafaf83ce119539977723b4b9 100644 (file)
@@ -550,9 +550,9 @@ close_unwanted(void)
 {
 if (smtp_input)
   {
 {
 if (smtp_input)
   {
-  #ifdef SUPPORT_TLS
-  tls_close(TRUE, TLS_NO_SHUTDOWN);      /* Shut down the TLS library */
-  #endif
+#ifdef SUPPORT_TLS
+  tls_close(NULL, TLS_NO_SHUTDOWN);      /* Shut down the TLS library */
+#endif
   (void)close(fileno(smtp_in));
   (void)close(fileno(smtp_out));
   smtp_in = NULL;
   (void)close(fileno(smtp_in));
   (void)close(fileno(smtp_out));
   smtp_in = NULL;
index e333f4e149cdc9769ab851c36f007ed9a8d45ff4..9b105774ebfdd5bfb44733c29be41d3269ea59cf 100644 (file)
@@ -44,13 +44,13 @@ extern uschar * tls_cert_fprt_md5(void *);
 extern uschar * tls_cert_fprt_sha1(void *);
 extern uschar * tls_cert_fprt_sha256(void *);
 
 extern uschar * tls_cert_fprt_sha1(void *);
 extern uschar * tls_cert_fprt_sha256(void *);
 
-extern int     tls_client_start(int, host_item *, address_item *,
+extern void *  tls_client_start(int, host_item *, address_item *,
                 transport_instance *,
 # ifdef SUPPORT_DANE
                dns_answer *,
 # endif
                 transport_instance *,
 # ifdef SUPPORT_DANE
                dns_answer *,
 # endif
-               uschar **);
-extern void    tls_close(BOOL, int);
+               tls_support *, uschar **);
+extern void    tls_close(void *, int);
 extern BOOL    tls_could_read(void);
 extern int     tls_export_cert(uschar *, size_t, void *);
 extern int     tls_feof(void);
 extern BOOL    tls_could_read(void);
 extern int     tls_export_cert(uschar *, size_t, void *);
 extern int     tls_feof(void);
@@ -60,11 +60,11 @@ extern int     tls_getc(unsigned);
 extern uschar *tls_getbuf(unsigned *);
 extern void    tls_get_cache(void);
 extern int     tls_import_cert(const uschar *, void **);
 extern uschar *tls_getbuf(unsigned *);
 extern void    tls_get_cache(void);
 extern int     tls_import_cert(const uschar *, void **);
-extern int     tls_read(BOOL, uschar *, size_t);
+extern int     tls_read(void *, uschar *, size_t);
 extern int     tls_server_start(const uschar *, uschar **);
 extern BOOL    tls_smtp_buffered(void);
 extern int     tls_ungetc(int);
 extern int     tls_server_start(const uschar *, uschar **);
 extern BOOL    tls_smtp_buffered(void);
 extern int     tls_ungetc(int);
-extern int     tls_write(BOOL, const uschar *, size_t, BOOL);
+extern int     tls_write(void *, const uschar *, size_t, BOOL);
 extern uschar *tls_validate_require_cipher(void);
 extern void    tls_version_report(FILE *);
 # ifndef USE_GNUTLS
 extern uschar *tls_validate_require_cipher(void);
 extern void    tls_version_report(FILE *);
 # ifndef USE_GNUTLS
@@ -271,7 +271,7 @@ extern int     ip_connectedsocket(int, const uschar *, int, int,
                  int, host_item *, uschar **, const blob *);
 extern int     ip_get_address_family(int);
 extern void    ip_keepalive(int, const uschar *, 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(int, uschar *, int, int);
+extern int     ip_recv(client_conn_ctx *, uschar *, int, int);
 extern int     ip_socket(int, int);
 
 extern int     ip_tcpsocket(const uschar *, uschar **, int);
 extern int     ip_socket(int, int);
 
 extern int     ip_tcpsocket(const uschar *, uschar **, int);
@@ -449,7 +449,7 @@ extern void    smtp_get_cache(void);
 extern int     smtp_handle_acl_fail(int, int, uschar *, uschar *);
 extern void    smtp_log_no_mail(void);
 extern void    smtp_message_code(uschar **, int *, uschar **, uschar **, BOOL);
 extern int     smtp_handle_acl_fail(int, int, uschar *, uschar *);
 extern void    smtp_log_no_mail(void);
 extern void    smtp_message_code(uschar **, int *, uschar **, uschar **, BOOL);
-extern void    smtp_proxy_tls(uschar *, size_t, int *, int);
+extern void    smtp_proxy_tls(void *, uschar *, size_t, int *, int);
 extern BOOL    smtp_read_response(smtp_inblock *, uschar *, int, int, int);
 extern void    smtp_reset(void *);
 extern void    smtp_respond(uschar *, int, BOOL, uschar *);
 extern BOOL    smtp_read_response(smtp_inblock *, uschar *, int, int, int);
 extern void    smtp_reset(void *);
 extern void    smtp_respond(uschar *, int, BOOL, uschar *);
index 138a29e8ad20408ed05834d0c1a69027c4db58e6..3fa0e3e3f76ae800ce7f2f7af2a9d164d7297c4c 100644 (file)
@@ -98,10 +98,11 @@ BOOL    move_frozen_messages   = FALSE;
 
 /* These variables are outside the #ifdef because it keeps the code less
 cluttered in several places (e.g. during logging) if we can always refer to
 
 /* These variables are outside the #ifdef because it keeps the code less
 cluttered in several places (e.g. during logging) if we can always refer to
-them. Also, the tls_ variables are now always visible. */
+them. Also, the tls_ variables are now always visible.  Note that these are
+only used for smtp connections, not for service-daemon access. */
 
 tls_support tls_in = {
 
 tls_support tls_in = {
- .active =             -1,
+ .active =             {.sock = -1},
  .bits =               0,
  .certificate_verified = FALSE,
 #ifdef SUPPORT_DANE
  .bits =               0,
  .certificate_verified = FALSE,
 #ifdef SUPPORT_DANE
@@ -118,7 +119,7 @@ tls_support tls_in = {
  .ocsp =               OCSP_NOT_REQ
 };
 tls_support tls_out = {
  .ocsp =               OCSP_NOT_REQ
 };
 tls_support tls_out = {
- .active =             -1,
+ .active =             {.sock = -1},
  .bits =               0,
  .certificate_verified = FALSE,
 #ifdef SUPPORT_DANE
  .bits =               0,
  .certificate_verified = FALSE,
 #ifdef SUPPORT_DANE
@@ -559,7 +560,7 @@ cut_t   cutthrough = {
   .delivery =          FALSE,                          /* when to attempt */
   .defer_pass =                FALSE,                          /* on defer: spool locally */
   .is_tls =            FALSE,                          /* not a TLS conn yet */
   .delivery =          FALSE,                          /* when to attempt */
   .defer_pass =                FALSE,                          /* on defer: spool locally */
   .is_tls =            FALSE,                          /* not a TLS conn yet */
-  .fd =                        -1,                             /* open connection */
+  .cctx =              {.sock = -1},                   /* open connection */
   .nrcpt =             0,                              /* number of addresses */
 };
 
   .nrcpt =             0,                              /* number of addresses */
 };
 
index f9be8b8324dc6d389d0517752401b89c0d9fa860..ef5b3a56015ea069099fae69c6287118c94b1e7d 100644 (file)
@@ -80,7 +80,7 @@ cluttered in several places (e.g. during logging) if we can always refer to
 them. Also, the tls_ variables are now always visible. */
 
 typedef struct {
 them. Also, the tls_ variables are now always visible. */
 
 typedef struct {
-  int     active;             /* fd/socket when in a TLS session */
+  client_conn_ctx active;     /* fd/socket when in a TLS session, and ptr to TLS context */
   int     bits;               /* bits used in TLS session */
   BOOL    certificate_verified; /* Client certificate verified */
 #ifdef SUPPORT_DANE
   int     bits;               /* bits used in TLS session */
   BOOL    certificate_verified; /* Client certificate verified */
 #ifdef SUPPORT_DANE
@@ -314,7 +314,7 @@ typedef struct {
   unsigned     delivery:1;             /* When to attempt */
   unsigned     defer_pass:1;           /* Pass 4xx to caller rather than spooling */
   unsigned     is_tls:1;              /* Conn has TLS active */
   unsigned     delivery:1;             /* When to attempt */
   unsigned     defer_pass:1;           /* Pass 4xx to caller rather than spooling */
   unsigned     is_tls:1;              /* Conn has TLS active */
-  int          fd;                     /* Open connection */
+  client_conn_ctx cctx;                /* Open connection */
   int          nrcpt;                  /* Count of addresses */
   uschar *     transport;             /* Name of transport */
   uschar *     interface;              /* (address of) */
   int          nrcpt;                  /* Count of addresses */
   uschar *     transport;             /* Name of transport */
   uschar *     interface;              /* (address of) */
index 2e8968528b62d4087a1590b3b67fb551b5989bb0..555dc2d84bee5bdb6c0109e94f4fdb146b6a82e8 100644 (file)
@@ -591,7 +591,7 @@ getting interrupted, and the possibility of select() returning with a positive
 result but no ready descriptor. Is this in fact possible?
 
 Arguments:
 result but no ready descriptor. Is this in fact possible?
 
 Arguments:
-  sock        the socket
+  cctx        the connection context (socket fd, possibly TLS context)
   buffer      to read into
   bufsize     the buffer size
   timeout     the timeout
   buffer      to read into
   bufsize     the buffer size
   timeout     the timeout
@@ -601,24 +601,24 @@ Returns:      > 0 => that much data read
 */
 
 int
 */
 
 int
-ip_recv(int sock, uschar *buffer, int buffsize, int timeout)
+ip_recv(client_conn_ctx * cctx, uschar * buffer, int buffsize, int timeout)
 {
 int rc;
 
 {
 int rc;
 
-if (!fd_ready(sock, timeout))
+if (!fd_ready(cctx->sock, timeout))
   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
   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
-if (tls_out.active == sock)
-  rc = tls_read(FALSE, buffer, buffsize);
-else if (tls_in.active == sock)
-  rc = tls_read(TRUE, buffer, buffsize);
+if (cctx->tls_ctx)                                     /* client TLS */
+  rc = tls_read(cctx->tls_ctx, buffer, buffsize);
+else if (tls_in.active.sock == cctx->sock)             /* server TLS */
+  rc = tls_read(NULL, buffer, buffsize);
 else
 #endif
 else
 #endif
-  rc = recv(sock, buffer, buffsize, 0);
+  rc = recv(cctx->sock, buffer, buffsize, 0);
 
 if (rc > 0) return rc;
 if (rc == 0) errno = 0;
 
 if (rc > 0) return rc;
 if (rc == 0) errno = 0;
index 85ceb0acb3b6a924ee62ed91470b8a46d231bf12..f22fe8c9ad712d51337bae56f3d73d1d06797ea8 100644 (file)
@@ -85,7 +85,7 @@ as unsigned. */
 a no-op once an SSL session is in progress. */
 
 #ifdef SUPPORT_TLS
 a no-op once an SSL session is in progress. */
 
 #ifdef SUPPORT_TLS
-#define mac_smtp_fflush() if (tls_in.active < 0) fflush(smtp_out);
+#define mac_smtp_fflush() if (tls_in.active.sock < 0) fflush(smtp_out);
 #else
 #define mac_smtp_fflush() fflush(smtp_out);
 #endif
 #else
 #define mac_smtp_fflush() fflush(smtp_out);
 #endif
index a5de8a93834253e9ab4781673923193c8c840922..49456de4e5507f5069e52885a21e2d778ebde0a7 100644 (file)
@@ -436,12 +436,13 @@ for (;;)
 static inline int
 mksd_read_lines (int sock, uschar *av_buffer, int av_buffer_size, int tmo)
 {
 static inline int
 mksd_read_lines (int sock, uschar *av_buffer, int av_buffer_size, int tmo)
 {
+client_conn_ctx cctx = {.sock = sock};
 int offset = 0;
 int i;
 
 do
   {
 int offset = 0;
 int i;
 
 do
   {
-  i = ip_recv(sock, av_buffer+offset, av_buffer_size-offset, tmo-time(NULL));
+  i = ip_recv(&cctx, av_buffer+offset, av_buffer_size-offset, tmo-time(NULL));
   if (i <= 0)
     {
     (void) malware_panic_defer(US"unable to read from mksd UNIX socket (/var/run/mksd/socket)");
   if (i <= 0)
     {
     (void) malware_panic_defer(US"unable to read from mksd UNIX socket (/var/run/mksd/socket)");
@@ -576,7 +577,7 @@ const pcre *re;
 uschar * errstr;
 struct scan * scanent;
 const uschar * scanner_options;
 uschar * errstr;
 struct scan * scanent;
 const uschar * scanner_options;
-int sock = -1;
+client_conn_ctx malware_daemon_ctx = {.sock = -1};
 time_t tmo;
 uschar * eml_filename, * eml_dir;
 
 time_t tmo;
 uschar * eml_filename, * eml_dir;
 
@@ -657,12 +658,16 @@ if (!malware_ok)
     DEBUG(D_acl) debug_printf_indent("%15s%10s%s\n", "", "socket: ", scanner_options);
     switch(scanent->conn)
     {
     DEBUG(D_acl) debug_printf_indent("%15s%10s%s\n", "", "socket: ", scanner_options);
     switch(scanent->conn)
     {
-    case MC_TCP:  sock = ip_tcpsocket(scanner_options, &errstr, 5);    break;
-    case MC_UNIX: sock = ip_unixsocket(scanner_options, &errstr);      break;
-    case MC_STRM: sock = ip_streamsocket(scanner_options, &errstr, 5);  break;
-    default: /* compiler quietening */ break;
+    case MC_TCP:
+      malware_daemon_ctx.sock = ip_tcpsocket(scanner_options, &errstr, 5);     break;
+    case MC_UNIX:
+      malware_daemon_ctx.sock = ip_unixsocket(scanner_options, &errstr);       break;
+    case MC_STRM:
+      malware_daemon_ctx.sock = ip_streamsocket(scanner_options, &errstr, 5);  break;
+    default:
+      /* compiler quietening */ break;
     }
     }
-    if (sock < 0)
+    if (malware_daemon_ctx.sock < 0)
       return m_panic_defer(scanent, CUS callout_address, errstr);
     break;
   }
       return m_panic_defer(scanent, CUS callout_address, errstr);
     break;
   }
@@ -693,10 +698,10 @@ if (!malware_ok)
        scanner_name, scanrequest);
 
       /* send scan request */
        scanner_name, scanrequest);
 
       /* send scan request */
-      if (m_sock_send(sock, scanrequest, Ustrlen(scanrequest)+1, &errstr) < 0)
+      if (m_sock_send(malware_daemon_ctx.sock, scanrequest, Ustrlen(scanrequest)+1, &errstr) < 0)
        return m_panic_defer(scanent, CUS callout_address, errstr);
 
        return m_panic_defer(scanent, CUS callout_address, errstr);
 
-      while ((len = recv_line(sock, buf, sizeof(buf), tmo)) >= 0)
+      while ((len = recv_line(malware_daemon_ctx.sock, buf, sizeof(buf), tmo)) >= 0)
        if (len > 0)
          {
          if (Ustrstr(buf, US"<detected type=\"") != NULL)
        if (len > 0)
          {
          if (Ustrstr(buf, US"<detected type=\"") != NULL)
@@ -718,7 +723,7 @@ if (!malware_ok)
          }
       if (len < -1)
        {
          }
       if (len < -1)
        {
-       (void)close(sock);
+       (void)close(malware_daemon_ctx.sock);
        return DEFER;
        }
       break;
        return DEFER;
        }
       break;
@@ -742,28 +747,28 @@ if (!malware_ok)
       DEBUG(D_acl) debug_printf_indent("Malware scan: issuing %s: %s\n",
         scanner_name, scanrequest);
 
       DEBUG(D_acl) debug_printf_indent("Malware scan: issuing %s: %s\n",
         scanner_name, scanrequest);
 
-      if (m_sock_send(sock, scanrequest, Ustrlen(scanrequest), &errstr) < 0)
+      if (m_sock_send(malware_daemon_ctx.sock, scanrequest, Ustrlen(scanrequest), &errstr) < 0)
         return m_panic_defer(scanent, CUS callout_address, errstr);
 
         return m_panic_defer(scanent, CUS callout_address, errstr);
 
-      bread = ip_recv(sock, av_buffer, sizeof(av_buffer), tmo-time(NULL));
+      bread = ip_recv(&malware_daemon_ctx, av_buffer, sizeof(av_buffer), tmo-time(NULL));
 
       if (bread <= 0)
         return m_panic_defer_3(scanent, CUS callout_address,
           string_sprintf("unable to read from socket (%s)", strerror(errno)),
 
       if (bread <= 0)
         return m_panic_defer_3(scanent, CUS callout_address,
           string_sprintf("unable to read from socket (%s)", strerror(errno)),
-          sock);
+          malware_daemon_ctx.sock);
 
       if (bread == sizeof(av_buffer))
         return m_panic_defer_3(scanent, CUS callout_address,
 
       if (bread == sizeof(av_buffer))
         return m_panic_defer_3(scanent, CUS callout_address,
-          US"buffer too small", sock);
+          US"buffer too small", malware_daemon_ctx.sock);
 
       av_buffer[bread] = '\0';
       linebuffer = string_copy(av_buffer);
 
 
       av_buffer[bread] = '\0';
       linebuffer = string_copy(av_buffer);
 
-      m_sock_send(sock, US"QUIT\n", 5, 0);
+      m_sock_send(malware_daemon_ctx.sock, US"QUIT\n", 5, 0);
 
       if ((e = m_pcre_exec(fprot6d_re_error, linebuffer)))
         return m_panic_defer_3(scanent, CUS callout_address,
 
       if ((e = m_pcre_exec(fprot6d_re_error, linebuffer)))
         return m_panic_defer_3(scanent, CUS callout_address,
-          string_sprintf("scanner reported error (%s)", e), sock);
+          string_sprintf("scanner reported error (%s)", e), malware_daemon_ctx.sock);
 
       if (!(malware_name = m_pcre_exec(fprot6d_re_virus, linebuffer)))
         malware_name = NULL;
 
       if (!(malware_name = m_pcre_exec(fprot6d_re_virus, linebuffer)))
         malware_name = NULL;
@@ -795,7 +800,7 @@ if (!malware_ok)
          return m_panic_defer_3(scanent, NULL,
            string_sprintf("can't open spool file %s: %s",
              eml_filename, strerror(errno)),
          return m_panic_defer_3(scanent, NULL,
            string_sprintf("can't open spool file %s: %s",
              eml_filename, strerror(errno)),
-           sock);
+           malware_daemon_ctx.sock);
 
        if ((fsize = lseek(drweb_fd, 0, SEEK_END)) == -1)
          {
 
        if ((fsize = lseek(drweb_fd, 0, SEEK_END)) == -1)
          {
@@ -805,7 +810,7 @@ badseek:  err = errno;
          return m_panic_defer_3(scanent, NULL,
            string_sprintf("can't seek spool file %s: %s",
              eml_filename, strerror(err)),
          return m_panic_defer_3(scanent, NULL,
            string_sprintf("can't seek spool file %s: %s",
              eml_filename, strerror(err)),
-           sock);
+           malware_daemon_ctx.sock);
          }
        fsize_uint = (unsigned int) fsize;
        if ((off_t)fsize_uint != fsize)
          }
        fsize_uint = (unsigned int) fsize;
        if ((off_t)fsize_uint != fsize)
@@ -814,7 +819,7 @@ badseek:  err = errno;
          return m_panic_defer_3(scanent, NULL,
            string_sprintf("seeking spool file %s, size overflow",
              eml_filename),
          return m_panic_defer_3(scanent, NULL,
            string_sprintf("seeking spool file %s, size overflow",
              eml_filename),
-           sock);
+           malware_daemon_ctx.sock);
          }
        drweb_slen = htonl(fsize);
        if (lseek(drweb_fd, 0, SEEK_SET) < 0)
          }
        drweb_slen = htonl(fsize);
        if (lseek(drweb_fd, 0, SEEK_SET) < 0)
@@ -824,15 +829,15 @@ badseek:  err = errno;
            scanner_name, scanner_options);
 
        /* send scan request */
            scanner_name, scanner_options);
 
        /* send scan request */
-       if ((send(sock, &drweb_cmd, sizeof(drweb_cmd), 0) < 0) ||
-           (send(sock, &drweb_flags, sizeof(drweb_flags), 0) < 0) ||
-           (send(sock, &drweb_fin, sizeof(drweb_fin), 0) < 0) ||
-           (send(sock, &drweb_slen, sizeof(drweb_slen), 0) < 0))
+       if ((send(malware_daemon_ctx.sock, &drweb_cmd, sizeof(drweb_cmd), 0) < 0) ||
+           (send(malware_daemon_ctx.sock, &drweb_flags, sizeof(drweb_flags), 0) < 0) ||
+           (send(malware_daemon_ctx.sock, &drweb_fin, sizeof(drweb_fin), 0) < 0) ||
+           (send(malware_daemon_ctx.sock, &drweb_slen, sizeof(drweb_slen), 0) < 0))
          {
          (void)close(drweb_fd);
          return m_panic_defer_3(scanent, CUS callout_address, string_sprintf(
            "unable to send commands to socket (%s)", scanner_options),
          {
          (void)close(drweb_fd);
          return m_panic_defer_3(scanent, CUS callout_address, string_sprintf(
            "unable to send commands to socket (%s)", scanner_options),
-           sock);
+           malware_daemon_ctx.sock);
          }
 
        if (!(drweb_fbuf = US malloc(fsize_uint)))
          }
 
        if (!(drweb_fbuf = US malloc(fsize_uint)))
@@ -841,7 +846,7 @@ badseek:  err = errno;
          return m_panic_defer_3(scanent, NULL,
            string_sprintf("unable to allocate memory %u for file (%s)",
              fsize_uint, eml_filename),
          return m_panic_defer_3(scanent, NULL,
            string_sprintf("unable to allocate memory %u for file (%s)",
              fsize_uint, eml_filename),
-           sock);
+           malware_daemon_ctx.sock);
          }
 
        if ((result = read (drweb_fd, drweb_fbuf, fsize)) == -1)
          }
 
        if ((result = read (drweb_fd, drweb_fbuf, fsize)) == -1)
@@ -852,17 +857,17 @@ badseek:  err = errno;
          return m_panic_defer_3(scanent, NULL,
            string_sprintf("can't read spool file %s: %s",
              eml_filename, strerror(err)),
          return m_panic_defer_3(scanent, NULL,
            string_sprintf("can't read spool file %s: %s",
              eml_filename, strerror(err)),
-           sock);
+           malware_daemon_ctx.sock);
          }
        (void)close(drweb_fd);
 
        /* send file body to socket */
          }
        (void)close(drweb_fd);
 
        /* send file body to socket */
-       if (send(sock, drweb_fbuf, fsize, 0) < 0)
+       if (send(malware_daemon_ctx.sock, drweb_fbuf, fsize, 0) < 0)
          {
          free(drweb_fbuf);
          return m_panic_defer_3(scanent, CUS callout_address, string_sprintf(
            "unable to send file body to socket (%s)", scanner_options),
          {
          free(drweb_fbuf);
          return m_panic_defer_3(scanent, CUS callout_address, string_sprintf(
            "unable to send file body to socket (%s)", scanner_options),
-           sock);
+           malware_daemon_ctx.sock);
          }
        }
       else
          }
        }
       else
@@ -873,25 +878,25 @@ badseek:  err = errno;
            scanner_name, scanner_options);
 
        /* send scan request */
            scanner_name, scanner_options);
 
        /* send scan request */
-       if ((send(sock, &drweb_cmd, sizeof(drweb_cmd), 0) < 0) ||
-           (send(sock, &drweb_flags, sizeof(drweb_flags), 0) < 0) ||
-           (send(sock, &drweb_slen, sizeof(drweb_slen), 0) < 0) ||
-           (send(sock, eml_filename, Ustrlen(eml_filename), 0) < 0) ||
-           (send(sock, &drweb_fin, sizeof(drweb_fin), 0) < 0))
+       if ((send(malware_daemon_ctx.sock, &drweb_cmd, sizeof(drweb_cmd), 0) < 0) ||
+           (send(malware_daemon_ctx.sock, &drweb_flags, sizeof(drweb_flags), 0) < 0) ||
+           (send(malware_daemon_ctx.sock, &drweb_slen, sizeof(drweb_slen), 0) < 0) ||
+           (send(malware_daemon_ctx.sock, eml_filename, Ustrlen(eml_filename), 0) < 0) ||
+           (send(malware_daemon_ctx.sock, &drweb_fin, sizeof(drweb_fin), 0) < 0))
          return m_panic_defer_3(scanent, CUS callout_address, string_sprintf(
            "unable to send commands to socket (%s)", scanner_options),
          return m_panic_defer_3(scanent, CUS callout_address, string_sprintf(
            "unable to send commands to socket (%s)", scanner_options),
-           sock);
+           malware_daemon_ctx.sock);
        }
 
       /* wait for result */
        }
 
       /* wait for result */
-      if (!recv_len(sock, &drweb_rc, sizeof(drweb_rc), tmo))
+      if (!recv_len(malware_daemon_ctx.sock, &drweb_rc, sizeof(drweb_rc), tmo))
        return m_panic_defer_3(scanent, CUS callout_address,
        return m_panic_defer_3(scanent, CUS callout_address,
-                   US"unable to read return code", sock);
+                   US"unable to read return code", malware_daemon_ctx.sock);
       drweb_rc = ntohl(drweb_rc);
 
       drweb_rc = ntohl(drweb_rc);
 
-      if (!recv_len(sock, &drweb_vnum, sizeof(drweb_vnum), tmo))
+      if (!recv_len(malware_daemon_ctx.sock, &drweb_vnum, sizeof(drweb_vnum), tmo))
        return m_panic_defer_3(scanent, CUS callout_address,
        return m_panic_defer_3(scanent, CUS callout_address,
-                           US"unable to read the number of viruses", sock);
+                           US"unable to read the number of viruses", malware_daemon_ctx.sock);
       drweb_vnum = ntohl(drweb_vnum);
 
       /* "virus(es) found" if virus number is > 0 */
       drweb_vnum = ntohl(drweb_vnum);
 
       /* "virus(es) found" if virus number is > 0 */
@@ -913,16 +918,16 @@ badseek:  err = errno;
          int ovector[10*3];
 
          /* read the size of report */
          int ovector[10*3];
 
          /* read the size of report */
-         if (!recv_len(sock, &drweb_slen, sizeof(drweb_slen), tmo))
+         if (!recv_len(malware_daemon_ctx.sock, &drweb_slen, sizeof(drweb_slen), tmo))
            return m_panic_defer_3(scanent, CUS callout_address,
            return m_panic_defer_3(scanent, CUS callout_address,
-                             US"cannot read report size", sock);
+                             US"cannot read report size", malware_daemon_ctx.sock);
          drweb_slen = ntohl(drweb_slen);
          tmpbuf = store_get(drweb_slen);
 
          /* read report body */
          drweb_slen = ntohl(drweb_slen);
          tmpbuf = store_get(drweb_slen);
 
          /* read report body */
-         if (!recv_len(sock, tmpbuf, drweb_slen, tmo))
+         if (!recv_len(malware_daemon_ctx.sock, tmpbuf, drweb_slen, tmo))
            return m_panic_defer_3(scanent, CUS callout_address,
            return m_panic_defer_3(scanent, CUS callout_address,
-                             US"cannot read report string", sock);
+                             US"cannot read report string", malware_daemon_ctx.sock);
          tmpbuf[drweb_slen] = '\0';
 
          /* try matcher on the line, grab substring */
          tmpbuf[drweb_slen] = '\0';
 
          /* try matcher on the line, grab substring */
@@ -961,7 +966,7 @@ badseek:  err = errno;
        if (drweb_s)
          return m_panic_defer_3(scanent, CUS callout_address,
            string_sprintf("drweb daemon retcode 0x%x (%s)", drweb_rc, drweb_s),
        if (drweb_s)
          return m_panic_defer_3(scanent, CUS callout_address,
            string_sprintf("drweb daemon retcode 0x%x (%s)", drweb_rc, drweb_s),
-           sock);
+           malware_daemon_ctx.sock);
 
        /* no virus found */
        malware_name = NULL;
 
        /* no virus found */
        malware_name = NULL;
@@ -978,13 +983,13 @@ badseek:  err = errno;
 
       /* read aveserver's greeting and see if it is ready (2xx greeting) */
       buf[0] = 0;
 
       /* read aveserver's greeting and see if it is ready (2xx greeting) */
       buf[0] = 0;
-      recv_line(sock, buf, sizeof(buf), tmo);
+      recv_line(malware_daemon_ctx.sock, buf, sizeof(buf), tmo);
 
       if (buf[0] != '2')               /* aveserver is having problems */
        return m_panic_defer_3(scanent, CUS callout_address,
          string_sprintf("unavailable (Responded: %s).",
                          ((buf[0] != 0) ? buf : US "nothing") ),
 
       if (buf[0] != '2')               /* aveserver is having problems */
        return m_panic_defer_3(scanent, CUS callout_address,
          string_sprintf("unavailable (Responded: %s).",
                          ((buf[0] != 0) ? buf : US "nothing") ),
-         sock);
+         malware_daemon_ctx.sock);
 
       /* prepare our command */
       (void)string_format(buf, sizeof(buf), "SCAN bPQRSTUW %s\r\n",
 
       /* prepare our command */
       (void)string_format(buf, sizeof(buf), "SCAN bPQRSTUW %s\r\n",
@@ -993,13 +998,13 @@ badseek:  err = errno;
       /* and send it */
       DEBUG(D_acl) debug_printf_indent("Malware scan: issuing %s %s\n",
        scanner_name, buf);
       /* and send it */
       DEBUG(D_acl) debug_printf_indent("Malware scan: issuing %s %s\n",
        scanner_name, buf);
-      if (m_sock_send(sock, buf, Ustrlen(buf), &errstr) < 0)
+      if (m_sock_send(malware_daemon_ctx.sock, buf, Ustrlen(buf), &errstr) < 0)
        return m_panic_defer(scanent, CUS callout_address, errstr);
 
       malware_name = NULL;
       result = 0;
       /* read response lines, find malware name and final response */
        return m_panic_defer(scanent, CUS callout_address, errstr);
 
       malware_name = NULL;
       result = 0;
       /* read response lines, find malware name and final response */
-      while (recv_line(sock, buf, sizeof(buf), tmo) > 0)
+      while (recv_line(malware_daemon_ctx.sock, buf, sizeof(buf), tmo) > 0)
        {
        if (buf[0] == '2')
          break;
        {
        if (buf[0] == '2')
          break;
@@ -1018,22 +1023,22 @@ badseek:  err = errno;
          }
        }
 
          }
        }
 
-      if (m_sock_send(sock, US"quit\r\n", 6, &errstr) < 0)
+      if (m_sock_send(malware_daemon_ctx.sock, US"quit\r\n", 6, &errstr) < 0)
        return m_panic_defer(scanent, CUS callout_address, errstr);
 
       /* read aveserver's greeting and see if it is ready (2xx greeting) */
       buf[0] = 0;
        return m_panic_defer(scanent, CUS callout_address, errstr);
 
       /* read aveserver's greeting and see if it is ready (2xx greeting) */
       buf[0] = 0;
-      recv_line(sock, buf, sizeof(buf), tmo);
+      recv_line(malware_daemon_ctx.sock, buf, sizeof(buf), tmo);
 
       if (buf[0] != '2')               /* aveserver is having problems */
        return m_panic_defer_3(scanent, CUS callout_address,
          string_sprintf("unable to quit dialogue (Responded: %s).",
                        ((buf[0] != 0) ? buf : US "nothing") ),
 
       if (buf[0] != '2')               /* aveserver is having problems */
        return m_panic_defer_3(scanent, CUS callout_address,
          string_sprintf("unable to quit dialogue (Responded: %s).",
                        ((buf[0] != 0) ? buf : US "nothing") ),
-         sock);
+         malware_daemon_ctx.sock);
 
       if (result == DEFER)
        {
 
       if (result == DEFER)
        {
-       (void)close(sock);
+       (void)close(malware_daemon_ctx.sock);
        return DEFER;
        }
       break;
        return DEFER;
        }
       break;
@@ -1060,15 +1065,15 @@ badseek:  err = errno;
       for (i = 0; i != nelem(cmdopt); i++)
        {
 
       for (i = 0; i != nelem(cmdopt); i++)
        {
 
-       if (m_sock_send(sock, cmdopt[i], Ustrlen(cmdopt[i]), &errstr) < 0)
+       if (m_sock_send(malware_daemon_ctx.sock, cmdopt[i], Ustrlen(cmdopt[i]), &errstr) < 0)
          return m_panic_defer(scanent, CUS callout_address, errstr);
 
          return m_panic_defer(scanent, CUS callout_address, errstr);
 
-       bread = ip_recv(sock, av_buffer, sizeof(av_buffer), tmo-time(NULL));
+       bread = ip_recv(&malware_daemon_ctx, av_buffer, sizeof(av_buffer), tmo-time(NULL));
        if (bread > 0) av_buffer[bread]='\0';
        if (bread < 0)
          return m_panic_defer_3(scanent, CUS callout_address,
            string_sprintf("unable to read answer %d (%s)", i, strerror(errno)),
        if (bread > 0) av_buffer[bread]='\0';
        if (bread < 0)
          return m_panic_defer_3(scanent, CUS callout_address,
            string_sprintf("unable to read answer %d (%s)", i, strerror(errno)),
-           sock);
+           malware_daemon_ctx.sock);
        for (j = 0; j < bread; j++)
          if (av_buffer[j] == '\r' || av_buffer[j] == '\n')
            av_buffer[j] ='@';
        for (j = 0; j < bread; j++)
          if (av_buffer[j] == '\r' || av_buffer[j] == '\n')
            av_buffer[j] ='@';
@@ -1077,7 +1082,7 @@ badseek:  err = errno;
       /* pass the mailfile to fsecure */
       file_name = string_sprintf("SCAN\t%s\n", eml_filename);
 
       /* pass the mailfile to fsecure */
       file_name = string_sprintf("SCAN\t%s\n", eml_filename);
 
-      if (m_sock_send(sock, file_name, Ustrlen(file_name), &errstr) < 0)
+      if (m_sock_send(malware_daemon_ctx.sock, file_name, Ustrlen(file_name), &errstr) < 0)
        return m_panic_defer(scanent, CUS callout_address, errstr);
 
       /* set up match */
        return m_panic_defer(scanent, CUS callout_address, errstr);
 
       /* set up match */
@@ -1095,10 +1100,10 @@ badseek:  err = errno;
          {
          errno = ETIMEDOUT;
          i =  av_buffer+sizeof(av_buffer)-p;
          {
          errno = ETIMEDOUT;
          i =  av_buffer+sizeof(av_buffer)-p;
-         if ((bread= ip_recv(sock, p, i-1, tmo-time(NULL))) < 0)
+         if ((bread= ip_recv(&malware_daemon_ctx, p, i-1, tmo-time(NULL))) < 0)
            return m_panic_defer_3(scanent, CUS callout_address,
              string_sprintf("unable to read result (%s)", strerror(errno)),
            return m_panic_defer_3(scanent, CUS callout_address,
              string_sprintf("unable to read result (%s)", strerror(errno)),
-             sock);
+             malware_daemon_ctx.sock);
 
          for (p[bread] = '\0'; (q = Ustrchr(p, '\n')); p = q+1)
            {
 
          for (p[bread] = '\0'; (q = Ustrchr(p, '\n')); p = q+1)
            {
@@ -1155,13 +1160,13 @@ badseek:  err = errno;
          scanner_name, scanner_options);
 
       /* send scan request */
          scanner_name, scanner_options);
 
       /* send scan request */
-      if (m_sock_send(sock, scanrequest, Ustrlen(scanrequest)+1, &errstr) < 0)
+      if (m_sock_send(malware_daemon_ctx.sock, scanrequest, Ustrlen(scanrequest)+1, &errstr) < 0)
        return m_panic_defer(scanent, CUS callout_address, errstr);
 
       /* wait for result */
        return m_panic_defer(scanent, CUS callout_address, errstr);
 
       /* wait for result */
-      if (!recv_len(sock, tmpbuf, 2, tmo))
+      if (!recv_len(malware_daemon_ctx.sock, tmpbuf, 2, tmo))
        return m_panic_defer_3(scanent, CUS callout_address,
        return m_panic_defer_3(scanent, CUS callout_address,
-                           US"unable to read 2 bytes from socket.", sock);
+                           US"unable to read 2 bytes from socket.", malware_daemon_ctx.sock);
 
       /* get errorcode from one nibble */
       kav_rc = tmpbuf[ test_byte_order()==LITTLE_MY_ENDIAN ? 0 : 1 ] & 0x0F;
 
       /* get errorcode from one nibble */
       kav_rc = tmpbuf[ test_byte_order()==LITTLE_MY_ENDIAN ? 0 : 1 ] & 0x0F;
@@ -1170,13 +1175,13 @@ badseek:  err = errno;
       case 5: case 6: /* improper kavdaemon configuration */
        return m_panic_defer_3(scanent, CUS callout_address,
                US"please reconfigure kavdaemon to NOT disinfect or remove infected files.",
       case 5: case 6: /* improper kavdaemon configuration */
        return m_panic_defer_3(scanent, CUS callout_address,
                US"please reconfigure kavdaemon to NOT disinfect or remove infected files.",
-               sock);
+               malware_daemon_ctx.sock);
       case 1:
        return m_panic_defer_3(scanent, CUS callout_address,
       case 1:
        return m_panic_defer_3(scanent, CUS callout_address,
-               US"reported 'scanning not completed' (code 1).", sock);
+               US"reported 'scanning not completed' (code 1).", malware_daemon_ctx.sock);
       case 7:
        return m_panic_defer_3(scanent, CUS callout_address,
       case 7:
        return m_panic_defer_3(scanent, CUS callout_address,
-               US"reported 'kavdaemon damaged' (code 7).", sock);
+               US"reported 'kavdaemon damaged' (code 7).", malware_daemon_ctx.sock);
       }
 
       /* code 8 is not handled, since it is ambiguous. It appears mostly on
       }
 
       /* code 8 is not handled, since it is ambiguous. It appears mostly on
@@ -1196,9 +1201,9 @@ badseek:  err = errno;
        if (report_flag == 1)
          {
          /* read report size */
        if (report_flag == 1)
          {
          /* read report size */
-         if (!recv_len(sock, &kav_reportlen, 4, tmo))
+         if (!recv_len(malware_daemon_ctx.sock, &kav_reportlen, 4, tmo))
            return m_panic_defer_3(scanent, CUS callout_address,
            return m_panic_defer_3(scanent, CUS callout_address,
-                 US"cannot read report size", sock);
+                 US"cannot read report size", malware_daemon_ctx.sock);
 
          /* it's possible that avp returns av_buffer[1] == 1 but the
          reportsize is 0 (!?) */
 
          /* it's possible that avp returns av_buffer[1] == 1 but the
          reportsize is 0 (!?) */
@@ -1221,7 +1226,7 @@ badseek:  err = errno;
            /* coverity[tainted_data] */
            while (kav_reportlen > 0)
              {
            /* coverity[tainted_data] */
            while (kav_reportlen > 0)
              {
-             if ((bread = recv_line(sock, tmpbuf, sizeof(tmpbuf), tmo)) < 0)
+             if ((bread = recv_line(malware_daemon_ctx.sock, tmpbuf, sizeof(tmpbuf), tmo)) < 0)
                break;
              kav_reportlen -= bread+1;
 
                break;
              kav_reportlen -= bread+1;
 
@@ -1391,19 +1396,19 @@ badseek:  err = errno;
       DEBUG(D_acl) debug_printf_indent("Malware scan: issuing %s scan [%s]\n",
          scanner_name, scanner_options);
 
       DEBUG(D_acl) debug_printf_indent("Malware scan: issuing %s scan [%s]\n",
          scanner_name, scanner_options);
 
-      if (  write(sock, file_name, Ustrlen(file_name)) < 0
-        || write(sock, "\n", 1) != 1
+      if (  write(malware_daemon_ctx.sock, file_name, Ustrlen(file_name)) < 0
+        || write(malware_daemon_ctx.sock, "\n", 1) != 1
         )
        return m_panic_defer_3(scanent, CUS callout_address,
          string_sprintf("unable to write to UNIX socket (%s)", scanner_options),
         )
        return m_panic_defer_3(scanent, CUS callout_address,
          string_sprintf("unable to write to UNIX socket (%s)", scanner_options),
-         sock);
+         malware_daemon_ctx.sock);
 
       /* wait for result */
       memset(av_buffer, 0, sizeof(av_buffer));
 
       /* wait for result */
       memset(av_buffer, 0, sizeof(av_buffer));
-      if ((bread = ip_recv(sock, av_buffer, sizeof(av_buffer), tmo-time(NULL))) <= 0)
+      if ((bread = ip_recv(&malware_daemon_ctx, av_buffer, sizeof(av_buffer), tmo-time(NULL))) <= 0)
        return m_panic_defer_3(scanent, CUS callout_address,
          string_sprintf("unable to read from UNIX socket (%s)", scanner_options),
        return m_panic_defer_3(scanent, CUS callout_address,
          string_sprintf("unable to read from UNIX socket (%s)", scanner_options),
-         sock);
+         malware_daemon_ctx.sock);
 
       /* infected ? */
       if (av_buffer[0] == '1') {
 
       /* infected ? */
       if (av_buffer[0] == '1') {
@@ -1414,7 +1419,7 @@ badseek:  err = errno;
       }
       else if (!strncmp(CS av_buffer, "-1", 2))
        return m_panic_defer_3(scanent, CUS callout_address,
       }
       else if (!strncmp(CS av_buffer, "-1", 2))
        return m_panic_defer_3(scanent, CUS callout_address,
-               US"scanner reported error", sock);
+               US"scanner reported error", malware_daemon_ctx.sock);
       else /* all ok, no virus */
        malware_name = NULL;
 
       else /* all ok, no virus */
        malware_name = NULL;
 
@@ -1573,7 +1578,7 @@ badseek:  err = errno;
           * on both connections (as one host could resolve to multiple ips) */
          for (;;)
            {
           * on both connections (as one host could resolve to multiple ips) */
          for (;;)
            {
-           if ((sock = m_tcpsocket(cd->hostspec, cd->tcp_port,
+           if ((malware_daemon_ctx.sock = m_tcpsocket(cd->hostspec, cd->tcp_port,
                                    &connhost, &errstr, &cmd_str)) >= 0)
              {
              /* Connection successfully established with a server */
                                    &connhost, &errstr, &cmd_str)) >= 0)
              {
              /* Connection successfully established with a server */
@@ -1584,7 +1589,7 @@ badseek:  err = errno;
            if (cd->retry <= 0) break;
            while (cd->retry > 0) cd->retry = sleep(cd->retry);
            }
            if (cd->retry <= 0) break;
            while (cd->retry > 0) cd->retry = sleep(cd->retry);
            }
-         if (sock >= 0)
+         if (malware_daemon_ctx.sock >= 0)
            break;
 
          (void) m_panic_defer(scanent, CUS callout_address, errstr);
            break;
 
          (void) m_panic_defer(scanent, CUS callout_address, errstr);
@@ -1601,7 +1606,7 @@ badseek:  err = errno;
       else
        for (;;)
          {
       else
        for (;;)
          {
-         if ((sock = ip_unixsocket(cv[0]->hostspec, &errstr)) >= 0)
+         if ((malware_daemon_ctx.sock = ip_unixsocket(cv[0]->hostspec, &errstr)) >= 0)
            {
            hostname = cv[0]->hostspec;
            break;
            {
            hostname = cv[0]->hostspec;
            break;
@@ -1628,11 +1633,11 @@ badseek:  err = errno;
 
        /* Pass the string to ClamAV (10 = "zINSTREAM\0"), if not already sent */
        if (cmd_str.len)
 
        /* Pass the string to ClamAV (10 = "zINSTREAM\0"), if not already sent */
        if (cmd_str.len)
-         if (send(sock, cmd_str.data, cmd_str.len, 0) < 0)
+         if (send(malware_daemon_ctx.sock, cmd_str.data, cmd_str.len, 0) < 0)
            return m_panic_defer_3(scanent, CUS hostname,
              string_sprintf("unable to send zINSTREAM to socket (%s)",
                strerror(errno)),
            return m_panic_defer_3(scanent, CUS hostname,
              string_sprintf("unable to send zINSTREAM to socket (%s)",
                strerror(errno)),
-             sock);
+             malware_daemon_ctx.sock);
 
        /* calc file size */
        if ((clam_fd = open(CS eml_filename, O_RDONLY)) < 0)
 
        /* calc file size */
        if ((clam_fd = open(CS eml_filename, O_RDONLY)) < 0)
@@ -1641,7 +1646,7 @@ badseek:  err = errno;
          return m_panic_defer_3(scanent, NULL,
            string_sprintf("can't open spool file %s: %s",
              eml_filename, strerror(err)),
          return m_panic_defer_3(scanent, NULL,
            string_sprintf("can't open spool file %s: %s",
              eml_filename, strerror(err)),
-           sock);
+           malware_daemon_ctx.sock);
          }
        if ((fsize = lseek(clam_fd, 0, SEEK_END)) < 0)
          {
          }
        if ((fsize = lseek(clam_fd, 0, SEEK_END)) < 0)
          {
@@ -1651,7 +1656,7 @@ b_seek:   err = errno;
          return m_panic_defer_3(scanent, NULL,
            string_sprintf("can't seek spool file %s: %s",
              eml_filename, strerror(err)),
          return m_panic_defer_3(scanent, NULL,
            string_sprintf("can't seek spool file %s: %s",
              eml_filename, strerror(err)),
-           sock);
+           malware_daemon_ctx.sock);
          }
        fsize_uint = (unsigned int) fsize;
        if ((off_t)fsize_uint != fsize)
          }
        fsize_uint = (unsigned int) fsize;
        if ((off_t)fsize_uint != fsize)
@@ -1660,7 +1665,7 @@ b_seek:   err = errno;
          return m_panic_defer_3(scanent, NULL,
            string_sprintf("seeking spool file %s, size overflow",
              eml_filename),
          return m_panic_defer_3(scanent, NULL,
            string_sprintf("seeking spool file %s, size overflow",
              eml_filename),
-           sock);
+           malware_daemon_ctx.sock);
          }
        if (lseek(clam_fd, 0, SEEK_SET) < 0)
          goto b_seek;
          }
        if (lseek(clam_fd, 0, SEEK_SET) < 0)
          goto b_seek;
@@ -1671,7 +1676,7 @@ b_seek:   err = errno;
          return m_panic_defer_3(scanent, NULL,
            string_sprintf("unable to allocate memory %u for file (%s)",
              fsize_uint, eml_filename),
          return m_panic_defer_3(scanent, NULL,
            string_sprintf("unable to allocate memory %u for file (%s)",
              fsize_uint, eml_filename),
-           sock);
+           malware_daemon_ctx.sock);
          }
 
        if ((result = read(clam_fd, clamav_fbuf, fsize_uint)) < 0)
          }
 
        if ((result = read(clam_fd, clamav_fbuf, fsize_uint)) < 0)
@@ -1681,21 +1686,21 @@ b_seek:   err = errno;
          return m_panic_defer_3(scanent, NULL,
            string_sprintf("can't read spool file %s: %s",
              eml_filename, strerror(err)),
          return m_panic_defer_3(scanent, NULL,
            string_sprintf("can't read spool file %s: %s",
              eml_filename, strerror(err)),
-           sock);
+           malware_daemon_ctx.sock);
          }
        (void)close(clam_fd);
 
        /* send file body to socket */
        send_size = htonl(fsize_uint);
        send_final_zeroblock = 0;
          }
        (void)close(clam_fd);
 
        /* send file body to socket */
        send_size = htonl(fsize_uint);
        send_final_zeroblock = 0;
-       if ((send(sock, &send_size, sizeof(send_size), 0) < 0) ||
-           (send(sock, clamav_fbuf, fsize_uint, 0) < 0) ||
-           (send(sock, &send_final_zeroblock, sizeof(send_final_zeroblock), 0) < 0))
+       if ((send(malware_daemon_ctx.sock, &send_size, sizeof(send_size), 0) < 0) ||
+           (send(malware_daemon_ctx.sock, clamav_fbuf, fsize_uint, 0) < 0) ||
+           (send(malware_daemon_ctx.sock, &send_final_zeroblock, sizeof(send_final_zeroblock), 0) < 0))
          {
          free(clamav_fbuf);
          return m_panic_defer_3(scanent, NULL,
            string_sprintf("unable to send file body to socket (%s)", hostname),
          {
          free(clamav_fbuf);
          return m_panic_defer_3(scanent, NULL,
            string_sprintf("unable to send file body to socket (%s)", hostname),
-           sock);
+           malware_daemon_ctx.sock);
          }
 
        free(clamav_fbuf);
          }
 
        free(clamav_fbuf);
@@ -1722,10 +1727,10 @@ b_seek:   err = errno;
            scanner_name, scanner_options);
 
        if (cmd_str.len)
            scanner_name, scanner_options);
 
        if (cmd_str.len)
-         if (send(sock, cmd_str.data, cmd_str.len, 0) < 0)
+         if (send(malware_daemon_ctx.sock, cmd_str.data, cmd_str.len, 0) < 0)
            return m_panic_defer_3(scanent, CUS callout_address,
              string_sprintf("unable to write to socket (%s)", strerror(errno)),
            return m_panic_defer_3(scanent, CUS callout_address,
              string_sprintf("unable to write to socket (%s)", strerror(errno)),
-             sock);
+             malware_daemon_ctx.sock);
 
        /* Do not shut down the socket for writing; a user report noted that
         * clamd 0.70 does not react well to this. */
 
        /* Do not shut down the socket for writing; a user report noted that
         * clamd 0.70 does not react well to this. */
@@ -1735,9 +1740,10 @@ b_seek:   err = errno;
 
       /* Read the result */
       memset(av_buffer, 0, sizeof(av_buffer));
 
       /* Read the result */
       memset(av_buffer, 0, sizeof(av_buffer));
-      bread = ip_recv(sock, av_buffer, sizeof(av_buffer), tmo-time(NULL));
-      (void)close(sock);
-      sock = -1;
+      bread = ip_recv(&malware_daemon_ctx, av_buffer, sizeof(av_buffer), tmo-time(NULL));
+      (void)close(malware_daemon_ctx.sock);
+      malware_daemon_ctx.sock = -1;
+      malware_daemon_ctx.tls_ctx = NULL;
 
       if (bread <= 0)
        return m_panic_defer(scanent, CUS callout_address,
 
       if (bread <= 0)
        return m_panic_defer(scanent, CUS callout_address,
@@ -1863,7 +1869,7 @@ b_seek:   err = errno;
        if (s++)
          if ((*s != 's' && *s != '%') || Ustrchr(s+1, '%'))
            return m_panic_defer_3(scanent, NULL,
        if (s++)
          if ((*s != 's' && *s != '%') || Ustrchr(s+1, '%'))
            return m_panic_defer_3(scanent, NULL,
-                                 US"unsafe sock scanner call spec", sock);
+                                 US"unsafe sock scanner call spec", malware_daemon_ctx.sock);
       }
       else
        sockline_scanner = sockline_scanner_default;
       }
       else
        sockline_scanner = sockline_scanner_default;
@@ -1874,13 +1880,13 @@ b_seek:   err = errno;
       sockline_trig_re = m_pcre_nextinlist(&av_scanner_work, &sep,
                                "missing trigger specification", &errstr);
       if (!sockline_trig_re)
       sockline_trig_re = m_pcre_nextinlist(&av_scanner_work, &sep,
                                "missing trigger specification", &errstr);
       if (!sockline_trig_re)
-       return m_panic_defer_3(scanent, NULL, errstr, sock);
+       return m_panic_defer_3(scanent, NULL, errstr, malware_daemon_ctx.sock);
 
       /* find virus name regex */
       sockline_name_re = m_pcre_nextinlist(&av_scanner_work, &sep,
                          "missing virus name regex specification", &errstr);
       if (!sockline_name_re)
 
       /* find virus name regex */
       sockline_name_re = m_pcre_nextinlist(&av_scanner_work, &sep,
                          "missing virus name regex specification", &errstr);
       if (!sockline_name_re)
-       return m_panic_defer_3(scanent, NULL, errstr, sock);
+       return m_panic_defer_3(scanent, NULL, errstr, malware_daemon_ctx.sock);
 
       /* prepare scanner call - security depends on expansions check above */
       commandline = string_sprintf( CS sockline_scanner, CS eml_filename);
 
       /* prepare scanner call - security depends on expansions check above */
       commandline = string_sprintf( CS sockline_scanner, CS eml_filename);
@@ -1888,20 +1894,20 @@ b_seek:   err = errno;
        string_printing(commandline));
 
       /* Pass the command string to the socket */
        string_printing(commandline));
 
       /* Pass the command string to the socket */
-      if (m_sock_send(sock, commandline, Ustrlen(commandline), &errstr) < 0)
+      if (m_sock_send(malware_daemon_ctx.sock, commandline, Ustrlen(commandline), &errstr) < 0)
        return m_panic_defer(scanent, CUS callout_address, errstr);
 
       /* Read the result */
        return m_panic_defer(scanent, CUS callout_address, errstr);
 
       /* Read the result */
-      bread = ip_recv(sock, av_buffer, sizeof(av_buffer), tmo-time(NULL));
+      bread = ip_recv(&malware_daemon_ctx, av_buffer, sizeof(av_buffer), tmo-time(NULL));
 
       if (bread <= 0)
        return m_panic_defer_3(scanent, CUS callout_address,
          string_sprintf("unable to read from socket (%s)", strerror(errno)),
 
       if (bread <= 0)
        return m_panic_defer_3(scanent, CUS callout_address,
          string_sprintf("unable to read from socket (%s)", strerror(errno)),
-         sock);
+         malware_daemon_ctx.sock);
 
       if (bread == sizeof(av_buffer))
        return m_panic_defer_3(scanent, CUS callout_address,
 
       if (bread == sizeof(av_buffer))
        return m_panic_defer_3(scanent, CUS callout_address,
-               US"buffer too small", sock);
+               US"buffer too small", malware_daemon_ctx.sock);
       av_buffer[bread] = '\0';
       linebuffer = string_copy(av_buffer);
       DEBUG(D_acl) debug_printf_indent("%15s%10s'%s'\n", "", "answer: ",
       av_buffer[bread] = '\0';
       linebuffer = string_copy(av_buffer);
       DEBUG(D_acl) debug_printf_indent("%15s%10s'%s'\n", "", "answer: ",
@@ -1940,16 +1946,16 @@ b_seek:   err = errno;
            string_sprintf("invalid option '%s'", scanner_options));
        }
 
            string_sprintf("invalid option '%s'", scanner_options));
        }
 
-      if((sock = ip_unixsocket(US "/var/run/mksd/socket", &errstr)) < 0)
+      if((malware_daemon_ctx.sock = ip_unixsocket(US "/var/run/mksd/socket", &errstr)) < 0)
        return m_panic_defer(scanent, CUS callout_address, errstr);
 
       malware_name = NULL;
 
       DEBUG(D_acl) debug_printf_indent("Malware scan: issuing %s scan\n", scanner_name);
 
        return m_panic_defer(scanent, CUS callout_address, errstr);
 
       malware_name = NULL;
 
       DEBUG(D_acl) debug_printf_indent("Malware scan: issuing %s scan\n", scanner_name);
 
-      if ((retval = mksd_scan_packed(scanent, sock, eml_filename, tmo)) != OK)
+      if ((retval = mksd_scan_packed(scanent, malware_daemon_ctx.sock, eml_filename, tmo)) != OK)
        {
        {
-       close (sock);
+       close (malware_daemon_ctx.sock);
        return retval;
        }
       break;
        return retval;
        }
       break;
@@ -2018,7 +2024,7 @@ b_seek:   err = errno;
 
       /* wait for result */
       for (avast_stage = AVA_HELO;
 
       /* wait for result */
       for (avast_stage = AVA_HELO;
-          (nread = recv_line(sock, buf, sizeof(buf), tmo)) > 0;
+          (nread = recv_line(malware_daemon_ctx.sock, buf, sizeof(buf), tmo)) > 0;
          )
        {
        int slen = Ustrlen(buf);
          )
        {
        int slen = Ustrlen(buf);
@@ -2072,12 +2078,12 @@ b_seek:   err = errno;
 
              /* send config-cmd or scan-request to socket */
              len = Ustrlen(scanrequest);
 
              /* send config-cmd or scan-request to socket */
              len = Ustrlen(scanrequest);
-             if (send(sock, scanrequest, len, 0) == -1)
+             if (send(malware_daemon_ctx.sock, scanrequest, len, 0) == -1)
                {
                scanrequest[len-1] = '\0';
                return m_panic_defer_3(scanent, CUS callout_address, string_sprintf(
                      "unable to send request '%s' to socket (%s): %s",
                {
                scanrequest[len-1] = '\0';
                return m_panic_defer_3(scanent, CUS callout_address, string_sprintf(
                      "unable to send request '%s' to socket (%s): %s",
-                     scanrequest, scanner_options, strerror(errno)), sock);
+                     scanrequest, scanner_options, strerror(errno)), malware_daemon_ctx.sock);
                }
              break;
              }
                }
              break;
              }
@@ -2136,20 +2142,20 @@ b_seek:   err = errno;
       else if (buf[0] != '2') error_message = buf;
 
       DEBUG(D_acl) debug_printf_indent("sent to avast QUIT\n");
       else if (buf[0] != '2') error_message = buf;
 
       DEBUG(D_acl) debug_printf_indent("sent to avast QUIT\n");
-      if (send(sock, "QUIT\n", 5, 0) == -1)
+      if (send(malware_daemon_ctx.sock, "QUIT\n", 5, 0) == -1)
         return m_panic_defer_3(scanent, CUS callout_address,
           string_sprintf("unable to send quit request to socket (%s): %s",
         return m_panic_defer_3(scanent, CUS callout_address,
           string_sprintf("unable to send quit request to socket (%s): %s",
-            scanner_options, strerror(errno)), sock);
+            scanner_options, strerror(errno)), malware_daemon_ctx.sock);
 
       if (error_message)
 
       if (error_message)
-        return m_panic_defer_3(scanent, CUS callout_address, error_message, sock);
+        return m_panic_defer_3(scanent, CUS callout_address, error_message, malware_daemon_ctx.sock);
 
       }
 #endif
   }    /* scanner type switch */
 
 
       }
 #endif
   }    /* scanner type switch */
 
-  if (sock >= 0)
-    (void) close (sock);
+  if (malware_daemon_ctx.sock >= 0)
+    (void) close (malware_daemon_ctx.sock);
   malware_ok = TRUE;                   /* set "been here, done that" marker */
   }
 
   malware_ok = TRUE;                   /* set "been here, done that" marker */
   }
 
index c3781420f3bef78f40f6c544b87204579b51e059..974756f06402f5856a87be0c3e70a04f942fb3bc 100644 (file)
@@ -1181,7 +1181,7 @@ switch(where)
   case ACL_WHERE_DKIM:
   case ACL_WHERE_MIME:
   case ACL_WHERE_DATA:
   case ACL_WHERE_DKIM:
   case ACL_WHERE_MIME:
   case ACL_WHERE_DATA:
-    if (  cutthrough.fd >= 0 && cutthrough.delivery
+    if (  cutthrough.cctx.sock >= 0 && cutthrough.delivery
        && (acl_removed_headers || acl_added_headers))
     {
     log_write(0, LOG_MAIN|LOG_PANIC, "Header modification in data ACLs"
        && (acl_removed_headers || acl_added_headers))
     {
     log_write(0, LOG_MAIN|LOG_PANIC, "Header modification in data ACLs"
@@ -2995,7 +2995,7 @@ We have to create the Received header now rather than at the end of reception,
 so the timestamp behaviour is a change to the normal case.
 Having created it, send the headers to the destination. */
 
 so the timestamp behaviour is a change to the normal case.
 Having created it, send the headers to the destination. */
 
-if (cutthrough.fd >= 0 && cutthrough.delivery)
+if (cutthrough.cctx.sock >= 0 && cutthrough.delivery)
   {
   if (received_count > received_headers_max)
     {
   {
   if (received_count > received_headers_max)
     {
@@ -4179,7 +4179,7 @@ for this message. */
 
    XXX We do not handle queue-only, freezing, or blackholes.
 */
 
    XXX We do not handle queue-only, freezing, or blackholes.
 */
-if(cutthrough.fd >= 0 && cutthrough.delivery)
+if(cutthrough.cctx.sock >= 0 && cutthrough.delivery)
   {
   uschar * msg = cutthrough_finaldot();        /* Ask the target system to accept the message */
                                        /* Logging was done in finaldot() */
   {
   uschar * msg = cutthrough_finaldot();        /* Ask the target system to accept the message */
                                        /* Logging was done in finaldot() */
index e5e25ae4bb231b0f06a792aeeefdfd7b9249d806..bc58a1f823edba22ab0ce05e3589abaa04ba133b 100644 (file)
@@ -230,7 +230,8 @@ while ((hostname = string_nextinlist(&listptr, &sep, host_buffer,
 
   for (h = host; h; h = h->next)
     {
 
   for (h = host; h; h = h->next)
     {
-    int host_af, query_socket;
+    int host_af;
+    client_conn_ctx query_cctx = {0};
 
     /* Skip any hosts for which we have no address */
 
 
     /* Skip any hosts for which we have no address */
 
@@ -241,9 +242,9 @@ while ((hostname = string_nextinlist(&listptr, &sep, host_buffer,
 
     host_af = (Ustrchr(h->address, ':') != NULL)? AF_INET6 : AF_INET;
 
 
     host_af = (Ustrchr(h->address, ':') != NULL)? AF_INET6 : AF_INET;
 
-    query_socket = ip_socket(ob->protocol == ip_udp ? SOCK_DGRAM:SOCK_STREAM,
+    query_cctx.sock = ip_socket(ob->protocol == ip_udp ? SOCK_DGRAM:SOCK_STREAM,
       host_af);
       host_af);
-    if (query_socket < 0)
+    if (query_cctx.sock < 0)
       {
       if (ob->optional) return PASS;
       addr->message = string_sprintf("failed to create socket in %s router",
       {
       if (ob->optional) return PASS;
       addr->message = string_sprintf("failed to create socket in %s router",
@@ -256,10 +257,10 @@ while ((hostname = string_nextinlist(&listptr, &sep, host_buffer,
     router will timeout later on the read call). */
 /*XXX could take advantage of TFO */
 
     router will timeout later on the read call). */
 /*XXX could take advantage of TFO */
 
-    if (ip_connect(query_socket, host_af, h->address,ob->port, ob->timeout,
+    if (ip_connect(query_cctx.sock, host_af, h->address,ob->port, ob->timeout,
                ob->protocol == ip_udp ? NULL : &tcp_fastopen_nodata) < 0)
       {
                ob->protocol == ip_udp ? NULL : &tcp_fastopen_nodata) < 0)
       {
-      close(query_socket);
+      close(query_cctx.sock);
       DEBUG(D_route)
         debug_printf("connection to %s failed: %s\n", h->address,
           strerror(errno));
       DEBUG(D_route)
         debug_printf("connection to %s failed: %s\n", h->address,
           strerror(errno));
@@ -268,18 +269,18 @@ while ((hostname = string_nextinlist(&listptr, &sep, host_buffer,
 
     /* Send the query. If it fails, just continue with the next address. */
 
 
     /* Send the query. If it fails, just continue with the next address. */
 
-    if (send(query_socket, query, query_len, 0) < 0)
+    if (send(query_cctx.sock, query, query_len, 0) < 0)
       {
       DEBUG(D_route) debug_printf("send to %s failed\n", h->address);
       {
       DEBUG(D_route) debug_printf("send to %s failed\n", h->address);
-      (void)close(query_socket);
+      (void)close(query_cctx.sock);
       continue;
       }
 
     /* Read the response and close the socket. If the read fails, try the
     next IP address. */
 
       continue;
       }
 
     /* Read the response and close the socket. If the read fails, try the
     next IP address. */
 
-    count = ip_recv(query_socket, reply, sizeof(reply) - 1, ob->timeout);
-    (void)close(query_socket);
+    count = ip_recv(&query_cctx, reply, sizeof(reply) - 1, ob->timeout);
+    (void)close(query_cctx.sock);
     if (count <= 0)
       {
       DEBUG(D_route) debug_printf("%s from %s\n", (errno == ETIMEDOUT)?
     if (count <= 0)
       {
       DEBUG(D_route) debug_printf("%s from %s\n", (errno == ETIMEDOUT)?
index e09d35cbaac163253a6e1f5a3a8e80de6509fd67..0afb97ca67a4aa77c926d19ffb6e600a1f5cc886 100644 (file)
@@ -340,7 +340,7 @@ fd_set fds;
 struct timeval tzero;
 
 #ifdef SUPPORT_TLS
 struct timeval tzero;
 
 #ifdef SUPPORT_TLS
-if (tls_in.active >= 0)
+if (tls_in.active.sock >= 0)
  return !tls_could_read();
 #endif
 
  return !tls_could_read();
 #endif
 
@@ -429,7 +429,7 @@ smtp_command_timeout_exit(void)
 {
 log_write(L_lost_incoming_connection,
          LOG_MAIN, "SMTP command timeout on%s connection from %s",
 {
 log_write(L_lost_incoming_connection,
          LOG_MAIN, "SMTP command timeout on%s connection from %s",
-         tls_in.active >= 0 ? " TLS" : "", host_and_ident(FALSE));
+         tls_in.active.sock >= 0 ? " TLS" : "", host_and_ident(FALSE));
 if (smtp_batched_input)
   moan_smtp_batch(NULL, "421 SMTP command timeout"); /* Does not return */
 smtp_notquit_exit(US"command-timeout", US"421",
 if (smtp_batched_input)
   moan_smtp_batch(NULL, "421 SMTP command timeout"); /* Does not return */
 smtp_notquit_exit(US"command-timeout", US"421",
@@ -922,9 +922,9 @@ if (rcpt_in_progress)
 /* Now write the string */
 
 #ifdef SUPPORT_TLS
 /* Now write the string */
 
 #ifdef SUPPORT_TLS
-if (tls_in.active >= 0)
+if (tls_in.active.sock >= 0)
   {
   {
-  if (tls_write(TRUE, big_buffer, Ustrlen(big_buffer), more) < 0)
+  if (tls_write(NULL, big_buffer, Ustrlen(big_buffer), more) < 0)
     smtp_write_error = -1;
   }
 else
     smtp_write_error = -1;
   }
 else
@@ -951,7 +951,7 @@ Returns:    0 for no error; -1 after an error
 int
 smtp_fflush(void)
 {
 int
 smtp_fflush(void)
 {
-if (tls_in.active < 0 && fflush(smtp_out) != 0) smtp_write_error = -1;
+if (tls_in.active.sock < 0 && fflush(smtp_out) != 0) smtp_write_error = -1;
 return smtp_write_error;
 }
 
 return smtp_write_error;
 }
 
@@ -3686,7 +3686,7 @@ switch(rc)
 
     received_protocol =
       (sender_host_address ? protocols : protocols_local)
 
     received_protocol =
       (sender_host_address ? protocols : protocols_local)
-       [pextend + pauthed + (tls_in.active >= 0 ? pcrpted:0)];
+       [pextend + pauthed + (tls_in.active.sock >= 0 ? pcrpted:0)];
     *s = *ss = US"235 Authentication succeeded";
     authenticated_by = au;
     break;
     *s = *ss = US"235 Authentication succeeded";
     authenticated_by = au;
     break;
@@ -3780,7 +3780,7 @@ else
   smtp_printf("221 %s closing connection\r\n", FALSE, smtp_active_hostname);
 
 #ifdef SUPPORT_TLS
   smtp_printf("221 %s closing connection\r\n", FALSE, smtp_active_hostname);
 
 #ifdef SUPPORT_TLS
-tls_close(TRUE, TLS_SHUTDOWN_NOWAIT);
+tls_close(NULL, TLS_SHUTDOWN_NOWAIT);
 #endif
 
 log_write(L_smtp_connection, LOG_MAIN, "%s closed by QUIT",
 #endif
 
 log_write(L_smtp_connection, LOG_MAIN, "%s closed by QUIT",
@@ -3891,7 +3891,7 @@ while (done <= 0)
 
 #ifdef AUTH_TLS
   /* Check once per STARTTLS or SSL-on-connect for a TLS AUTH */
 
 #ifdef AUTH_TLS
   /* Check once per STARTTLS or SSL-on-connect for a TLS AUTH */
-  if (  tls_in.active >= 0
+  if (  tls_in.active.sock >= 0
      && tls_in.peercert
      && tls_in.certificate_verified
      && cmd_list[CMD_LIST_TLS_AUTH].is_mail_cmd
      && tls_in.peercert
      && tls_in.certificate_verified
      && cmd_list[CMD_LIST_TLS_AUTH].is_mail_cmd
@@ -4108,7 +4108,7 @@ while (done <= 0)
 
       host_build_sender_fullhost();  /* Rebuild */
       set_process_info("handling%s incoming connection from %s",
 
       host_build_sender_fullhost();  /* Rebuild */
       set_process_info("handling%s incoming connection from %s",
-        (tls_in.active >= 0)? " TLS" : "", host_and_ident(FALSE));
+        (tls_in.active.sock >= 0)? " TLS" : "", host_and_ident(FALSE));
 
       /* Verify if configured. This doesn't give much security, but it does
       make some people happy to be able to do it. If helo_required is set,
 
       /* Verify if configured. This doesn't give much security, but it does
       make some people happy to be able to do it. If helo_required is set,
@@ -4364,7 +4364,7 @@ while (done <= 0)
       secure connection. */
 
 #ifdef SUPPORT_TLS
       secure connection. */
 
 #ifdef SUPPORT_TLS
-      if (tls_in.active < 0 &&
+      if (tls_in.active.sock < 0 &&
           verify_check_host(&tls_advertise_hosts) != FAIL)
         {
         g = string_catn(g, smtp_code, 3);
           verify_check_host(&tls_advertise_hosts) != FAIL)
         {
         g = string_catn(g, smtp_code, 3);
@@ -4402,7 +4402,7 @@ while (done <= 0)
     has been seen. */
 
 #ifdef SUPPORT_TLS
     has been seen. */
 
 #ifdef SUPPORT_TLS
-    if (tls_in.active >= 0) (void)tls_write(TRUE, g->s, g->ptr, FALSE); else
+    if (tls_in.active.sock >= 0) (void)tls_write(NULL, g->s, g->ptr, FALSE); else
 #endif
 
       {
 #endif
 
       {
@@ -4425,7 +4425,7 @@ while (done <= 0)
        [ (esmtp
          ? pextend + (sender_host_authenticated ? pauthed : 0)
          : pnormal)
        [ (esmtp
          ? pextend + (sender_host_authenticated ? pauthed : 0)
          : pnormal)
-       + (tls_in.active >= 0 ? pcrpted : 0)
+       + (tls_in.active.sock >= 0 ? pcrpted : 0)
        ];
     cancel_cutthrough_connection(TRUE, US"sent EHLO response");
     smtp_reset(reset_point);
        ];
     cancel_cutthrough_connection(TRUE, US"sent EHLO response");
     smtp_reset(reset_point);
@@ -5213,7 +5213,7 @@ while (done <= 0)
       ACL may have delayed.  To handle cutthrough delivery enforce a dummy call
       to get the DATA command sent. */
 
       ACL may have delayed.  To handle cutthrough delivery enforce a dummy call
       to get the DATA command sent. */
 
-      if (acl_smtp_predata == NULL && cutthrough.fd < 0)
+      if (acl_smtp_predata == NULL && cutthrough.cctx.sock < 0)
        rc = OK;
       else
        {
        rc = OK;
       else
        {
@@ -5367,7 +5367,7 @@ while (done <= 0)
       {
       DEBUG(D_any)
         debug_printf("Non-empty input buffer after STARTTLS; naive attack?\n");
       {
       DEBUG(D_any)
         debug_printf("Non-empty input buffer after STARTTLS; naive attack?\n");
-      if (tls_in.active < 0)
+      if (tls_in.active.sock < 0)
         smtp_inend = smtp_inptr = smtp_inbuffer;
       /* and if TLS is already active, tls_server_start() should fail */
       }
         smtp_inend = smtp_inptr = smtp_inbuffer;
       /* and if TLS is already active, tls_server_start() should fail */
       }
@@ -5410,7 +5410,7 @@ while (done <= 0)
          [ (esmtp
            ? pextend + (sender_host_authenticated ? pauthed : 0)
            : pnormal)
          [ (esmtp
            ? pextend + (sender_host_authenticated ? pauthed : 0)
            : pnormal)
-         + (tls_in.active >= 0 ? pcrpted : 0)
+         + (tls_in.active.sock >= 0 ? pcrpted : 0)
          ];
 
       sender_host_auth_pubname = sender_host_authenticated = NULL;
          ];
 
       sender_host_auth_pubname = sender_host_authenticated = NULL;
@@ -5470,7 +5470,7 @@ while (done <= 0)
        smtp_printf("554 Security failure\r\n", FALSE);
        break;
       }
        smtp_printf("554 Security failure\r\n", FALSE);
        break;
       }
-    tls_close(TRUE, TLS_SHUTDOWN_NOWAIT);
+    tls_close(NULL, TLS_SHUTDOWN_NOWAIT);
     break;
     #endif
 
     break;
     #endif
 
@@ -5512,7 +5512,7 @@ while (done <= 0)
       buffer[0] = 0;
       Ustrcat(buffer, " AUTH");
       #ifdef SUPPORT_TLS
       buffer[0] = 0;
       Ustrcat(buffer, " AUTH");
       #ifdef SUPPORT_TLS
-      if (tls_in.active < 0 &&
+      if (tls_in.active.sock < 0 &&
           verify_check_host(&tls_advertise_hosts) != FAIL)
         Ustrcat(buffer, " STARTTLS");
       #endif
           verify_check_host(&tls_advertise_hosts) != FAIL)
         Ustrcat(buffer, " STARTTLS");
       #endif
index c968f70e219312fff38eb95cc6adfe97ae7e0514..70aaef3e346061bb6e32b1acce55d362c14e83c2 100644 (file)
@@ -411,11 +411,11 @@ HDEBUG(D_transport|D_acl) debug_printf_indent("cmd buf flush %d bytes%s\n", n,
   more ? " (more expected)" : "");
 
 #ifdef SUPPORT_TLS
   more ? " (more expected)" : "");
 
 #ifdef SUPPORT_TLS
-if (tls_out.active == outblock->sock)
-  rc = tls_write(FALSE, outblock->buffer, n, more);
+if (outblock->cctx->tls_ctx)
+  rc = tls_write(outblock->cctx->tls_ctx, outblock->buffer, n, more);
 else
 #endif
 else
 #endif
-  rc = send(outblock->sock, outblock->buffer, n,
+  rc = send(outblock->cctx->sock, outblock->buffer, n,
 #ifdef MSG_MORE
            more ? MSG_MORE : 0
 #else
 #ifdef MSG_MORE
            more ? MSG_MORE : 0
 #else
@@ -546,7 +546,7 @@ read_response_line(smtp_inblock *inblock, uschar *buffer, int size, int timeout)
 uschar *p = buffer;
 uschar *ptr = inblock->ptr;
 uschar *ptrend = inblock->ptrend;
 uschar *p = buffer;
 uschar *ptr = inblock->ptr;
 uschar *ptrend = inblock->ptrend;
-int sock = inblock->sock;
+client_conn_ctx * cctx = inblock->cctx;
 
 /* Loop for reading multiple packets or reading another packet after emptying
 a previously-read one. */
 
 /* Loop for reading multiple packets or reading another packet after emptying
 a previously-read one. */
@@ -584,7 +584,7 @@ for (;;)
 
   /* Need to read a new input packet. */
 
 
   /* Need to read a new input packet. */
 
-  if((rc = ip_recv(sock, inblock->buffer, inblock->buffersize, timeout)) <= 0)
+  if((rc = ip_recv(cctx, inblock->buffer, inblock->buffersize, timeout)) <= 0)
     {
     DEBUG(D_deliver|D_transport|D_acl)
       debug_printf_indent(errno ? "  SMTP(%s)<<\n" : "  SMTP(closed)<<\n",
     {
     DEBUG(D_deliver|D_transport|D_acl)
       debug_printf_indent(errno ? "  SMTP(%s)<<\n" : "  SMTP(closed)<<\n",
index 8e08a40b7416519d4948f5a8c38975ce29c32fab..9384bfa0dc120a65dc315e6b57b4c6178d7a7352 100644 (file)
@@ -193,7 +193,7 @@ uschar *user_name;
 uschar user_name_buffer[128];
 unsigned long mbox_size;
 FILE *mbox_file;
 uschar user_name_buffer[128];
 unsigned long mbox_size;
 FILE *mbox_file;
-int spamd_sock = -1;
+client_conn_ctx spamd_cctx = {.sock = -1};
 uschar spamd_buffer[32600];
 int i, j, offset, result;
 uschar spamd_version[8];
 uschar spamd_buffer[32600];
 int i, j, offset, result;
 uschar spamd_version[8];
@@ -344,14 +344,14 @@ start = time(NULL);
     for (;;)
       {
       /*XXX could potentially use TFO early-data here */
     for (;;)
       {
       /*XXX could potentially use TFO early-data here */
-      if (  (spamd_sock = ip_streamsocket(sd->hostspec, &errstr, 5)) >= 0
+      if (  (spamd_cctx.sock = ip_streamsocket(sd->hostspec, &errstr, 5)) >= 0
          || sd->retry <= 0
         )
        break;
       DEBUG(D_acl) debug_printf_indent("spamd: server %s: retry conn\n", sd->hostspec);
       while (sd->retry > 0) sd->retry = sleep(sd->retry);
       }
          || sd->retry <= 0
         )
        break;
       DEBUG(D_acl) debug_printf_indent("spamd: server %s: retry conn\n", sd->hostspec);
       while (sd->retry > 0) sd->retry = sleep(sd->retry);
       }
-    if (spamd_sock >= 0)
+    if (spamd_cctx.sock >= 0)
       break;
 
     log_write(0, LOG_MAIN, "%s spamd: %s", loglabel, errstr);
       break;
 
     log_write(0, LOG_MAIN, "%s spamd: %s", loglabel, errstr);
@@ -367,8 +367,8 @@ start = time(NULL);
     }
   }
 
     }
   }
 
-(void)fcntl(spamd_sock, F_SETFL, O_NONBLOCK);
-/* now we are connected to spamd on spamd_sock */
+(void)fcntl(spamd_cctx.sock, F_SETFL, O_NONBLOCK);
+/* now we are connected to spamd on spamd_cctx.sock */
 if (sd->is_rspamd)
   {
   gstring * req_str;
 if (sd->is_rspamd)
   {
   gstring * req_str;
@@ -392,7 +392,7 @@ if (sd->is_rspamd)
   if ((s = expand_string(US"$authenticated_id")) && *s)
     req_str = string_append(req_str, 3, "User: ", s, "\r\n");
   req_str = string_catn(req_str, US"\r\n", 2);
   if ((s = expand_string(US"$authenticated_id")) && *s)
     req_str = string_append(req_str, 3, "User: ", s, "\r\n");
   req_str = string_catn(req_str, US"\r\n", 2);
-  wrote = send(spamd_sock, req_str->s, req_str->ptr, 0);
+  wrote = send(spamd_cctx.sock, req_str->s, req_str->ptr, 0);
   }
 else
   {                            /* spamassassin variant */
   }
 else
   {                            /* spamassassin variant */
@@ -402,12 +402,12 @@ else
          user_name,
          mbox_size);
   /* send our request */
          user_name,
          mbox_size);
   /* send our request */
-  wrote = send(spamd_sock, spamd_buffer, Ustrlen(spamd_buffer), 0);
+  wrote = send(spamd_cctx.sock, spamd_buffer, Ustrlen(spamd_buffer), 0);
   }
 
 if (wrote == -1)
   {
   }
 
 if (wrote == -1)
   {
-  (void)close(spamd_sock);
+  (void)close(spamd_cctx.sock);
   log_write(0, LOG_MAIN|LOG_PANIC,
        "%s spamd %s send failed: %s", loglabel, callout_address, strerror(errno));
   goto defer;
   log_write(0, LOG_MAIN|LOG_PANIC,
        "%s spamd %s send failed: %s", loglabel, callout_address, strerror(errno));
   goto defer;
@@ -424,10 +424,10 @@ if (wrote == -1)
  *       broken in more recent versions (up to 10.4).
  */
 #ifndef NO_POLL_H
  *       broken in more recent versions (up to 10.4).
  */
 #ifndef NO_POLL_H
-pollfd.fd = spamd_sock;
+pollfd.fd = spamd_cctx.sock;
 pollfd.events = POLLOUT;
 #endif
 pollfd.events = POLLOUT;
 #endif
-(void)fcntl(spamd_sock, F_SETFL, O_NONBLOCK);
+(void)fcntl(spamd_cctx.sock, F_SETFL, O_NONBLOCK);
 do
   {
   read = fread(spamd_buffer,1,sizeof(spamd_buffer),mbox_file);
 do
   {
   read = fread(spamd_buffer,1,sizeof(spamd_buffer),mbox_file);
@@ -443,8 +443,8 @@ again:
     select_tv.tv_sec = 1;
     select_tv.tv_usec = 0;
     FD_ZERO(&select_fd);
     select_tv.tv_sec = 1;
     select_tv.tv_usec = 0;
     FD_ZERO(&select_fd);
-    FD_SET(spamd_sock, &select_fd);
-    result = select(spamd_sock+1, NULL, &select_fd, NULL, &select_tv);
+    FD_SET(spamd_cctx.sock, &select_fd);
+    result = select(spamd_cctx.sock+1, NULL, &select_fd, NULL, &select_tv);
 #endif
 /* End Erik's patch */
 
 #endif
 /* End Erik's patch */
 
@@ -462,16 +462,16 @@ again:
        log_write(0, LOG_MAIN|LOG_PANIC,
          "%s timed out writing spamd %s, socket", loglabel, callout_address);
        }
        log_write(0, LOG_MAIN|LOG_PANIC,
          "%s timed out writing spamd %s, socket", loglabel, callout_address);
        }
-      (void)close(spamd_sock);
+      (void)close(spamd_cctx.sock);
       goto defer;
       }
 
       goto defer;
       }
 
-    wrote = send(spamd_sock,spamd_buffer + offset,read - offset,0);
+    wrote = send(spamd_cctx.sock,spamd_buffer + offset,read - offset,0);
     if (wrote == -1)
       {
       log_write(0, LOG_MAIN|LOG_PANIC,
          "%s %s on spamd %s socket", loglabel, callout_address, strerror(errno));
     if (wrote == -1)
       {
       log_write(0, LOG_MAIN|LOG_PANIC,
          "%s %s on spamd %s socket", loglabel, callout_address, strerror(errno));
-      (void)close(spamd_sock);
+      (void)close(spamd_cctx.sock);
       goto defer;
       }
     if (offset + wrote != read)
       goto defer;
       }
     if (offset + wrote != read)
@@ -487,7 +487,7 @@ if (ferror(mbox_file))
   {
   log_write(0, LOG_MAIN|LOG_PANIC,
     "%s error reading spool file: %s", loglabel, strerror(errno));
   {
   log_write(0, LOG_MAIN|LOG_PANIC,
     "%s error reading spool file: %s", loglabel, strerror(errno));
-  (void)close(spamd_sock);
+  (void)close(spamd_cctx.sock);
   goto defer;
   }
 
   goto defer;
   }
 
@@ -495,12 +495,12 @@ if (ferror(mbox_file))
 
 /* we're done sending, close socket for writing */
 if (!sd->is_rspamd)
 
 /* we're done sending, close socket for writing */
 if (!sd->is_rspamd)
-  shutdown(spamd_sock,SHUT_WR);
+  shutdown(spamd_cctx.sock,SHUT_WR);
 
 /* read spamd response using what's left of the timeout.  */
 memset(spamd_buffer, 0, sizeof(spamd_buffer));
 offset = 0;
 
 /* read spamd response using what's left of the timeout.  */
 memset(spamd_buffer, 0, sizeof(spamd_buffer));
 offset = 0;
-while ((i = ip_recv(spamd_sock,
+while ((i = ip_recv(&spamd_cctx,
                   spamd_buffer + offset,
                   sizeof(spamd_buffer) - offset - 1,
                   sd->timeout - time(NULL) + start)) > 0)
                   spamd_buffer + offset,
                   sizeof(spamd_buffer) - offset - 1,
                   sd->timeout - time(NULL) + start)) > 0)
@@ -512,12 +512,12 @@ if (i <= 0 && errno != 0)
   {
   log_write(0, LOG_MAIN|LOG_PANIC,
        "%s error reading from spamd %s, socket: %s", loglabel, callout_address, strerror(errno));
   {
   log_write(0, LOG_MAIN|LOG_PANIC,
        "%s error reading from spamd %s, socket: %s", loglabel, callout_address, strerror(errno));
-  (void)close(spamd_sock);
+  (void)close(spamd_cctx.sock);
   return DEFER;
   }
 
 /* reading done */
   return DEFER;
   }
 
 /* reading done */
-(void)close(spamd_sock);
+(void)close(spamd_cctx.sock);
 
 if (sd->is_rspamd)
   {                            /* rspamd variant of reply */
 
 if (sd->is_rspamd)
   {                            /* rspamd variant of reply */
index 8384920ee16f16b94329f753276aecf8e687f5b7..9ee3dba8d7b25672f24cc71ff805832d70990177 100644 (file)
@@ -780,15 +780,21 @@ md5;
 typedef struct sha1 {
   unsigned int H[5];
   unsigned int length;
 typedef struct sha1 {
   unsigned int H[5];
   unsigned int length;
-  }
-sha1;
+} sha1;
+
+/* A client-initiated connection. If TLS, the second element is non-NULL */
+typedef struct {
+  int  sock;
+  void * tls_ctx;
+} client_conn_ctx;
+
 
 /* Structure used to hold incoming packets of SMTP responses for a specific
 socket. The packets which may contain multiple lines (and in some cases,
 multiple responses). */
 
 typedef struct smtp_inblock {
 
 /* Structure used to hold incoming packets of SMTP responses for a specific
 socket. The packets which may contain multiple lines (and in some cases,
 multiple responses). */
 
 typedef struct smtp_inblock {
-  int     sock;                   /* the socket */
+  client_conn_ctx * cctx;        /* the connection */
   int     buffersize;             /* the size of the buffer */
   uschar *ptr;                    /* current position in the buffer */
   uschar *ptrend;                 /* end of data in the buffer */
   int     buffersize;             /* the size of the buffer */
   uschar *ptr;                    /* current position in the buffer */
   uschar *ptrend;                 /* end of data in the buffer */
@@ -800,7 +806,7 @@ specific socket. The packets which may contain multiple lines when pipelining
 is in use. */
 
 typedef struct smtp_outblock {
 is in use. */
 
 typedef struct smtp_outblock {
-  int     sock;                   /* the socket */
+  client_conn_ctx * cctx;        /* the connection */
   int     cmd_count;              /* count of buffered commands */
   int     buffersize;             /* the size of the buffer */
   BOOL    authenticating;         /* TRUE when authenticating */
   int     cmd_count;              /* count of buffered commands */
   int     buffersize;             /* the size of the buffer */
   BOOL    authenticating;         /* TRUE when authenticating */
index 08c1d939ee4f5c35a2064668878c3c033aad826c..12c9fdb38ab4aaf53c324ee64783cf3c436a9248 100644 (file)
@@ -213,7 +213,7 @@ second connection.
 XXX But see gnutls_session_get_ptr()
 */
 
 XXX But see gnutls_session_get_ptr()
 */
 
-static exim_gnutls_state_st state_server, state_client;
+static exim_gnutls_state_st state_server;
 
 /* dh_params are initialised once within the lifetime of a process using TLS;
 if we used TLS in a long-lived daemon, we'd have to reconsider this.  But we
 
 /* dh_params are initialised once within the lifetime of a process using TLS;
 if we used TLS in a long-lived daemon, we'd have to reconsider this.  But we
@@ -448,7 +448,8 @@ gnutls_datum_t channel;
 #endif
 tls_support * tlsp = state->tlsp;
 
 #endif
 tls_support * tlsp = state->tlsp;
 
-tlsp->active = state->fd_out;
+tlsp->active.sock = state->fd_out;
+tlsp->active.tls_ctx = state;
 
 cipher = gnutls_cipher_get(state->session);
 /* returns size in "bytes" */
 
 cipher = gnutls_cipher_get(state->session);
 /* returns size in "bytes" */
@@ -1262,6 +1263,7 @@ tls_init(
     const uschar *crl,
     const uschar *require_ciphers,
     exim_gnutls_state_st **caller_state,
     const uschar *crl,
     const uschar *require_ciphers,
     exim_gnutls_state_st **caller_state,
+    tls_support * tlsp,
     uschar ** errstr)
 {
 exim_gnutls_state_st *state;
     uschar ** errstr)
 {
 exim_gnutls_state_st *state;
@@ -1310,9 +1312,15 @@ if (!exim_gnutls_base_init_done)
 
 if (host)
   {
 
 if (host)
   {
-  state = &state_client;
+  /* For client-side sessions we allocate a context. This lets us run
+  several in parallel. */
+  int old_pool = store_pool;
+  store_pool = POOL_PERM;
+  state = store_get(sizeof(exim_gnutls_state_st));
+  store_pool = old_pool;
+
   memcpy(state, &exim_gnutls_state_init, sizeof(exim_gnutls_state_init));
   memcpy(state, &exim_gnutls_state_init, sizeof(exim_gnutls_state_init));
-  state->tlsp = &tls_out;
+  state->tlsp = tlsp;
   DEBUG(D_tls) debug_printf("initialising GnuTLS client session\n");
   rc = gnutls_init(&state->session, GNUTLS_CLIENT);
   }
   DEBUG(D_tls) debug_printf("initialising GnuTLS client session\n");
   rc = gnutls_init(&state->session, GNUTLS_CLIENT);
   }
@@ -1320,7 +1328,7 @@ else
   {
   state = &state_server;
   memcpy(state, &exim_gnutls_state_init, sizeof(exim_gnutls_state_init));
   {
   state = &state_server;
   memcpy(state, &exim_gnutls_state_init, sizeof(exim_gnutls_state_init));
-  state->tlsp = &tls_in;
+  state->tlsp = tlsp;
   DEBUG(D_tls) debug_printf("initialising GnuTLS server session\n");
   rc = gnutls_init(&state->session, GNUTLS_SERVER);
   }
   DEBUG(D_tls) debug_printf("initialising GnuTLS server session\n");
   rc = gnutls_init(&state->session, GNUTLS_SERVER);
   }
@@ -1980,7 +1988,7 @@ int rc;
 exim_gnutls_state_st * state = NULL;
 
 /* Check for previous activation */
 exim_gnutls_state_st * state = NULL;
 
 /* Check for previous activation */
-if (tls_in.active >= 0)
+if (tls_in.active.sock >= 0)
   {
   tls_error(US"STARTTLS received after TLS started", "", NULL, errstr);
   smtp_printf("554 Already in TLS\r\n", FALSE);
   {
   tls_error(US"STARTTLS received after TLS started", "", NULL, errstr);
   smtp_printf("554 Already in TLS\r\n", FALSE);
@@ -1994,7 +2002,7 @@ DEBUG(D_tls) debug_printf("initialising GnuTLS as a server\n");
 
 if ((rc = tls_init(NULL, tls_certificate, tls_privatekey,
     NULL, tls_verify_certificates, tls_crl,
 
 if ((rc = tls_init(NULL, tls_certificate, tls_privatekey,
     NULL, tls_verify_certificates, tls_crl,
-    require_ciphers, &state, errstr)) != OK) return rc;
+    require_ciphers, &state, &tls_in, errstr)) != OK) return rc;
 
 /* If this is a host for which certificate verification is mandatory or
 optional, set up appropriately. */
 
 /* If this is a host for which certificate verification is mandatory or
 optional, set up appropriately. */
@@ -2241,26 +2249,27 @@ Arguments:
                    Which implies cert must be requested and supplied, dane
                    verify must pass, and cert verify irrelevant (incl.
                    hostnames), and (caller handled) require_tls
                    Which implies cert must be requested and supplied, dane
                    verify must pass, and cert verify irrelevant (incl.
                    hostnames), and (caller handled) require_tls
+  tlsp             record details of channel configuration
   errstr           error string pointer
 
   errstr           error string pointer
 
-Returns:            OK/DEFER/FAIL (because using common functions),
-                    but for a client, DEFER and FAIL have the same meaning
+Returns:            Pointer to TLS session context, or NULL on error
 */
 
 */
 
-int
+void *
 tls_client_start(int fd, host_item *host,
     address_item *addr ARG_UNUSED,
     transport_instance * tb,
 #ifdef SUPPORT_DANE
     dns_answer * tlsa_dnsa,
 #endif
 tls_client_start(int fd, host_item *host,
     address_item *addr ARG_UNUSED,
     transport_instance * tb,
 #ifdef SUPPORT_DANE
     dns_answer * tlsa_dnsa,
 #endif
-    uschar ** errstr)
+    tls_support * tlsp, uschar ** errstr)
 {
 smtp_transport_options_block *ob =
   (smtp_transport_options_block *)tb->options_block;
 int rc;
 exim_gnutls_state_st * state = NULL;
 uschar *cipher_list = NULL;
 {
 smtp_transport_options_block *ob =
   (smtp_transport_options_block *)tb->options_block;
 int rc;
 exim_gnutls_state_st * state = NULL;
 uschar *cipher_list = NULL;
+
 #ifndef DISABLE_OCSP
 BOOL require_ocsp =
   verify_check_given_host(&ob->hosts_require_ocsp, host) == OK;
 #ifndef DISABLE_OCSP
 BOOL require_ocsp =
   verify_check_given_host(&ob->hosts_require_ocsp, host) == OK;
@@ -2276,7 +2285,7 @@ if (tlsa_dnsa && ob->dane_require_tls_ciphers)
   /* not using expand_check_tlsvar because not yet in state */
   if (!expand_check(ob->dane_require_tls_ciphers, US"dane_require_tls_ciphers",
       &cipher_list, errstr))
   /* not using expand_check_tlsvar because not yet in state */
   if (!expand_check(ob->dane_require_tls_ciphers, US"dane_require_tls_ciphers",
       &cipher_list, errstr))
-    return DEFER;
+    return NULL;
   cipher_list = cipher_list && *cipher_list
     ? ob->dane_require_tls_ciphers : ob->tls_require_ciphers;
   }
   cipher_list = cipher_list && *cipher_list
     ? ob->dane_require_tls_ciphers : ob->tls_require_ciphers;
   }
@@ -2285,10 +2294,10 @@ if (tlsa_dnsa && ob->dane_require_tls_ciphers)
 if (!cipher_list)
   cipher_list = ob->tls_require_ciphers;
 
 if (!cipher_list)
   cipher_list = ob->tls_require_ciphers;
 
-if ((rc = tls_init(host, ob->tls_certificate, ob->tls_privatekey,
+if (tls_init(host, ob->tls_certificate, ob->tls_privatekey,
     ob->tls_sni, ob->tls_verify_certificates, ob->tls_crl,
     ob->tls_sni, ob->tls_verify_certificates, ob->tls_crl,
-    cipher_list, &state, errstr)) != OK)
-  return rc;
+    cipher_list, &state, tlsp, errstr) != OK)
+  return NULL;
 
   {
   int dh_min_bits = ob->tls_dh_min_bits;
 
   {
   int dh_min_bits = ob->tls_dh_min_bits;
@@ -2357,9 +2366,11 @@ if (request_ocsp)
   DEBUG(D_tls) debug_printf("TLS: will request OCSP stapling\n");
   if ((rc = gnutls_ocsp_status_request_enable_client(state->session,
                    NULL, 0, NULL)) != OK)
   DEBUG(D_tls) debug_printf("TLS: will request OCSP stapling\n");
   if ((rc = gnutls_ocsp_status_request_enable_client(state->session,
                    NULL, 0, NULL)) != OK)
-    return tls_error(US"cert-status-req",
-                   gnutls_strerror(rc), state->host, errstr);
-  tls_out.ocsp = OCSP_NOT_RESP;
+    {
+    tls_error(US"cert-status-req", gnutls_strerror(rc), state->host, errstr);
+    return NULL;
+    }
+  tlsp->ocsp = OCSP_NOT_RESP;
   }
 #endif
 
   }
 #endif
 
@@ -2387,20 +2398,26 @@ while (rc == GNUTLS_E_AGAIN || rc == GNUTLS_E_INTERRUPTED && !sigalrm_seen);
 alarm(0);
 
 if (rc != GNUTLS_E_SUCCESS)
 alarm(0);
 
 if (rc != GNUTLS_E_SUCCESS)
+  {
   if (sigalrm_seen)
     {
     gnutls_alert_send(state->session, GNUTLS_AL_FATAL, GNUTLS_A_USER_CANCELED);
   if (sigalrm_seen)
     {
     gnutls_alert_send(state->session, GNUTLS_AL_FATAL, GNUTLS_A_USER_CANCELED);
-    return tls_error(US"gnutls_handshake", "timed out", state->host, errstr);
+    tls_error(US"gnutls_handshake", "timed out", state->host, errstr);
     }
   else
     }
   else
-    return tls_error(US"gnutls_handshake", gnutls_strerror(rc), state->host, errstr);
+    tls_error(US"gnutls_handshake", gnutls_strerror(rc), state->host, errstr);
+  return NULL;
+  }
 
 DEBUG(D_tls) debug_printf("gnutls_handshake was successful\n");
 
 /* Verify late */
 
 if (!verify_certificate(state, errstr))
 
 DEBUG(D_tls) debug_printf("gnutls_handshake was successful\n");
 
 /* Verify late */
 
 if (!verify_certificate(state, errstr))
-  return tls_error(US"certificate verification failed", *errstr, state->host, errstr);
+  {
+  tls_error(US"certificate verification failed", *errstr, state->host, errstr);
+  return NULL;
+  }
 
 #ifndef DISABLE_OCSP
 if (require_ocsp)
 
 #ifndef DISABLE_OCSP
 if (require_ocsp)
@@ -2425,24 +2442,25 @@ if (require_ocsp)
 
   if (gnutls_ocsp_status_request_is_checked(state->session, 0) == 0)
     {
 
   if (gnutls_ocsp_status_request_is_checked(state->session, 0) == 0)
     {
-    tls_out.ocsp = OCSP_FAILED;
-    return tls_error(US"certificate status check failed", NULL, state->host, errstr);
+    tlsp->ocsp = OCSP_FAILED;
+    tls_error(US"certificate status check failed", NULL, state->host, errstr);
+    return NULL;
     }
   DEBUG(D_tls) debug_printf("Passed OCSP checking\n");
     }
   DEBUG(D_tls) debug_printf("Passed OCSP checking\n");
-  tls_out.ocsp = OCSP_VFIED;
+  tlsp->ocsp = OCSP_VFIED;
   }
 #endif
 
 /* Figure out peer DN, and if authenticated, etc. */
 
   }
 #endif
 
 /* Figure out peer DN, and if authenticated, etc. */
 
-if ((rc = peer_status(state, errstr)) != OK)
-  return rc;
+if (peer_status(state, errstr) != OK)
+  return NULL;
 
 /* Sets various Exim expansion variables; may need to adjust for ACL callouts */
 
 extract_exim_vars_from_tls_state(state);
 
 
 /* Sets various Exim expansion variables; may need to adjust for ACL callouts */
 
 extract_exim_vars_from_tls_state(state);
 
-return OK;
+return state;
 }
 
 
 }
 
 
@@ -2457,6 +2475,7 @@ daemon, to shut down the TLS library, without actually doing a shutdown (which
 would tamper with the TLS session in the parent process).
 
 Arguments:
 would tamper with the TLS session in the parent process).
 
 Arguments:
+  ct_ctx       client context pointer, or NULL for the one global server context
   shutdown     1 if TLS close-alert is to be sent,
                2 if also response to be waited for
 
   shutdown     1 if TLS close-alert is to be sent,
                2 if also response to be waited for
 
@@ -2464,11 +2483,11 @@ Returns:     nothing
 */
 
 void
 */
 
 void
-tls_close(BOOL is_server, int shutdown)
+tls_close(void * ct_ctx, int shutdown)
 {
 {
-exim_gnutls_state_st *state = is_server ? &state_server : &state_client;
+exim_gnutls_state_st * state = ct_ctx ? ct_ctx : &state_server;
 
 
-if (!state->tlsp || state->tlsp->active < 0) return;  /* TLS was not active */
+if (!state->tlsp || state->tlsp->active.sock < 0) return;  /* TLS was not active */
 
 if (shutdown)
   {
 
 if (shutdown)
   {
@@ -2484,15 +2503,10 @@ gnutls_deinit(state->session);
 gnutls_certificate_free_credentials(state->x509_cred);
 
 
 gnutls_certificate_free_credentials(state->x509_cred);
 
 
-state->tlsp->active = -1;
+state->tlsp->active.sock = -1;
+state->tlsp->active.tls_ctx = NULL;
 if (state->xfer_buffer) store_free(state->xfer_buffer);
 memcpy(state, &exim_gnutls_state_init, sizeof(exim_gnutls_state_init));
 if (state->xfer_buffer) store_free(state->xfer_buffer);
 memcpy(state, &exim_gnutls_state_init, sizeof(exim_gnutls_state_init));
-
-if (!state_server.session && !state_client.session)
-  {
-  gnutls_global_deinit();
-  exim_gnutls_base_init_done = FALSE;
-  }
 }
 
 
 }
 
 
@@ -2549,7 +2563,8 @@ else if (inbytes == 0)
   gnutls_certificate_free_credentials(state->x509_cred);
 
   state->session = NULL;
   gnutls_certificate_free_credentials(state->x509_cred);
 
   state->session = NULL;
-  state->tlsp->active = -1;
+  state->tlsp->active.sock = -1;
+  state->tlsp->active.tls_ctx = NULL;
   state->tlsp->bits = 0;
   state->tlsp->certificate_verified = FALSE;
   tls_channelbinding_b64 = NULL;
   state->tlsp->bits = 0;
   state->tlsp->certificate_verified = FALSE;
   tls_channelbinding_b64 = NULL;
@@ -2658,6 +2673,7 @@ return state_server.xfer_buffer_lwm < state_server.xfer_buffer_hwm
 then the caller must feed DKIM.
 
 Arguments:
 then the caller must feed DKIM.
 
 Arguments:
+  ct_ctx    client context pointer, or NULL for the one global server context
   buff      buffer of data
   len       size of buffer
 
   buff      buffer of data
   len       size of buffer
 
@@ -2666,9 +2682,9 @@ Returns:    the number of bytes read
 */
 
 int
 */
 
 int
-tls_read(BOOL is_server, uschar *buff, size_t len)
+tls_read(void * ct_ctx, uschar *buff, size_t len)
 {
 {
-exim_gnutls_state_st *state = is_server ? &state_server : &state_client;
+exim_gnutls_state_st * state = ct_ctx ? ct_ctx : &state_server;
 ssize_t inbytes;
 
 if (len > INT_MAX)
 ssize_t inbytes;
 
 if (len > INT_MAX)
@@ -2704,7 +2720,7 @@ return -1;
 
 /*
 Arguments:
 
 /*
 Arguments:
-  is_server channel specifier
+  ct_ctx    client context pointer, or NULL for the one global server context
   buff      buffer of data
   len       number of bytes
   more     more data expected soon
   buff      buffer of data
   len       number of bytes
   more     more data expected soon
@@ -2714,11 +2730,11 @@ Returns:    the number of bytes after a successful write,
 */
 
 int
 */
 
 int
-tls_write(BOOL is_server, const uschar *buff, size_t len, BOOL more)
+tls_write(void * ct_ctx, const uschar * buff, size_t len, BOOL more)
 {
 ssize_t outbytes;
 size_t left = len;
 {
 ssize_t outbytes;
 size_t left = len;
-exim_gnutls_state_st *state = is_server ? &state_server : &state_client;
+exim_gnutls_state_st * state = ct_ctx ? ct_ctx : &state_server;
 #ifdef SUPPORT_CORK
 static BOOL corked = FALSE;
 
 #ifdef SUPPORT_CORK
 static BOOL corked = FALSE;
 
index e7bba022158eba7a7cac67dc14c7a3ec9c35045e..adabc963e43db09f7172f7414d1ad44bbbe08798 100644 (file)
@@ -117,7 +117,9 @@ static const uschar *sid_ctx = US"exim";
 Simple case: client, `client_ctx`
  As a client, we can be doing a callout or cut-through delivery while receiving
  a message.  So we have a client context, which should have options initialised
 Simple case: client, `client_ctx`
  As a client, we can be doing a callout or cut-through delivery while receiving
  a message.  So we have a client context, which should have options initialised
- from the SMTP Transport.
+ from the SMTP Transport.  We may also concurrently want to make TLS connections
+ to utility daemons, so client-contexts are allocated and passed around in call
+ args rather than using a gobal.
 
 Server:
  There are two cases: with and without ServerNameIndication from the client.
 
 Server:
  There are two cases: with and without ServerNameIndication from the client.
@@ -131,9 +133,12 @@ Server:
  configuration.
 */
 
  configuration.
 */
 
-static SSL_CTX *client_ctx = NULL;
+typedef struct {
+  SSL_CTX *    ctx;
+  SSL *                ssl;
+} exim_openssl_client_tls_ctx;
+
 static SSL_CTX *server_ctx = NULL;
 static SSL_CTX *server_ctx = NULL;
-static SSL     *client_ssl = NULL;
 static SSL     *server_ssl = NULL;
 
 #ifdef EXIM_HAVE_OPENSSL_TLSEXT
 static SSL     *server_ssl = NULL;
 
 #ifdef EXIM_HAVE_OPENSSL_TLSEXT
@@ -1975,7 +1980,7 @@ static uschar cipherbuf[256];
 
 /* Check for previous activation */
 
 
 /* Check for previous activation */
 
-if (tls_in.active >= 0)
+if (tls_in.active.sock >= 0)
   {
   tls_error(US"STARTTLS received after TLS started", NULL, US"", errstr);
   smtp_printf("554 Already in TLS\r\n", FALSE);
   {
   tls_error(US"STARTTLS received after TLS started", NULL, US"", errstr);
   smtp_printf("554 Already in TLS\r\n", FALSE);
@@ -2130,7 +2135,8 @@ receive_feof = tls_feof;
 receive_ferror = tls_ferror;
 receive_smtp_buffered = tls_smtp_buffered;
 
 receive_ferror = tls_ferror;
 receive_smtp_buffered = tls_smtp_buffered;
 
-tls_in.active = fileno(smtp_out);
+tls_in.active.sock = fileno(smtp_out);
+tls_in.active.tls_ctx = NULL;  /* not using explicit ctx for server-side */
 return OK;
 }
 
 return OK;
 }
 
@@ -2249,23 +2255,23 @@ Argument:
   addr             the first address
   tb               transport (always smtp)
   tlsa_dnsa        tlsa lookup, if DANE, else null
   addr             the first address
   tb               transport (always smtp)
   tlsa_dnsa        tlsa lookup, if DANE, else null
+  tlsp            record details of channel configuration
   errstr          error string pointer
 
   errstr          error string pointer
 
-Returns:           OK on success
-                   FAIL otherwise - note that tls_error() will not give DEFER
-                     because this is not a server
+Returns:           Pointer to TLS session context, or NULL on error
 */
 
 */
 
-int
+void *
 tls_client_start(int fd, host_item *host, address_item *addr,
   transport_instance * tb,
 #ifdef SUPPORT_DANE
   dns_answer * tlsa_dnsa,
 #endif
 tls_client_start(int fd, host_item *host, address_item *addr,
   transport_instance * tb,
 #ifdef SUPPORT_DANE
   dns_answer * tlsa_dnsa,
 #endif
-  uschar ** errstr)
+  tls_support * tlsp, uschar ** errstr)
 {
 smtp_transport_options_block * ob =
   (smtp_transport_options_block *)tb->options_block;
 {
 smtp_transport_options_block * ob =
   (smtp_transport_options_block *)tb->options_block;
+exim_openssl_client_tls_ctx * exim_client_ctx;
 static uschar peerdn[256];
 uschar * expciphers;
 int rc;
 static uschar peerdn[256];
 uschar * expciphers;
 int rc;
@@ -2276,8 +2282,13 @@ BOOL request_ocsp = FALSE;
 BOOL require_ocsp = FALSE;
 #endif
 
 BOOL require_ocsp = FALSE;
 #endif
 
+rc = store_pool;
+store_pool = POOL_PERM;
+exim_client_ctx = store_get(sizeof(exim_openssl_client_tls_ctx));
+store_pool = rc;
+
 #ifdef SUPPORT_DANE
 #ifdef SUPPORT_DANE
-tls_out.tlsa_usage = 0;
+tlsp->tlsa_usage = 0;
 #endif
 
 #ifndef DISABLE_OCSP
 #endif
 
 #ifndef DISABLE_OCSP
@@ -2308,15 +2319,15 @@ tls_out.tlsa_usage = 0;
   }
 #endif
 
   }
 #endif
 
-rc = tls_init(&client_ctx, host, NULL,
+rc = tls_init(&exim_client_ctx->ctx, host, NULL,
     ob->tls_certificate, ob->tls_privatekey,
 #ifndef DISABLE_OCSP
     (void *)(long)request_ocsp,
 #endif
     addr, &client_static_cbinfo, errstr);
     ob->tls_certificate, ob->tls_privatekey,
 #ifndef DISABLE_OCSP
     (void *)(long)request_ocsp,
 #endif
     addr, &client_static_cbinfo, errstr);
-if (rc != OK) return rc;
+if (rc != OK) return NULL;
 
 
-tls_out.certificate_verified = FALSE;
+tlsp->certificate_verified = FALSE;
 client_verify_callback_called = FALSE;
 
 expciphers = NULL;
 client_verify_callback_called = FALSE;
 
 expciphers = NULL;
@@ -2328,7 +2339,7 @@ if (tlsa_dnsa)
   if (ob->dane_require_tls_ciphers &&
       !expand_check(ob->dane_require_tls_ciphers, US"dane_require_tls_ciphers",
         &expciphers, errstr))
   if (ob->dane_require_tls_ciphers &&
       !expand_check(ob->dane_require_tls_ciphers, US"dane_require_tls_ciphers",
         &expciphers, errstr))
-    return FAIL;
+    return NULL;
   if (expciphers && *expciphers == '\0')
     expciphers = NULL;
   }
   if (expciphers && *expciphers == '\0')
     expciphers = NULL;
   }
@@ -2336,7 +2347,7 @@ if (tlsa_dnsa)
 if (!expciphers &&
     !expand_check(ob->tls_require_ciphers, US"tls_require_ciphers",
       &expciphers, errstr))
 if (!expciphers &&
     !expand_check(ob->tls_require_ciphers, US"tls_require_ciphers",
       &expciphers, errstr))
-  return FAIL;
+  return NULL;
 
 /* In OpenSSL, cipher components are separated by hyphens. In GnuTLS, they
 are separated by underscores. So that I can use either form in my tests, and
 
 /* In OpenSSL, cipher components are separated by hyphens. In GnuTLS, they
 are separated by underscores. So that I can use either form in my tests, and
@@ -2347,62 +2358,74 @@ if (expciphers)
   uschar *s = expciphers;
   while (*s) { if (*s == '_') *s = '-'; s++; }
   DEBUG(D_tls) debug_printf("required ciphers: %s\n", expciphers);
   uschar *s = expciphers;
   while (*s) { if (*s == '_') *s = '-'; s++; }
   DEBUG(D_tls) debug_printf("required ciphers: %s\n", expciphers);
-  if (!SSL_CTX_set_cipher_list(client_ctx, CS expciphers))
-    return tls_error(US"SSL_CTX_set_cipher_list", host, NULL, errstr);
+  if (!SSL_CTX_set_cipher_list(exim_client_ctx->ctx, CS expciphers))
+    {
+    tls_error(US"SSL_CTX_set_cipher_list", host, NULL, errstr);
+    return NULL;
+    }
   }
 
 #ifdef SUPPORT_DANE
 if (tlsa_dnsa)
   {
   }
 
 #ifdef SUPPORT_DANE
 if (tlsa_dnsa)
   {
-  SSL_CTX_set_verify(client_ctx,
+  SSL_CTX_set_verify(exim_client_ctx->ctx,
     SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT,
     verify_callback_client_dane);
 
   if (!DANESSL_library_init())
     SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT,
     verify_callback_client_dane);
 
   if (!DANESSL_library_init())
-    return tls_error(US"library init", host, NULL, errstr);
-  if (DANESSL_CTX_init(client_ctx) <= 0)
-    return tls_error(US"context init", host, NULL, errstr);
+    {
+    tls_error(US"library init", host, NULL, errstr);
+    return NULL;
+    }
+  if (DANESSL_CTX_init(exim_client_ctx->ctx) <= 0)
+    {
+    tls_error(US"context init", host, NULL, errstr);
+    return NULL;
+    }
   }
 else
 
 #endif
 
   }
 else
 
 #endif
 
-  if ((rc = tls_client_basic_ctx_init(client_ctx, host, ob,
-      client_static_cbinfo, errstr)) != OK)
-    return rc;
+  if (tls_client_basic_ctx_init(exim_client_ctx->ctx, host, ob,
+       client_static_cbinfo, errstr) != OK)
+    return NULL;
 
 
-if (!(client_ssl = SSL_new(client_ctx)))
-  return tls_error(US"SSL_new", host, NULL, errstr);
-SSL_set_session_id_context(client_ssl, sid_ctx, Ustrlen(sid_ctx));
-SSL_set_fd(client_ssl, fd);
-SSL_set_connect_state(client_ssl);
+if (!(exim_client_ctx->ssl = SSL_new(exim_client_ctx->ctx)))
+  {
+  tls_error(US"SSL_new", host, NULL, errstr);
+  return NULL;
+  }
+SSL_set_session_id_context(exim_client_ctx->ssl, sid_ctx, Ustrlen(sid_ctx));
+SSL_set_fd(exim_client_ctx->ssl, fd);
+SSL_set_connect_state(exim_client_ctx->ssl);
 
 if (ob->tls_sni)
   {
 
 if (ob->tls_sni)
   {
-  if (!expand_check(ob->tls_sni, US"tls_sni", &tls_out.sni, errstr))
-    return FAIL;
-  if (!tls_out.sni)
+  if (!expand_check(ob->tls_sni, US"tls_sni", &tlsp->sni, errstr))
+    return NULL;
+  if (!tlsp->sni)
     {
     DEBUG(D_tls) debug_printf("Setting TLS SNI forced to fail, not sending\n");
     }
     {
     DEBUG(D_tls) debug_printf("Setting TLS SNI forced to fail, not sending\n");
     }
-  else if (!Ustrlen(tls_out.sni))
-    tls_out.sni = NULL;
+  else if (!Ustrlen(tlsp->sni))
+    tlsp->sni = NULL;
   else
     {
 #ifdef EXIM_HAVE_OPENSSL_TLSEXT
   else
     {
 #ifdef EXIM_HAVE_OPENSSL_TLSEXT
-    DEBUG(D_tls) debug_printf("Setting TLS SNI \"%s\"\n", tls_out.sni);
-    SSL_set_tlsext_host_name(client_ssl, tls_out.sni);
+    DEBUG(D_tls) debug_printf("Setting TLS SNI \"%s\"\n", tlsp->sni);
+    SSL_set_tlsext_host_name(exim_client_ctx->ssl, tlsp->sni);
 #else
     log_write(0, LOG_MAIN, "SNI unusable with this OpenSSL library version; ignoring \"%s\"\n",
 #else
     log_write(0, LOG_MAIN, "SNI unusable with this OpenSSL library version; ignoring \"%s\"\n",
-          tls_out.sni);
+          tlsp->sni);
 #endif
     }
   }
 
 #ifdef SUPPORT_DANE
 if (tlsa_dnsa)
 #endif
     }
   }
 
 #ifdef SUPPORT_DANE
 if (tlsa_dnsa)
-  if ((rc = dane_tlsa_load(client_ssl, host, tlsa_dnsa, errstr)) != OK)
-    return rc;
+  if (dane_tlsa_load(exim_client_ctx->ssl, host, tlsa_dnsa, errstr) != OK)
+    return NULL;
 #endif
 
 #ifndef DISABLE_OCSP
 #endif
 
 #ifndef DISABLE_OCSP
@@ -2427,9 +2450,9 @@ if (request_ocsp)
 
 if (request_ocsp)
   {
 
 if (request_ocsp)
   {
-  SSL_set_tlsext_status_type(client_ssl, TLSEXT_STATUSTYPE_ocsp);
+  SSL_set_tlsext_status_type(exim_client_ctx->ssl, TLSEXT_STATUSTYPE_ocsp);
   client_static_cbinfo->u_ocsp.client.verify_required = require_ocsp;
   client_static_cbinfo->u_ocsp.client.verify_required = require_ocsp;
-  tls_out.ocsp = OCSP_NOT_RESP;
+  tlsp->ocsp = OCSP_NOT_RESP;
   }
 #endif
 
   }
 #endif
 
@@ -2442,33 +2465,36 @@ client_static_cbinfo->event_action = tb->event_action;
 DEBUG(D_tls) debug_printf("Calling SSL_connect\n");
 sigalrm_seen = FALSE;
 alarm(ob->command_timeout);
 DEBUG(D_tls) debug_printf("Calling SSL_connect\n");
 sigalrm_seen = FALSE;
 alarm(ob->command_timeout);
-rc = SSL_connect(client_ssl);
+rc = SSL_connect(exim_client_ctx->ssl);
 alarm(0);
 
 #ifdef SUPPORT_DANE
 if (tlsa_dnsa)
 alarm(0);
 
 #ifdef SUPPORT_DANE
 if (tlsa_dnsa)
-  DANESSL_cleanup(client_ssl);
+  DANESSL_cleanup(exim_client_ctx->ssl);
 #endif
 
 if (rc <= 0)
 #endif
 
 if (rc <= 0)
-  return tls_error(US"SSL_connect", host, sigalrm_seen ? US"timed out" : NULL,
-    errstr);
+  {
+  tls_error(US"SSL_connect", host, sigalrm_seen ? US"timed out" : NULL, errstr);
+  return NULL;
+  }
 
 DEBUG(D_tls) debug_printf("SSL_connect succeeded\n");
 
 
 DEBUG(D_tls) debug_printf("SSL_connect succeeded\n");
 
-peer_cert(client_ssl, &tls_out, peerdn, sizeof(peerdn));
+peer_cert(exim_client_ctx->ssl, tlsp, peerdn, sizeof(peerdn));
 
 
-construct_cipher_name(client_ssl, cipherbuf, sizeof(cipherbuf), &tls_out.bits);
-tls_out.cipher = cipherbuf;
+construct_cipher_name(exim_client_ctx->ssl, cipherbuf, sizeof(cipherbuf), &tlsp->bits);
+tlsp->cipher = cipherbuf;
 
 /* Record the certificate we presented */
   {
 
 /* Record the certificate we presented */
   {
-  X509 * crt = SSL_get_certificate(client_ssl);
-  tls_out.ourcert = crt ? X509_dup(crt) : NULL;
+  X509 * crt = SSL_get_certificate(exim_client_ctx->ssl);
+  tlsp->ourcert = crt ? X509_dup(crt) : NULL;
   }
 
   }
 
-tls_out.active = fd;
-return OK;
+tlsp->active.sock = fd;
+tlsp->active.tls_ctx = exim_client_ctx;
+return exim_client_ctx;
 }
 
 
 }
 
 
@@ -2503,53 +2529,55 @@ if (had_data_sigint)
 closed down, not that the socket itself has been closed down. Revert to
 non-SSL handling. */
 
 closed down, not that the socket itself has been closed down. Revert to
 non-SSL handling. */
 
-if (error == SSL_ERROR_ZERO_RETURN)
+switch(error)
   {
   {
-  DEBUG(D_tls) debug_printf("Got SSL_ERROR_ZERO_RETURN\n");
+  case SSL_ERROR_NONE:
+    break;
 
 
-  receive_getc = smtp_getc;
-  receive_getbuf = smtp_getbuf;
-  receive_get_cache = smtp_get_cache;
-  receive_ungetc = smtp_ungetc;
-  receive_feof = smtp_feof;
-  receive_ferror = smtp_ferror;
-  receive_smtp_buffered = smtp_buffered;
+  case SSL_ERROR_ZERO_RETURN:
+    DEBUG(D_tls) debug_printf("Got SSL_ERROR_ZERO_RETURN\n");
 
 
-  if (SSL_get_shutdown(server_ssl) == SSL_RECEIVED_SHUTDOWN)
-       SSL_shutdown(server_ssl);
+    receive_getc = smtp_getc;
+    receive_getbuf = smtp_getbuf;
+    receive_get_cache = smtp_get_cache;
+    receive_ungetc = smtp_ungetc;
+    receive_feof = smtp_feof;
+    receive_ferror = smtp_ferror;
+    receive_smtp_buffered = smtp_buffered;
 
 
-#ifndef DISABLE_OCSP
-  sk_X509_pop_free(server_static_cbinfo->verify_stack, X509_free);
-  server_static_cbinfo->verify_stack = NULL;
-#endif
-  SSL_free(server_ssl);
-  SSL_CTX_free(server_ctx);
-  server_ctx = NULL;
-  server_ssl = NULL;
-  tls_in.active = -1;
-  tls_in.bits = 0;
-  tls_in.cipher = NULL;
-  tls_in.peerdn = NULL;
-  tls_in.sni = NULL;
+    if (SSL_get_shutdown(server_ssl) == SSL_RECEIVED_SHUTDOWN)
+         SSL_shutdown(server_ssl);
 
 
-  return FALSE;
-  }
+#ifndef DISABLE_OCSP
+    sk_X509_pop_free(server_static_cbinfo->verify_stack, X509_free);
+    server_static_cbinfo->verify_stack = NULL;
+#endif
+    SSL_free(server_ssl);
+    SSL_CTX_free(server_ctx);
+    server_ctx = NULL;
+    server_ssl = NULL;
+    tls_in.active.sock = -1;
+    tls_in.active.tls_ctx = NULL;
+    tls_in.bits = 0;
+    tls_in.cipher = NULL;
+    tls_in.peerdn = NULL;
+    tls_in.sni = NULL;
 
 
-/* Handle genuine errors */
+    return FALSE;
 
 
-else if (error == SSL_ERROR_SSL)
-  {
-  ERR_error_string(ERR_get_error(), ssl_errstring);
-  log_write(0, LOG_MAIN, "TLS error (SSL_read): %s", ssl_errstring);
-  ssl_xfer_error = TRUE;
-  return FALSE;
-  }
+  /* Handle genuine errors */
+  case SSL_ERROR_SSL:
+    ERR_error_string(ERR_get_error(), ssl_errstring);
+    log_write(0, LOG_MAIN, "TLS error (SSL_read): %s", ssl_errstring);
+    ssl_xfer_error = TRUE;
+    return FALSE;
 
 
-else if (error != SSL_ERROR_NONE)
-  {
-  DEBUG(D_tls) debug_printf("Got SSL error %d\n", error);
-  ssl_xfer_error = TRUE;
-  return FALSE;
+  default:
+    DEBUG(D_tls) debug_printf("Got SSL error %d\n", error);
+    DEBUG(D_tls) if (error == SSL_ERROR_SYSCALL)
+      debug_printf(" - syscall %s\n", strerror(errno));
+    ssl_xfer_error = TRUE;
+    return FALSE;
   }
 
 #ifndef DISABLE_DKIM
   }
 
 #ifndef DISABLE_DKIM
@@ -2633,6 +2661,7 @@ return ssl_xfer_buffer_lwm < ssl_xfer_buffer_hwm || SSL_pending(server_ssl) > 0;
 
 /*
 Arguments:
 
 /*
 Arguments:
+  ct_ctx    client context pointer, or NULL for the one global server context
   buff      buffer of data
   len       size of buffer
 
   buff      buffer of data
   len       size of buffer
 
@@ -2643,9 +2672,9 @@ Only used by the client-side TLS.
 */
 
 int
 */
 
 int
-tls_read(BOOL is_server, uschar *buff, size_t len)
+tls_read(void * ct_ctx, uschar *buff, size_t len)
 {
 {
-SSL *ssl = is_server ? server_ssl : client_ssl;
+SSL * ssl = ct_ctx ? ((exim_openssl_client_tls_ctx *)ct_ctx)->ssl : server_ssl;
 int inbytes;
 int error;
 
 int inbytes;
 int error;
 
@@ -2676,7 +2705,7 @@ return inbytes;
 
 /*
 Arguments:
 
 /*
 Arguments:
-  is_server channel specifier
+  ct_ctx    client context pointer, or NULL for the one global server context
   buff      buffer of data
   len       number of bytes
   more     further data expected soon
   buff      buffer of data
   len       number of bytes
   more     further data expected soon
@@ -2688,10 +2717,10 @@ Used by both server-side and client-side TLS.
 */
 
 int
 */
 
 int
-tls_write(BOOL is_server, const uschar *buff, size_t len, BOOL more)
+tls_write(void * ct_ctx, const uschar *buff, size_t len, BOOL more)
 {
 int outbytes, error, left;
 {
 int outbytes, error, left;
-SSL *ssl = is_server ? server_ssl : client_ssl;
+SSL * ssl = ct_ctx ? ((exim_openssl_client_tls_ctx *)ct_ctx)->ssl : server_ssl;
 static gstring * corked = NULL;
 
 DEBUG(D_tls) debug_printf("%s(%p, %lu%s)\n", __FUNCTION__,
 static gstring * corked = NULL;
 
 DEBUG(D_tls) debug_printf("%s(%p, %lu%s)\n", __FUNCTION__,
@@ -2702,7 +2731,7 @@ DEBUG(D_tls) debug_printf("%s(%p, %lu%s)\n", __FUNCTION__,
 one stream does it, in one context (i.e. no store reset).  Currently it is used
 for the responses to the received SMTP MAIL , RCPT, DATA sequence, only. */
 
 one stream does it, in one context (i.e. no store reset).  Currently it is used
 for the responses to the received SMTP MAIL , RCPT, DATA sequence, only. */
 
-if (is_server && (more || corked))
+if (!ct_ctx && (more || corked))
   {
   corked = string_catn(corked, buff, len);
   if (more)
   {
   corked = string_catn(corked, buff, len);
   if (more)
@@ -2714,7 +2743,7 @@ if (is_server && (more || corked))
 
 for (left = len; left > 0;)
   {
 
 for (left = len; left > 0;)
   {
-  DEBUG(D_tls) debug_printf("SSL_write(SSL, %p, %d)\n", buff, left);
+  DEBUG(D_tls) debug_printf("SSL_write(%p, %p, %d)\n", ssl, buff, left);
   outbytes = SSL_write(ssl, CS buff, left);
   error = SSL_get_error(ssl, outbytes);
   DEBUG(D_tls) debug_printf("outbytes=%d error=%d\n", outbytes, error);
   outbytes = SSL_write(ssl, CS buff, left);
   error = SSL_get_error(ssl, outbytes);
   DEBUG(D_tls) debug_printf("outbytes=%d error=%d\n", outbytes, error);
@@ -2759,6 +2788,7 @@ daemon, to shut down the TLS library, without actually doing a shutdown (which
 would tamper with the SSL session in the parent process).
 
 Arguments:
 would tamper with the SSL session in the parent process).
 
 Arguments:
+  ct_ctx       client TLS context pointer, or NULL for the one global server context
   shutdown     1 if TLS close-alert is to be sent,
                2 if also response to be waited for
 
   shutdown     1 if TLS close-alert is to be sent,
                2 if also response to be waited for
 
@@ -2768,11 +2798,12 @@ Used by both server-side and client-side TLS.
 */
 
 void
 */
 
 void
-tls_close(BOOL is_server, int shutdown)
+tls_close(void * ct_ctx, int shutdown)
 {
 {
-SSL_CTX **ctxp = is_server ? &server_ctx : &client_ctx;
-SSL **sslp = is_server ? &server_ssl : &client_ssl;
-int *fdp = is_server ? &tls_in.active : &tls_out.active;
+exim_openssl_client_tls_ctx * o_ctx = ct_ctx;
+SSL_CTX **ctxp = o_ctx ? &o_ctx->ctx : &server_ctx;
+SSL **sslp =     o_ctx ? &o_ctx->ssl : &server_ssl;
+int *fdp = o_ctx ? &tls_out.active.sock : &tls_in.active.sock;
 
 if (*fdp < 0) return;  /* TLS was not active */
 
 
 if (*fdp < 0) return;  /* TLS was not active */
 
@@ -2798,7 +2829,7 @@ if (shutdown)
   }
 
 #ifndef DISABLE_OCSP
   }
 
 #ifndef DISABLE_OCSP
-if (is_server)
+if (!o_ctx)            /* server side */
   {
   sk_X509_pop_free(server_static_cbinfo->verify_stack, X509_free);
   server_static_cbinfo->verify_stack = NULL;
   {
   sk_X509_pop_free(server_static_cbinfo->verify_stack, X509_free);
   server_static_cbinfo->verify_stack = NULL;
index a2da32159fe93f9982b2556e31d5aeabee0ee8c8..d5ff7380b01d66fbff5281cd43c4992463a87821 100644 (file)
@@ -242,7 +242,7 @@ for (i = 0; i < 100; i++)
     {
     rc =
 #ifdef SUPPORT_TLS
     {
     rc =
 #ifdef SUPPORT_TLS
-       tls_out.active == fd ? tls_write(FALSE, block, len, more) :
+       tls_out.active.sock == fd ? tls_write(tls_out.active.tls_ctx, block, len, more) :
 #endif
 #ifdef MSG_MORE
        more && !(tctx->options & topt_not_socket)
 #endif
 #ifdef MSG_MORE
        more && !(tctx->options & topt_not_socket)
@@ -260,7 +260,7 @@ for (i = 0; i < 100; i++)
 
     rc =
 #ifdef SUPPORT_TLS
 
     rc =
 #ifdef SUPPORT_TLS
-       tls_out.active == fd ? tls_write(FALSE, block, len, more) :
+       tls_out.active.sock == fd ? tls_write(tls_out.active.tls_ctx, block, len, more) :
 #endif
 #ifdef MSG_MORE
        more && !(tctx->options & topt_not_socket)
 #endif
 #ifdef MSG_MORE
        more && !(tctx->options & topt_not_socket)
@@ -1075,7 +1075,7 @@ dkim signing,  when we had CHUNKING input.  */
 if (  spool_file_wireformat
    && !(tctx->options & (topt_no_body | topt_end_dot))
    && !nl_check_length
 if (  spool_file_wireformat
    && !(tctx->options & (topt_no_body | topt_end_dot))
    && !nl_check_length
-   && tls_out.active != tctx->u.fd
+   && tls_out.active.sock != tctx->u.fd
    )
   {
   ssize_t copied = 0;
    )
   {
   ssize_t copied = 0;
@@ -1877,12 +1877,12 @@ if (smtp_peer_options & OPTION_PIPE)            argv[i++] = US"-MCP";
 if (smtp_peer_options & OPTION_SIZE)           argv[i++] = US"-MCS";
 #ifdef SUPPORT_TLS
 if (smtp_peer_options & OPTION_TLS)
 if (smtp_peer_options & OPTION_SIZE)           argv[i++] = US"-MCS";
 #ifdef SUPPORT_TLS
 if (smtp_peer_options & OPTION_TLS)
-  if (tls_out.active >= 0 || continue_proxy_cipher)
+  if (tls_out.active.sock >= 0 || continue_proxy_cipher)
     {
     argv[i++] = US"-MCt";
     argv[i++] = sending_ip_address;
     argv[i++] = string_sprintf("%d", sending_port);
     {
     argv[i++] = US"-MCt";
     argv[i++] = sending_ip_address;
     argv[i++] = string_sprintf("%d", sending_port);
-    argv[i++] = tls_out.active >= 0 ? tls_out.cipher : continue_proxy_cipher;
+    argv[i++] = tls_out.active.sock >= 0 ? tls_out.cipher : continue_proxy_cipher;
     }
   else
     argv[i++] = US"-MCT";
     }
   else
     argv[i++] = US"-MCT";
index 6d7085881d76c88fe8d6fb681ebaf5f1f7da0130..1f0256f3d694d5fc883be76b1dc9e7b591e55ad6 100644 (file)
@@ -1646,11 +1646,13 @@ if (!continue_hostname)
 
   /* Make the TCP connection */
 
 
   /* Make the TCP connection */
 
-  sx->inblock.sock = sx->outblock.sock =
+  sx->cctx.sock =
     smtp_connect(sx->host, sx->host_af, sx->interface,
                  sx->ob->connect_timeout, sx->tblock);
     smtp_connect(sx->host, sx->host_af, sx->interface,
                  sx->ob->connect_timeout, sx->tblock);
+  sx->cctx.tls_ctx = NULL;
+  sx->inblock.cctx = sx->outblock.cctx = &sx->cctx;
 
 
-  if (sx->inblock.sock < 0)
+  if (sx->cctx.sock < 0)
     {
     uschar * msg = NULL;
     if (sx->verify)
     {
     uschar * msg = NULL;
     if (sx->verify)
@@ -1703,7 +1705,7 @@ if (!continue_hostname)
     BOOL good_response;
 
 #ifdef TCP_QUICKACK
     BOOL good_response;
 
 #ifdef TCP_QUICKACK
-    (void) setsockopt(sx->inblock.sock, IPPROTO_TCP, TCP_QUICKACK, US &off, sizeof(off));
+    (void) setsockopt(sx->cctx.sock, IPPROTO_TCP, TCP_QUICKACK, US &off, sizeof(off));
 #endif
     good_response = smtp_read_response(&sx->inblock, sx->buffer, sizeof(sx->buffer),
       '2', sx->ob->command_timeout);
 #endif
     good_response = smtp_read_response(&sx->inblock, sx->buffer, sizeof(sx->buffer),
       '2', sx->ob->command_timeout);
@@ -1882,16 +1884,18 @@ separate - we could match up by host ip+port as a bodge. */
 
 else
   {
 
 else
   {
-  if (cutthrough.fd >= 0 && cutthrough.callout_hold_only)
+  if (cutthrough.cctx.sock >= 0 && cutthrough.callout_hold_only)
     {
     {
-    sx->inblock.sock = sx->outblock.sock = cutthrough.fd;
+    sx->cctx = cutthrough.cctx;
     sx->host->port = sx->port = cutthrough.host.port;
     }
   else
     {
     sx->host->port = sx->port = cutthrough.host.port;
     }
   else
     {
-    sx->inblock.sock = sx->outblock.sock = 0;  /* stdin */
+    sx->cctx.sock = 0;                         /* stdin */
+    sx->cctx.tls_ctx = NULL;
     smtp_port_for_connect(sx->host, sx->port); /* Record the port that was used */
     }
     smtp_port_for_connect(sx->host, sx->port); /* Record the port that was used */
     }
+  sx->inblock.cctx = sx->outblock.cctx = &sx->cctx;
   smtp_command = big_buffer;
   sx->helo_data = NULL;                /* ensure we re-expand ob->helo_data */
 
   smtp_command = big_buffer;
   sx->helo_data = NULL;                /* ensure we re-expand ob->helo_data */
 
@@ -1899,7 +1903,8 @@ else
   held-open verify connection with TLS, nothing more to do. */
 
   if (  continue_proxy_cipher
   held-open verify connection with TLS, nothing more to do. */
 
   if (  continue_proxy_cipher
-     || (cutthrough.fd >= 0 && cutthrough.callout_hold_only && cutthrough.is_tls)
+     || (cutthrough.cctx.sock >= 0 && cutthrough.callout_hold_only
+         && cutthrough.is_tls)
      )
     {
     sx->peer_offered = smtp_peer_options;
      )
     {
     sx->peer_offered = smtp_peer_options;
@@ -1959,18 +1964,19 @@ if (  smtp_peer_options & OPTION_TLS
     {
     address_item * addr;
     uschar * errstr;
     {
     address_item * addr;
     uschar * errstr;
-    int rc = tls_client_start(sx->inblock.sock, sx->host, sx->addrlist, sx->tblock,
+    sx->cctx.tls_ctx = tls_client_start(sx->cctx.sock, sx->host,
+                           sx->addrlist, sx->tblock,
 # ifdef SUPPORT_DANE
                             sx->dane ? &tlsa_dnsa : NULL,
 # endif
 # ifdef SUPPORT_DANE
                             sx->dane ? &tlsa_dnsa : NULL,
 # endif
-                            &errstr);
+                            &tls_out, &errstr);
 
 
-    /* TLS negotiation failed; give an error. From outside, this function may
-    be called again to try in clear on a new connection, if the options permit
-    it for this host. */
-
-    if (rc != OK)
+    if (!sx->cctx.tls_ctx)
       {
       {
+      /* TLS negotiation failed; give an error. From outside, this function may
+      be called again to try in clear on a new connection, if the options permit
+      it for this host. */
+
 # ifdef SUPPORT_DANE
       if (sx->dane)
         {
 # ifdef SUPPORT_DANE
       if (sx->dane)
         {
@@ -2013,7 +2019,7 @@ another process, and so we won't have expanded helo_data above. We have to
 expand it here. $sending_ip_address and $sending_port are set up right at the
 start of the Exim process (in exim.c). */
 
 expand it here. $sending_ip_address and $sending_port are set up right at the
 start of the Exim process (in exim.c). */
 
-if (tls_out.active >= 0)
+if (tls_out.active.sock >= 0)
   {
   char *greeting_cmd;
   BOOL good_response;
   {
   char *greeting_cmd;
   BOOL good_response;
@@ -2091,7 +2097,7 @@ we skip this. */
 
 if (continue_hostname == NULL
 #ifdef SUPPORT_TLS
 
 if (continue_hostname == NULL
 #ifdef SUPPORT_TLS
-    || tls_out.active >= 0
+    || tls_out.active.sock >= 0
 #endif
     )
   {
 #endif
     )
   {
@@ -2270,7 +2276,11 @@ if (sx->send_quit)
   (void)smtp_write_command(&sx->outblock, SCMD_FLUSH, "QUIT\r\n");
 
 #ifdef SUPPORT_TLS
   (void)smtp_write_command(&sx->outblock, SCMD_FLUSH, "QUIT\r\n");
 
 #ifdef SUPPORT_TLS
-tls_close(FALSE, TLS_SHUTDOWN_NOWAIT);
+if (sx->cctx.tls_ctx)
+  {
+  tls_close(sx->cctx.tls_ctx, TLS_SHUTDOWN_NOWAIT);
+  sx->cctx.tls_ctx = NULL;
+  }
 #endif
 
 /* Close the socket, and return the appropriate value, first setting
 #endif
 
 /* Close the socket, and return the appropriate value, first setting
@@ -2281,14 +2291,14 @@ remote_max_parallel is forced to 1 when delivering over an existing connection,
 HDEBUG(D_transport|D_acl|D_v) debug_printf_indent("  SMTP(close)>>\n");
 if (sx->send_quit)
   {
 HDEBUG(D_transport|D_acl|D_v) debug_printf_indent("  SMTP(close)>>\n");
 if (sx->send_quit)
   {
-  shutdown(sx->outblock.sock, SHUT_WR);
-  if (fcntl(sx->inblock.sock, F_SETFL, O_NONBLOCK) == 0)
-    for (rc = 16; read(sx->inblock.sock, sx->inbuffer, sizeof(sx->inbuffer)) > 0 && rc > 0;)
+  shutdown(sx->cctx.sock, SHUT_WR);
+  if (fcntl(sx->cctx.sock, F_SETFL, O_NONBLOCK) == 0)
+    for (rc = 16; read(sx->cctx.sock, sx->inbuffer, sizeof(sx->inbuffer)) > 0 && rc > 0;)
       rc--;                            /* drain socket */
   sx->send_quit = FALSE;
   }
       rc--;                            /* drain socket */
   sx->send_quit = FALSE;
   }
-(void)close(sx->inblock.sock);
-sx->inblock.sock = sx->outblock.sock = -1;
+(void)close(sx->cctx.sock);
+sx->cctx.sock = -1;
 
 #ifndef DISABLE_EVENT
 (void) event_raise(sx->tblock->event_action, US"tcp:close", NULL);
 
 #ifndef DISABLE_EVENT
 (void) event_raise(sx->tblock->event_action, US"tcp:close", NULL);
@@ -2624,6 +2634,7 @@ Do blocking full-size writes, and reads under a timeout.  Once both input
 channels are closed, exit the process.
 
 Arguments:
 channels are closed, exit the process.
 
 Arguments:
+  ct_ctx       tls context
   buf          space to use for buffering
   bufsiz       size of buffer
   pfd          pipe filedescriptor array; [0] is comms to proxied process
   buf          space to use for buffering
   bufsiz       size of buffer
   pfd          pipe filedescriptor array; [0] is comms to proxied process
@@ -2631,10 +2642,11 @@ Arguments:
 */
 
 void
 */
 
 void
-smtp_proxy_tls(uschar * buf, size_t bsize, int * pfd, int timeout)
+smtp_proxy_tls(void * ct_ctx, uschar * buf, size_t bsize, int * pfd,
+  int timeout)
 {
 fd_set rfds, efds;
 {
 fd_set rfds, efds;
-int max_fd = MAX(pfd[0], tls_out.active) + 1;
+int max_fd = MAX(pfd[0], tls_out.active.sock) + 1;
 int rc, i, fd_bits, nbytes;
 
 close(pfd[1]);
 int rc, i, fd_bits, nbytes;
 
 close(pfd[1]);
@@ -2647,7 +2659,7 @@ if ((rc = fork()))
 if (running_in_test_harness) millisleep(100); /* let parent debug out */
 set_process_info("proxying TLS connection for continued transport");
 FD_ZERO(&rfds);
 if (running_in_test_harness) millisleep(100); /* let parent debug out */
 set_process_info("proxying TLS connection for continued transport");
 FD_ZERO(&rfds);
-FD_SET(tls_out.active, &rfds);
+FD_SET(tls_out.active.sock, &rfds);
 FD_SET(pfd[0], &rfds);
 
 for (fd_bits = 3; fd_bits; )
 FD_SET(pfd[0], &rfds);
 
 for (fd_bits = 3; fd_bits; )
@@ -2673,21 +2685,21 @@ for (fd_bits = 3; fd_bits; )
       goto done;
       }
 
       goto done;
       }
 
-    if (FD_ISSET(tls_out.active, &efds) || FD_ISSET(pfd[0], &efds))
+    if (FD_ISSET(tls_out.active.sock, &efds) || FD_ISSET(pfd[0], &efds))
       {
       DEBUG(D_transport) debug_printf("select: exceptional cond on %s fd\n",
        FD_ISSET(pfd[0], &efds) ? "proxy" : "tls");
       goto done;
       }
     }
       {
       DEBUG(D_transport) debug_printf("select: exceptional cond on %s fd\n",
        FD_ISSET(pfd[0], &efds) ? "proxy" : "tls");
       goto done;
       }
     }
-  while (rc < 0 || !(FD_ISSET(tls_out.active, &rfds) || FD_ISSET(pfd[0], &rfds)));
+  while (rc < 0 || !(FD_ISSET(tls_out.active.sock, &rfds) || FD_ISSET(pfd[0], &rfds)));
 
   /* handle inbound data */
 
   /* handle inbound data */
-  if (FD_ISSET(tls_out.active, &rfds))
-    if ((rc = tls_read(FALSE, buf, bsize)) <= 0)
+  if (FD_ISSET(tls_out.active.sock, &rfds))
+    if ((rc = tls_read(ct_ctx, buf, bsize)) <= 0)
       {
       fd_bits &= ~1;
       {
       fd_bits &= ~1;
-      FD_CLR(tls_out.active, &rfds);
+      FD_CLR(tls_out.active.sock, &rfds);
       shutdown(pfd[0], SHUT_WR);
       timeout = 5;
       }
       shutdown(pfd[0], SHUT_WR);
       timeout = 5;
       }
@@ -2697,19 +2709,19 @@ for (fd_bits = 3; fd_bits; )
        if ((i = write(pfd[0], buf + nbytes, rc - nbytes)) < 0) goto done;
       }
   else if (fd_bits & 1)
        if ((i = write(pfd[0], buf + nbytes, rc - nbytes)) < 0) goto done;
       }
   else if (fd_bits & 1)
-    FD_SET(tls_out.active, &rfds);
+    FD_SET(tls_out.active.sock, &rfds);
 
   /* handle outbound data */
   if (FD_ISSET(pfd[0], &rfds))
     if ((rc = read(pfd[0], buf, bsize)) <= 0)
       {
       fd_bits = 0;
 
   /* handle outbound data */
   if (FD_ISSET(pfd[0], &rfds))
     if ((rc = read(pfd[0], buf, bsize)) <= 0)
       {
       fd_bits = 0;
-      tls_close(FALSE, TLS_SHUTDOWN_NOWAIT);
+      tls_close(ct_ctx, TLS_SHUTDOWN_NOWAIT);
       }
     else
       {
       for (nbytes = 0; rc - nbytes > 0; nbytes += i)
       }
     else
       {
       for (nbytes = 0; rc - nbytes > 0; nbytes += i)
-       if ((i = tls_write(FALSE, buf + nbytes, rc - nbytes, FALSE)) < 0)
+       if ((i = tls_write(ct_ctx, buf + nbytes, rc - nbytes, FALSE)) < 0)
          goto done;
       }
   else if (fd_bits & 2)
          goto done;
       }
   else if (fd_bits & 2)
@@ -2952,7 +2964,7 @@ if (!(sx.peer_offered & OPTION_CHUNKING) && !sx.ok)
 else
   {
   transport_ctx tctx = {
 else
   {
   transport_ctx tctx = {
-    {sx.inblock.sock},
+    {sx.cctx.sock},            /*XXX will this need TLS info? */
     tblock,
     addrlist,
     US".", US"..",    /* Escaping strings */
     tblock,
     addrlist,
     US".", US"..",    /* Escaping strings */
@@ -3465,7 +3477,7 @@ if (sx.completed_addr && sx.ok && sx.send_quit)
      || continue_more
      || (
 #ifdef SUPPORT_TLS
      || continue_more
      || (
 #ifdef SUPPORT_TLS
-          (  tls_out.active < 0  &&  !continue_proxy_cipher
+          (  tls_out.active.sock < 0  &&  !continue_proxy_cipher
            || verify_check_given_host(&sx.ob->hosts_nopass_tls, host) != OK
           )
         &&
            || verify_check_given_host(&sx.ob->hosts_nopass_tls, host) != OK
           )
         &&
@@ -3503,7 +3515,7 @@ if (sx.completed_addr && sx.ok && sx.send_quit)
     if (sx.ok)
       {
       int pfd[2];
     if (sx.ok)
       {
       int pfd[2];
-      int socket_fd = sx.inblock.sock;
+      int socket_fd = sx.cctx.sock;
 
 
       if (sx.first_addr != NULL)         /* More addresses still to be sent */
 
 
       if (sx.first_addr != NULL)         /* More addresses still to be sent */
@@ -3518,7 +3530,7 @@ if (sx.completed_addr && sx.ok && sx.send_quit)
       the connection still open. */
 
 #ifdef SUPPORT_TLS
       the connection still open. */
 
 #ifdef SUPPORT_TLS
-      if (tls_out.active >= 0)
+      if (tls_out.active.sock >= 0)
        if (  continue_more
           || verify_check_given_host(&sx.ob->hosts_noproxy_tls, host) == OK)
          {
        if (  continue_more
           || verify_check_given_host(&sx.ob->hosts_noproxy_tls, host) == OK)
          {
@@ -3528,7 +3540,7 @@ if (sx.completed_addr && sx.ok && sx.send_quit)
          a new EHLO. If we don't get a good response, we don't attempt to pass
          the socket on. */
 
          a new EHLO. If we don't get a good response, we don't attempt to pass
          the socket on. */
 
-         tls_close(FALSE, TLS_SHUTDOWN_WAIT);
+         tls_close(sx.cctx.tls_ctx, TLS_SHUTDOWN_WAIT);
          smtp_peer_options = smtp_peer_options_wrap;
          sx.ok = !sx.smtps
            && smtp_write_command(&sx.outblock, SCMD_FLUSH,
          smtp_peer_options = smtp_peer_options_wrap;
          sx.ok = !sx.smtps
            && smtp_write_command(&sx.outblock, SCMD_FLUSH,
@@ -3576,14 +3588,14 @@ propagate it from the initial
        get logging done asap.  Which way to place the work makes assumptions
        about post-fork prioritisation which may not hold on all platforms. */
 #ifdef SUPPORT_TLS
        get logging done asap.  Which way to place the work makes assumptions
        about post-fork prioritisation which may not hold on all platforms. */
 #ifdef SUPPORT_TLS
-       if (tls_out.active >= 0)
+       if (tls_out.active.sock >= 0)
          {
          int pid = fork();
          if (pid == 0)         /* child; fork again to disconnect totally */
            {
            if (running_in_test_harness) millisleep(100); /* let parent debug out */
            /* does not return */
          {
          int pid = fork();
          if (pid == 0)         /* child; fork again to disconnect totally */
            {
            if (running_in_test_harness) millisleep(100); /* let parent debug out */
            /* does not return */
-           smtp_proxy_tls(sx.buffer, sizeof(sx.buffer), pfd,
+           smtp_proxy_tls(sx.cctx.tls_ctx, sx.buffer, sizeof(sx.buffer), pfd,
                            sx.ob->command_timeout);
            }
 
                            sx.ob->command_timeout);
            }
 
@@ -3593,8 +3605,10 @@ propagate it from the initial
            close(pfd[0]);
            /* tidy the inter-proc to disconn the proxy proc */
            waitpid(pid, NULL, 0);
            close(pfd[0]);
            /* tidy the inter-proc to disconn the proxy proc */
            waitpid(pid, NULL, 0);
-           tls_close(FALSE, TLS_NO_SHUTDOWN);
-           (void)close(sx.inblock.sock);
+           tls_close(sx.cctx.tls_ctx, TLS_NO_SHUTDOWN);
+           sx.cctx.tls_ctx = NULL;
+           (void)close(sx.cctx.sock);
+           sx.cctx.sock = -1;
            continue_transport = NULL;
            continue_hostname = NULL;
            return yield;
            continue_transport = NULL;
            continue_hostname = NULL;
            return yield;
@@ -3639,7 +3653,7 @@ if (sx.send_quit) (void)smtp_write_command(&sx.outblock, SCMD_FLUSH, "QUIT\r\n")
 END_OFF:
 
 #ifdef SUPPORT_TLS
 END_OFF:
 
 #ifdef SUPPORT_TLS
-tls_close(FALSE, TLS_SHUTDOWN_NOWAIT);
+tls_close(sx.cctx.tls_ctx, TLS_SHUTDOWN_NOWAIT);
 #endif
 
 /* Close the socket, and return the appropriate value, first setting
 #endif
 
 /* Close the socket, and return the appropriate value, first setting
@@ -3655,12 +3669,12 @@ case continue_more won't get set. */
 HDEBUG(D_transport|D_acl|D_v) debug_printf_indent("  SMTP(close)>>\n");
 if (sx.send_quit)
   {
 HDEBUG(D_transport|D_acl|D_v) debug_printf_indent("  SMTP(close)>>\n");
 if (sx.send_quit)
   {
-  shutdown(sx.outblock.sock, SHUT_WR);
-  if (fcntl(sx.inblock.sock, F_SETFL, O_NONBLOCK) == 0)
-    for (rc = 16; read(sx.inblock.sock, sx.inbuffer, sizeof(sx.inbuffer)) > 0 && rc > 0;)
+  shutdown(sx.cctx.sock, SHUT_WR);
+  if (fcntl(sx.cctx.sock, F_SETFL, O_NONBLOCK) == 0)
+    for (rc = 16; read(sx.cctx.sock, sx.inbuffer, sizeof(sx.inbuffer)) > 0 && rc > 0;)
       rc--;                            /* drain socket */
   }
       rc--;                            /* drain socket */
   }
-(void)close(sx.inblock.sock);
+(void)close(sx.cctx.sock);
 
 #ifndef DISABLE_EVENT
 (void) event_raise(tblock->event_action, US"tcp:close", NULL);
 
 #ifndef DISABLE_EVENT
 (void) event_raise(tblock->event_action, US"tcp:close", NULL);
@@ -3696,19 +3710,24 @@ smtp_transport_closedown(transport_instance *tblock)
 {
 smtp_transport_options_block *ob =
   (smtp_transport_options_block *)tblock->options_block;
 {
 smtp_transport_options_block *ob =
   (smtp_transport_options_block *)tblock->options_block;
+client_conn_ctx cctx;
 smtp_inblock inblock;
 smtp_outblock outblock;
 uschar buffer[256];
 uschar inbuffer[4096];
 uschar outbuffer[16];
 
 smtp_inblock inblock;
 smtp_outblock outblock;
 uschar buffer[256];
 uschar inbuffer[4096];
 uschar outbuffer[16];
 
-inblock.sock = fileno(stdin);
+/*XXX really we need an active-smtp-client ctx, rather than assuming stdout */
+cctx.sock = fileno(stdin);
+cctx.tls_ctx = cctx.sock == tls_out.active.sock ? tls_out.active.tls_ctx : NULL;
+
+inblock.cctx = &cctx;
 inblock.buffer = inbuffer;
 inblock.buffersize = sizeof(inbuffer);
 inblock.ptr = inbuffer;
 inblock.ptrend = inbuffer;
 
 inblock.buffer = inbuffer;
 inblock.buffersize = sizeof(inbuffer);
 inblock.ptr = inbuffer;
 inblock.ptrend = inbuffer;
 
-outblock.sock = inblock.sock;
+outblock.cctx = &cctx;
 outblock.buffersize = sizeof(outbuffer);
 outblock.buffer = outbuffer;
 outblock.ptr = outbuffer;
 outblock.buffersize = sizeof(outbuffer);
 outblock.buffer = outbuffer;
 outblock.ptr = outbuffer;
@@ -3718,7 +3737,7 @@ outblock.authenticating = FALSE;
 (void)smtp_write_command(&outblock, SCMD_FLUSH, "QUIT\r\n");
 (void)smtp_read_response(&inblock, buffer, sizeof(buffer), '2',
   ob->command_timeout);
 (void)smtp_write_command(&outblock, SCMD_FLUSH, "QUIT\r\n");
 (void)smtp_read_response(&inblock, buffer, sizeof(buffer), '2',
   ob->command_timeout);
-(void)close(inblock.sock);
+(void)close(cctx.sock);
 }
 
 
 }
 
 
@@ -3819,7 +3838,7 @@ DEBUG(D_transport)
   if (continue_hostname)
     debug_printf("already connected to %s [%s] (on fd %d)\n",
       continue_hostname, continue_host_address,
   if (continue_hostname)
     debug_printf("already connected to %s [%s] (on fd %d)\n",
       continue_hostname, continue_host_address,
-      cutthrough.fd >= 0 ? cutthrough.fd : 0);
+      cutthrough.cctx.sock >= 0 ? cutthrough.cctx.sock : 0);
   }
 
 /* Set the flag requesting that these hosts be added to the waiting
   }
 
 /* Set the flag requesting that these hosts be added to the waiting
@@ -4604,21 +4623,27 @@ retry_non_continued:
 
   if (continue_hostname && !continue_host_tried)
     {
 
   if (continue_hostname && !continue_host_tried)
     {
-    int fd = cutthrough.fd >= 0 ? cutthrough.fd : 0;
+    int fd = cutthrough.cctx.sock >= 0 ? cutthrough.cctx.sock : 0;
 
     DEBUG(D_transport) debug_printf("no hosts match already-open connection\n");
 #ifdef SUPPORT_TLS
 
     DEBUG(D_transport) debug_printf("no hosts match already-open connection\n");
 #ifdef SUPPORT_TLS
-    if (tls_out.active == fd)
+    /* A TLS conn could be open for a cutthrough, but not for a plain continued-
+    transport */
+/*XXX doublecheck that! */
+
+    if (cutthrough.cctx.sock >= 0 && cutthrough.is_tls)
       {
       {
-      (void) tls_write(FALSE, US"QUIT\r\n", 6, FALSE);
-      tls_close(FALSE, TLS_SHUTDOWN_NOWAIT);
+      (void) tls_write(cutthrough.cctx.tls_ctx, US"QUIT\r\n", 6, FALSE);
+      tls_close(cutthrough.cctx.tls_ctx, TLS_SHUTDOWN_NOWAIT);
+      cutthrough.cctx.tls_ctx = NULL;
+      cutthrough.is_tls = FALSE;
       }
     else
 #else
       (void) write(fd, US"QUIT\r\n", 6);
 #endif
     (void) close(fd);
       }
     else
 #else
       (void) write(fd, US"QUIT\r\n", 6);
 #endif
     (void) close(fd);
-    cutthrough.fd = -1;
+    cutthrough.cctx.sock = -1;
     continue_hostname = NULL;
     goto retry_non_continued;
     }
     continue_hostname = NULL;
     goto retry_non_continued;
     }
index 7727c0c6db5d20cb108c276f92b5ecd1db9205d3..a33ef437f7940bf8a143a12d2b6230734f3b7f82 100644 (file)
@@ -146,8 +146,9 @@ typedef struct {
   address_item *       next_addr;
   address_item *       sync_addr;
 
   address_item *       next_addr;
   address_item *       sync_addr;
 
-  smtp_inblock  inblock;
-  smtp_outblock outblock;
+  client_conn_ctx      cctx;
+  smtp_inblock         inblock;
+  smtp_outblock                outblock;
   uschar       buffer[DELIVER_BUFFER_SIZE];
   uschar       inbuffer[4096];
   uschar       outbuffer[4096];
   uschar       buffer[DELIVER_BUFFER_SIZE];
   uschar       inbuffer[4096];
   uschar       outbuffer[4096];
index 5d0551e89c5ea8ac87c75eb37b6c68955b69ed32..c7c769a164746d22eb372e1bdca4c6d57519af19 100644 (file)
@@ -39,7 +39,7 @@ static tree_node *dnsbl_cache = NULL;
 #define MT_NOT 1
 #define MT_ALL 2
 
 #define MT_NOT 1
 #define MT_ALL 2
 
-static uschar cutthrough_response(int, char, uschar **, int);
+static uschar cutthrough_response(client_conn_ctx *, char, uschar **, int);
 
 
 
 
 
 
@@ -410,10 +410,11 @@ if (addr->transport == cutthrough.addr.transport)
 
        /* Match!  Send the RCPT TO, set done from the response */
        done =
 
        /* Match!  Send the RCPT TO, set done from the response */
        done =
-         smtp_write_command(&ctblock, SCMD_FLUSH, "RCPT TO:<%.1000s>\r\n",
-           transport_rcpt_address(addr,
-              addr->transport->rcpt_include_affixes)) >= 0 &&
-         cutthrough_response(cutthrough.fd, '2', &resp, CUTTHROUGH_DATA_TIMEOUT) == '2';
+            smtp_write_command(&ctblock, SCMD_FLUSH, "RCPT TO:<%.1000s>\r\n",
+             transport_rcpt_address(addr,
+                addr->transport->rcpt_include_affixes)) >= 0
+         && cutthrough_response(&cutthrough.cctx, '2', &resp,
+             CUTTHROUGH_DATA_TIMEOUT) == '2';
 
        /* This would go horribly wrong if a callout fail was ignored by ACL.
        We punt by abandoning cutthrough on a reject, like the
 
        /* This would go horribly wrong if a callout fail was ignored by ACL.
        We punt by abandoning cutthrough on a reject, like the
@@ -612,7 +613,7 @@ that conn for verification purposes (and later delivery also).  Simplest
 coding means skipping this whole loop and doing the append separately.  */
 
   /* Can we re-use an open cutthrough connection? */
 coding means skipping this whole loop and doing the append separately.  */
 
   /* Can we re-use an open cutthrough connection? */
-  if (  cutthrough.fd >= 0
+  if (  cutthrough.cctx.sock >= 0
      && (options & (vopt_callout_recipsender | vopt_callout_recippmaster))
        == vopt_callout_recipsender
      && !random_local_part
      && (options & (vopt_callout_recipsender | vopt_callout_recippmaster))
        == vopt_callout_recipsender
      && !random_local_part
@@ -823,11 +824,11 @@ tls_retry_connection:
              debug_printf_indent("problem after random/rset/mfrom; reopen conn\n");
            random_local_part = NULL;
 #ifdef SUPPORT_TLS
              debug_printf_indent("problem after random/rset/mfrom; reopen conn\n");
            random_local_part = NULL;
 #ifdef SUPPORT_TLS
-           tls_close(FALSE, TLS_SHUTDOWN_NOWAIT);
+           tls_close(sx.cctx.tls_ctx, TLS_SHUTDOWN_NOWAIT);
 #endif
            HDEBUG(D_transport|D_acl|D_v) debug_printf_indent("  SMTP(close)>>\n");
 #endif
            HDEBUG(D_transport|D_acl|D_v) debug_printf_indent("  SMTP(close)>>\n");
-           (void)close(sx.inblock.sock);
-           sx.inblock.sock = sx.outblock.sock = -1;
+           (void)close(sx.cctx.sock);
+           sx.cctx.sock = -1;
 #ifndef DISABLE_EVENT
            (void) event_raise(addr->transport->event_action,
                              US"tcp:close", NULL);
 #ifndef DISABLE_EVENT
            (void) event_raise(addr->transport->event_action,
                              US"tcp:close", NULL);
@@ -1059,7 +1060,7 @@ no_conn:
           == vopt_callout_recipsender
        && !random_local_part
        && !pm_mailfrom
           == vopt_callout_recipsender
        && !random_local_part
        && !pm_mailfrom
-       && cutthrough.fd < 0
+       && cutthrough.cctx.sock < 0
        && !sx.lmtp
        )
       {
        && !sx.lmtp
        )
       {
@@ -1068,8 +1069,9 @@ no_conn:
        ? "cutthrough delivery" : "potential further verifies and delivery");
 
       cutthrough.callout_hold_only = !cutthrough.delivery;
        ? "cutthrough delivery" : "potential further verifies and delivery");
 
       cutthrough.callout_hold_only = !cutthrough.delivery;
-      cutthrough.is_tls =      tls_out.active >= 0;
-      cutthrough.fd =  sx.outblock.sock;       /* We assume no buffer in use in the outblock */
+      cutthrough.is_tls =      tls_out.active.sock >= 0;
+      /* We assume no buffer in use in the outblock */
+      cutthrough.cctx =                sx.cctx;
       cutthrough.nrcpt =       1;
       cutthrough.transport =   addr->transport->name;
       cutthrough.interface =   interface;
       cutthrough.nrcpt =       1;
       cutthrough.transport =   addr->transport->name;
       cutthrough.interface =   interface;
@@ -1094,7 +1096,7 @@ no_conn:
       ctblock.buffersize = sizeof(ctbuffer);
       ctblock.ptr = ctbuffer;
       /* ctblock.cmd_count = 0; ctblock.authenticating = FALSE; */
       ctblock.buffersize = sizeof(ctbuffer);
       ctblock.ptr = ctbuffer;
       /* ctblock.cmd_count = 0; ctblock.authenticating = FALSE; */
-      ctblock.sock = cutthrough.fd;
+      ctblock.cctx = &cutthrough.cctx;
       }
     else
       {
       }
     else
       {
@@ -1110,14 +1112,18 @@ no_conn:
          '2', 1);
        }
 
          '2', 1);
        }
 
-      if (sx.inblock.sock >= 0)
+      if (sx.cctx.sock >= 0)
        {
 #ifdef SUPPORT_TLS
        {
 #ifdef SUPPORT_TLS
-       tls_close(FALSE, TLS_SHUTDOWN_NOWAIT);
+       if (sx.cctx.tls_ctx)
+         {
+         tls_close(sx.cctx.tls_ctx, TLS_SHUTDOWN_NOWAIT);
+         sx.cctx.tls_ctx = NULL;
+         }
 #endif
        HDEBUG(D_transport|D_acl|D_v) debug_printf_indent("  SMTP(close)>>\n");
 #endif
        HDEBUG(D_transport|D_acl|D_v) debug_printf_indent("  SMTP(close)>>\n");
-       (void)close(sx.inblock.sock);
-       sx.inblock.sock = sx.outblock.sock = -1;
+       (void)close(sx.cctx.sock);
+       sx.cctx.sock = -1;
 #ifndef DISABLE_EVENT
        (void) event_raise(addr->transport->event_action, US"tcp:close", NULL);
 #endif
 #ifndef DISABLE_EVENT
        (void) event_raise(addr->transport->event_action, US"tcp:close", NULL);
 #endif
@@ -1210,14 +1216,16 @@ return rc;
 static BOOL
 cutthrough_send(int n)
 {
 static BOOL
 cutthrough_send(int n)
 {
-if(cutthrough.fd < 0)
+if(cutthrough.cctx.sock < 0)
   return TRUE;
 
 if(
 #ifdef SUPPORT_TLS
   return TRUE;
 
 if(
 #ifdef SUPPORT_TLS
-   tls_out.active == cutthrough.fd ? tls_write(FALSE, ctblock.buffer, n, FALSE) :
+   cutthrough.is_tls
+   ? tls_write(cutthrough.cctx.tls_ctx, ctblock.buffer, n, FALSE)
+   :
 #endif
 #endif
-   send(cutthrough.fd, ctblock.buffer, n, 0) > 0
+     send(cutthrough.cctx.sock, ctblock.buffer, n, 0) > 0
   )
 {
   transport_count += n;
   )
 {
   transport_count += n;
@@ -1249,8 +1257,8 @@ return TRUE;
 static BOOL
 cutthrough_puts(uschar * cp, int n)
 {
 static BOOL
 cutthrough_puts(uschar * cp, int n)
 {
-if (cutthrough.fd < 0)       return TRUE;
-if (_cutthrough_puts(cp, n)) return TRUE;
+if (cutthrough.cctx.sock < 0) return TRUE;
+if (_cutthrough_puts(cp, n))  return TRUE;
 cancel_cutthrough_connection(TRUE, US"transmit failed");
 return FALSE;
 }
 cancel_cutthrough_connection(TRUE, US"transmit failed");
 return FALSE;
 }
@@ -1301,7 +1309,7 @@ cutthrough_data_puts(US"\r\n", 2);
 
 /* Get and check response from cutthrough target */
 static uschar
 
 /* Get and check response from cutthrough target */
 static uschar
-cutthrough_response(int fd, char expect, uschar ** copy, int timeout)
+cutthrough_response(client_conn_ctx * cctx, char expect, uschar ** copy, int timeout)
 {
 smtp_inblock inblock;
 uschar inbuffer[4096];
 {
 smtp_inblock inblock;
 uschar inbuffer[4096];
@@ -1311,8 +1319,7 @@ inblock.buffer = inbuffer;
 inblock.buffersize = sizeof(inbuffer);
 inblock.ptr = inbuffer;
 inblock.ptrend = inbuffer;
 inblock.buffersize = sizeof(inbuffer);
 inblock.ptr = inbuffer;
 inblock.ptrend = inbuffer;
-inblock.sock = fd;
-/* this relies on (inblock.sock == tls_out.active) */
+inblock.cctx = cctx;
 if(!smtp_read_response(&inblock, responsebuffer, sizeof(responsebuffer), expect, timeout))
   cancel_cutthrough_connection(TRUE, US"target timeout on read");
 
 if(!smtp_read_response(&inblock, responsebuffer, sizeof(responsebuffer), expect, timeout))
   cancel_cutthrough_connection(TRUE, US"target timeout on read");
 
@@ -1334,7 +1341,7 @@ return responsebuffer[0];
 BOOL
 cutthrough_predata(void)
 {
 BOOL
 cutthrough_predata(void)
 {
-if(cutthrough.fd < 0 || cutthrough.callout_hold_only)
+if(cutthrough.cctx.sock < 0 || cutthrough.callout_hold_only)
   return FALSE;
 
 HDEBUG(D_transport|D_acl|D_v) debug_printf_indent("  SMTP>> DATA\n");
   return FALSE;
 
 HDEBUG(D_transport|D_acl|D_v) debug_printf_indent("  SMTP>> DATA\n");
@@ -1342,7 +1349,7 @@ cutthrough_puts(US"DATA\r\n", 6);
 cutthrough_flush_send();
 
 /* Assume nothing buffered.  If it was it gets ignored. */
 cutthrough_flush_send();
 
 /* Assume nothing buffered.  If it was it gets ignored. */
-return cutthrough_response(cutthrough.fd, '3', NULL, CUTTHROUGH_DATA_TIMEOUT) == '3';
+return cutthrough_response(&cutthrough.cctx, '3', NULL, CUTTHROUGH_DATA_TIMEOUT) == '3';
 }
 
 
 }
 
 
@@ -1369,7 +1376,7 @@ cutthrough_headers_send(void)
 {
 transport_ctx tctx;
 
 {
 transport_ctx tctx;
 
-if(cutthrough.fd < 0 || cutthrough.callout_hold_only)
+if(cutthrough.cctx.sock < 0 || cutthrough.callout_hold_only)
   return FALSE;
 
 /* We share a routine with the mainline transport to handle header add/remove/rewrites,
   return FALSE;
 
 /* We share a routine with the mainline transport to handle header add/remove/rewrites,
@@ -1377,7 +1384,7 @@ if(cutthrough.fd < 0 || cutthrough.callout_hold_only)
 */
 HDEBUG(D_acl) debug_printf_indent("----------- start cutthrough headers send -----------\n");
 
 */
 HDEBUG(D_acl) debug_printf_indent("----------- start cutthrough headers send -----------\n");
 
-tctx.u.fd = cutthrough.fd;
+tctx.u.fd = cutthrough.cctx.sock;
 tctx.tblock = cutthrough.addr.transport;
 tctx.addr = &cutthrough.addr;
 tctx.check_string = US".";
 tctx.tblock = cutthrough.addr.transport;
 tctx.addr = &cutthrough.addr;
 tctx.check_string = US".";
@@ -1396,25 +1403,31 @@ return TRUE;
 static void
 close_cutthrough_connection(const uschar * why)
 {
 static void
 close_cutthrough_connection(const uschar * why)
 {
-int fd = cutthrough.fd;
+int fd = cutthrough.cctx.sock;
 if(fd >= 0)
   {
   /* We could be sending this after a bunch of data, but that is ok as
      the only way to cancel the transfer in dataphase is to drop the tcp
      conn before the final dot.
   */
 if(fd >= 0)
   {
   /* We could be sending this after a bunch of data, but that is ok as
      the only way to cancel the transfer in dataphase is to drop the tcp
      conn before the final dot.
   */
+  client_conn_ctx tmp_ctx = cutthrough.cctx;
   ctblock.ptr = ctbuffer;
   HDEBUG(D_transport|D_acl|D_v) debug_printf_indent("  SMTP>> QUIT\n");
   _cutthrough_puts(US"QUIT\r\n", 6);   /* avoid recursion */
   _cutthrough_flush_send();
   ctblock.ptr = ctbuffer;
   HDEBUG(D_transport|D_acl|D_v) debug_printf_indent("  SMTP>> QUIT\n");
   _cutthrough_puts(US"QUIT\r\n", 6);   /* avoid recursion */
   _cutthrough_flush_send();
-  cutthrough.fd = -1;                  /* avoid recursion via read timeout */
+  cutthrough.cctx.sock = -1;           /* avoid recursion via read timeout */
   cutthrough.nrcpt = 0;                        /* permit re-cutthrough on subsequent message */
 
   /* Wait a short time for response, and discard it */
   cutthrough.nrcpt = 0;                        /* permit re-cutthrough on subsequent message */
 
   /* Wait a short time for response, and discard it */
-  cutthrough_response(fd, '2', NULL, 1);
+  cutthrough_response(&tmp_ctx, '2', NULL, 1);
 
 #ifdef SUPPORT_TLS
 
 #ifdef SUPPORT_TLS
-  tls_close(FALSE, TLS_SHUTDOWN_NOWAIT);
+  if (cutthrough.is_tls)
+    {
+    tls_close(cutthrough.cctx.tls_ctx, TLS_SHUTDOWN_NOWAIT);
+    cutthrough.cctx.tls_ctx = NULL;
+    cutthrough.is_tls = FALSE;
+    }
 #endif
   HDEBUG(D_transport|D_acl|D_v) debug_printf_indent("  SMTP(close)>>\n");
   (void)close(fd);
 #endif
   HDEBUG(D_transport|D_acl|D_v) debug_printf_indent("  SMTP(close)>>\n");
   (void)close(fd);
@@ -1435,9 +1448,10 @@ cutthrough.delivery = cutthrough.callout_hold_only = FALSE;
 void
 release_cutthrough_connection(const uschar * why)
 {
 void
 release_cutthrough_connection(const uschar * why)
 {
-if (cutthrough.fd < 0) return;
+if (cutthrough.cctx.sock < 0) return;
 HDEBUG(D_acl) debug_printf_indent("release cutthrough conn: %s\n", why);
 HDEBUG(D_acl) debug_printf_indent("release cutthrough conn: %s\n", why);
-cutthrough.fd = -1;
+cutthrough.cctx.sock = -1;
+cutthrough.cctx.tls_ctx = NULL;
 cutthrough.delivery = cutthrough.callout_hold_only = FALSE;
 }
 
 cutthrough.delivery = cutthrough.callout_hold_only = FALSE;
 }
 
@@ -1463,7 +1477,8 @@ if(  !cutthrough_puts(US".", 1)
   )
   return cutthrough.addr.message;
 
   )
   return cutthrough.addr.message;
 
-res = cutthrough_response(cutthrough.fd, '2', &cutthrough.addr.message, CUTTHROUGH_DATA_TIMEOUT);
+res = cutthrough_response(&cutthrough.cctx, '2', &cutthrough.addr.message,
+       CUTTHROUGH_DATA_TIMEOUT);
 for (addr = &cutthrough.addr; addr; addr = addr->next)
   {
   addr->message = cutthrough.addr.message;
 for (addr = &cutthrough.addr; addr; addr = addr->next)
   {
   addr->message = cutthrough.addr.message;
@@ -2681,7 +2696,8 @@ Side effect: any received ident value is put in sender_ident (NULL otherwise)
 void
 verify_get_ident(int port)
 {
 void
 verify_get_ident(int port)
 {
-int sock, host_af, qlen;
+client_conn_ctx ident_conn_ctx = {0};
+int host_af, qlen;
 int received_sender_port, received_interface_port, n;
 uschar *p;
 blob early_data;
 int received_sender_port, received_interface_port, n;
 uschar *p;
 blob early_data;
@@ -2701,9 +2717,9 @@ to the incoming interface address. If the sender host address is an IPv6
 address, the incoming interface address will also be IPv6. */
 
 host_af = Ustrchr(sender_host_address, ':') == NULL ? AF_INET : AF_INET6;
 address, the incoming interface address will also be IPv6. */
 
 host_af = Ustrchr(sender_host_address, ':') == NULL ? AF_INET : AF_INET6;
-if ((sock = ip_socket(SOCK_STREAM, host_af)) < 0) return;
+if ((ident_conn_ctx.sock = ip_socket(SOCK_STREAM, host_af)) < 0) return;
 
 
-if (ip_bind(sock, host_af, interface_address, 0) < 0)
+if (ip_bind(ident_conn_ctx.sock, host_af, interface_address, 0) < 0)
   {
   DEBUG(D_ident) debug_printf("bind socket for ident failed: %s\n",
     strerror(errno));
   {
   DEBUG(D_ident) debug_printf("bind socket for ident failed: %s\n",
     strerror(errno));
@@ -2717,7 +2733,7 @@ qlen = snprintf(CS buffer, sizeof(buffer), "%d , %d\r\n",
 early_data.data = buffer;
 early_data.len = qlen;
 
 early_data.data = buffer;
 early_data.len = qlen;
 
-if (ip_connect(sock, host_af, sender_host_address, port,
+if (ip_connect(ident_conn_ctx.sock, host_af, sender_host_address, port,
                rfc1413_query_timeout, &early_data) < 0)
   {
   if (errno == ETIMEDOUT && LOGGING(ident_timeout))
                rfc1413_query_timeout, &early_data) < 0)
   {
   if (errno == ETIMEDOUT && LOGGING(ident_timeout))
@@ -2741,7 +2757,7 @@ for (;;)
   int size = sizeof(buffer) - (p - buffer);
 
   if (size <= 0) goto END_OFF;   /* Buffer filled without seeing \n. */
   int size = sizeof(buffer) - (p - buffer);
 
   if (size <= 0) goto END_OFF;   /* Buffer filled without seeing \n. */
-  count = ip_recv(sock, p, size, rfc1413_query_timeout);
+  count = ip_recv(&ident_conn_ctx, p, size, 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
   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
@@ -2806,7 +2822,7 @@ sender_ident = US string_printing(string_copyn(p, 127));
 DEBUG(D_ident) debug_printf("sender_ident = %s\n", sender_ident);
 
 END_OFF:
 DEBUG(D_ident) debug_printf("sender_ident = %s\n", sender_ident);
 
 END_OFF:
-(void)close(sock);
+(void)close(ident_conn_ctx.sock);
 return;
 }
 
 return;
 }