Fix build with DRWEB malware scanner
[users/jgh/exim.git] / src / src / malware.c
index 730a2be22389330608576d7c5c6d6532db64eff9..cfff9ee5d7b50083ba8eebfa1e4122f166277dff 100644 (file)
 #include "exim.h"
 #ifdef WITH_CONTENT_SCAN       /* entire file */
 
-typedef enum {M_FPROTD, M_DRWEB, M_AVES, M_FSEC, M_KAVD, M_CMDL,
-               M_SOPHIE, M_CLAMD, M_SOCK, M_MKSD, M_AVAST, M_FPROT6D} scanner_t;
+typedef enum {
+#ifndef DISABLE_MAL_FFROTD
+       M_FPROTD,
+#endif
+#ifndef DISABLE_MAL_FFROT6D
+       M_FPROT6D,
+#endif
+#ifndef DISABLE_MAL_DRWEB
+       M_DRWEB,
+#endif
+#ifndef DISABLE_MAL_AVE
+       M_AVES,
+#endif
+#ifndef DISABLE_MAL_FSECURE
+       M_FSEC,
+#endif
+#ifndef DISABLE_MAL_KAV
+       M_KAVD,
+#endif
+#ifndef DISABLE_MAL_SOPHIE
+       M_SOPHIE,
+#endif
+#ifndef DISABLE_MAL_CLAM
+       M_CLAMD,
+#endif
+#ifndef DISABLE_MAL_MKS
+       M_MKSD,
+#endif
+#ifndef DISABLE_MAL_AVAST
+       M_AVAST,
+#endif
+#ifndef DISABLE_MAL_SOCK
+       M_SOCK,
+#endif
+#ifndef DISABLE_MAL_CMDLINE
+       M_CMDL,
+#endif
+       M_DUMMY
+       } scanner_t;
 typedef enum {MC_NONE, MC_TCP, MC_UNIX, MC_STRM} contype_t;
 static struct scan
 {
@@ -70,16 +107,16 @@ static struct scan
 void
 features_malware(void)
 {
-const struct scan * sc;
 const uschar * s;
 uschar * t;
 uschar buf[64];
 
 spf(buf, sizeof(buf), US"_HAVE_MALWARE_");
 
-for (sc = m_scans; sc->scancode != -1; sc++)
+for (const struct scan * sc = m_scans; sc->scancode != -1; sc++)
   {
-  for(s = sc->name, t = buf+14; *s; s++) if (*s != '-') *t++ = toupper(*s);
+  for (s = sc->name, t = buf+14; *s; s++) if (*s != '-')
+    *t++ = toupper(*s);
   *t = '\0';
   builtin_macro_create(buf);
   }
@@ -155,11 +192,12 @@ static const pcre * fprot6d_re_virus = NULL;
 
 /******************************************************************************/
 
+#ifndef DISABLE_MAL_KAV
 /* Routine to check whether a system is big- or little-endian.
    Ripped from http://www.faqs.org/faqs/graphics/fileformats-faq/part4/section-7.html
    Needed for proper kavdaemon implementation. Sigh. */
-#define BIG_MY_ENDIAN      0
-#define LITTLE_MY_ENDIAN   1
+# define BIG_MY_ENDIAN      0
+# define LITTLE_MY_ENDIAN   1
 static int test_byte_order(void);
 static inline int
 test_byte_order()
@@ -168,6 +206,7 @@ test_byte_order()
   char *byte = CS  &word;
   return(byte[0] ? LITTLE_MY_ENDIAN : BIG_MY_ENDIAN);
 }
+#endif
 
 BOOL malware_ok = FALSE;
 
@@ -180,12 +219,14 @@ extern uschar spooled_message_id[MESSAGE_ID_LENGTH+1];
 
 /* Some (currently avast only) use backslash escaped whitespace,
 this function undoes these escapes */
+
 static inline void
-unescape(char *p) {
-  uschar *p0;
-  for (; *p; ++p)
-    if (*p == '\\' && (isspace(p[1]) || p[1] == '\\'))
-      for (p0 = p; *p0; ++p0) *p0 = p0[1];
+unescape(uschar *p)
+{
+uschar *p0;
+for (; *p; ++p)
+  if (*p == '\\' && (isspace(p[1]) || p[1] == '\\'))
+    for (p0 = p; *p0; ++p0) *p0 = p0[1];
 }
 
 /* --- malware_*_defer --- */
@@ -224,13 +265,6 @@ m_panic_defer_3(struct scan * scanent, const uschar * hostport,
 (void) close(fd_to_close);
 return m_panic_defer(scanent, hostport, str);
 }
-static inline int
-m_log_defer_3(struct scan * scanent, const uschar * hostport,
-  const uschar * str, int fd_to_close)
-{
-(void) close(fd_to_close);
-return m_log_defer(scanent, hostport, str);
-}
 
 /*************************************************/
 
@@ -268,8 +302,7 @@ const uschar * rerror;
 int roffset;
 const pcre * cre;
 
-cre = pcre_compile(CS re, PCRE_COPT, (const char **)&rerror, &roffset, NULL);
-if (!cre)
+if (!(cre = pcre_compile(CS re, PCRE_COPT, CCSS &rerror, &roffset, NULL)))
   *errstr= string_sprintf("regular expression error in '%s': %s at offset %d",
       re, rerror, roffset);
 return cre;
@@ -283,7 +316,7 @@ int i = pcre_exec(cre, NULL, CS text, Ustrlen(text), 0, 0,
              ovector, nelem(ovector));
 uschar * substr = NULL;
 if (i >= 2)                            /* Got it */
-  pcre_get_substring(CS text, ovector, i, 1, (const char **) &substr);
+  pcre_get_substring(CS text, ovector, i, 1, CCSS &substr);
 return substr;
 }
 
@@ -315,13 +348,13 @@ return cre;
          -2 on timeout or error
 */
 static int
-recv_line(int fd, uschar * buffer, int bsize, int tmo)
+recv_line(int fd, uschar * buffer, int bsize, time_t tmo)
 {
 uschar * p = buffer;
 ssize_t rcv;
 BOOL ok = FALSE;
 
-if (!fd_ready(fd, tmo-time(NULL)))
+if (!fd_ready(fd, tmo))
   return -2;
 
 /*XXX tmo handling assumes we always get a whole line */
@@ -348,9 +381,9 @@ return p - buffer;
 
 /* return TRUE iff size as requested */
 static BOOL
-recv_len(int sock, void * buf, int size, int tmo)
+recv_len(int sock, void * buf, int size, time_t tmo)
 {
-return fd_ready(sock, tmo-time(NULL))
+return fd_ready(sock, tmo)
   ? recv(sock, buf, size, 0) == size
   : FALSE;
 }
@@ -396,14 +429,15 @@ for (;;)
 }
 
 static inline int
-mksd_read_lines (int sock, uschar *av_buffer, int av_buffer_size, int tmo)
+mksd_read_lines (int sock, uschar *av_buffer, int av_buffer_size, time_t tmo)
 {
+client_conn_ctx cctx = {.sock = sock};
 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);
   if (i <= 0)
     {
     (void) malware_panic_defer(US"unable to read from mksd UNIX socket (/var/run/mksd/socket)");
@@ -462,7 +496,7 @@ switch (*line)
 
 static int
 mksd_scan_packed(struct scan * scanent, int sock, const uschar * scan_filename,
-  int tmo)
+  time_t tmo)
 {
 struct iovec iov[3];
 const char *cmd = "MSQ\n";
@@ -538,7 +572,7 @@ const pcre *re;
 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;
 
@@ -619,12 +653,16 @@ if (!malware_ok)
     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;
   }
@@ -655,10 +693,10 @@ if (!malware_ok)
        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);
 
-      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)
@@ -680,7 +718,7 @@ if (!malware_ok)
          }
       if (len < -1)
        {
-       (void)close(sock);
+       (void)close(malware_daemon_ctx.sock);
        return DEFER;
        }
       break;
@@ -704,28 +742,28 @@ if (!malware_ok)
       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);
 
-      bread = ip_recv(sock, av_buffer, sizeof(av_buffer), tmo-time(NULL));
+      bread = ip_recv(&malware_daemon_ctx, av_buffer, sizeof(av_buffer), tmo);
 
       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,
-          US"buffer too small", sock);
+          US"buffer too small", malware_daemon_ctx.sock);
 
       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,
-          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;
@@ -753,11 +791,11 @@ if (!malware_ok)
       if (*scanner_options != '/')
        {
        /* calc file size */
-       if ((drweb_fd = open(CCS eml_filename, O_RDONLY)) == -1)
+       if ((drweb_fd = exim_open2(CCS eml_filename, O_RDONLY)) == -1)
          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)
          {
@@ -767,7 +805,7 @@ badseek:  err = errno;
          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)
@@ -776,7 +814,7 @@ badseek:  err = errno;
          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)
@@ -786,46 +824,47 @@ badseek:  err = errno;
            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),
-           sock);
+           malware_daemon_ctx.sock);
          }
 
-       if (!(drweb_fbuf = US malloc(fsize_uint)))
+       if (!(drweb_fbuf = store_malloc(fsize_uint)))
          {
          (void)close(drweb_fd);
          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)
          {
          int err = errno;
          (void)close(drweb_fd);
-         free(drweb_fbuf);
+         store_free(drweb_fbuf);
          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 */
-       if (send(sock, drweb_fbuf, fsize, 0) < 0)
+       if (send(malware_daemon_ctx.sock, drweb_fbuf, fsize, 0) < 0)
          {
-         free(drweb_fbuf);
+         store_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);
          }
+       store_free(drweb_fbuf);
        }
       else
        {
@@ -835,31 +874,30 @@ badseek:  err = errno;
            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),
-           sock);
+           malware_daemon_ctx.sock);
        }
 
       /* 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,
-                   US"unable to read return code", sock);
+                   US"unable to read return code", malware_daemon_ctx.sock);
       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,
-                           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 */
       if (drweb_vnum)
        {
-       int i;
        gstring * g = NULL;
 
        /* setup default virus name */
@@ -870,21 +908,23 @@ badseek:  err = errno;
          drweb_re = m_pcre_compile(drweb_re_str, &errstr);
 
        /* read and concatenate virus names into one string */
-       for (i = 0; i < drweb_vnum; i++)
+       for (int i = 0; i < drweb_vnum; i++)
          {
          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,
-                             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);
+
+         /* assume tainted, since it is external input */
+         tmpbuf = store_get(drweb_slen, TRUE);
 
          /* 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,
-                             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 */
@@ -923,7 +963,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),
-           sock);
+           malware_daemon_ctx.sock);
 
        /* no virus found */
        malware_name = NULL;
@@ -940,13 +980,13 @@ badseek:  err = errno;
 
       /* 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") ),
-         sock);
+         malware_daemon_ctx.sock);
 
       /* prepare our command */
       (void)string_format(buf, sizeof(buf), "SCAN bPQRSTUW %s\r\n",
@@ -955,13 +995,13 @@ badseek:  err = errno;
       /* 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 */
-      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;
@@ -980,22 +1020,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;
-      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") ),
-         sock);
+         malware_daemon_ctx.sock);
 
       if (result == DEFER)
        {
-       (void)close(sock);
+       (void)close(malware_daemon_ctx.sock);
        return DEFER;
        }
       break;
@@ -1005,7 +1045,7 @@ badseek:  err = errno;
 #ifndef DISABLE_MAL_FSECURE
     case M_FSEC: /* "fsecure" scanner type ---------------------------------- */
       {
-      int i, j, bread = 0;
+      int i, bread = 0;
       uschar * file_name;
       uschar av_buffer[1024];
       static uschar *cmdopt[] = { US"CONFIGURE\tARCHIVE\t1\n",
@@ -1022,16 +1062,16 @@ badseek:  err = errno;
       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);
 
-       bread = ip_recv(sock, av_buffer, sizeof(av_buffer), tmo-time(NULL));
+       bread = ip_recv(&malware_daemon_ctx, av_buffer, sizeof(av_buffer), tmo);
        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);
-       for (j = 0; j < bread; j++)
+           malware_daemon_ctx.sock);
+       for (int j = 0; j < bread; j++)
          if (av_buffer[j] == '\r' || av_buffer[j] == '\n')
            av_buffer[j] ='@';
        }
@@ -1039,7 +1079,7 @@ badseek:  err = errno;
       /* 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 */
@@ -1057,10 +1097,10 @@ badseek:  err = errno;
          {
          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)) < 0)
            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)
            {
@@ -1117,13 +1157,13 @@ badseek:  err = errno;
          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 */
-      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,
-                           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;
@@ -1132,13 +1172,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.",
-               sock);
+               malware_daemon_ctx.sock);
       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,
-               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
@@ -1158,9 +1198,9 @@ badseek:  err = errno;
        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,
-                 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 (!?) */
@@ -1183,7 +1223,7 @@ badseek:  err = errno;
            /* 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;
 
@@ -1322,13 +1362,10 @@ badseek:  err = errno;
        malware_name = US"unknown";
 
        /* re-open the scanner output file, look for name match */
-       scanner_record = fopen(CS file_name, "rb");
-       while (fgets(CS linebuffer, sizeof(linebuffer), scanner_record))
-         {
-         /* try match */
-         if ((s = m_pcre_exec(cmdline_regex_re, linebuffer)))
+       scanner_record = Ufopen(file_name, "rb");
+       while (Ufgets(linebuffer, sizeof(linebuffer), scanner_record))
+         if ((s = m_pcre_exec(cmdline_regex_re, linebuffer))) /* try match */
            malware_name = s;
-         }
        (void)fclose(scanner_record);
        }
       else /* no virus found */
@@ -1353,19 +1390,19 @@ badseek:  err = errno;
       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),
-         sock);
+         malware_daemon_ctx.sock);
 
       /* 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)) <= 0)
        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') {
@@ -1376,7 +1413,7 @@ badseek:  err = errno;
       }
       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;
 
@@ -1425,7 +1462,7 @@ badseek:  err = errno;
        /* Local file; so we def want to use_scan_command and don't want to try
         * passing IP/port combinations */
        use_scan_command = TRUE;
-       cd = (clamd_address *) store_get(sizeof(clamd_address));
+       cd = (clamd_address *) store_get(sizeof(clamd_address), FALSE);
 
        /* extract socket-path part */
        sublist = scanner_options;
@@ -1459,7 +1496,7 @@ badseek:  err = errno;
            continue;
            }
 
-         cd = (clamd_address *) store_get(sizeof(clamd_address));
+         cd = (clamd_address *) store_get(sizeof(clamd_address), FALSE);
 
          /* extract host and port part */
          sublist = scanner_options;
@@ -1535,7 +1572,8 @@ badseek:  err = errno;
           * on both connections (as one host could resolve to multiple ips) */
          for (;;)
            {
-           if ((sock = m_tcpsocket(cd->hostspec, cd->tcp_port,
+           /*XXX we trust that the cmd_str is ideempotent */
+           if ((malware_daemon_ctx.sock = m_tcpsocket(cd->hostspec, cd->tcp_port,
                                    &connhost, &errstr, &cmd_str)) >= 0)
              {
              /* Connection successfully established with a server */
@@ -1546,7 +1584,7 @@ badseek:  err = errno;
            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);
@@ -1563,7 +1601,7 @@ badseek:  err = errno;
       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;
@@ -1590,20 +1628,20 @@ badseek:  err = errno;
 
        /* 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)),
-             sock);
+             malware_daemon_ctx.sock);
 
        /* calc file size */
-       if ((clam_fd = open(CS eml_filename, O_RDONLY)) < 0)
+       if ((clam_fd = exim_open2(CS eml_filename, O_RDONLY)) < 0)
          {
          int err = errno;
          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)
          {
@@ -1613,7 +1651,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)),
-           sock);
+           malware_daemon_ctx.sock);
          }
        fsize_uint = (unsigned int) fsize;
        if ((off_t)fsize_uint != fsize)
@@ -1622,45 +1660,44 @@ b_seek:   err = errno;
          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 (!(clamav_fbuf = US malloc(fsize_uint)))
+       if (!(clamav_fbuf = store_malloc(fsize_uint)))
          {
          (void)close(clam_fd);
          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)
          {
          int err = errno;
-         free(clamav_fbuf); (void)close(clam_fd);
+         store_free(clamav_fbuf); (void)close(clam_fd);
          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;
-       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);
+         store_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);
+       store_free(clamav_fbuf);
        }
       else
        { /* use scan command */
@@ -1684,10 +1721,10 @@ b_seek:   err = errno;
            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)),
-             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. */
@@ -1697,9 +1734,10 @@ b_seek:   err = errno;
 
       /* 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);
+      (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,
@@ -1825,7 +1863,7 @@ b_seek:   err = errno;
        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;
@@ -1836,13 +1874,13 @@ b_seek:   err = errno;
       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)
-       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);
@@ -1850,20 +1888,20 @@ b_seek:   err = errno;
        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 */
-      bread = ip_recv(sock, av_buffer, sizeof(av_buffer), tmo-time(NULL));
+      bread = ip_recv(&malware_daemon_ctx, av_buffer, sizeof(av_buffer), tmo);
 
       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,
-               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: ",
@@ -1902,16 +1940,16 @@ b_seek:   err = errno;
            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);
 
-      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;
@@ -1921,14 +1959,13 @@ b_seek:   err = errno;
 #ifndef DISABLE_MAL_AVAST
     case M_AVAST: /* "avast" scanner type ----------------------------------- */
       {
-      int ovector[3*3];
       uschar buf[1024];
       uschar * scanrequest;
       enum {AVA_HELO, AVA_OPT, AVA_RSP, AVA_DONE} avast_stage;
       int nread;
-      int more_data;
       uschar * error_message = NULL;
-      int strict = TRUE;
+      BOOL more_data = FALSE;
+      BOOL strict = TRUE;
 
       /* According to Martin Tuma @avast the protocol uses "escaped
       whitespace", that is, every embedded whitespace is backslash
@@ -1937,7 +1974,7 @@ b_seek:   err = errno;
       and the [ ] marker.
       [+] - not infected
       [L] - infected
-      [E] - some error occured
+      [E] - some error occurred
       Such marker follows the first non-escaped TAB.  For more information
       see avast-protocol(5)
 
@@ -1981,7 +2018,7 @@ b_seek:   err = errno;
 
       /* 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);
@@ -2035,41 +2072,28 @@ b_seek:   err = errno;
 
              /* 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, scanner_options, strerror(errno)), sock);
+                     scanrequest, scanner_options, strerror(errno)), malware_daemon_ctx.sock);
                }
              break;
              }
 
            case AVA_RSP:
 
-             if (isdigit(buf[0]))
-               { /* we're done, this is the last response line from the scanner */
-                DEBUG(D_acl) debug_printf_indent("sent to avast QUIT\n");
-               if (send(sock, "QUIT\n", 5, 0) == -1) /* courtesy */
-                 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);
-
-                if (buf[0] != '2') error_message = buf;
-               avast_stage = AVA_DONE;
+             if (isdigit(buf[0]))  /* We're done */
                 goto endloop;
-               }
 
-              if (malware_name) /* nothing else matters, just read on */
+              if (malware_name)     /* Nothing else matters, just read on */
                 break;
 
-             if (pcre_exec(ava_re_clean, NULL, CS buf, slen,
-                   0, 0, ovector, nelem(ovector)) > 0)
+             if (pcre_exec(ava_re_clean, NULL, CS buf, slen, 0, 0, NULL, 0) == 0)
                break;
 
-              if (malware_name = m_pcre_exec(ava_re_virus, buf))
+              if ((malware_name = m_pcre_exec(ava_re_virus, buf)))
                 {
                 unescape(malware_name);
                 DEBUG(D_acl)
@@ -2077,15 +2101,17 @@ b_seek:   err = errno;
                 break;
                 }
 
-              if (strict && (malware_name = m_pcre_exec(ava_re_error, buf)))
+              if (strict)           /* treat scanner errors as malware */
                 {
-                unescape(malware_name);
-                DEBUG(D_acl)
-                  debug_printf_indent("unescaped error message: '%s'\n", malware_name);
-                break;
+                if ((malware_name = m_pcre_exec(ava_re_error, buf)))
+                  {
+                  unescape(malware_name);
+                  DEBUG(D_acl)
+                    debug_printf_indent("unescaped error message: '%s'\n", malware_name);
+                  break;
+                  }
                 }
-
-              if (pcre_exec(ava_re_error, NULL, CS buf, slen, 0, 0, NULL, 0) == 0)
+              else if (pcre_exec(ava_re_error, NULL, CS buf, slen, 0, 0, NULL, 0) == 0)
                 {
                 log_write(0, LOG_MAIN, "internal scanner error (ignored): %s", buf);
                 break;
@@ -2093,7 +2119,7 @@ b_seek:   err = errno;
 
              /* here also for any unexpected response from the scanner */
               DEBUG(D_acl) debug_printf("avast response not handled: '%s'\n", buf);
-              error_message = string_sprintf(string_sprintf("unexpected response from scanner: '%s'", buf));
+
              goto endloop;
 
            default:    log_write(0, LOG_PANIC, "%s:%d:%s: should not happen",
@@ -2101,29 +2127,29 @@ b_seek:   err = errno;
            }
          }
        }
+
       endloop:
 
-      switch(avast_stage)
-       {
-       case AVA_HELO:
-       case AVA_OPT:
-       case AVA_RSP:
-                        if (nread == -1) error_message = "EOF from scanner";
-                        else if (nread < 0) error_message = "timeout from scanner";
+      if (nread == -1) error_message = US"EOF from scanner";
+      else if (nread < 0) error_message = US"timeout from scanner";
+      else if (nread == 0) error_message = US"got nothing from scanner";
+      else if (buf[0] != '2') error_message = buf;
+
+      DEBUG(D_acl) debug_printf_indent("sent to avast QUIT\n");
+      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",
+            scanner_options, strerror(errno)), malware_daemon_ctx.sock);
 
-        case AVA_DONE:
-                        if (error_message)
-                          return m_panic_defer_3(scanent, CUS callout_address, error_message, sock);
+      if (error_message)
+        return m_panic_defer_3(scanent, CUS callout_address, error_message, malware_daemon_ctx.sock);
 
-       default:        break;
-       }
-      break;
       }
 #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 */
   }
 
@@ -2194,7 +2220,7 @@ sender_address = US"malware-sender@example.net";
 return_path = US"";
 recipients_list = NULL;
 receive_add_recipient(US"malware-victim@example.net", -1);
-enable_dollar_recipients = TRUE;
+f.enable_dollar_recipients = TRUE;
 
 ret = malware_internal(US"*", eml_filename, 0);
 
@@ -2253,9 +2279,8 @@ if (!fprot6d_re_virus)
 void
 malware_show_supported(FILE * f)
 {
-struct scan * sc;
 fprintf(f, "Malware:");
-for (sc = m_scans; sc->scancode != -1; sc++) fprintf(f, " %s", sc->name);
+for (struct scan * sc = m_scans; sc->scancode != (scanner_t)-1; sc++) fprintf(f, " %s", sc->name);
 fprintf(f, "\n");
 }