.vlist
.vitem &%avast%&
.cindex "virus scanners" "avast"
-This is the scanner daemon of Avast version 1.1.7 and 1.1.6
-(as reported by "/bin/avast -v").
-You can get a trial version at &url(http://www.avast.com).
-This scanner type requires one option,
-either a full path to a UNIX socket,
+This is the scanner daemon of Avast. It has been tested with Avast Core
+Security (currenty at version 1.1.7).
+You can get a trial version at &url(http://www.avast.com) or for Linux
+at &url(http://www.avast.com/linux-server-antivirus).
+This scanner type takes one option,
+which can be either a full path to a UNIX socket,
or host and port specifiers separated by white space.
-The host may a name or an IP address; the port is either a
+The host may be a name or an IP address; the port is either a
single number or a pair of numbers with a dash between.
Any further options are given, on separate lines,
to the daemon as options before the main scan command.
For example:
.code
-av_scanner = avast:/var/run/avast4/local.sock:FLAGS -fullfiles:SENSITIVITY -pup
+av_scanner = avast:/var/run/avast/scan.sock:FLAGS -fullfiles:SENSITIVITY -pup
av_scanner = avast:192.168.2.22 5036
.endd
If you omit the argument, the default path
-&_/var/run/avast4/local.sock_&
+&_/var/run/avast/scan.sock_&
is used.
If you use a remote host,
you need to make Exim's spool directory available to it,
as the scanner is passed a file path, not file contents.
+For information about available commands and their options you may use
+.code
+$ socat UNIX:/var/run/avast/scan.sock STDIO:
+ FLAGS
+ SENSITIVITY
+ PACK
+.endd
.vitem &%aveserver%&
int ovector[1*3];
uschar buf[1024];
uschar * scanrequest;
- const pcre * avast_scan_ok_re, * avast_virus_re;
- enum {AVA_HELO, AVA_OPT, AVA_CMD, AVA_RSP, AVA_POS, AVA_NEG} avast_stage;
-
- if ( !(avast_scan_ok_re = m_pcre_compile(US"\\[\\+\\]", &errstr))
+ const pcre * avast_clean_re, * avast_virus_re;
+ enum {AVA_HELO, AVA_OPT, AVA_RSP, AVA_DONE} avast_stage;
+
+ /* According to Martin Tuma @avast the protocol uses "escaped
+ whitespace", that is, every embedded whitespace is backslash
+ escaped, as well as backslash is protected by backslash.
+ The returned lines contain the name of the scanned file, a tab
+ and the [ ] marker.
+ [+] - not infected
+ [L] - infected
+ [E] - some error occured
+ Such marker follows the first non-escaped TAB. */
+ if ( !(avast_clean_re =
+ m_pcre_compile(US"(?!\\\\)\\t\\[\\+\\]", &errstr))
|| !(avast_virus_re =
- m_pcre_compile(US"\\[L\\]\\d\\.\\d\\t\\d\\s(.*)", &errstr))
+ m_pcre_compile(US"(?!\\\\)\\t\\[L\\]\\d\\.\\d\\t\\d\\s(.*)",
+ &errstr))
)
return malware_errlog_defer(errstr);
for (avast_stage = AVA_HELO; recv_line(sock, buf, sizeof(buf)) > 0; )
{
int slen = Ustrlen(buf);
- if (slen >= 1) switch (avast_stage)
+ if (slen >= 1)
{
- case AVA_HELO:
- 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 (Ustrncmp(buf, "200", 3) != 0)
- goto endloop; /* require a 200 */
-
- sendreq:
- /* Check for another option to send. Newline-terminate it. */
- if ((scanrequest = string_nextinlist(&av_scanner_work, &sep,
- NULL, 0)))
- {
- scanrequest = string_sprintf("%s\n", scanrequest);
- avast_stage = AVA_OPT; /* just sent option */
- }
- else
- {
- scanrequest = string_sprintf("SCAN %s/scan/%s\r\n",
- spool_directory, message_id);
- avast_stage = AVA_CMD; /* just sent command */
- }
-
- /* send config-cmd or scan-request to socket */
-
- if (send(sock, scanrequest, Ustrlen(scanrequest), 0) < 0)
- return m_errlog_defer_3(scanent,
- string_sprintf("unable to send scan request to socket (%s): %s",
- scanner_options, strerror(errno)),
- sock);
- break;
-
- case AVA_CMD:
- if (Ustrncmp(buf, "210", 3) == 0)
- break; /* ignore 210 responses */
+ DEBUG(D_acl) debug_printf("got from avast: %s\n", buf);
+ switch (avast_stage)
+ {
+ case AVA_HELO:
+ 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 (Ustrncmp(buf, "200", 3) != 0)
+ goto endloop; /* require a 200 */
+
+ sendreq:
+ /* Check for another option to send. Newline-terminate it. */
+ if ((scanrequest = string_nextinlist(&av_scanner_work, &sep,
+ NULL, 0)))
+ {
+ scanrequest = string_sprintf("%s\n", scanrequest);
+ avast_stage = AVA_OPT; /* just sent option */
+ }
+ else
+ {
+ scanrequest = string_sprintf("SCAN %s/scan/%s\r\n",
+ spool_directory, message_id);
+ avast_stage = AVA_RSP; /* just sent command */
+ }
- /* send (pipelined) quit request to socket */
- if (send(sock, "QUIT\n", 5, 0) < 0)
- return m_errlog_defer_3(scanent,
- string_sprintf("unable to send quit request to socket (%s): %s",
- scanner_options, strerror(errno)),
- sock);
- malware_name = NULL;
- avast_stage = AVA_RSP; /* waiting for actual response line */
- break;
+ /* send config-cmd or scan-request to socket */
+ if (send(sock, scanrequest, Ustrlen(scanrequest), 0) < 0)
+ return m_errlog_defer_3(scanent, string_sprintf(
+ "unable to send scan request to socket (%s): %s",
+ scanner_options, strerror(errno)),
+ sock);
+ break;
- default:
- if (Ustrncmp(buf, "221", 3) == 0)
- goto endloop; /* a "quit" response */
+ case AVA_RSP:
+ if (Ustrncmp(buf, "210", 3) == 0)
+ break; /* ignore the "210 SCAN DATA" message */
+
+ if (pcre_exec(avast_clean_re, NULL, CS buf, slen,
+ 0, 0, ovector, nelements(ovector)) > 0)
+ break;
+
+ if ((malware_name = m_pcre_exec(avast_virus_re, 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;
+ }
- if ((malware_name = m_pcre_exec(avast_virus_re, buf)))
- {
- /* remove backslashes from the virus string */
- uschar *p, *q;
- for (p = malware_name; *p; p++) if (*p == '\\')
- for (q = p; *q; q++) *q = q[1];
+ if (Ustrncmp(buf, "200 SCAN OK", 11) == 0)
+ { /* we're done finally */
+ if (send(sock, "QUIT\n", 5, 0) < 0) /* courtesy */
+ return m_errlog_defer_3(scanent, string_sprintf(
+ "unable to send quit request to socket (%s): %s",
+ scanner_options, strerror(errno)),
+ sock);
+ malware_name = NULL;
+ avast_stage = AVA_DONE;
+ goto endloop;
+ }
- avast_stage = AVA_POS;
+ /* here for any unexpected response from the scanner */
goto endloop;
- }
-
- if (pcre_exec(avast_scan_ok_re, NULL, CS buf, slen,
- 0, 0, ovector, nelements(ovector)) > 0)
- avast_stage = AVA_NEG;
- break;
- }
+ }
}
+ }
endloop:
switch(avast_stage)
{
- case AVA_HELO: return m_errlog_defer_3(scanent,
- US"invalid response from scanner", sock);
- case AVA_CMD: return m_errlog_defer_3(scanent,
- US"unable to read return code", sock);
- case AVA_RSP: return m_errlog_defer_3(scanent,
- US"response interrupted", sock);
+ case AVA_HELO:
+ case AVA_OPT:
+ case AVA_RSP: return m_errlog_defer_3(scanent, string_sprintf(
+ "invalid response from scanner: %s\n", buf), sock);
default: break;
}
}