Fix ClamAV command send
[exim.git] / src / src / malware.c
index abe627ded3a7e537c61e32985068579236ec0472..e696f7a497d8c54473c464c12766d7f9ac3a3ca0 100644 (file)
@@ -109,7 +109,7 @@ features_malware(void)
 {
 const uschar * s;
 uschar * t;
-uschar buf[64];
+uschar buf[EXIM_DRIVERNAME_MAX];
 
 spf(buf, sizeof(buf), US"_HAVE_MALWARE_");
 
@@ -280,7 +280,7 @@ if (fd >= 0)
   {
   struct timeval tv = {.tv_sec = 5};
   fd_set fds;
-  FD_ZERO(fds); FD_SET(fd, &fds); (void) select(fd+1, NULL, &fds, NULL, &tv);
+  FD_ZERO(&fds); FD_SET(fd, &fds); (void) select(fd+1, NULL, &fds, NULL, &tv);
   }
 #endif
 return fd;
@@ -1560,8 +1560,9 @@ badseek:  err = errno;
        { cmd_str.data = US"zINSTREAM"; cmd_str.len = 10; }
       else
        {
-       cmd_str.data = string_sprintf("SCAN %s\n", eml_filename);
-       cmd_str.len = Ustrlen(cmd_str.data);
+       int n;
+       cmd_str.data = string_sprintf("SCAN %s\n%n", eml_filename, &n);
+       cmd_str.len = n;                /* .len is a size_t */
        }
 
       /* We have some network servers specified */
@@ -1583,13 +1584,14 @@ badseek:  err = errno;
          on both connections (as one host could resolve to multiple ips) */
          for (;;)
            {
-           /*XXX we trust that the cmd_str is ideempotent */
+           /*XXX we trust that the cmd_str is idempotent */
            if ((malware_daemon_ctx.sock = m_tcpsocket(cd->hostspec, cd->tcp_port,
-                                   &connhost, &errstr, &cmd_str)) >= 0)
+                                   &connhost, &errstr,
+                                   use_scan_command ? &cmd_str : NULL)) >= 0)
              {
              /* Connection successfully established with a server */
              hostname = cd->hostspec;
-             cmd_str.len = 0;
+             if (use_scan_command) cmd_str.len = 0;
              break;
              }
            if (cd->retry <= 0) break;
@@ -1630,17 +1632,21 @@ badseek:  err = errno;
       if (!use_scan_command)
        {
        struct stat st;
-#ifdef EXIM_TCP_CORK
+#if defined(EXIM_TCP_CORK) && !defined(OS_SENDFILE)
        BOOL corked = TRUE;
 #endif
        /* New protocol: "zINSTREAM\n" followed by a sequence of <length><data>
        chunks, <n> a 4-byte number (network order), terminated by a zero-length
-       chunk. */
+       chunk. We only send one chunk. */
 
        DEBUG(D_acl) debug_printf_indent(
            "Malware scan: issuing %s new-style remote scan (zINSTREAM)\n",
            scanner_name);
 
+#if defined(EXIM_TCP_CORK)
+       (void) setsockopt(malware_daemon_ctx.sock, IPPROTO_TCP, EXIM_TCP_CORK,
+                         US &on, sizeof(on));
+#endif
        /* Pass the string to ClamAV (10 = "zINSTREAM\0"), if not already sent */
        if (cmd_str.len)
          if (send(malware_daemon_ctx.sock, cmd_str.data, cmd_str.len, 0) < 0)
@@ -1676,10 +1682,6 @@ badseek:  err = errno;
          }
 
        /* send file size */
-#ifdef EXIM_TCP_CORK
-       (void) setsockopt(malware_daemon_ctx.sock, IPPROTO_TCP, EXIM_TCP_CORK,
-                         US &on, sizeof(on));
-#endif
        send_size = htonl(fsize_uint);
        if (send(malware_daemon_ctx.sock, &send_size, sizeof(send_size), 0) < 0)
          return m_panic_defer_3(scanent, NULL,
@@ -1687,10 +1689,17 @@ badseek:  err = errno;
            malware_daemon_ctx.sock);
 
        /* send file body */
-       /*XXX sendfile? */
        while (fsize_uint)
          {
-         unsigned n = MIN(fsize_uint, big_buffer_size);
+#ifdef OS_SENDFILE
+         int n = os_sendfile(malware_daemon_ctx.sock, clam_fd, NULL, (size_t)fsize_uint);
+         if (n < 0)
+           return m_panic_defer_3(scanent, NULL,
+             string_sprintf("unable to send file body to socket (%s): %s", hostname, strerror(errno)),
+             malware_daemon_ctx.sock);
+         fsize_uint -= n;
+#else
+         int n = MIN(fsize_uint, big_buffer_size);
          if ((n = read(clam_fd, big_buffer, n)) < 0)
            return m_panic_defer_3(scanent, NULL,
              string_sprintf("can't read spool file %s: %s",
@@ -1701,14 +1710,16 @@ badseek:  err = errno;
              string_sprintf("unable to send file body to socket (%s): %s", hostname, strerror(errno)),
              malware_daemon_ctx.sock);
          fsize_uint -= n;
-#ifdef EXIM_TCP_CORK
+# ifdef EXIM_TCP_CORK
          if (corked)
            {
            corked = FALSE;
            (void) setsockopt(malware_daemon_ctx.sock, IPPROTO_TCP, EXIM_TCP_CORK,
                              US &off, sizeof(off));
            }
-#endif
+# endif
+#endif /*!OS_SENDFILE*/
+
          }
 
        send_final_zeroblock = 0;
@@ -1716,11 +1727,15 @@ badseek:  err = errno;
          return m_panic_defer_3(scanent, NULL,
            string_sprintf("unable to send file terminator to socket (%s)", hostname),
            malware_daemon_ctx.sock);
+#ifdef OS_SENDFILE
+       (void) setsockopt(malware_daemon_ctx.sock, IPPROTO_TCP, EXIM_TCP_CORK,
+                         US &off, sizeof(off));
+#endif
        }
       else
        { /* use scan command */
        /* Send a SCAN command pointing to a filename; then in the then in the
-        * scan-method-neutral part, read the response back */
+       scan-method-neutral part, read the response back */
 
 /* ================================================================= */
 
@@ -1805,7 +1820,7 @@ badseek:  err = errno;
       if (*p) ++p;
 
       /* colon in returned output? */
-      if(!(p = Ustrchr(av_buffer,':')))
+      if (!(p = Ustrchr(av_buffer,':')))
        return m_panic_defer(scanent, CUS callout_address, string_sprintf(
                  "ClamAV returned malformed result (missing colon): %s",
                  av_buffer));