TFO: early-data for ClamAV and for readsocket expansion
[exim.git] / src / src / malware.c
index b626b18a8ea451d6d66d92bf89507aaf665b8eea..32f2e9e492461fd4209e70570925df71a29bf4fb 100644 (file)
@@ -147,9 +147,10 @@ uses the returned in_addr to get a second connection to the same system.
 */
 static inline int
 m_tcpsocket(const uschar * hostname, unsigned int port,
-       host_item * host, uschar ** errstr)
+       host_item * host, uschar ** errstr, const blob * fastopen)
 {
-return ip_connectedsocket(SOCK_STREAM, hostname, port, port, 5, host, errstr);
+return ip_connectedsocket(SOCK_STREAM, hostname, port, port, 5,
+                         host, errstr, fastopen);
 }
 
 static int
@@ -1254,6 +1255,7 @@ badseek:  err = errno;
 #else
       uint32_t send_size, send_final_zeroblock;
 #endif
+      blob cmd_str;
 
       /*XXX if unixdomain socket, only one server supported. Needs fixing;
       there's no reason we should not mix local and remote servers */
@@ -1349,6 +1351,19 @@ badseek:  err = errno;
          string_sprintf("local/SCAN mode incompatible with" \
            " : in path to email filename [%s]", eml_filename));
 
+      /* Set up the very first data we will be sending */
+      if (!use_scan_command)
+#ifdef WITH_OLD_CLAMAV_STREAM
+       { cmd_str.data = US"STREAM\n"; cmd_str.len = 7; }
+#else
+       { cmd_str.data = US"zINSTREAM"; cmd_str.len = 10; }
+#endif
+      else
+       {
+       cmd_str.data = string_sprintf("SCAN %s\n", eml_filename);
+       cmd_str.len = Ustrlen(cmd_str.data);
+       }
+
       /* We have some network servers specified */
       if (num_servers)
        {
@@ -1358,7 +1373,7 @@ badseek:  err = errno;
 
        while (num_servers > 0)
          {
-         int i = random_number( num_servers );
+         int i = random_number(num_servers);
          clamd_address * cd = cv[i];
 
          DEBUG(D_acl) debug_printf_indent("trying server name %s, port %u\n",
@@ -1368,11 +1383,12 @@ badseek:  err = errno;
           * on both connections (as one host could resolve to multiple ips) */
          for (;;)
            {
-           sock= m_tcpsocket(cd->hostspec, cd->tcp_port, &connhost, &errstr);
-           if (sock >= 0)
+           if ((sock = m_tcpsocket(cd->hostspec, cd->tcp_port,
+                                   &connhost, &errstr, &cmd_str)) >= 0)
              {
              /* Connection successfully established with a server */
              hostname = cd->hostspec;
+             cmd_str.len = 0;
              break;
              }
            if (cd->retry <= 0) break;
@@ -1421,9 +1437,10 @@ badseek:  err = errno;
            "Malware scan: issuing %s old-style remote scan (PORT)\n",
            scanner_name);
 
-       /* Pass the string to ClamAV (7 = "STREAM\n") */
-       if (m_sock_send(sock, US"STREAM\n", 7, &errstr) < 0)
-         return m_errlog_defer(scanent, CUS callout_address, errstr);
+       /* Pass the string to ClamAV (7 = "STREAM\n"), if not already sent */
+       if (cmd_str.len)
+         if (m_sock_send(sock, cmd_str.data, cmd_str.len, &errstr) < 0)
+           return m_errlog_defer(scanent, CUS callout_address, errstr);
 
        memset(av_buffer2, 0, sizeof(av_buffer2));
        bread = ip_recv(sock, av_buffer2, sizeof(av_buffer2), tmo-time(NULL));
@@ -1443,13 +1460,13 @@ badseek:  err = errno;
                  "ClamAV returned null", sock);
 
        av_buffer2[bread] = '\0';
-       if( sscanf(CS av_buffer2, "PORT %u\n", &port) != 1 )
+       if(sscanf(CS av_buffer2, "PORT %u\n", &port) != 1)
          return m_errlog_defer_3(scanent, CUS callout_address,
            string_sprintf("Expected port information from clamd, got '%s'",
              av_buffer2),
            sock);
 
-       sockData = m_tcpsocket(connhost.address, port, NULL, &errstr);
+       sockData = m_tcpsocket(connhost.address, port, NULL, &errstr, NULL);
        if (sockData < 0)
          return m_errlog_defer_3(scanent, CUS callout_address, errstr, sock);
 
@@ -1463,12 +1480,13 @@ badseek:  err = errno;
            "Malware scan: issuing %s new-style remote scan (zINSTREAM)\n",
            scanner_name);
 
-       /* Pass the string to ClamAV (10 = "zINSTREAM\0") */
-       if (send(sock, "zINSTREAM", 10, 0) < 0)
-         return m_errlog_defer_3(scanent, CUS hostname,
-           string_sprintf("unable to send zINSTREAM to socket (%s)",
-             strerror(errno)),
-           sock);
+       /* 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)
+           return m_errlog_defer_3(scanent, CUS hostname,
+             string_sprintf("unable to send zINSTREAM to socket (%s)",
+               strerror(errno)),
+             sock);
 
 # define CLOSE_SOCKDATA /**/
 #endif
@@ -1569,17 +1587,17 @@ b_seek:   err = errno;
        scanned twice, in the broken out files and from the original .eml.
        Since ClamAV now handles emails (and has for quite some time) we can
        just use the email file itself. */
-       /* Pass the string to ClamAV (7 = "SCAN \n" + \0) */
-       file_name = string_sprintf("SCAN %s\n", eml_filename);
+       /* Pass the string to ClamAV (7 = "SCAN \n" + \0), if not already sent */
 
        DEBUG(D_acl) debug_printf_indent(
            "Malware scan: issuing %s local-path scan [%s]\n",
            scanner_name, scanner_options);
 
-       if (send(sock, file_name, Ustrlen(file_name), 0) < 0)
-         return m_errlog_defer_3(scanent, CUS callout_address,
-           string_sprintf("unable to write to socket (%s)", strerror(errno)),
-           sock);
+       if (cmd_str.len)
+         if (send(sock, cmd_str.data, cmd_str.len, 0) < 0)
+           return m_errlog_defer_3(scanent, CUS callout_address,
+             string_sprintf("unable to write to socket (%s)", strerror(errno)),
+             sock);
 
        /* Do not shut down the socket for writing; a user report noted that
         * clamd 0.70 does not react well to this. */