diff -urN ../exim-4.90.1.orig/src/malware.c ./src/malware.c --- ../exim-4.90.1.orig/src/malware.c 2018-02-08 15:22:40.000000000 +0200 +++ ./src/malware.c 2018-03-10 14:35:47.886709000 +0200 @@ -1831,6 +1831,7 @@ uschar * scanrequest; enum {AVA_HELO, AVA_OPT, AVA_RSP, AVA_DONE} avast_stage; int nread; + int more_data; /* According to Martin Tuma @avast the protocol uses "escaped whitespace", that is, every embedded whitespace is backslash @@ -1840,7 +1841,9 @@ [+] - not infected [L] - infected [E] - some error occured - Such marker follows the first non-escaped TAB. */ + Such marker follows the first non-escaped TAB. + For more information see avast-protocol(5) + */ if ( ( !ava_re_clean && !(ava_re_clean = m_pcre_compile(ava_re_clean_str, &errstr))) || ( !ava_re_virus @@ -1856,17 +1859,21 @@ int slen = Ustrlen(buf); if (slen >= 1) { - DEBUG(D_acl) debug_printf_indent("got from avast: %s\n", buf); + + /* Multi line responses are bracketed between 210 … and nnn … */ + if (Ustrncmp(buf, "210", 3) == 0) { more_data = 1; continue; } + else more_data = more_data && !isdigit(buf[0]); + switch (avast_stage) { case AVA_HELO: + if (more_data) continue; if (Ustrncmp(buf, "220", 3) != 0) goto endloop; /* require a 220 */ goto sendreq; case AVA_OPT: - if (Ustrncmp(buf, "210", 3) == 0) - break; /* ignore 210 responses */ + if (more_data) continue; if (Ustrncmp(buf, "200", 3) != 0) goto endloop; /* require a 200 */ @@ -1879,11 +1886,13 @@ { scanrequest = string_sprintf("%s\n", scanrequest); avast_stage = AVA_OPT; /* just sent option */ + DEBUG(D_acl) debug_printf_indent("send to avast OPTION: %s", scanrequest); } else { scanrequest = string_sprintf("SCAN %s\n", eml_dir); avast_stage = AVA_RSP; /* just sent command */ + DEBUG(D_acl) debug_printf_indent("send to avast REQUEST: SCAN %s\n", eml_dir); } /* send config-cmd or scan-request to socket */ @@ -1899,24 +1908,25 @@ } case AVA_RSP: - if (Ustrncmp(buf, "210", 3) == 0) - break; /* ignore the "210 SCAN DATA" message */ - if (pcre_exec(ava_re_clean, NULL, CS buf, slen, 0, 0, ovector, nelements(ovector)) > 0) break; - if ((malware_name = m_pcre_exec(ava_re_virus, buf))) + if ( !malware_name + && (malware_name = m_pcre_exec(ava_re_virus, buf))) { /* remove backslash in front of [whitespace|backslash] */ uschar * p, * p0; for (p = malware_name; *p; ++p) if (*p == '\\' && (isspace(p[1]) || p[1] == '\\')) for (p0 = p; *p0; ++p0) *p0 = p0[1]; - avast_stage = AVA_DONE; - goto endloop; + DEBUG(D_acl) + debug_printf_indent("unescaped malware name: '%s'\n", malware_name); + break; } + if (more_data) continue; + if (Ustrncmp(buf, "200 SCAN OK", 11) == 0) { /* we're done finally */ if (send(sock, "QUIT\n", 5, 0) < 0) /* courtesy */ @@ -1925,15 +1935,14 @@ "unable to send quit request to socket (%s): %s", scanner_options, strerror(errno)), sock); - malware_name = NULL; + avast_stage = AVA_DONE; - goto endloop; } - /* here for any unexpected response from the scanner */ + /* here also for any unexpected response from the scanner */ goto endloop; - case AVA_DONE: log_write(0, LOG_PANIC, "%s:%d:%s: should not happen", + default: log_write(0, LOG_PANIC, "%s:%d:%s: should not happen", __FILE__, __LINE__, __FUNCTION__); } }