+
+
+/*************************************************
+* Scan content for malware *
+*************************************************/
+
+/* This is an internal interface for scanning an email; the normal interface
+is via malware(), or there's malware_in_file() used for testing/debugging.
+
+Arguments:
+ malware_re match condition for "malware="
+ cacheable the RE did not use any dynamic elements during expansion
+ scan_filename the file holding the email to be scanned, if we're faking
+ this up for the -bmalware test, else NULL
+ timeout if nonzero, non-default timeoutl
+
+Returns: Exim message processing code (OK, FAIL, DEFER, ...)
+ where true means malware was found (condition applies)
+*/
+static int
+malware_internal(const uschar * malware_re, BOOL cacheable,
+ const uschar * scan_filename, int timeout)
+{
+int sep = 0;
+const uschar *av_scanner_work = av_scanner;
+BOOL av_scanner_textonly;
+uschar *scanner_name;
+unsigned long mbox_size;
+FILE *mbox_file;
+const pcre2_code *re;
+uschar * errstr;
+struct scan * scanent;
+const uschar * scanner_options;
+client_conn_ctx malware_daemon_ctx = {.sock = -1};
+time_t tmo;
+uschar * eml_filename, * eml_dir;
+
+if (!malware_re)
+ return FAIL; /* empty means "don't match anything" */
+
+/* Ensure the eml mbox file is spooled up */
+
+if (!(mbox_file = spool_mbox(&mbox_size, scan_filename, &eml_filename)))
+ return malware_panic_defer(US"error while creating mbox spool file");
+
+/* None of our current scanners need the mbox file as a stream (they use
+the name), so we can close it right away. Get the directory too. */
+
+(void) fclose(mbox_file);
+eml_dir = string_copyn(eml_filename, Ustrrchr(eml_filename, '/') - eml_filename);
+
+/* parse 1st option */
+if (strcmpic(malware_re, US"false") == 0 || Ustrcmp(malware_re, "0") == 0)
+ return FAIL; /* explicitly no matching */
+
+/* special cases (match anything except empty) */
+if ( strcmpic(malware_re, US"true") == 0
+ || Ustrcmp(malware_re, "*") == 0
+ || Ustrcmp(malware_re, "1") == 0
+ )
+ {
+ if ( !malware_default_re
+ && !(malware_default_re = m_pcre_compile(malware_regex_default, FALSE, &errstr)))
+ return malware_panic_defer(errstr);
+ malware_re = malware_regex_default;
+ re = malware_default_re;
+ }
+
+/* compile the regex, see if it works */
+else if (!(re = m_pcre_compile(malware_re, cacheable, &errstr)))
+ return malware_panic_defer(errstr);
+
+/* if av_scanner starts with a dollar, expand it first */
+if (*av_scanner == '$')
+ {
+ if (!(av_scanner_work = expand_string_2(av_scanner, &av_scanner_textonly)))
+ return malware_panic_defer(
+ string_sprintf("av_scanner starts with $, but expansion failed: %s",
+ expand_string_message));
+
+ DEBUG(D_acl)
+ debug_printf_indent("Expanded av_scanner global: %s\n", av_scanner_work);
+ /* disable result caching in this case */
+ malware_name = NULL;
+ malware_ok = FALSE;
+ }
+else
+ av_scanner_textonly = TRUE;
+
+/* Do not scan twice (unless av_scanner is dynamic). */
+if (!malware_ok)
+ {
+ /* find the scanner type from the av_scanner option */
+ if (!(scanner_name = string_nextinlist(&av_scanner_work, &sep, NULL, 0)))
+ return malware_panic_defer(US"av_scanner configuration variable is empty");
+ if (!timeout) timeout = MALWARE_TIMEOUT;
+ tmo = time(NULL) + timeout;
+
+ for (scanent = m_scans; ; scanent++)
+ {
+ if (!scanent->name)
+ return malware_panic_defer(string_sprintf("unknown scanner type '%s'",
+ scanner_name));
+ if (strcmpic(scanner_name, US scanent->name) != 0)
+ continue;
+ DEBUG(D_acl) debug_printf_indent("Malware scan: %s tmo=%s\n",
+ scanner_name, readconf_printtime(timeout));
+
+ if (!(scanner_options = string_nextinlist(&av_scanner_work, &sep, NULL, 0)))
+ scanner_options = scanent->options_default;
+ if (scanent->conn == MC_NONE)
+ break;
+
+ DEBUG(D_acl) debug_printf_indent("%15s%10s%s\n", "", "socket: ", scanner_options);
+ switch(scanent->conn)
+ {
+ case MC_TCP:
+ malware_daemon_ctx.sock = ip_tcpsocket(scanner_options, &errstr, 5, NULL); 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, NULL); break;
+ default:
+ /* compiler quietening */ break;