+ /* Do not shut down the socket for writing; a user report noted that
+ * clamd 0.70 does not react well to this. */
+ }
+ /* Commands have been sent, no matter which scan method or connection
+ * type we're using; now just read the result, independent of method. */
+
+ /* Read the result */
+ memset(av_buffer, 0, sizeof(av_buffer));
+ 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,
+ string_sprintf("unable to read from socket (%s)",
+ errno == 0 ? "EOF" : strerror(errno)));
+
+ if (bread == sizeof(av_buffer))
+ return m_panic_defer(scanent, CUS callout_address,
+ US"buffer too small");
+ /* We're now assured of a NULL at the end of av_buffer */
+
+ /* Check the result. ClamAV returns one of two result formats.
+ In the basic mode, the response is of the form:
+ infected: -> "<filename>: <virusname> FOUND"
+ not-infected: -> "<filename>: OK"
+ error: -> "<filename>: <errcode> ERROR
+ If the ExtendedDetectionInfo option has been turned on, then we get:
+ "<filename>: <virusname>(<virushash>:<virussize>) FOUND"
+ for the infected case. Compare:
+/tmp/eicar.com: Eicar-Test-Signature FOUND
+/tmp/eicar.com: Eicar-Test-Signature(44d88612fea8a8f36de82e1278abb02f:68) FOUND
+
+ In the streaming case, clamd uses the filename "stream" which you should
+ be able to verify with { ktrace clamdscan --stream /tmp/eicar.com }. (The
+ client app will replace "stream" with the original filename before returning
+ results to stdout, but the trace shows the data).
+
+ We will assume that the pathname passed to clamd from Exim does not contain
+ a colon. We will have whined loudly above if the eml_filename does (and we're
+ passing a filename to clamd). */
+
+ if (!(*av_buffer))
+ return m_panic_defer(scanent, CUS callout_address,
+ US"ClamAV returned null");
+
+ /* strip newline at the end (won't be present for zINSTREAM)
+ (also any trailing whitespace, which shouldn't exist, but we depend upon
+ this below, so double-check) */
+ p = av_buffer + Ustrlen(av_buffer) - 1;
+ if (*p == '\n') *p = '\0';
+
+ DEBUG(D_acl) debug_printf_indent("Malware response: %s\n", av_buffer);
+
+ while (isspace(*--p) && (p > av_buffer))
+ *p = '\0';
+ if (*p) ++p;
+
+ /* colon in returned output? */
+ if(!(p = Ustrchr(av_buffer,':')))
+ return m_panic_defer(scanent, CUS callout_address, string_sprintf(
+ "ClamAV returned malformed result (missing colon): %s",
+ av_buffer));
+
+ /* strip filename */
+ while (*p && isspace(*++p)) /**/;
+ vname = p;
+
+ /* It would be bad to encounter a virus with "FOUND" in part of the name,
+ but we should at least be resistant to it. */
+ p = Ustrrchr(vname, ' ');
+ result_tag = p ? p+1 : vname;
+
+ if (Ustrcmp(result_tag, "FOUND") == 0)
+ {
+ /* p should still be the whitespace before the result_tag */
+ while (isspace(*p)) --p;
+ *++p = '\0';
+ /* Strip off the extended information too, which will be in parens
+ after the virus name, with no intervening whitespace. */
+ if (*--p == ')')
+ {
+ /* "(hash:size)", so previous '(' will do; if not found, we have
+ a curious virus name, but not an error. */
+ p = Ustrrchr(vname, '(');
+ if (p)
+ *p = '\0';
+ }
+ malware_name = string_copy(vname);
+ DEBUG(D_acl) debug_printf_indent("Malware found, name \"%s\"\n", malware_name);