From: Jeremy Harris Date: Sun, 28 Dec 2014 17:16:54 +0000 (+0000) Subject: Support timeout option on malware= X-Git-Tag: exim-4_91_RC1~148^2~42^2~41 X-Git-Url: https://git.exim.org/users/heiko/exim.git/commitdiff_plain/0e42da1b15894b66e3726d8ce0e41e25bdc21149?ds=inline Support timeout option on malware= --- diff --git a/doc/doc-docbook/spec.xfpt b/doc/doc-docbook/spec.xfpt index 0b592e006..d7d003a30 100644 --- a/doc/doc-docbook/spec.xfpt +++ b/doc/doc-docbook/spec.xfpt @@ -30343,7 +30343,7 @@ It supports a &"generic"& interface to scanners called via the shell, and specialized interfaces for &"daemon"& type virus scanners, which are resident in memory and thus are much faster. -A timeout of 2 minutes is applied to a scanner call; +A timeout of 2 minutes is applied to a scanner call (by default); if it expires then a defer action is taken. .oindex "&%av_scanner%&" @@ -30576,7 +30576,8 @@ which case each use of the &%malware%& condition causes a new scan of the message. The &%malware%& condition takes a right-hand argument that is expanded before -use. It can then be one of +use and taken as a list, slash-separated by default. +The first element can then be one of .ilist &"true"&, &"*"&, or &"1"&, in which case the message is scanned for viruses. @@ -30589,11 +30590,21 @@ the condition fails immediately. A regular expression, in which case the message is scanned for viruses. The condition succeeds if a virus is found and its name matches the regular expression. This allows you to take special actions on certain types of virus. +Note that &"/"& characters in the RE must be doubled due to the list-processing, +unless the separator is changed (in the usual way). .endlist -You can append &`/defer_ok`& to the &%malware%& condition to accept messages -even if there is a problem with the virus scanner. Otherwise, such a problem -causes the ACL to defer. +You can append a &`defer_ok`& element to the &%malware%& argument list to accept +messages even if there is a problem with the virus scanner. +Otherwise, such a problem causes the ACL to defer. + +You can append a &`tmo=`& element to the &%malware%& argument list to +specify a non-default timeout. The default is two minutes. +For example: +.code +malware = * / defer_ok / tmo=10s +.endc +A timeout causes the ACL to defer. .vindex "&$malware_name$&" When a virus is found, the condition sets up an expansion variable called diff --git a/doc/doc-txt/ChangeLog b/doc/doc-txt/ChangeLog index 7eaa074ba..2dfd7d347 100644 --- a/doc/doc-txt/ChangeLog +++ b/doc/doc-txt/ChangeLog @@ -35,7 +35,9 @@ JH/08 The EXPERIMENTAL_DSN compile option is no longer needed; all Delivery under the control of the dsn_advertise_hosts option, and routers may have a dsn_lasthop option. -JH/09 A timeout of 2 minutes is now applied to all malware scanner types. +JH/09 A timeout of 2 minutes is now applied to all malware scanner types by + default, modifiable by a malware= option. The list separator for + the options can now be changed in the usual way. diff --git a/src/src/acl.c b/src/src/acl.c index fe1e254bd..4b66fad45 100644 --- a/src/src/acl.c +++ b/src/src/acl.c @@ -2979,7 +2979,7 @@ uschar *debug_opts = NULL; uschar *p = NULL; int rc = OK; #ifdef WITH_CONTENT_SCAN -int sep = '/'; +int sep = -'/'; #endif for (; cb != NULL; cb = cb->next) @@ -3580,21 +3580,28 @@ for (; cb != NULL; cb = cb->next) break; #ifdef WITH_CONTENT_SCAN - case ACLC_MALWARE: + case ACLC_MALWARE: /* Run the malware backend. */ { /* Separate the regular expression and any optional parameters. */ uschar *ss = string_nextinlist(&arg, &sep, big_buffer, big_buffer_size); - /* Run the malware backend. */ - rc = malware(&ss); - /* Modify return code based upon the existance of options. */ - while ((ss = string_nextinlist(&arg, &sep, big_buffer, big_buffer_size)) - != NULL) { - if (strcmpic(ss, US"defer_ok") == 0 && rc == DEFER) - { - /* FAIL so that the message is passed to the next ACL */ - rc = FAIL; - } - } + uschar *opt; + BOOL defer_ok = FALSE; + int timeout = 0; + + while ((opt = string_nextinlist(&arg, &sep, NULL, 0))) + if (strcmpic(opt, US"defer_ok") == 0) + defer_ok = TRUE; + else if ( strncmpic(opt, US"tmo=", 4) == 0 + && (timeout = readconf_readtime(opt+4, '\0', FALSE)) < 0 + ) + { + *log_msgptr = string_sprintf("bad timeout value in '%s'", opt); + return ERROR; + } + + rc = malware(ss, timeout); + if (rc == DEFER && defer_ok) + rc = FAIL; /* FAIL so that the message is passed to the next ACL */ } break; diff --git a/src/src/expand.c b/src/src/expand.c index 9e3b33059..bf79a8222 100644 --- a/src/src/expand.c +++ b/src/src/expand.c @@ -1192,10 +1192,10 @@ int sep= 0; uschar dummy; if(field<0) -{ + { for(field++; string_nextinlist(&tlist, &sep, &dummy, 1); ) field++; sep= 0; -} + } if(field==0) return NULL; while(--field>0 && (string_nextinlist(&list, &sep, &dummy, 1))) ; return string_nextinlist(&list, &sep, NULL, 0); diff --git a/src/src/functions.h b/src/src/functions.h index e2e9befb3..ce78288fb 100644 --- a/src/src/functions.h +++ b/src/src/functions.h @@ -220,7 +220,7 @@ extern int log_create_as_exim(uschar *); extern void log_close_all(void); #ifdef WITH_CONTENT_SCAN -extern int malware(uschar **); +extern int malware(const uschar *, int); extern int malware_in_file(uschar *); #endif extern int match_address_list(uschar *, BOOL, BOOL, uschar **, @@ -375,7 +375,7 @@ extern BOOL smtp_verify_helo(void); extern int smtp_write_command(smtp_outblock *, BOOL, const char *, ...) PRINTF_FUNCTION(3,4); #ifdef WITH_CONTENT_SCAN extern int spam(uschar **); -extern FILE *spool_mbox(unsigned long *, uschar *); +extern FILE *spool_mbox(unsigned long *, const uschar *); #endif extern BOOL spool_move_message(uschar *, uschar *, uschar *, uschar *); extern BOOL spool_open_datafile(uschar *); diff --git a/src/src/ip.c b/src/src/ip.c index a50e209a2..91b74e20e 100644 --- a/src/src/ip.c +++ b/src/src/ip.c @@ -413,7 +413,7 @@ for (;;) tv.tv_sec = timeout; tv.tv_usec = 0; - DEBUG(D_transport) debug_printf("waiting for data on fd\n"); + /*DEBUG(D_transport) debug_printf("waiting for data on fd\n");*/ rc = select(fd + 1, (SELECT_ARG2_TYPE *)&select_inset, NULL, NULL, &tv); /* If some interrupt arrived, just retry. We presume this to be rare, diff --git a/src/src/malware.c b/src/src/malware.c index 30586feda..659357633 100644 --- a/src/src/malware.c +++ b/src/src/malware.c @@ -47,16 +47,12 @@ typedef struct clamd_address_container { unsigned int tcp_port; } clamd_address_container; -/* declaration of private routines */ -static int mksd_scan_packed(struct scan * scanent, int sock, uschar *scan_filename, int tmo); -static int malware_internal(uschar **listptr, uschar *eml_filename, BOOL faking); - #ifndef nelements # define nelements(arr) (sizeof(arr) / sizeof(arr[0])) #endif -#define MALWARE_TIMEOUT 120 +#define MALWARE_TIMEOUT 120 /* default timeout, seconds */ #define DRWEBD_SCAN_CMD (1) /* scan file, buffer or diskfile */ @@ -90,83 +86,6 @@ needed header variables if not already set on the command-line? */ extern int spool_mbox_ok; extern uschar spooled_message_id[17]; -/************************************************* -* Scan an email for malware * -*************************************************/ - -/* This is the normal interface for scanning an email, which doesn't need a -filename; it's a wrapper around the malware_file function. - -Arguments: - listptr the list of options to the "malware = ..." ACL condition - -Returns: Exim message processing code (OK, FAIL, DEFER, ...) - where true means malware was found (condition applies) -*/ -int -malware(uschar **listptr) -{ - uschar * scan_filename; - int ret; - - scan_filename = string_sprintf("%s/scan/%s/%s.eml", - spool_directory, message_id, message_id); - ret = malware_internal(listptr, scan_filename, FALSE); - if (ret == DEFER) av_failed = TRUE; - - return ret; -} - - -/************************************************* -* Scan a file for malware * -*************************************************/ - -/* This is a test wrapper for scanning an email, which is not used in -normal processing. Scan any file, using the Exim scanning interface. -This function tampers with various global variables so is unsafe to use -in any other context. - -Arguments: - eml_filename a file holding the message to be scanned - -Returns: Exim message processing code (OK, FAIL, DEFER, ...) - where true means malware was found (condition applies) -*/ -int -malware_in_file(uschar *eml_filename) -{ - uschar *scan_options[2]; - uschar message_id_buf[64]; - int ret; - - scan_options[0] = US"*"; - scan_options[1] = NULL; - - /* spool_mbox() assumes various parameters exist, when creating - the relevant directory and the email within */ - (void) string_format(message_id_buf, sizeof(message_id_buf), - "dummy-%d", vaguely_random_number(INT_MAX)); - message_id = message_id_buf; - sender_address = US"malware-sender@example.net"; - return_path = US""; - recipients_list = NULL; - receive_add_recipient(US"malware-victim@example.net", -1); - enable_dollar_recipients = TRUE; - - ret = malware_internal(scan_options, eml_filename, TRUE); - - Ustrncpy(spooled_message_id, message_id, sizeof(spooled_message_id)); - spool_mbox_ok = 1; - /* don't set no_mbox_unspool; at present, there's no way for it to become - set, but if that changes, then it should apply to these tests too */ - unspool_mbox(); - - /* silence static analysis tools */ - message_id = NULL; - - return ret; -} static inline int @@ -310,25 +229,36 @@ m_pcre_nextinlist(uschar ** list, int * sep, char * listerr, uschar ** errstr) trailing newline. Can return early on buffer full. Null-terminate. Apply initial timeout if no data ready. - Return: number of chars - zero for an empty line or EOF + Return: number of chars - zero for an empty line + -1 on EOF + -2 on timeout or error */ static int recv_line(int fd, uschar * buffer, int bsize, int tmo) { uschar * p = buffer; ssize_t rcv; +BOOL ok = FALSE; if (!fd_ready(fd, tmo-time(NULL))) - return -1; + return -2; - /*XXX tmo handling assumes we always get a whole line */ +/*XXX tmo handling assumes we always get a whole line */ /* read until \n */ +errno = 0; while ((rcv = read(fd, p, 1)) > 0) { + ok = TRUE; if (p-buffer > bsize-2) break; if (*p == '\n') break; if (*p != '\r') p++; } +if (!ok) + { + DEBUG(D_acl) debug_printf("Malware scan: read %s (%s)\n", + rcv==0 ? "EOF" : "error", strerror(errno)); + return rcv==0 ? -1 : -2; + } *p = '\0'; DEBUG(D_acl) debug_printf("Malware scan: read '%s'\n", buffer); @@ -345,6 +275,133 @@ return fd_ready(sock, tmo-time(NULL)) } + +/* ============= private routines for the "mksd" scanner type ============== */ + +#include + +static inline int +mksd_writev (int sock, struct iovec * iov, int iovcnt) +{ +int i; + +for (;;) + { + do + i = writev (sock, iov, iovcnt); + while (i < 0 && errno == EINTR); + if (i <= 0) + { + (void) malware_errlog_defer( + US"unable to write to mksd UNIX socket (/var/run/mksd/socket)"); + return -1; + } + for (;;) /* check for short write */ + if (i >= iov->iov_len) + { + if (--iovcnt == 0) + return 0; + i -= iov->iov_len; + iov++; + } + else + { + iov->iov_len -= i; + iov->iov_base = CS iov->iov_base + i; + break; + } + } +} + +static inline int +mksd_read_lines (int sock, uschar *av_buffer, int av_buffer_size, int tmo) +{ +int offset = 0; +int i; + +do + { + i = ip_recv(sock, av_buffer+offset, av_buffer_size-offset, tmo-time(NULL)); + if (i <= 0) + { + (void) malware_errlog_defer(US"unable to read from mksd UNIX socket (/var/run/mksd/socket)"); + return -1; + } + + offset += i; + /* offset == av_buffer_size -> buffer full */ + if (offset == av_buffer_size) + { + (void) malware_errlog_defer(US"malformed reply received from mksd"); + return -1; + } + } while (av_buffer[offset-1] != '\n'); + +av_buffer[offset] = '\0'; +return offset; +} + +static inline int +mksd_parse_line(struct scan * scanent, char * line) +{ +char *p; + +switch (*line) + { + case 'O': /* OK */ + return OK; + + case 'E': + case 'A': /* ERR */ + if ((p = strchr (line, '\n')) != NULL) + *p = '\0'; + return m_errlog_defer(scanent, + string_sprintf("scanner failed: %s", line)); + + default: /* VIR */ + if ((p = strchr (line, '\n')) != NULL) + { + *p = '\0'; + if ( p-line > 5 + && line[3] == ' ' + && (p = strchr(line+4, ' ')) != NULL + && p-line > 4 + ) + { + *p = '\0'; + malware_name = string_copy(US line+4); + return OK; + } + } + return m_errlog_defer(scanent, + string_sprintf("malformed reply received: %s", line)); + } +} + +static int +mksd_scan_packed(struct scan * scanent, int sock, const uschar * scan_filename, + int tmo) +{ +struct iovec iov[3]; +const char *cmd = "MSQ\n"; +uschar av_buffer[1024]; + +iov[0].iov_base = (void *) cmd; +iov[0].iov_len = 3; +iov[1].iov_base = (void *) scan_filename; +iov[1].iov_len = Ustrlen(scan_filename); +iov[2].iov_base = (void *) (cmd + 3); +iov[2].iov_len = 1; + +if (mksd_writev (sock, iov, 3) < 0) + return DEFER; + +if (mksd_read_lines (sock, av_buffer, sizeof (av_buffer), tmo) < 0) + return DEFER; + +return mksd_parse_line (scanent, CS av_buffer); +} + /************************************************* * Scan content for malware * *************************************************/ @@ -353,493 +410,500 @@ return fd_ready(sock, tmo-time(NULL)) is via malware(), or there's malware_in_file() used for testing/debugging. Arguments: - listptr the list of options to the "malware = ..." ACL condition + malware_re match condition for "malware=" eml_filename the file holding the email to be scanned + timeout if nonzero, non-default timeoutl faking whether or not we're faking this up for the -bmalware test Returns: Exim message processing code (OK, FAIL, DEFER, ...) where true means malware was found (condition applies) */ static int -malware_internal(uschar **listptr, uschar *eml_filename, BOOL faking) +malware_internal(const uschar * malware_re, const uschar * eml_filename, + int timeout, BOOL faking) { - int sep = 0; - uschar *list = *listptr; - uschar *av_scanner_work = av_scanner; - uschar *scanner_name; - uschar *malware_regex; - uschar malware_regex_default[] = ".+"; - unsigned long mbox_size; - FILE *mbox_file; - const pcre *re; - uschar * errstr; - struct scan * scanent; - const uschar * scanner_options; - int sock = -1; - time_t tmo; - - /* make sure the eml mbox file is spooled up */ - if (!(mbox_file = spool_mbox(&mbox_size, faking ? eml_filename : NULL))) - return malware_errlog_defer(US"error while creating mbox spool file"); - - /* none of our current scanners need the mbox - file as a stream, so we can close it right away */ - (void)fclose(mbox_file); - - /* extract the malware regex to match against from the option list */ - if (!(malware_regex = string_nextinlist(&list, &sep, NULL, 0))) - return FAIL; /* empty means "don't match anything" */ - - /* parse 1st option */ - if ( (strcmpic(malware_regex,US"false") == 0) || - (Ustrcmp(malware_regex,"0") == 0) ) - return FAIL; /* explicitly no matching */ - - /* special cases (match anything except empty) */ - if ( (strcmpic(malware_regex,US"true") == 0) || - (Ustrcmp(malware_regex,"*") == 0) || - (Ustrcmp(malware_regex,"1") == 0) ) - malware_regex = malware_regex_default; - - /* Reset sep that is set by previous string_nextinlist() call */ - sep = 0; - - /* compile the regex, see if it works */ - if (!(re = m_pcre_compile(malware_regex, &errstr))) - return malware_errlog_defer(errstr); - - /* if av_scanner starts with a dollar, expand it first */ - if (*av_scanner == '$') { - if (!(av_scanner_work = expand_string(av_scanner))) - return malware_errlog_defer( - string_sprintf("av_scanner starts with $, but expansion failed: %s", - expand_string_message)); +int sep = 0; +uschar *av_scanner_work = av_scanner; +uschar *scanner_name; +uschar malware_regex_default[] = ".+"; +unsigned long mbox_size; +FILE *mbox_file; +const pcre *re; +uschar * errstr; +struct scan * scanent; +const uschar * scanner_options; +int sock = -1; +time_t tmo; + +/* make sure the eml mbox file is spooled up */ +if (!(mbox_file = spool_mbox(&mbox_size, faking ? eml_filename : NULL))) + return malware_errlog_defer(US"error while creating mbox spool file"); + +/* none of our current scanners need the mbox + file as a stream, so we can close it right away */ +(void)fclose(mbox_file); + +if (!malware_re) + return FAIL; /* empty means "don't match anything" */ + +/* 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) ) + malware_re = malware_regex_default; + +/* Reset sep that is set by previous string_nextinlist() call */ +sep = 0; + +/* compile the regex, see if it works */ +if (!(re = m_pcre_compile(malware_re, &errstr))) + return malware_errlog_defer(errstr); + +/* if av_scanner starts with a dollar, expand it first */ +if (*av_scanner == '$') + { + if (!(av_scanner_work = expand_string(av_scanner))) + return malware_errlog_defer( + string_sprintf("av_scanner starts with $, but expansion failed: %s", + expand_string_message)); + DEBUG(D_acl) debug_printf("Expanded av_scanner global: %s\n", av_scanner_work); - /* disable result caching in this case */ - malware_name = NULL; - malware_ok = FALSE; + /* disable result caching in this case */ + malware_name = NULL; + malware_ok = FALSE; } - /* 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_errlog_defer(US"av_scanner configuration variable is empty"); - tmo = time(NULL) + MALWARE_TIMEOUT; - - for (scanent = m_scans; ; scanent++) { - if (!scanent->name) - return malware_errlog_defer(string_sprintf("unknown scanner type '%s'", - scanner_name)); - if (strcmpic(scanner_name, US scanent->name) != 0) - continue; - if (!(scanner_options = string_nextinlist(&av_scanner_work, &sep, NULL, 0))) - scanner_options = scanent->options_default; - if (scanent->conn == MC_NONE) - break; - switch(scanent->conn) - { - case MC_TCP: sock = m_tcpsocket_fromdef(scanner_options, &errstr); break; - case MC_UNIX: sock = m_unixsocket(scanner_options, &errstr); break; - case MC_STRM: sock = m_streamsocket(scanner_options, &errstr); break; - default: /* compiler quietening */ break; - } - if (sock < 0) - return m_errlog_defer(scanent, errstr); +/* 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_errlog_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_errlog_defer(string_sprintf("unknown scanner type '%s'", + scanner_name)); + if (strcmpic(scanner_name, US scanent->name) != 0) + continue; + if (!(scanner_options = string_nextinlist(&av_scanner_work, &sep, NULL, 0))) + scanner_options = scanent->options_default; + if (scanent->conn == MC_NONE) break; + switch(scanent->conn) + { + case MC_TCP: sock = m_tcpsocket_fromdef(scanner_options, &errstr); break; + case MC_UNIX: sock = m_unixsocket(scanner_options, &errstr); break; + case MC_STRM: sock = m_streamsocket(scanner_options, &errstr); break; + default: /* compiler quietening */ break; } - DEBUG(D_lookup) debug_printf("Malware scan: %s\n", scanner_name); + if (sock < 0) + return m_errlog_defer(scanent, errstr); + break; + } + DEBUG(D_acl) debug_printf("Malware scan: %s tmo %s\n", scanner_name, readconf_printtime(timeout)); - switch (scanent->scancode) + switch (scanent->scancode) + { + case M_FPROTD: /* "f-protd" scanner type -------------------------------- */ { - case M_FPROTD: /* "f-protd" scanner type ------------------------------ */ - { - uschar *fp_scan_option; - unsigned int detected=0, par_count=0; - uschar * scanrequest; - uschar buf[32768], *strhelper, *strhelper2; - uschar * malware_name_internal = NULL; - int len; + uschar *fp_scan_option; + unsigned int detected=0, par_count=0; + uschar * scanrequest; + uschar buf[32768], *strhelper, *strhelper2; + uschar * malware_name_internal = NULL; + int len; - scanrequest = string_sprintf("GET %s", eml_filename); + scanrequest = string_sprintf("GET %s", eml_filename); - while ((fp_scan_option = string_nextinlist(&av_scanner_work, &sep, - NULL, 0))) - { - scanrequest = string_sprintf("%s%s%s", scanrequest, - par_count ? "%20" : "?", fp_scan_option); - par_count++; - } - scanrequest = string_sprintf("%s HTTP/1.0\r\n\r\n", scanrequest); - DEBUG(D_acl) debug_printf("Malware scan: issuing %s: %s\n", - scanner_name, scanrequest); + while ((fp_scan_option = string_nextinlist(&av_scanner_work, &sep, + NULL, 0))) + { + scanrequest = string_sprintf("%s%s%s", scanrequest, + par_count ? "%20" : "?", fp_scan_option); + par_count++; + } + scanrequest = string_sprintf("%s HTTP/1.0\r\n\r\n", scanrequest); + DEBUG(D_acl) debug_printf("Malware scan: issuing %s: %s\n", + scanner_name, scanrequest); - /* send scan request */ - if (m_sock_send(sock, scanrequest, Ustrlen(scanrequest)+1, &errstr) < 0) - return m_errlog_defer(scanent, errstr); + /* send scan request */ + if (m_sock_send(sock, scanrequest, Ustrlen(scanrequest)+1, &errstr) < 0) + return m_errlog_defer(scanent, errstr); - while ((len = recv_line(sock, buf, sizeof(buf), tmo)) >= 0) - if (len > 0) + while ((len = recv_line(sock, buf, sizeof(buf), tmo)) >= 0) + if (len > 0) + { + if (Ustrstr(buf, US""))) { - if (Ustrstr(buf, US""))) - { - if ((strhelper2 = Ustrstr(buf, US"")) != NULL) - { - *strhelper2 = '\0'; - malware_name_internal = string_copy(strhelper+6); - } - } - else if (Ustrstr(buf, US"")) != NULL) { - malware_name = Ustrstr(buf, US"") - ? malware_name_internal : NULL; - break; + *strhelper2 = '\0'; + malware_name_internal = string_copy(strhelper+6); } } - break; - } /* f-protd */ - - case M_DRWEB: /* "drweb" scanner type --------------------------------- */ - /* v0.1 - added support for tcp sockets */ - /* v0.0 - initial release -- support for unix sockets */ - { - int result; - off_t fsize; - unsigned int fsize_uint; - uschar * tmpbuf, *drweb_fbuf; - int drweb_rc, drweb_cmd, drweb_flags = 0x0000, drweb_fd, - drweb_vnum, drweb_slen, drweb_fin = 0x0000; - const pcre *drweb_re; - - /* prepare variables */ - drweb_cmd = htonl(DRWEBD_SCAN_CMD); - drweb_flags = htonl(DRWEBD_RETURN_VIRUSES | DRWEBD_IS_MAIL); - - if (*scanner_options != '/') - { - /* calc file size */ - if ((drweb_fd = open(CS eml_filename, O_RDONLY)) == -1) - return m_errlog_defer_3(scanent, - string_sprintf("can't open spool file %s: %s", - eml_filename, strerror(errno)), - sock); - - if ((fsize = lseek(drweb_fd, 0, SEEK_END)) == -1) - { - int err = errno; - (void)close(drweb_fd); - return m_errlog_defer_3(scanent, - string_sprintf("can't seek spool file %s: %s", - eml_filename, strerror(err)), - sock); - } - fsize_uint = (unsigned int) fsize; - if ((off_t)fsize_uint != fsize) + else if (Ustrstr(buf, US"") + ? malware_name_internal : NULL; + break; } + } + if (len < -1) + { + (void)close(sock); + return DEFER; + } + break; + } /* f-protd */ - if (!(drweb_fbuf = (uschar *) malloc (fsize_uint))) - { - (void)close(drweb_fd); - return m_errlog_defer_3(scanent, - string_sprintf("unable to allocate memory %u for file (%s)", - fsize_uint, eml_filename), - sock); - } + case M_DRWEB: /* "drweb" scanner type ----------------------------------- */ + /* v0.1 - added support for tcp sockets */ + /* v0.0 - initial release -- support for unix sockets */ + { + int result; + off_t fsize; + unsigned int fsize_uint; + uschar * tmpbuf, *drweb_fbuf; + int drweb_rc, drweb_cmd, drweb_flags = 0x0000, drweb_fd, + drweb_vnum, drweb_slen, drweb_fin = 0x0000; + const pcre *drweb_re; + + /* prepare variables */ + drweb_cmd = htonl(DRWEBD_SCAN_CMD); + drweb_flags = htonl(DRWEBD_RETURN_VIRUSES | DRWEBD_IS_MAIL); + + if (*scanner_options != '/') + { + /* calc file size */ + if ((drweb_fd = open(CCS eml_filename, O_RDONLY)) == -1) + return m_errlog_defer_3(scanent, + string_sprintf("can't open spool file %s: %s", + eml_filename, strerror(errno)), + sock); - if ((result = read (drweb_fd, drweb_fbuf, fsize)) == -1) - { - int err = errno; - (void)close(drweb_fd); - free(drweb_fbuf); - return m_errlog_defer_3(scanent, - string_sprintf("can't read spool file %s: %s", - eml_filename, strerror(err)), - sock); - } + if ((fsize = lseek(drweb_fd, 0, SEEK_END)) == -1) + { + int err = errno; (void)close(drweb_fd); - - /* send file body to socket */ - if (send(sock, drweb_fbuf, fsize, 0) < 0) - { - free(drweb_fbuf); - return m_errlog_defer_3(scanent, string_sprintf( - "unable to send file body to socket (%s)", scanner_options), - sock); - } + return m_errlog_defer_3(scanent, + string_sprintf("can't seek spool file %s: %s", + eml_filename, strerror(err)), + sock); + } + fsize_uint = (unsigned int) fsize; + if ((off_t)fsize_uint != fsize) + { (void)close(drweb_fd); + return m_errlog_defer_3(scanent, + string_sprintf("seeking spool file %s, size overflow", + eml_filename), + sock); } - else + drweb_slen = htonl(fsize); + lseek(drweb_fd, 0, SEEK_SET); + + DEBUG(D_acl) debug_printf("Malware scan: issuing %s remote scan [%s]\n", + scanner_name, scanner_options); + + /* send scan request */ + if ((send(sock, &drweb_cmd, sizeof(drweb_cmd), 0) < 0) || + (send(sock, &drweb_flags, sizeof(drweb_flags), 0) < 0) || + (send(sock, &drweb_fin, sizeof(drweb_fin), 0) < 0) || + (send(sock, &drweb_slen, sizeof(drweb_slen), 0) < 0)) { - drweb_slen = htonl(Ustrlen(eml_filename)); - - DEBUG(D_acl) debug_printf("Malware scan: issuing %s local scan [%s]\n", - scanner_name, scanner_options); - - /* send scan request */ - if ((send(sock, &drweb_cmd, sizeof(drweb_cmd), 0) < 0) || - (send(sock, &drweb_flags, sizeof(drweb_flags), 0) < 0) || - (send(sock, &drweb_slen, sizeof(drweb_slen), 0) < 0) || - (send(sock, eml_filename, Ustrlen(eml_filename), 0) < 0) || - (send(sock, &drweb_fin, sizeof(drweb_fin), 0) < 0)) - return m_errlog_defer_3(scanent, string_sprintf( - "unable to send commands to socket (%s)", scanner_options), - sock); + (void)close(drweb_fd); + return m_errlog_defer_3(scanent, string_sprintf( + "unable to send commands to socket (%s)", scanner_options), + sock); } - /* wait for result */ - if (!recv_len(sock, &drweb_rc, sizeof(drweb_rc), tmo)) + if (!(drweb_fbuf = (uschar *) malloc (fsize_uint))) + { + (void)close(drweb_fd); return m_errlog_defer_3(scanent, - US"unable to read return code", sock); - drweb_rc = ntohl(drweb_rc); + string_sprintf("unable to allocate memory %u for file (%s)", + fsize_uint, eml_filename), + sock); + } - if (!recv_len(sock, &drweb_vnum, sizeof(drweb_vnum), tmo)) + if ((result = read (drweb_fd, drweb_fbuf, fsize)) == -1) + { + int err = errno; + (void)close(drweb_fd); + free(drweb_fbuf); return m_errlog_defer_3(scanent, - US"unable to read the number of viruses", sock); - drweb_vnum = ntohl(drweb_vnum); + string_sprintf("can't read spool file %s: %s", + eml_filename, strerror(err)), + sock); + } + (void)close(drweb_fd); - /* "virus(es) found" if virus number is > 0 */ - if (drweb_vnum) + /* send file body to socket */ + if (send(sock, drweb_fbuf, fsize, 0) < 0) { - int i; - - /* setup default virus name */ - malware_name = US"unknown"; + free(drweb_fbuf); + return m_errlog_defer_3(scanent, string_sprintf( + "unable to send file body to socket (%s)", scanner_options), + sock); + } + (void)close(drweb_fd); + } + else + { + drweb_slen = htonl(Ustrlen(eml_filename)); - /* set up match regex */ - drweb_re = m_pcre_compile(US"infected\\swith\\s*(.+?)$", &errstr); + DEBUG(D_acl) debug_printf("Malware scan: issuing %s local scan [%s]\n", + scanner_name, scanner_options); - /* read and concatenate virus names into one string */ - for (i = 0; i < drweb_vnum; i++) - { - int size = 0, off = 0, ovector[10*3]; - /* read the size of report */ - if (!recv_len(sock, &drweb_slen, sizeof(drweb_slen), tmo)) - return m_errlog_defer_3(scanent, - US"cannot read report size", sock); - drweb_slen = ntohl(drweb_slen); - tmpbuf = store_get(drweb_slen); - - /* read report body */ - if (!recv_len(sock, tmpbuf, drweb_slen, tmo)) - return m_errlog_defer_3(scanent, - US"cannot read report string", sock); - tmpbuf[drweb_slen] = '\0'; - - /* try matcher on the line, grab substring */ - result = pcre_exec(drweb_re, NULL, CS tmpbuf, Ustrlen(tmpbuf), 0, 0, - ovector, nelements(ovector)); - if (result >= 2) - { - const char * pre_malware_nb; + /* send scan request */ + if ((send(sock, &drweb_cmd, sizeof(drweb_cmd), 0) < 0) || + (send(sock, &drweb_flags, sizeof(drweb_flags), 0) < 0) || + (send(sock, &drweb_slen, sizeof(drweb_slen), 0) < 0) || + (send(sock, eml_filename, Ustrlen(eml_filename), 0) < 0) || + (send(sock, &drweb_fin, sizeof(drweb_fin), 0) < 0)) + return m_errlog_defer_3(scanent, string_sprintf( + "unable to send commands to socket (%s)", scanner_options), + sock); + } - pcre_get_substring(CS tmpbuf, ovector, result, 1, &pre_malware_nb); + /* wait for result */ + if (!recv_len(sock, &drweb_rc, sizeof(drweb_rc), tmo)) + return m_errlog_defer_3(scanent, + US"unable to read return code", sock); + drweb_rc = ntohl(drweb_rc); + + if (!recv_len(sock, &drweb_vnum, sizeof(drweb_vnum), tmo)) + return m_errlog_defer_3(scanent, + US"unable to read the number of viruses", sock); + drweb_vnum = ntohl(drweb_vnum); + + /* "virus(es) found" if virus number is > 0 */ + if (drweb_vnum) + { + int i; - if (i==0) /* the first name we just copy to malware_name */ - malware_name = string_append(NULL, &size, &off, - 1, pre_malware_nb); + /* setup default virus name */ + malware_name = US"unknown"; - else /* concatenate each new virus name to previous */ - malware_name = string_append(malware_name, &size, &off, - 2, "/", pre_malware_nb); + /* set up match regex */ + drweb_re = m_pcre_compile(US"infected\\swith\\s*(.+?)$", &errstr); - pcre_free_substring(pre_malware_nb); - } - } - } - else + /* read and concatenate virus names into one string */ + for (i = 0; i < drweb_vnum; i++) { - const char *drweb_s = NULL; - - if (drweb_rc & DERR_READ_ERR) drweb_s = "read error"; - if (drweb_rc & DERR_NOMEMORY) drweb_s = "no memory"; - if (drweb_rc & DERR_TIMEOUT) drweb_s = "timeout"; - if (drweb_rc & DERR_BAD_CALL) drweb_s = "wrong command"; - /* retcodes DERR_SYMLINK, DERR_NO_REGFILE, DERR_SKIPPED. - * DERR_TOO_BIG, DERR_TOO_COMPRESSED, DERR_SPAM, - * DERR_CRC_ERROR, DERR_READSOCKET, DERR_WRITE_ERR - * and others are ignored */ - if (drweb_s) + int size = 0, off = 0, ovector[10*3]; + /* read the size of report */ + if (!recv_len(sock, &drweb_slen, sizeof(drweb_slen), tmo)) return m_errlog_defer_3(scanent, - string_sprintf("drweb daemon retcode 0x%x (%s)", drweb_rc, drweb_s), - sock); + US"cannot read report size", sock); + drweb_slen = ntohl(drweb_slen); + tmpbuf = store_get(drweb_slen); - /* no virus found */ - malware_name = NULL; - } - break; - } /* drweb */ - - case M_AVES: /* "aveserver" scanner type -------------------------------- */ - { - uschar buf[32768]; - int result; + /* read report body */ + if (!recv_len(sock, tmpbuf, drweb_slen, tmo)) + return m_errlog_defer_3(scanent, + US"cannot read report string", sock); + tmpbuf[drweb_slen] = '\0'; - /* read aveserver's greeting and see if it is ready (2xx greeting) */ - recv_line(sock, buf, sizeof(buf), tmo); + /* try matcher on the line, grab substring */ + result = pcre_exec(drweb_re, NULL, CS tmpbuf, Ustrlen(tmpbuf), 0, 0, + ovector, nelements(ovector)); + if (result >= 2) + { + const char * pre_malware_nb; - if (buf[0] != '2') /* aveserver is having problems */ - return m_errlog_defer_3(scanent, - string_sprintf("unavailable (Responded: %s).", - ((buf[0] != 0) ? buf : (uschar *)"nothing") ), - sock); + pcre_get_substring(CS tmpbuf, ovector, result, 1, &pre_malware_nb); - /* prepare our command */ - (void)string_format(buf, sizeof(buf), "SCAN bPQRSTUW %s\r\n", - eml_filename); + if (i==0) /* the first name we just copy to malware_name */ + malware_name = string_append(NULL, &size, &off, + 1, pre_malware_nb); - DEBUG(D_acl) debug_printf("Malware scan: issuing %s SCAN\n", scanner_name); + else /* concatenate each new virus name to previous */ + malware_name = string_append(malware_name, &size, &off, + 2, "/", pre_malware_nb); - /* and send it */ - if (m_sock_send(sock, buf, Ustrlen(buf), &errstr) < 0) - return m_errlog_defer(scanent, errstr); + pcre_free_substring(pre_malware_nb); + } + } + } + else + { + const char *drweb_s = NULL; + + if (drweb_rc & DERR_READ_ERR) drweb_s = "read error"; + if (drweb_rc & DERR_NOMEMORY) drweb_s = "no memory"; + if (drweb_rc & DERR_TIMEOUT) drweb_s = "timeout"; + if (drweb_rc & DERR_BAD_CALL) drweb_s = "wrong command"; + /* retcodes DERR_SYMLINK, DERR_NO_REGFILE, DERR_SKIPPED. + * DERR_TOO_BIG, DERR_TOO_COMPRESSED, DERR_SPAM, + * DERR_CRC_ERROR, DERR_READSOCKET, DERR_WRITE_ERR + * and others are ignored */ + if (drweb_s) + return m_errlog_defer_3(scanent, + string_sprintf("drweb daemon retcode 0x%x (%s)", drweb_rc, drweb_s), + sock); + /* no virus found */ malware_name = NULL; - result = 0; - /* read response lines, find malware name and final response */ - while (recv_line(sock, buf, sizeof(buf), tmo) > 0) - { - debug_printf("aveserver: %s\n", buf); - if (buf[0] == '2') - break; - if (buf[0] == '5') /* aveserver is having problems */ - { - result = m_errlog_defer(scanent, - string_sprintf("unable to scan file %s (Responded: %s).", - eml_filename, buf)); - break; - } - if (Ustrncmp(buf,"322",3) == 0) - { - uschar *p = Ustrchr(&buf[4], ' '); - *p = '\0'; - malware_name = string_copy(&buf[4]); - } - } + } + break; + } /* drweb */ - /* and send it */ - if (m_sock_send(sock, US"quit\r\n", 6, &errstr) < 0) - return m_errlog_defer(scanent, errstr); + case M_AVES: /* "aveserver" scanner type -------------------------------- */ + { + uschar buf[32768]; + int result; - /* read aveserver's greeting and see if it is ready (2xx greeting) */ - recv_line(sock, buf, sizeof(buf), tmo); + /* read aveserver's greeting and see if it is ready (2xx greeting) */ + buf[0] = 0; + recv_line(sock, buf, sizeof(buf), tmo); - if (buf[0] != '2') /* aveserver is having problems */ - return m_errlog_defer_3(scanent, - string_sprintf("unable to quit dialogue (Responded: %s).", + if (buf[0] != '2') /* aveserver is having problems */ + return m_errlog_defer_3(scanent, + string_sprintf("unavailable (Responded: %s).", ((buf[0] != 0) ? buf : (uschar *)"nothing") ), - sock); + sock); - if (result == DEFER) + /* prepare our command */ + (void)string_format(buf, sizeof(buf), "SCAN bPQRSTUW %s\r\n", + eml_filename); + + /* and send it */ + DEBUG(D_acl) debug_printf("Malware scan: issuing %s %s\n", + scanner_name, buf); + if (m_sock_send(sock, buf, Ustrlen(buf), &errstr) < 0) + return m_errlog_defer(scanent, errstr); + + malware_name = NULL; + result = 0; + /* read response lines, find malware name and final response */ + while (recv_line(sock, buf, sizeof(buf), tmo) > 0) + { + if (buf[0] == '2') + break; + if (buf[0] == '5') /* aveserver is having problems */ { - (void)close(sock); - return DEFER; + result = m_errlog_defer(scanent, + string_sprintf("unable to scan file %s (Responded: %s).", + eml_filename, buf)); + break; } - break; + if (Ustrncmp(buf,"322",3) == 0) + { + uschar *p = Ustrchr(&buf[4], ' '); + *p = '\0'; + malware_name = string_copy(&buf[4]); + } + } + + if (m_sock_send(sock, US"quit\r\n", 6, &errstr) < 0) + return m_errlog_defer(scanent, errstr); + + /* read aveserver's greeting and see if it is ready (2xx greeting) */ + buf[0] = 0; + recv_line(sock, buf, sizeof(buf), tmo); + + if (buf[0] != '2') /* aveserver is having problems */ + return m_errlog_defer_3(scanent, + string_sprintf("unable to quit dialogue (Responded: %s).", + ((buf[0] != 0) ? buf : (uschar *)"nothing") ), + sock); + + if (result == DEFER) + { + (void)close(sock); + return DEFER; + } + break; } /* aveserver */ case M_FSEC: /* "fsecure" scanner type ---------------------------------- */ { - int i, j, bread = 0; - uschar * file_name; - uschar av_buffer[1024]; - const pcre * fs_inf; - static uschar *cmdopt[] = { US"CONFIGURE\tARCHIVE\t1\n", - US"CONFIGURE\tTIMEOUT\t0\n", - US"CONFIGURE\tMAXARCH\t5\n", - US"CONFIGURE\tMIME\t1\n" }; - - malware_name = NULL; - - DEBUG(D_acl) debug_printf("Malware scan: issuing %s scan [%s]\n", - scanner_name, scanner_options); - /* pass options */ - memset(av_buffer, 0, sizeof(av_buffer)); - for (i = 0; i != nelements(cmdopt); i++) - { + int i, j, bread = 0; + uschar * file_name; + uschar av_buffer[1024]; + const pcre * fs_inf; + static uschar *cmdopt[] = { US"CONFIGURE\tARCHIVE\t1\n", + US"CONFIGURE\tTIMEOUT\t0\n", + US"CONFIGURE\tMAXARCH\t5\n", + US"CONFIGURE\tMIME\t1\n" }; + + malware_name = NULL; + + DEBUG(D_acl) debug_printf("Malware scan: issuing %s scan [%s]\n", + scanner_name, scanner_options); + /* pass options */ + memset(av_buffer, 0, sizeof(av_buffer)); + for (i = 0; i != nelements(cmdopt); i++) + { - if (m_sock_send(sock, cmdopt[i], Ustrlen(cmdopt[i]), &errstr) < 0) - return m_errlog_defer(scanent, errstr); + if (m_sock_send(sock, cmdopt[i], Ustrlen(cmdopt[i]), &errstr) < 0) + return m_errlog_defer(scanent, errstr); - bread = ip_recv(sock, av_buffer, sizeof(av_buffer), tmo-time(NULL)); - if (bread > 0) av_buffer[bread]='\0'; - if (bread < 0) - return m_errlog_defer_3(scanent, - string_sprintf("unable to read answer %d (%s)", i, strerror(errno)), - sock); - for (j = 0; j < bread; j++) - if (av_buffer[j] == '\r' || av_buffer[j] == '\n') - av_buffer[j] ='@'; - } + bread = ip_recv(sock, av_buffer, sizeof(av_buffer), tmo-time(NULL)); + if (bread > 0) av_buffer[bread]='\0'; + if (bread < 0) + return m_errlog_defer_3(scanent, + string_sprintf("unable to read answer %d (%s)", i, strerror(errno)), + sock); + for (j = 0; j < bread; j++) + if (av_buffer[j] == '\r' || av_buffer[j] == '\n') + av_buffer[j] ='@'; + } - /* pass the mailfile to fsecure */ - file_name = string_sprintf("SCAN\t%s\n", eml_filename); + /* pass the mailfile to fsecure */ + file_name = string_sprintf("SCAN\t%s\n", eml_filename); - if (m_sock_send(sock, file_name, Ustrlen(file_name), &errstr) < 0) - return m_errlog_defer(scanent, errstr); + if (m_sock_send(sock, file_name, Ustrlen(file_name), &errstr) < 0) + return m_errlog_defer(scanent, errstr); - /* set up match */ - /* todo also SUSPICION\t */ - fs_inf = m_pcre_compile(US"\\S{0,5}INFECTED\\t[^\\t]*\\t([^\\t]+)\\t\\S*$", &errstr); + /* set up match */ + /* todo also SUSPICION\t */ + fs_inf = m_pcre_compile(US"\\S{0,5}INFECTED\\t[^\\t]*\\t([^\\t]+)\\t\\S*$", &errstr); - /* read report, linewise. Apply a timeout as the Fsecure daemon - sometimes wants an answer to "PING" but they won't tell us what */ + /* read report, linewise. Apply a timeout as the Fsecure daemon + sometimes wants an answer to "PING" but they won't tell us what */ { - uschar * p = av_buffer; - uschar * q; + uschar * p = av_buffer; + uschar * q; - for (;;) - { - errno = ETIME; - i = av_buffer+sizeof(av_buffer)-p; - if ((bread= ip_recv(sock, p, i-1, tmo-time(NULL))) < 0) - return m_errlog_defer_3(scanent, - string_sprintf("unable to read result (%s)", strerror(errno)), - sock); + for (;;) + { + errno = ETIME; + i = av_buffer+sizeof(av_buffer)-p; + if ((bread= ip_recv(sock, p, i-1, tmo-time(NULL))) < 0) + return m_errlog_defer_3(scanent, + string_sprintf("unable to read result (%s)", strerror(errno)), + sock); - for (p[bread] = '\0'; q = strchr(p, '\n'); p = q+1) - { - *q = '\0'; + for (p[bread] = '\0'; q = strchr(p, '\n'); p = q+1) + { + *q = '\0'; - /* Really search for virus again? */ - if (!malware_name) - /* try matcher on the line, grab substring */ - malware_name = m_pcre_exec(fs_inf, p); + /* Really search for virus again? */ + if (!malware_name) + /* try matcher on the line, grab substring */ + malware_name = m_pcre_exec(fs_inf, p); - if (Ustrstr(p, "OK\tScan ok.")) - goto fsec_found; - } + if (Ustrstr(p, "OK\tScan ok.")) + goto fsec_found; + } - /* copy down the trailing partial line then read another chunk */ - i = av_buffer+sizeof(av_buffer)-p; - memmove(av_buffer, p, i); - p = av_buffer+i; + /* copy down the trailing partial line then read another chunk */ + i = av_buffer+sizeof(av_buffer)-p; + memmove(av_buffer, p, i); + p = av_buffer+i; } } @@ -849,808 +913,820 @@ malware_internal(uschar **listptr, uschar *eml_filename, BOOL faking) case M_KAVD: /* "kavdaemon" scanner type -------------------------------- */ { - time_t t; - uschar tmpbuf[1024]; - uschar * scanrequest; - int kav_rc; - unsigned long kav_reportlen, bread; - const pcre *kav_re; - uschar *p; - - /* get current date and time, build scan request */ - time(&t); - /* pdp note: before the eml_filename parameter, this scanned the - directory; not finding documentation, so we'll strip off the directory. - The side-effect is that the test framework scanning may end up in - scanning more than was requested, but for the normal interface, this is - fine. */ - - strftime(CS tmpbuf, sizeof(tmpbuf), "%d %b %H:%M:%S", localtime(&t)); - scanrequest = string_sprintf("<0>%s:%s", CS tmpbuf, eml_filename); - p = Ustrrchr(scanrequest, '/'); - if (p) - *p = '\0'; + time_t t; + uschar tmpbuf[1024]; + uschar * scanrequest; + int kav_rc; + unsigned long kav_reportlen, bread; + const pcre *kav_re; + uschar *p; + + /* get current date and time, build scan request */ + time(&t); + /* pdp note: before the eml_filename parameter, this scanned the + directory; not finding documentation, so we'll strip off the directory. + The side-effect is that the test framework scanning may end up in + scanning more than was requested, but for the normal interface, this is + fine. */ + + strftime(CS tmpbuf, sizeof(tmpbuf), "%d %b %H:%M:%S", localtime(&t)); + scanrequest = string_sprintf("<0>%s:%s", CS tmpbuf, eml_filename); + p = Ustrrchr(scanrequest, '/'); + if (p) + *p = '\0'; + + DEBUG(D_acl) debug_printf("Malware scan: issuing %s scan [%s]\n", + scanner_name, scanner_options); + + /* send scan request */ + if (m_sock_send(sock, scanrequest, Ustrlen(scanrequest)+1, &errstr) < 0) + return m_errlog_defer(scanent, errstr); - DEBUG(D_acl) debug_printf("Malware scan: issuing %s scan [%s]\n", - scanner_name, scanner_options); + /* wait for result */ + if (!recv_len(sock, tmpbuf, 2, tmo)) + return m_errlog_defer_3(scanent, + US"unable to read 2 bytes from socket.", sock); - /* send scan request */ - if (m_sock_send(sock, scanrequest, Ustrlen(scanrequest)+1, &errstr) < 0) - return m_errlog_defer(scanent, errstr); + /* get errorcode from one nibble */ + kav_rc = tmpbuf[ test_byte_order()==LITTLE_MY_ENDIAN ? 0 : 1 ] & 0x0F; + switch(kav_rc) + { + case 5: case 6: /* improper kavdaemon configuration */ + return m_errlog_defer_3(scanent, + US"please reconfigure kavdaemon to NOT disinfect or remove infected files.", + sock); + case 1: + return m_errlog_defer_3(scanent, + US"reported 'scanning not completed' (code 1).", sock); + case 7: + return m_errlog_defer_3(scanent, + US"reported 'kavdaemon damaged' (code 7).", sock); + } - /* wait for result */ - if (!recv_len(sock, tmpbuf, 2, tmo)) - return m_errlog_defer_3(scanent, - US"unable to read 2 bytes from socket.", sock); + /* code 8 is not handled, since it is ambigous. It appears mostly on + bounces where part of a file has been cut off */ - /* get errorcode from one nibble */ - kav_rc = tmpbuf[ test_byte_order()==LITTLE_MY_ENDIAN ? 0 : 1 ] & 0x0F; - switch(kav_rc) + /* "virus found" return codes (2-4) */ + if (kav_rc > 1 && kav_rc < 5) { - case 5: case 6: /* improper kavdaemon configuration */ - return m_errlog_defer_3(scanent, - US"please reconfigure kavdaemon to NOT disinfect or remove infected files.", - sock); - case 1: - return m_errlog_defer_3(scanent, - US"reported 'scanning not completed' (code 1).", sock); - case 7: - return m_errlog_defer_3(scanent, - US"reported 'kavdaemon damaged' (code 7).", sock); - } + int report_flag = 0; - /* code 8 is not handled, since it is ambigous. It appears mostly on - bounces where part of a file has been cut off */ + /* setup default virus name */ + malware_name = US"unknown"; - /* "virus found" return codes (2-4) */ - if (kav_rc > 1 && kav_rc < 5) - { - int report_flag = 0; + report_flag = tmpbuf[ test_byte_order() == LITTLE_MY_ENDIAN ? 1 : 0 ]; - /* setup default virus name */ - malware_name = US"unknown"; - - report_flag = tmpbuf[ test_byte_order() == LITTLE_MY_ENDIAN ? 1 : 0 ]; + /* read the report, if available */ + if (report_flag == 1) + { + /* read report size */ + if (!recv_len(sock, &kav_reportlen, 4, tmo)) + return m_errlog_defer_3(scanent, + US"cannot read report size", sock); - /* read the report, if available */ - if (report_flag == 1) + /* it's possible that avp returns av_buffer[1] == 1 but the + reportsize is 0 (!?) */ + if (kav_reportlen > 0) { - /* read report size */ - if (!recv_len(sock, &kav_reportlen, 4, tmo)) - return m_errlog_defer_3(scanent, - US"cannot read report size", sock); - - /* it's possible that avp returns av_buffer[1] == 1 but the - reportsize is 0 (!?) */ - if (kav_reportlen > 0) + /* set up match regex, depends on retcode */ + kav_re = m_pcre_compile( kav_rc == 3 + ? US"suspicion:\\s*(.+?)\\s*$" + : US"infected:\\s*(.+?)\\s*$", + &errstr ); + + /* read report, linewise */ + while (kav_reportlen > 0) { - /* set up match regex, depends on retcode */ - kav_re = m_pcre_compile( kav_rc == 3 - ? US"suspicion:\\s*(.+?)\\s*$" - : US"infected:\\s*(.+?)\\s*$", - &errstr ); - - /* read report, linewise */ - while (kav_reportlen > 0) - { - if ((bread = recv_line(sock, tmpbuf, sizeof(tmpbuf), tmo)) < 0) - break; - kav_reportlen -= bread+1; - - /* try matcher on the line, grab substring */ - if ((malware_name = m_pcre_exec(kav_re, tmpbuf))) - break; - } + if ((bread = recv_line(sock, tmpbuf, sizeof(tmpbuf), tmo)) < 0) + break; + kav_reportlen -= bread+1; + + /* try matcher on the line, grab substring */ + if ((malware_name = m_pcre_exec(kav_re, tmpbuf))) + break; } } } - else /* no virus found */ - malware_name = NULL; + } + else /* no virus found */ + malware_name = NULL; - break; + break; } case M_CMDL: /* "cmdline" scanner type ---------------------------------- */ { - const uschar *cmdline_scanner = scanner_options; - const pcre *cmdline_trigger_re; - const pcre *cmdline_regex_re; - uschar * file_name; - uschar * commandline; - void (*eximsigchld)(int); - void (*eximsigpipe)(int); - FILE *scanner_out = NULL; - int scanner_fd; - FILE *scanner_record = NULL; - uschar linebuffer[32767]; - int rcnt; - int trigger = 0; - uschar *p; - - if (!cmdline_scanner) - return m_errlog_defer(scanent, errstr); + const uschar *cmdline_scanner = scanner_options; + const pcre *cmdline_trigger_re; + const pcre *cmdline_regex_re; + uschar * file_name; + uschar * commandline; + void (*eximsigchld)(int); + void (*eximsigpipe)(int); + FILE *scanner_out = NULL; + int scanner_fd; + FILE *scanner_record = NULL; + uschar linebuffer[32767]; + int rcnt; + int trigger = 0; + uschar *p; + + if (!cmdline_scanner) + return m_errlog_defer(scanent, errstr); - /* find scanner output trigger */ - cmdline_trigger_re = m_pcre_nextinlist(&av_scanner_work, &sep, - "missing trigger specification", &errstr); - if (!cmdline_trigger_re) - return m_errlog_defer(scanent, errstr); + /* find scanner output trigger */ + cmdline_trigger_re = m_pcre_nextinlist(&av_scanner_work, &sep, + "missing trigger specification", &errstr); + if (!cmdline_trigger_re) + return m_errlog_defer(scanent, errstr); - /* find scanner name regex */ - cmdline_regex_re = m_pcre_nextinlist(&av_scanner_work, &sep, - "missing virus name regex specification", &errstr); - if (!cmdline_regex_re) - return m_errlog_defer(scanent, errstr); + /* find scanner name regex */ + cmdline_regex_re = m_pcre_nextinlist(&av_scanner_work, &sep, + "missing virus name regex specification", &errstr); + if (!cmdline_regex_re) + return m_errlog_defer(scanent, errstr); - /* prepare scanner call; despite the naming, file_name holds a directory - name which is documented as the value given to %s. */ + /* prepare scanner call; despite the naming, file_name holds a directory + name which is documented as the value given to %s. */ - file_name = string_copy(eml_filename); - p = Ustrrchr(file_name, '/'); - if (p) - *p = '\0'; - commandline = string_sprintf(CS cmdline_scanner, file_name); + file_name = string_copy(eml_filename); + p = Ustrrchr(file_name, '/'); + if (p) + *p = '\0'; + commandline = string_sprintf(CS cmdline_scanner, file_name); - /* redirect STDERR too */ - commandline = string_sprintf("%s 2>&1", commandline); + /* redirect STDERR too */ + commandline = string_sprintf("%s 2>&1", commandline); - DEBUG(D_acl) debug_printf("Malware scan: issuing %s scan [%s]\n", - scanner_name, commandline); + DEBUG(D_acl) debug_printf("Malware scan: issuing %s scan [%s]\n", + scanner_name, commandline); - /* store exims signal handlers */ - eximsigchld = signal(SIGCHLD,SIG_DFL); - eximsigpipe = signal(SIGPIPE,SIG_DFL); + /* store exims signal handlers */ + eximsigchld = signal(SIGCHLD,SIG_DFL); + eximsigpipe = signal(SIGPIPE,SIG_DFL); - if (!(scanner_out = popen(CS commandline,"r"))) - { - int err = errno; - signal(SIGCHLD,eximsigchld); signal(SIGPIPE,eximsigpipe); - return m_errlog_defer(scanent, - string_sprintf("call (%s) failed: %s.", commandline, strerror(err))); - } - scanner_fd = fileno(scanner_out); + if (!(scanner_out = popen(CS commandline,"r"))) + { + int err = errno; + signal(SIGCHLD,eximsigchld); signal(SIGPIPE,eximsigpipe); + return m_errlog_defer(scanent, + string_sprintf("call (%s) failed: %s.", commandline, strerror(err))); + } + scanner_fd = fileno(scanner_out); - file_name = string_sprintf("%s/scan/%s/%s_scanner_output", - spool_directory, message_id, message_id); + file_name = string_sprintf("%s/scan/%s/%s_scanner_output", + spool_directory, message_id, message_id); - if (!(scanner_record = modefopen(file_name, "wb", SPOOL_MODE))) - { - int err = errno; - (void) pclose(scanner_out); - signal(SIGCHLD,eximsigchld); signal(SIGPIPE,eximsigpipe); - return m_errlog_defer(scanent, string_sprintf( - "opening scanner output file (%s) failed: %s.", - file_name, strerror(err))); - } + if (!(scanner_record = modefopen(file_name, "wb", SPOOL_MODE))) + { + int err = errno; + (void) pclose(scanner_out); + signal(SIGCHLD,eximsigchld); signal(SIGPIPE,eximsigpipe); + return m_errlog_defer(scanent, string_sprintf( + "opening scanner output file (%s) failed: %s.", + file_name, strerror(err))); + } - /* look for trigger while recording output */ - while ((rcnt = recv_line(scanner_fd, linebuffer, - sizeof(linebuffer), tmo))) - { - if (rcnt < 0) + /* look for trigger while recording output */ + while ((rcnt = recv_line(scanner_fd, linebuffer, + sizeof(linebuffer), tmo))) + { + if (rcnt < 0) + if (rcnt == -1) + break; + else { + int err = errno; (void) pclose(scanner_out); signal(SIGCHLD,eximsigchld); signal(SIGPIPE,eximsigpipe); return m_errlog_defer(scanent, string_sprintf( - "unable to read from scanner (%s)", scanner_options)); + "unable to read from scanner (%s): %s", + commandline, strerror(err))); } - if (Ustrlen(linebuffer) > fwrite(linebuffer, 1, Ustrlen(linebuffer), scanner_record)) - { - /* short write */ - (void) pclose(scanner_out); - signal(SIGCHLD,eximsigchld); signal(SIGPIPE,eximsigpipe); - return m_errlog_defer(scanent, string_sprintf( - "short write on scanner output file (%s).", file_name)); - } - putc('\n', scanner_record); - /* try trigger match */ - if ( !trigger - && regex_match_and_setup(cmdline_trigger_re, linebuffer, 0, -1) - ) - trigger = 1; + if (Ustrlen(linebuffer) > fwrite(linebuffer, 1, Ustrlen(linebuffer), scanner_record)) + { + /* short write */ + (void) pclose(scanner_out); + signal(SIGCHLD,eximsigchld); signal(SIGPIPE,eximsigpipe); + return m_errlog_defer(scanent, string_sprintf( + "short write on scanner output file (%s).", file_name)); } + putc('\n', scanner_record); + /* try trigger match */ + if ( !trigger + && regex_match_and_setup(cmdline_trigger_re, linebuffer, 0, -1) + ) + trigger = 1; + } - (void)fclose(scanner_record); - sep = pclose(scanner_out); - signal(SIGCHLD,eximsigchld); signal(SIGPIPE,eximsigpipe); - if (sep != 0) - return m_errlog_defer(scanent, - sep == -1 - ? string_sprintf("running scanner failed: %s", strerror(sep)) - : string_sprintf("scanner returned error code: %d", sep)); + (void)fclose(scanner_record); + sep = pclose(scanner_out); + signal(SIGCHLD,eximsigchld); signal(SIGPIPE,eximsigpipe); + if (sep != 0) + return m_errlog_defer(scanent, + sep == -1 + ? string_sprintf("running scanner failed: %s", strerror(sep)) + : string_sprintf("scanner returned error code: %d", sep)); - if (trigger) - { - uschar * s; - /* setup default virus name */ - malware_name = US"unknown"; + if (trigger) + { + uschar * s; + /* setup default virus name */ + malware_name = US"unknown"; - /* re-open the scanner output file, look for name match */ - scanner_record = fopen(CS file_name, "rb"); - while (fgets(CS linebuffer, sizeof(linebuffer), scanner_record)) - { - /* try match */ - if ((s = m_pcre_exec(cmdline_regex_re, linebuffer))) - malware_name = s; - } - (void)fclose(scanner_record); + /* re-open the scanner output file, look for name match */ + scanner_record = fopen(CS file_name, "rb"); + while (fgets(CS linebuffer, sizeof(linebuffer), scanner_record)) + { + /* try match */ + if ((s = m_pcre_exec(cmdline_regex_re, linebuffer))) + malware_name = s; } - else /* no virus found */ - malware_name = NULL; - break; + (void)fclose(scanner_record); + } + else /* no virus found */ + malware_name = NULL; + break; } /* cmdline */ case M_SOPHIE: /* "sophie" scanner type --------------------------------- */ { - int bread = 0; - uschar *p; - uschar * file_name; - uschar av_buffer[1024]; - - /* pass the scan directory to sophie */ - file_name = string_copy(eml_filename); - if ((p = Ustrrchr(file_name, '/'))) - *p = '\0'; + int bread = 0; + uschar *p; + uschar * file_name; + uschar av_buffer[1024]; - DEBUG(D_acl) debug_printf("Malware scan: issuing %s scan [%s]\n", - scanner_name, scanner_options); + /* pass the scan directory to sophie */ + file_name = string_copy(eml_filename); + if ((p = Ustrrchr(file_name, '/'))) + *p = '\0'; - if ( write(sock, file_name, Ustrlen(file_name)) < 0 - || write(sock, "\n", 1) != 1 - ) - return m_errlog_defer_3(scanent, - string_sprintf("unable to write to UNIX socket (%s)", scanner_options), - sock); + DEBUG(D_acl) debug_printf("Malware scan: issuing %s scan [%s]\n", + scanner_name, scanner_options); - /* wait for result */ - memset(av_buffer, 0, sizeof(av_buffer)); - if ((bread = ip_recv(sock, av_buffer, sizeof(av_buffer), tmo-time(NULL))) <= 0) - return m_errlog_defer_3(scanent, - string_sprintf("unable to read from UNIX socket (%s)", scanner_options), - sock); + if ( write(sock, file_name, Ustrlen(file_name)) < 0 + || write(sock, "\n", 1) != 1 + ) + return m_errlog_defer_3(scanent, + string_sprintf("unable to write to UNIX socket (%s)", scanner_options), + sock); - /* infected ? */ - if (av_buffer[0] == '1') { - uschar * s = Ustrchr(av_buffer, '\n'); - if (s) - *s = '\0'; - malware_name = string_copy(&av_buffer[2]); - } - else if (!strncmp(CS av_buffer, "-1", 2)) - return m_errlog_defer_3(scanent, US"scanner reported error", sock); - else /* all ok, no virus */ - malware_name = NULL; + /* wait for result */ + memset(av_buffer, 0, sizeof(av_buffer)); + if ((bread = ip_recv(sock, av_buffer, sizeof(av_buffer), tmo-time(NULL))) <= 0) + return m_errlog_defer_3(scanent, + string_sprintf("unable to read from UNIX socket (%s)", scanner_options), + sock); + + /* infected ? */ + if (av_buffer[0] == '1') { + uschar * s = Ustrchr(av_buffer, '\n'); + if (s) + *s = '\0'; + malware_name = string_copy(&av_buffer[2]); + } + else if (!strncmp(CS av_buffer, "-1", 2)) + return m_errlog_defer_3(scanent, US"scanner reported error", sock); + else /* all ok, no virus */ + malware_name = NULL; - break; + break; } case M_CLAMD: /* "clamd" scanner type ----------------------------------- */ { - /* This code was originally contributed by David Saez */ - /* There are three scanning methods available to us: - * (1) Use the SCAN command, pointing to a file in the filesystem - * (2) Use the STREAM command, send the data on a separate port - * (3) Use the zINSTREAM command, send the data inline - * The zINSTREAM command was introduced with ClamAV 0.95, which marked - * STREAM deprecated; see: http://wiki.clamav.net/bin/view/Main/UpgradeNotes095 - * In Exim, we use SCAN if using a Unix-domain socket or explicitly told that - * the TCP-connected daemon is actually local; otherwise we use zINSTREAM unless - * WITH_OLD_CLAMAV_STREAM is defined. - * See Exim bug 926 for details. */ - - uschar *p, *vname, *result_tag, *response_end; - int bread=0; - uschar * file_name; - uschar av_buffer[1024]; - uschar *hostname = US""; - host_item connhost; - uschar *clamav_fbuf; - int clam_fd, result; - off_t fsize; - unsigned int fsize_uint; - BOOL use_scan_command = FALSE; - clamd_address_container * clamd_address_vector[MAX_CLAMD_SERVERS]; - int current_server; - int num_servers = 0; - #ifdef WITH_OLD_CLAMAV_STREAM - unsigned int port; - uschar av_buffer2[1024]; - int sockData; - #else - uint32_t send_size, send_final_zeroblock; - #endif - - if (*scanner_options == '/') - /* Local file; so we def want to use_scan_command and don't want to try - * passing IP/port combinations */ - use_scan_command = TRUE; - else +/* This code was originally contributed by David Saez */ +/* There are three scanning methods available to us: +* (1) Use the SCAN command, pointing to a file in the filesystem +* (2) Use the STREAM command, send the data on a separate port +* (3) Use the zINSTREAM command, send the data inline +* The zINSTREAM command was introduced with ClamAV 0.95, which marked +* STREAM deprecated; see: http://wiki.clamav.net/bin/view/Main/UpgradeNotes095 +* In Exim, we use SCAN if using a Unix-domain socket or explicitly told that +* the TCP-connected daemon is actually local; otherwise we use zINSTREAM unless +* WITH_OLD_CLAMAV_STREAM is defined. +* See Exim bug 926 for details. */ + + uschar *p, *vname, *result_tag, *response_end; + int bread=0; + uschar * file_name; + uschar av_buffer[1024]; + uschar *hostname = US""; + host_item connhost; + uschar *clamav_fbuf; + int clam_fd, result; + off_t fsize; + unsigned int fsize_uint; + BOOL use_scan_command = FALSE; + clamd_address_container * clamd_address_vector[MAX_CLAMD_SERVERS]; + int current_server; + int num_servers = 0; +#ifdef WITH_OLD_CLAMAV_STREAM + unsigned int port; + uschar av_buffer2[1024]; + int sockData; +#else + uint32_t send_size, send_final_zeroblock; +#endif + + if (*scanner_options == '/') + /* Local file; so we def want to use_scan_command and don't want to try + * passing IP/port combinations */ + use_scan_command = TRUE; + else + { + const uschar *address = scanner_options; + uschar address_buffer[MAX_CLAMD_ADDRESS_LENGTH + 20]; + + /* Go through the rest of the list of host/port and construct an array + * of servers to try. The first one is the bit we just passed from + * scanner_options so process that first and then scan the remainder of + * the address buffer */ + do { - const uschar *address = scanner_options; - uschar address_buffer[MAX_CLAMD_ADDRESS_LENGTH + 20]; - - /* Go through the rest of the list of host/port and construct an array - * of servers to try. The first one is the bit we just passed from - * scanner_options so process that first and then scan the remainder of - * the address buffer */ - do - { - clamd_address_container *this_clamd; + clamd_address_container *this_clamd; - /* The 'local' option means use the SCAN command over the network - * socket (ie common file storage in use) */ - if (strcmpic(address,US"local") == 0) - { - use_scan_command = TRUE; - continue; - } + /* The 'local' option means use the SCAN command over the network + * socket (ie common file storage in use) */ + if (strcmpic(address,US"local") == 0) + { + use_scan_command = TRUE; + continue; + } - /* XXX: If unsuccessful we should free this memory */ - this_clamd = - (clamd_address_container *)store_get(sizeof(clamd_address_container)); + /* XXX: If unsuccessful we should free this memory */ + this_clamd = + (clamd_address_container *)store_get(sizeof(clamd_address_container)); - /* extract host and port part */ - if( sscanf(CS address, "%" MAX_CLAMD_ADDRESS_LENGTH_S "s %u", - this_clamd->tcp_addr, &(this_clamd->tcp_port)) != 2 ) - { - (void) m_errlog_defer(scanent, - string_sprintf("invalid address '%s'", address)); - continue; - } + /* extract host and port part */ + if( sscanf(CS address, "%" MAX_CLAMD_ADDRESS_LENGTH_S "s %u", + this_clamd->tcp_addr, &(this_clamd->tcp_port)) != 2 ) + { + (void) m_errlog_defer(scanent, + string_sprintf("invalid address '%s'", address)); + continue; + } - clamd_address_vector[num_servers] = this_clamd; - num_servers++; - if (num_servers >= MAX_CLAMD_SERVERS) - { - (void) m_errlog_defer(scanent, - US"More than " MAX_CLAMD_SERVERS_S " clamd servers " - "specified; only using the first " MAX_CLAMD_SERVERS_S ); - break; - } - } while ((address = string_nextinlist(&av_scanner_work, &sep, - address_buffer, - sizeof(address_buffer))) != NULL); - - /* check if we have at least one server */ - if (!num_servers) - return m_errlog_defer(scanent, - US"no useable server addresses in malware configuration option."); - } + clamd_address_vector[num_servers] = this_clamd; + num_servers++; + if (num_servers >= MAX_CLAMD_SERVERS) + { + (void) m_errlog_defer(scanent, + US"More than " MAX_CLAMD_SERVERS_S " clamd servers " + "specified; only using the first " MAX_CLAMD_SERVERS_S ); + break; + } + } while ((address = string_nextinlist(&av_scanner_work, &sep, + address_buffer, + sizeof(address_buffer))) != NULL); - /* See the discussion of response formats below to see why we really - don't like colons in filenames when passing filenames to ClamAV. */ - if (use_scan_command && Ustrchr(eml_filename, ':')) + /* check if we have at least one server */ + if (!num_servers) return m_errlog_defer(scanent, - string_sprintf("local/SCAN mode incompatible with" \ - " : in path to email filename [%s]", eml_filename)); - - /* We have some network servers specified */ - if (num_servers) - { - /* Confirmed in ClamAV source (0.95.3) that the TCPAddr option of clamd - * only supports AF_INET, but we should probably be looking to the - * future and rewriting this to be protocol-independent anyway. */ + US"no useable server addresses in malware configuration option."); + } - while (num_servers > 0) - { - int i; - /* Randomly pick a server to start with */ - current_server = random_number( num_servers ); + /* See the discussion of response formats below to see why we really + don't like colons in filenames when passing filenames to ClamAV. */ + if (use_scan_command && Ustrchr(eml_filename, ':')) + return m_errlog_defer(scanent, + string_sprintf("local/SCAN mode incompatible with" \ + " : in path to email filename [%s]", eml_filename)); - debug_printf("trying server name %s, port %u\n", - clamd_address_vector[current_server]->tcp_addr, - clamd_address_vector[current_server]->tcp_port); - - /* Lookup the host. This is to ensure that we connect to the same IP - * on both connections (as one host could resolve to multiple ips) */ - sock= m_tcpsocket(clamd_address_vector[current_server]->tcp_addr, - clamd_address_vector[current_server]->tcp_port, - &connhost, &errstr); - if (sock >= 0) - { - /* Connection successfully established with a server */ - hostname = clamd_address_vector[current_server]->tcp_addr; - break; - } + /* We have some network servers specified */ + if (num_servers) + { + /* Confirmed in ClamAV source (0.95.3) that the TCPAddr option of clamd + * only supports AF_INET, but we should probably be looking to the + * future and rewriting this to be protocol-independent anyway. */ - (void) m_errlog_defer(scanent, errstr); + while (num_servers > 0) + { + int i; + /* Randomly pick a server to start with */ + current_server = random_number( num_servers ); - /* Remove the server from the list. XXX We should free the memory */ - num_servers--; - for (i = current_server; i < num_servers; i++) - clamd_address_vector[i] = clamd_address_vector[i+1]; + DEBUG(D_acl) + debug_printf("trying server name %s, port %u\n", + clamd_address_vector[current_server]->tcp_addr, + clamd_address_vector[current_server]->tcp_port); + + /* Lookup the host. This is to ensure that we connect to the same IP + * on both connections (as one host could resolve to multiple ips) */ + sock= m_tcpsocket(clamd_address_vector[current_server]->tcp_addr, + clamd_address_vector[current_server]->tcp_port, + &connhost, &errstr); + if (sock >= 0) + { + /* Connection successfully established with a server */ + hostname = clamd_address_vector[current_server]->tcp_addr; + break; } - if (num_servers == 0) - return m_errlog_defer(scanent, US"all servers failed"); - } - else - { - if ((sock = m_unixsocket(scanner_options, &errstr)) < 0) - return m_errlog_defer(scanent, errstr); + (void) m_errlog_defer(scanent, errstr); + + /* Remove the server from the list. XXX We should free the memory */ + num_servers--; + for (i = current_server; i < num_servers; i++) + clamd_address_vector[i] = clamd_address_vector[i+1]; } - /* have socket in variable "sock"; command to use is semi-independent of - * the socket protocol. We use SCAN if is local (either Unix/local - * domain socket, or explicitly told local) else we stream the data. - * How we stream the data depends upon how we were built. */ + if (num_servers == 0) + return m_errlog_defer(scanent, US"all servers failed"); + } + else + { + if ((sock = m_unixsocket(scanner_options, &errstr)) < 0) + return m_errlog_defer(scanent, errstr); + } - if (!use_scan_command) - { + /* have socket in variable "sock"; command to use is semi-independent of + * the socket protocol. We use SCAN if is local (either Unix/local + * domain socket, or explicitly told local) else we stream the data. + * How we stream the data depends upon how we were built. */ + + if (!use_scan_command) + { #ifdef WITH_OLD_CLAMAV_STREAM - /* "STREAM\n" command, get back a "PORT \n" response, send data to - * that port on a second connection; then in the scan-method-neutral - * part, read the response back on the original connection. */ + /* "STREAM\n" command, get back a "PORT \n" response, send data to + * that port on a second connection; then in the scan-method-neutral + * part, read the response back on the original connection. */ - DEBUG(D_acl) debug_printf("Malware scan: issuing %s old-style remote scan (PORT)\n", - scanner_name); + DEBUG(D_acl) debug_printf( + "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, errstr); + /* Pass the string to ClamAV (7 = "STREAM\n") */ + if (m_sock_send(sock, US"STREAM\n", 7, &errstr) < 0) + return m_errlog_defer(scanent, errstr); - memset(av_buffer2, 0, sizeof(av_buffer2)); - bread = ip_recv(sock, av_buffer2, sizeof(av_buffer2), tmo-time(NULL)); + memset(av_buffer2, 0, sizeof(av_buffer2)); + bread = ip_recv(sock, av_buffer2, sizeof(av_buffer2), tmo-time(NULL)); - if (bread < 0) - return m_errlog_defer_3(scanent, - string_sprintf("unable to read PORT from socket (%s)", - strerror(errno)), - sock); + if (bread < 0) + return m_errlog_defer_3(scanent, + string_sprintf("unable to read PORT from socket (%s)", + strerror(errno)), + sock); - if (bread == sizeof(av_buffer2)) - return m_errlog_defer_3(scanent, "buffer too small", sock); + if (bread == sizeof(av_buffer2)) + return m_errlog_defer_3(scanent, "buffer too small", sock); - if (!(*av_buffer2)) - return m_errlog_defer_3(scanent, "ClamAV returned null", sock); + if (!(*av_buffer2)) + return m_errlog_defer_3(scanent, "ClamAV returned null", sock); - av_buffer2[bread] = '\0'; - if( sscanf(CS av_buffer2, "PORT %u\n", &port) != 1 ) - return m_errlog_defer_3(scanent, - string_sprintf("Expected port information from clamd, got '%s'", - av_buffer2), - sock); + av_buffer2[bread] = '\0'; + if( sscanf(CS av_buffer2, "PORT %u\n", &port) != 1 ) + return m_errlog_defer_3(scanent, + string_sprintf("Expected port information from clamd, got '%s'", + av_buffer2), + sock); - sockData = m_tcpsocket(connhost.address, port, NULL, &errstr); - if (sockData < 0) - return m_errlog_defer_3(scanent, errstr, sock); + sockData = m_tcpsocket(connhost.address, port, NULL, &errstr); + if (sockData < 0) + return m_errlog_defer_3(scanent, errstr, sock); # define CLOSE_SOCKDATA (void)close(sockData) #else /* WITH_OLD_CLAMAV_STREAM not defined */ - /* New protocol: "zINSTREAM\n" followed by a sequence of - chunks, a 4-byte number (network order), terminated by a zero-length - chunk. */ + /* New protocol: "zINSTREAM\n" followed by a sequence of + chunks, a 4-byte number (network order), terminated by a zero-length + chunk. */ - DEBUG(D_acl) debug_printf("Malware scan: issuing %s new-style remote scan (zINSTREAM)\n", - scanner_name); + DEBUG(D_acl) debug_printf( + "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, - string_sprintf("unable to send zINSTREAM to socket (%s)", - strerror(errno)), - sock); + /* Pass the string to ClamAV (10 = "zINSTREAM\0") */ + if (send(sock, "zINSTREAM", 10, 0) < 0) + return m_errlog_defer_3(scanent, + string_sprintf("unable to send zINSTREAM to socket (%s)", + strerror(errno)), + sock); # define CLOSE_SOCKDATA /**/ #endif - /* calc file size */ - if ((clam_fd = open(CS eml_filename, O_RDONLY)) < 0) - { - int err = errno; - CLOSE_SOCKDATA; - return m_errlog_defer_3(scanent, - string_sprintf("can't open spool file %s: %s", - eml_filename, strerror(err)), - sock); - } - if ((fsize = lseek(clam_fd, 0, SEEK_END)) < 0) - { - int err = errno; - CLOSE_SOCKDATA; (void)close(clam_fd); - return m_errlog_defer_3(scanent, - string_sprintf("can't seek spool file %s: %s", - eml_filename, strerror(err)), - sock); - } - fsize_uint = (unsigned int) fsize; - if ((off_t)fsize_uint != fsize) - { - CLOSE_SOCKDATA; (void)close(clam_fd); - return m_errlog_defer_3(scanent, - string_sprintf("seeking spool file %s, size overflow", - eml_filename), - sock); - } - lseek(clam_fd, 0, SEEK_SET); + /* calc file size */ + if ((clam_fd = open(CS eml_filename, O_RDONLY)) < 0) + { + int err = errno; + CLOSE_SOCKDATA; + return m_errlog_defer_3(scanent, + string_sprintf("can't open spool file %s: %s", + eml_filename, strerror(err)), + sock); + } + if ((fsize = lseek(clam_fd, 0, SEEK_END)) < 0) + { + int err = errno; + CLOSE_SOCKDATA; (void)close(clam_fd); + return m_errlog_defer_3(scanent, + string_sprintf("can't seek spool file %s: %s", + eml_filename, strerror(err)), + sock); + } + fsize_uint = (unsigned int) fsize; + if ((off_t)fsize_uint != fsize) + { + CLOSE_SOCKDATA; (void)close(clam_fd); + return m_errlog_defer_3(scanent, + string_sprintf("seeking spool file %s, size overflow", + eml_filename), + sock); + } + lseek(clam_fd, 0, SEEK_SET); - if (!(clamav_fbuf = (uschar *) malloc (fsize_uint))) - { - CLOSE_SOCKDATA; (void)close(clam_fd); - return m_errlog_defer_3(scanent, - string_sprintf("unable to allocate memory %u for file (%s)", - fsize_uint, eml_filename), - sock); - } + if (!(clamav_fbuf = (uschar *) malloc (fsize_uint))) + { + CLOSE_SOCKDATA; (void)close(clam_fd); + return m_errlog_defer_3(scanent, + string_sprintf("unable to allocate memory %u for file (%s)", + fsize_uint, eml_filename), + sock); + } - if ((result = read(clam_fd, clamav_fbuf, fsize_uint)) < 0) - { - int err = errno; - free(clamav_fbuf); CLOSE_SOCKDATA; (void)close(clam_fd); - return m_errlog_defer_3(scanent, - string_sprintf("can't read spool file %s: %s", - eml_filename, strerror(err)), - sock); - } - (void)close(clam_fd); + if ((result = read(clam_fd, clamav_fbuf, fsize_uint)) < 0) + { + int err = errno; + free(clamav_fbuf); CLOSE_SOCKDATA; (void)close(clam_fd); + return m_errlog_defer_3(scanent, + string_sprintf("can't read spool file %s: %s", + eml_filename, strerror(err)), + sock); + } + (void)close(clam_fd); - /* send file body to socket */ + /* send file body to socket */ #ifdef WITH_OLD_CLAMAV_STREAM - if (send(sockData, clamav_fbuf, fsize_uint, 0) < 0) - { - free(clamav_fbuf); CLOSE_SOCKDATA; - return m_errlog_defer_3(scanent, - string_sprintf("unable to send file body to socket (%s:%u)", - hostname, port), - sock); - } + if (send(sockData, clamav_fbuf, fsize_uint, 0) < 0) + { + free(clamav_fbuf); CLOSE_SOCKDATA; + return m_errlog_defer_3(scanent, + string_sprintf("unable to send file body to socket (%s:%u)", + hostname, port), + sock); + } #else - send_size = htonl(fsize_uint); - send_final_zeroblock = 0; - if ((send(sock, &send_size, sizeof(send_size), 0) < 0) || - (send(sock, clamav_fbuf, fsize_uint, 0) < 0) || - (send(sock, &send_final_zeroblock, sizeof(send_final_zeroblock), 0) < 0)) - { - free(clamav_fbuf); - return m_errlog_defer_3(scanent, - string_sprintf("unable to send file body to socket (%s)", hostname), - sock); - } -#endif - + send_size = htonl(fsize_uint); + send_final_zeroblock = 0; + if ((send(sock, &send_size, sizeof(send_size), 0) < 0) || + (send(sock, clamav_fbuf, fsize_uint, 0) < 0) || + (send(sock, &send_final_zeroblock, sizeof(send_final_zeroblock), 0) < 0)) + { free(clamav_fbuf); - - CLOSE_SOCKDATA; -#undef CLOSE_SOCKDATA - } - else - { /* use scan command */ - /* Send a SCAN command pointing to a filename; then in the then in the - * scan-method-neutral part, read the response back */ - - /* ================================================================= */ - - /* Prior to the reworking post-Exim-4.72, this scanned a directory, - which dates to when ClamAV needed us to break apart the email into the - MIME parts (eg, with the now deprecated demime condition coming first). - Some time back, ClamAV gained the ability to deconstruct the emails, so - doing this would actually have resulted in the mail attachments being - 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); - - DEBUG(D_acl) debug_printf("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, - 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. */ + return m_errlog_defer_3(scanent, + string_sprintf("unable to send file body to socket (%s)", hostname), + sock); } - /* 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(sock, av_buffer, sizeof(av_buffer), tmo-time(NULL)); - (void)close(sock); - sock = -1; - - if (bread <= 0) - return m_errlog_defer(scanent, - string_sprintf("unable to read from socket (%s)", strerror(errno))); - - if (bread == sizeof(av_buffer)) - return m_errlog_defer(scanent, 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: -> ": FOUND" - not-infected: -> ": OK" - error: -> ": ERROR - If the ExtendedDetectionInfo option has been turned on, then we get: - ": (:) 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_errlog_defer(scanent, 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("Malware response: %s\n", av_buffer); - - while (isspace(*--p) && (p > av_buffer)) - *p = '\0'; - if (*p) ++p; - response_end = p; +#endif - /* colon in returned output? */ - if((p = Ustrchr(av_buffer,':')) == NULL) - return m_errlog_defer(scanent, string_sprintf( - "ClamAV returned malformed result (missing colon): %s", - av_buffer)); + free(clamav_fbuf); - /* strip filename */ - while (*p && isspace(*++p)) /**/; - vname = p; + CLOSE_SOCKDATA; +#undef CLOSE_SOCKDATA + } + else + { /* use scan command */ + /* Send a SCAN command pointing to a filename; then in the then in the + * scan-method-neutral part, read the response back */ + +/* ================================================================= */ + + /* Prior to the reworking post-Exim-4.72, this scanned a directory, + which dates to when ClamAV needed us to break apart the email into the + MIME parts (eg, with the now deprecated demime condition coming first). + Some time back, ClamAV gained the ability to deconstruct the emails, so + doing this would actually have resulted in the mail attachments being + 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); + + DEBUG(D_acl) debug_printf( + "Malware scan: issuing %s local-path scan [%s]\n", + scanner_name, scanner_options); - /* 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 (send(sock, file_name, Ustrlen(file_name), 0) < 0) + return m_errlog_defer_3(scanent, + string_sprintf("unable to write to socket (%s)", strerror(errno)), + sock); - if (Ustrcmp(result_tag, "FOUND") == 0) + /* 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(sock, av_buffer, sizeof(av_buffer), tmo-time(NULL)); + (void)close(sock); + sock = -1; + + if (bread <= 0) + return m_errlog_defer(scanent, + string_sprintf("unable to read from socket (%s)", + errno == 0 ? "EOF" : strerror(errno))); + + if (bread == sizeof(av_buffer)) + return m_errlog_defer(scanent, 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: -> ": FOUND" + not-infected: -> ": OK" + error: -> ": ERROR + If the ExtendedDetectionInfo option has been turned on, then we get: + ": (:) 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_errlog_defer(scanent, 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("Malware response: %s\n", av_buffer); + + while (isspace(*--p) && (p > av_buffer)) + *p = '\0'; + if (*p) ++p; + response_end = p; + + /* colon in returned output? */ + if((p = Ustrchr(av_buffer,':')) == NULL) + return m_errlog_defer(scanent, 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 == ')') { - /* 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("Malware found, name \"%s\"\n", malware_name); - + /* "(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'; } - else if (Ustrcmp(result_tag, "ERROR") == 0) - return m_errlog_defer(scanent, - string_sprintf("ClamAV returned: %s", av_buffer)); + malware_name = string_copy(vname); + DEBUG(D_acl) debug_printf("Malware found, name \"%s\"\n", malware_name); - else if (Ustrcmp(result_tag, "OK") == 0) - { - /* Everything should be OK */ - malware_name = NULL; - DEBUG(D_acl) debug_printf("Malware not found\n"); + } + else if (Ustrcmp(result_tag, "ERROR") == 0) + return m_errlog_defer(scanent, + string_sprintf("ClamAV returned: %s", av_buffer)); - } - else - return m_errlog_defer(scanent, - string_sprintf("unparseable response from ClamAV: {%s}", av_buffer)); + else if (Ustrcmp(result_tag, "OK") == 0) + { + /* Everything should be OK */ + malware_name = NULL; + DEBUG(D_acl) debug_printf("Malware not found\n"); - break; + } + else + return m_errlog_defer(scanent, + string_sprintf("unparseable response from ClamAV: {%s}", av_buffer)); + + break; } /* clamd */ case M_SOCK: /* "sock" scanner type ------------------------------------- */ - /* This code was derived by Martin Poole from the clamd code contributed - by David Saez and the cmdline code - */ + /* This code was derived by Martin Poole from the clamd code contributed + by David Saez and the cmdline code + */ { - int bread; - uschar * commandline; - uschar av_buffer[1024]; - uschar * linebuffer; - uschar * sockline_scanner; - uschar sockline_scanner_default[] = "%s\n"; - const pcre *sockline_trig_re; - const pcre *sockline_name_re; - - /* find scanner command line */ - if ((sockline_scanner = string_nextinlist(&av_scanner_work, &sep, - NULL, 0))) - { /* check for no expansions apart from one %s */ - uschar * s = Ustrchr(sockline_scanner, '%'); - if (s++) - if ((*s != 's' && *s != '%') || Ustrchr(s+1, '%')) - return m_errlog_defer_3(scanent, - US"unsafe sock scanner call spec", sock); - } - else - sockline_scanner = sockline_scanner_default; + int bread; + uschar * commandline; + uschar av_buffer[1024]; + uschar * linebuffer; + uschar * sockline_scanner; + uschar sockline_scanner_default[] = "%s\n"; + const pcre *sockline_trig_re; + const pcre *sockline_name_re; + + /* find scanner command line */ + if ((sockline_scanner = string_nextinlist(&av_scanner_work, &sep, + NULL, 0))) + { /* check for no expansions apart from one %s */ + uschar * s = Ustrchr(sockline_scanner, '%'); + if (s++) + if ((*s != 's' && *s != '%') || Ustrchr(s+1, '%')) + return m_errlog_defer_3(scanent, + US"unsafe sock scanner call spec", sock); + } + else + sockline_scanner = sockline_scanner_default; - /* find scanner output trigger */ - sockline_trig_re = m_pcre_nextinlist(&av_scanner_work, &sep, - "missing trigger specification", &errstr); - if (!sockline_trig_re) - return m_errlog_defer_3(scanent, errstr, sock); + /* find scanner output trigger */ + sockline_trig_re = m_pcre_nextinlist(&av_scanner_work, &sep, + "missing trigger specification", &errstr); + if (!sockline_trig_re) + return m_errlog_defer_3(scanent, errstr, sock); - /* find virus name regex */ - sockline_name_re = m_pcre_nextinlist(&av_scanner_work, &sep, - "missing virus name regex specification", &errstr); - if (!sockline_name_re) - return m_errlog_defer_3(scanent, errstr, sock); + /* find virus name regex */ + sockline_name_re = m_pcre_nextinlist(&av_scanner_work, &sep, + "missing virus name regex specification", &errstr); + if (!sockline_name_re) + return m_errlog_defer_3(scanent, errstr, sock); - /* prepare scanner call - security depends on expansions check above */ - commandline = string_sprintf("%s/scan/%s/%s.eml", spool_directory, message_id, message_id); - commandline = string_sprintf( CS sockline_scanner, CS commandline); + /* prepare scanner call - security depends on expansions check above */ + commandline = string_sprintf("%s/scan/%s/%s.eml", spool_directory, message_id, message_id); + commandline = string_sprintf( CS sockline_scanner, CS commandline); - /* Pass the command string to the socket */ - if (m_sock_send(sock, commandline, Ustrlen(commandline), &errstr) < 0) - return m_errlog_defer(scanent, errstr); + /* Pass the command string to the socket */ + if (m_sock_send(sock, commandline, Ustrlen(commandline), &errstr) < 0) + return m_errlog_defer(scanent, errstr); - /* Read the result */ - bread = ip_recv(sock, av_buffer, sizeof(av_buffer), tmo-time(NULL)); + /* Read the result */ + bread = ip_recv(sock, av_buffer, sizeof(av_buffer), tmo-time(NULL)); - if (bread <= 0) - return m_errlog_defer_3(scanent, - string_sprintf("unable to read from socket (%s)", strerror(errno)), - sock); + if (bread <= 0) + return m_errlog_defer_3(scanent, + string_sprintf("unable to read from socket (%s)", strerror(errno)), + sock); - if (bread == sizeof(av_buffer)) - return m_errlog_defer_3(scanent, US"buffer too small", sock); - av_buffer[bread] = '\0'; - linebuffer = string_copy(av_buffer); + if (bread == sizeof(av_buffer)) + return m_errlog_defer_3(scanent, US"buffer too small", sock); + av_buffer[bread] = '\0'; + linebuffer = string_copy(av_buffer); - /* try trigger match */ - if (regex_match_and_setup(sockline_trig_re, linebuffer, 0, -1)) { - if (!(malware_name = m_pcre_exec(sockline_name_re, av_buffer))) - malware_name = US "unknown"; + /* try trigger match */ + if (regex_match_and_setup(sockline_trig_re, linebuffer, 0, -1)) + { + if (!(malware_name = m_pcre_exec(sockline_name_re, av_buffer))) + malware_name = US "unknown"; } - else /* no virus found */ - malware_name = NULL; - break; + else /* no virus found */ + malware_name = NULL; + break; } case M_MKSD: /* "mksd" scanner type ------------------------------------- */ { - char *mksd_options_end; - int mksd_maxproc = 1; /* default, if no option supplied */ - int retval; + char *mksd_options_end; + int mksd_maxproc = 1; /* default, if no option supplied */ + int retval; - if (scanner_options) - { - mksd_maxproc = (int)strtol(CS scanner_options, &mksd_options_end, 10); - if ( *scanner_options == '\0' - || *mksd_options_end != '\0' - || mksd_maxproc < 1 - || mksd_maxproc > 32 - ) - return m_errlog_defer(scanent, - string_sprintf("invalid option '%s'", scanner_options)); - } + if (scanner_options) + { + mksd_maxproc = (int)strtol(CS scanner_options, &mksd_options_end, 10); + if ( *scanner_options == '\0' + || *mksd_options_end != '\0' + || mksd_maxproc < 1 + || mksd_maxproc > 32 + ) + return m_errlog_defer(scanent, + string_sprintf("invalid option '%s'", scanner_options)); + } - if((sock = m_unixsocket(US "/var/run/mksd/socket", &errstr)) < 0) - return m_errlog_defer(scanent, errstr); + if((sock = m_unixsocket(US "/var/run/mksd/socket", &errstr)) < 0) + return m_errlog_defer(scanent, errstr); - malware_name = NULL; + malware_name = NULL; - DEBUG(D_acl) debug_printf("Malware scan: issuing %s scan\n", scanner_name); + DEBUG(D_acl) debug_printf("Malware scan: issuing %s scan\n", scanner_name); - if ((retval = mksd_scan_packed(scanent, sock, eml_filename, tmo)) != OK) - { - close (sock); - return retval; - } - break; + if ((retval = mksd_scan_packed(scanent, sock, eml_filename, tmo)) != OK) + { + close (sock); + return retval; + } + break; } + case M_AVAST: /* "avast" scanner type ----------------------------------- */ { int ovector[1*3]; @@ -1658,6 +1734,7 @@ malware_internal(uschar **listptr, uschar *eml_filename, BOOL faking) uschar * scanrequest; const pcre * avast_clean_re, * avast_virus_re; enum {AVA_HELO, AVA_OPT, AVA_RSP, AVA_DONE} avast_stage; + int nread; /* According to Martin Tuma @avast the protocol uses "escaped whitespace", that is, every embedded whitespace is backslash @@ -1677,7 +1754,9 @@ malware_internal(uschar **listptr, uschar *eml_filename, BOOL faking) return malware_errlog_defer(errstr); /* wait for result */ - for (avast_stage = AVA_HELO; recv_line(sock, buf, sizeof(buf), tmo) > 0; ) + for (avast_stage = AVA_HELO; + (nread = recv_line(sock, buf, sizeof(buf), tmo)) > 0; + ) { int slen = Ustrlen(buf); if (slen >= 1) @@ -1759,149 +1838,118 @@ malware_internal(uschar **listptr, uschar *eml_filename, BOOL faking) /* here for any unexpected response from the scanner */ goto endloop; } + } } - } endloop: switch(avast_stage) { - case AVA_HELO: + 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); + case AVA_RSP: return m_errlog_defer_3(scanent, + nread >= 0 + ? string_sprintf( + "invalid response from scanner: '%s'", buf) + : nread == -1 + ? US"EOF from scanner" + : US"timeout from scanner", + sock); default: break; } } - } /* scanner type switch */ + break; + } /* scanner type switch */ - if (sock >= 0) - (void) close (sock); - malware_ok = TRUE; /* set "been here, done that" marker */ + if (sock >= 0) + (void) close (sock); + malware_ok = TRUE; /* set "been here, done that" marker */ } - /* match virus name against pattern (caseless ------->----------v) */ - if (malware_name && regex_match_and_setup(re, malware_name, 0, -1)) - { - DEBUG(D_acl) debug_printf("Matched regex to malware [%s] [%s]\n", malware_regex, malware_name); - return OK; - } - else - return FAIL; +/* match virus name against pattern (caseless ------->----------v) */ +if (malware_name && regex_match_and_setup(re, malware_name, 0, -1)) + { + DEBUG(D_acl) debug_printf( + "Matched regex to malware [%s] [%s]\n", malware_re, malware_name); + return OK; + } +else + return FAIL; } -/* ============= private routines for the "mksd" scanner type ============== */ +/************************************************* +* Scan an email for malware * +*************************************************/ -#include +/* This is the normal interface for scanning an email, which doesn't need a +filename; it's a wrapper around the malware_file function. -static inline int -mksd_writev (int sock, struct iovec *iov, int iovcnt) +Arguments: + malware_re match condition for "malware=" + timeout if nonzero, timeout in seconds + +Returns: Exim message processing code (OK, FAIL, DEFER, ...) + where true means malware was found (condition applies) +*/ +int +malware(const uschar * malware_re, int timeout) { - int i; - - for (;;) { - do - i = writev (sock, iov, iovcnt); - while ((i < 0) && (errno == EINTR)); - if (i <= 0) { - (void) malware_errlog_defer(US"unable to write to mksd UNIX socket (/var/run/mksd/socket)"); - return -1; - } + uschar * scan_filename; + int ret; - for (;;) - if (i >= iov->iov_len) { - if (--iovcnt == 0) - return 0; - i -= iov->iov_len; - iov++; - } else { - iov->iov_len -= i; - iov->iov_base = CS iov->iov_base + i; - break; - } - } + scan_filename = string_sprintf("%s/scan/%s/%s.eml", + spool_directory, message_id, message_id); + ret = malware_internal(malware_re, scan_filename, timeout, FALSE); + if (ret == DEFER) av_failed = TRUE; + + return ret; } -static inline int -mksd_read_lines (int sock, uschar *av_buffer, int av_buffer_size, int tmo) -{ - int offset = 0; - int i; - do - { - i = ip_recv(sock, av_buffer+offset, av_buffer_size-offset, tmo-time(NULL)); - if (i <= 0) - { - (void) malware_errlog_defer(US"unable to read from mksd UNIX socket (/var/run/mksd/socket)"); - return -1; - } +/************************************************* +* Scan a file for malware * +*************************************************/ - offset += i; - /* offset == av_buffer_size -> buffer full */ - if (offset == av_buffer_size) - { - (void) malware_errlog_defer(US"malformed reply received from mksd"); - return -1; - } - } while (av_buffer[offset-1] != '\n'); +/* This is a test wrapper for scanning an email, which is not used in +normal processing. Scan any file, using the Exim scanning interface. +This function tampers with various global variables so is unsafe to use +in any other context. - av_buffer[offset] = '\0'; - return offset; -} +Arguments: + eml_filename a file holding the message to be scanned -static inline int -mksd_parse_line(struct scan * scanent, char *line) +Returns: Exim message processing code (OK, FAIL, DEFER, ...) + where true means malware was found (condition applies) +*/ +int +malware_in_file(uschar *eml_filename) { - char *p; - - switch (*line) { - case 'O': /* OK */ - return OK; - - case 'E': - case 'A': /* ERR */ - if ((p = strchr (line, '\n')) != NULL) - *p = '\0'; - return m_errlog_defer(scanent, - string_sprintf("scanner failed: %s", line)); - - default: /* VIR */ - if ((p = strchr (line, '\n')) != NULL) { - *p = '\0'; - if (((p-line) > 5) && (line[3] == ' ')) - if (((p = strchr (line+4, ' ')) != NULL) && ((p-line) > 4)) { - *p = '\0'; - malware_name = string_copy(US line+4); - return OK; - } - } - return m_errlog_defer(scanent, - string_sprintf("malformed reply received: %s", line)); - } -} + uschar message_id_buf[64]; + int ret; -static int -mksd_scan_packed(struct scan * scanent, int sock, uschar *scan_filename, int tmo) -{ - struct iovec iov[3]; - const char *cmd = "MSQ\n"; - uschar av_buffer[1024]; + /* spool_mbox() assumes various parameters exist, when creating + the relevant directory and the email within */ + (void) string_format(message_id_buf, sizeof(message_id_buf), + "dummy-%d", vaguely_random_number(INT_MAX)); + message_id = message_id_buf; + sender_address = US"malware-sender@example.net"; + return_path = US""; + recipients_list = NULL; + receive_add_recipient(US"malware-victim@example.net", -1); + enable_dollar_recipients = TRUE; - iov[0].iov_base = (void *) cmd; - iov[0].iov_len = 3; - iov[1].iov_base = CS scan_filename; - iov[1].iov_len = Ustrlen(scan_filename); - iov[2].iov_base = (void *) (cmd + 3); - iov[2].iov_len = 1; + ret = malware_internal(US"*", eml_filename, 0, TRUE); - if (mksd_writev (sock, iov, 3) < 0) - return DEFER; + Ustrncpy(spooled_message_id, message_id, sizeof(spooled_message_id)); + spool_mbox_ok = 1; + /* don't set no_mbox_unspool; at present, there's no way for it to become + set, but if that changes, then it should apply to these tests too */ + unspool_mbox(); - if (mksd_read_lines (sock, av_buffer, sizeof (av_buffer), tmo) < 0) - return DEFER; + /* silence static analysis tools */ + message_id = NULL; - return mksd_parse_line (scanent, CS av_buffer); + return ret; } #endif /*WITH_CONTENT_SCAN*/ diff --git a/src/src/spool_mbox.c b/src/src/spool_mbox.c index 12cf3d43e..edc28034c 100644 --- a/src/src/spool_mbox.c +++ b/src/src/spool_mbox.c @@ -26,7 +26,9 @@ uschar spooled_message_id[17]; /* returns a pointer to the FILE, and puts the size in bytes into mbox_file_size * normally, source_file_override is NULL */ -FILE *spool_mbox(unsigned long *mbox_file_size, uschar *source_file_override) { +FILE * +spool_mbox(unsigned long *mbox_file_size, const uschar *source_file_override) +{ uschar message_subdir[2]; uschar buffer[16384]; uschar *temp_string; diff --git a/test/aux-fixed/4007.script b/test/aux-fixed/4007.script index 9cdd098ff..e391a2bb2 100755 --- a/test/aux-fixed/4007.script +++ b/test/aux-fixed/4007.script @@ -18,5 +18,6 @@ fi case $action in accept) ;; reject) echo "VNAME found in file $1";; +pause3) sleep 3;; esac diff --git a/test/confs/4001 b/test/confs/4001 index 39e770545..00406b4ee 100644 --- a/test/confs/4001 +++ b/test/confs/4001 @@ -19,7 +19,7 @@ acl_smtp_data = c_data begin acl c_data: - accept !malware = * + accept !malware = * OPT deny logwrite = malware_name $malware_name # ----- Routers ----- diff --git a/test/confs/4002 b/test/confs/4002 index 5ebb937a6..cc0d05ebb 100644 --- a/test/confs/4002 +++ b/test/confs/4002 @@ -8,6 +8,7 @@ spool_directory = DIR/spool log_file_path = DIR/spool/log/%slog gecos_pattern = "" gecos_name = CALLER_NAME +log_selector = +subject av_scanner = aveserver : DIR/eximdir/aveserver_sock @@ -19,7 +20,7 @@ acl_smtp_data = c_data begin acl c_data: - accept !malware = * + accept !malware = * OPT deny logwrite = malware_name $malware_name # ----- Routers ----- diff --git a/test/confs/4003 b/test/confs/4003 index e2d65baa1..7fbd61ba0 100644 --- a/test/confs/4003 +++ b/test/confs/4003 @@ -8,6 +8,7 @@ spool_directory = DIR/spool log_file_path = DIR/spool/log/%slog gecos_pattern = "" gecos_name = CALLER_NAME +log_selector = +subject av_scanner = fsecure : DIR/eximdir/fsec_sock @@ -19,7 +20,7 @@ acl_smtp_data = c_data begin acl c_data: - accept !malware = * + accept !malware = <; * OPT deny logwrite = malware_name $malware_name # ----- Routers ----- diff --git a/test/confs/4004 b/test/confs/4004 index 22cbbd0f4..84471acac 100644 --- a/test/confs/4004 +++ b/test/confs/4004 @@ -8,6 +8,7 @@ spool_directory = DIR/spool log_file_path = DIR/spool/log/%slog gecos_pattern = "" gecos_name = CALLER_NAME +log_selector = +subject av_scanner = sophie : DIR/eximdir/sophie_sock @@ -19,7 +20,7 @@ acl_smtp_data = c_data begin acl c_data: - accept !malware = * + accept !malware = * OPT deny logwrite = malware_name $malware_name # ----- Routers ----- diff --git a/test/confs/4005 b/test/confs/4005 index 6d0b797b9..8ed28d4a6 100644 --- a/test/confs/4005 +++ b/test/confs/4005 @@ -1,4 +1,4 @@ -# Exim test configuration 4002 +# Exim test configuration 4005 # Content-scan: clamav interface exim_path = EXIM_PATH @@ -8,6 +8,7 @@ spool_directory = DIR/spool log_file_path = DIR/spool/log/%slog gecos_pattern = "" gecos_name = CALLER_NAME +log_selector = +subject av_scanner = clamd : DIR/eximdir/clam_sock @@ -19,7 +20,7 @@ acl_smtp_data = c_data begin acl c_data: - accept !malware = * + accept !malware = * OPT deny logwrite = malware_name $malware_name # ----- Routers ----- diff --git a/test/confs/4006 b/test/confs/4006 index 230f1159e..8ac65a584 100644 --- a/test/confs/4006 +++ b/test/confs/4006 @@ -1,4 +1,4 @@ -# Exim test configuration 4002 +# Exim test configuration 4006 # Content-scan: avast interface exim_path = EXIM_PATH @@ -8,6 +8,7 @@ spool_directory = DIR/spool log_file_path = DIR/spool/log/%slog gecos_pattern = "" gecos_name = CALLER_NAME +log_selector = +subject av_scanner = avast : DIR/eximdir/avast_sock : OPTION @@ -19,7 +20,7 @@ acl_smtp_data = c_data begin acl c_data: - accept !malware = * + accept !malware = * INSERT deny logwrite = malware_name $malware_name # ----- Routers ----- diff --git a/test/confs/4007 b/test/confs/4007 index 576c0087f..110a590f0 100644 --- a/test/confs/4007 +++ b/test/confs/4007 @@ -8,6 +8,7 @@ spool_directory = DIR/spool log_file_path = DIR/spool/log/%slog gecos_pattern = "" gecos_name = CALLER_NAME +log_selector = +subject av_scanner = cmdline : DIR/aux-fixed/TESTNUM.script -o OPT %s : found in file : ^(\S*) @@ -19,7 +20,7 @@ acl_smtp_data = c_data begin acl c_data: - accept !malware = * + accept !malware = * INSERT deny logwrite = malware_name $malware_name # ----- Routers ----- diff --git a/test/log/4001 b/test/log/4001 index f478b2723..d88f1bbe6 100644 --- a/test/log/4001 +++ b/test/log/4001 @@ -1,5 +1,9 @@ -1999-03-02 09:44:33 10HmaY-0005vi-00 <= CALLER@myhost.test.ex U=CALLER P=local-esmtp S=sss -1999-03-02 09:44:33 10HmaY-0005vi-00 => :blackhole: R=r -1999-03-02 09:44:33 10HmaY-0005vi-00 Completed +1999-03-02 09:44:33 10HmaZ-0005vi-00 <= CALLER@myhost.test.ex U=CALLER P=local-esmtp S=sss +1999-03-02 09:44:33 10HmaZ-0005vi-00 => :blackhole: R=r +1999-03-02 09:44:33 10HmaZ-0005vi-00 Completed 1999-03-02 09:44:33 10HmaX-0005vi-00 malware_name wibble 1999-03-02 09:44:33 10HmaX-0005vi-00 U=CALLER F= rejected after DATA +1999-03-02 09:44:33 10HmaY-0005vi-00 U=CALLER F= temporarily rejected after DATA +1999-03-02 09:44:33 10HmbA-0005vi-00 <= CALLER@myhost.test.ex U=CALLER P=local-esmtp S=sss +1999-03-02 09:44:33 10HmbA-0005vi-00 => :blackhole: R=r +1999-03-02 09:44:33 10HmbA-0005vi-00 Completed diff --git a/test/log/4002 b/test/log/4002 index 5ea545def..d60fa4e09 100644 --- a/test/log/4002 +++ b/test/log/4002 @@ -1,7 +1,19 @@ -1999-03-02 09:44:33 10HmaZ-0005vi-00 <= CALLER@myhost.test.ex U=CALLER P=local-esmtp S=sss -1999-03-02 09:44:33 10HmaZ-0005vi-00 => :blackhole: R=r -1999-03-02 09:44:33 10HmaZ-0005vi-00 Completed +1999-03-02 09:44:33 10HmbD-0005vi-00 <= CALLER@myhost.test.ex U=CALLER P=local-esmtp S=sss T="message should be accepted" +1999-03-02 09:44:33 10HmbD-0005vi-00 => :blackhole: R=r +1999-03-02 09:44:33 10HmbD-0005vi-00 Completed 1999-03-02 09:44:33 10HmaX-0005vi-00 malware acl condition: aveserver: unable to scan file TESTSUITE/spool/scan/10HmaX-0005vi-00/10HmaX-0005vi-00.eml (Responded: 5xx defer). 1999-03-02 09:44:33 10HmaX-0005vi-00 U=CALLER F= temporarily rejected after DATA -1999-03-02 09:44:33 10HmaY-0005vi-00 malware_name VNAME -1999-03-02 09:44:33 10HmaY-0005vi-00 U=CALLER F= rejected after DATA +1999-03-02 09:44:33 10HmbB-0005vi-00 malware_name VNAME +1999-03-02 09:44:33 10HmbB-0005vi-00 U=CALLER F= rejected after DATA +1999-03-02 09:44:33 10HmaY-0005vi-00 malware acl condition: aveserver: unavailable (Responded: nothing). +1999-03-02 09:44:33 10HmaY-0005vi-00 U=CALLER F= temporarily rejected after DATA +1999-03-02 09:44:33 10HmaZ-0005vi-00 malware acl condition: aveserver: unavailable (Responded: nothing). +1999-03-02 09:44:33 10HmaZ-0005vi-00 <= CALLER@myhost.test.ex U=CALLER P=local-esmtp S=sss T="message should be accepted despite timeout" +1999-03-02 09:44:33 10HmaZ-0005vi-00 => :blackhole: R=r +1999-03-02 09:44:33 10HmaZ-0005vi-00 Completed +1999-03-02 09:44:33 10HmbA-0005vi-00 malware acl condition: aveserver: unable to scan file TESTSUITE/spool/scan/10HmbA-0005vi-00/10HmbA-0005vi-00.eml (Responded: 5xx defer). +1999-03-02 09:44:33 10HmbA-0005vi-00 <= CALLER@myhost.test.ex U=CALLER P=local-esmtp S=sss T="accept this, overriding the scan daemon temp-error" +1999-03-02 09:44:33 10HmbA-0005vi-00 => :blackhole: R=r +1999-03-02 09:44:33 10HmbA-0005vi-00 Completed +1999-03-02 09:44:33 10HmbC-0005vi-00 malware_name VNAME +1999-03-02 09:44:33 10HmbC-0005vi-00 U=CALLER F= rejected after DATA diff --git a/test/log/4003 b/test/log/4003 index 1425f521e..ad916571e 100644 --- a/test/log/4003 +++ b/test/log/4003 @@ -1,5 +1,13 @@ -1999-03-02 09:44:33 10HmaY-0005vi-00 <= CALLER@myhost.test.ex U=CALLER P=local-esmtp S=sss +1999-03-02 09:44:33 10HmbB-0005vi-00 <= CALLER@myhost.test.ex U=CALLER P=local-esmtp S=sss T="message should be accepted" +1999-03-02 09:44:33 10HmbB-0005vi-00 => :blackhole: R=r +1999-03-02 09:44:33 10HmbB-0005vi-00 Completed +1999-03-02 09:44:33 10HmaZ-0005vi-00 malware_name VNAME +1999-03-02 09:44:33 10HmaZ-0005vi-00 U=CALLER F= rejected after DATA +1999-03-02 09:44:33 10HmaX-0005vi-00 malware acl condition: fsecure: unable to read answer 0 (Connection timed out) +1999-03-02 09:44:33 10HmaX-0005vi-00 U=CALLER F= temporarily rejected after DATA +1999-03-02 09:44:33 10HmaY-0005vi-00 malware acl condition: fsecure: unable to read answer 0 (Connection timed out) +1999-03-02 09:44:33 10HmaY-0005vi-00 <= CALLER@myhost.test.ex U=CALLER P=local-esmtp S=sss T="message should be accepted despite timeout" 1999-03-02 09:44:33 10HmaY-0005vi-00 => :blackhole: R=r 1999-03-02 09:44:33 10HmaY-0005vi-00 Completed -1999-03-02 09:44:33 10HmaX-0005vi-00 malware_name VNAME -1999-03-02 09:44:33 10HmaX-0005vi-00 U=CALLER F= rejected after DATA +1999-03-02 09:44:33 10HmbA-0005vi-00 malware_name VNAME +1999-03-02 09:44:33 10HmbA-0005vi-00 U=CALLER F= rejected after DATA diff --git a/test/log/4004 b/test/log/4004 index 624fb3232..1929e96a0 100644 --- a/test/log/4004 +++ b/test/log/4004 @@ -1,7 +1,13 @@ -1999-03-02 09:44:33 10HmaZ-0005vi-00 <= CALLER@myhost.test.ex U=CALLER P=local-esmtp S=sss -1999-03-02 09:44:33 10HmaZ-0005vi-00 => :blackhole: R=r -1999-03-02 09:44:33 10HmaZ-0005vi-00 Completed +1999-03-02 09:44:33 10HmbB-0005vi-00 <= CALLER@myhost.test.ex U=CALLER P=local-esmtp S=sss T="message should be accepted" +1999-03-02 09:44:33 10HmbB-0005vi-00 => :blackhole: R=r +1999-03-02 09:44:33 10HmbB-0005vi-00 Completed 1999-03-02 09:44:33 10HmaX-0005vi-00 malware acl condition: sophie: scanner reported error 1999-03-02 09:44:33 10HmaX-0005vi-00 U=CALLER F= temporarily rejected after DATA -1999-03-02 09:44:33 10HmaY-0005vi-00 malware_name VNAME -1999-03-02 09:44:33 10HmaY-0005vi-00 U=CALLER F= rejected after DATA +1999-03-02 09:44:33 10HmbA-0005vi-00 malware_name VNAME +1999-03-02 09:44:33 10HmbA-0005vi-00 U=CALLER F= rejected after DATA +1999-03-02 09:44:33 10HmaY-0005vi-00 malware acl condition: sophie: unable to read from UNIX socket (TESTSUITE/eximdir/sophie_sock) +1999-03-02 09:44:33 10HmaY-0005vi-00 U=CALLER F= temporarily rejected after DATA +1999-03-02 09:44:33 10HmaZ-0005vi-00 malware acl condition: sophie: unable to read from UNIX socket (TESTSUITE/eximdir/sophie_sock) +1999-03-02 09:44:33 10HmaZ-0005vi-00 <= CALLER@myhost.test.ex U=CALLER P=local-esmtp S=sss T="message should be accepted destpite timeout" +1999-03-02 09:44:33 10HmaZ-0005vi-00 => :blackhole: R=r +1999-03-02 09:44:33 10HmaZ-0005vi-00 Completed diff --git a/test/log/4005 b/test/log/4005 index 7e2dac20e..7a4bb1cd0 100644 --- a/test/log/4005 +++ b/test/log/4005 @@ -1,7 +1,13 @@ -1999-03-02 09:44:33 10HmaZ-0005vi-00 <= CALLER@myhost.test.ex U=CALLER P=local-esmtp S=sss -1999-03-02 09:44:33 10HmaZ-0005vi-00 => :blackhole: R=r -1999-03-02 09:44:33 10HmaZ-0005vi-00 Completed +1999-03-02 09:44:33 10HmbB-0005vi-00 <= CALLER@myhost.test.ex U=CALLER P=local-esmtp S=sss T="message should be accepted" +1999-03-02 09:44:33 10HmbB-0005vi-00 => :blackhole: R=r +1999-03-02 09:44:33 10HmbB-0005vi-00 Completed 1999-03-02 09:44:33 10HmaX-0005vi-00 malware acl condition: clamd: ClamAV returned: scanned_file_name: 666 ERROR 1999-03-02 09:44:33 10HmaX-0005vi-00 U=CALLER F= temporarily rejected after DATA -1999-03-02 09:44:33 10HmaY-0005vi-00 malware_name VNAME -1999-03-02 09:44:33 10HmaY-0005vi-00 U=CALLER F= rejected after DATA +1999-03-02 09:44:33 10HmbA-0005vi-00 malware_name VNAME +1999-03-02 09:44:33 10HmbA-0005vi-00 U=CALLER F= rejected after DATA +1999-03-02 09:44:33 10HmaY-0005vi-00 malware acl condition: clamd: unable to read from socket (Connection timed out) +1999-03-02 09:44:33 10HmaY-0005vi-00 U=CALLER F= temporarily rejected after DATA +1999-03-02 09:44:33 10HmaZ-0005vi-00 malware acl condition: clamd: unable to read from socket (Connection timed out) +1999-03-02 09:44:33 10HmaZ-0005vi-00 <= CALLER@myhost.test.ex U=CALLER P=local-esmtp S=sss T="accept this one despite timeout" +1999-03-02 09:44:33 10HmaZ-0005vi-00 => :blackhole: R=r +1999-03-02 09:44:33 10HmaZ-0005vi-00 Completed diff --git a/test/log/4006 b/test/log/4006 index 4304b8789..ccdd53595 100644 --- a/test/log/4006 +++ b/test/log/4006 @@ -1,8 +1,13 @@ -1999-03-02 09:44:33 10HmaZ-0005vi-00 <= CALLER@myhost.test.ex U=CALLER P=local-esmtp S=sss +1999-03-02 09:44:33 10HmbB-0005vi-00 <= CALLER@myhost.test.ex U=CALLER P=local-esmtp S=sss T="message should be accepted" +1999-03-02 09:44:33 10HmbB-0005vi-00 => :blackhole: R=r +1999-03-02 09:44:33 10HmbB-0005vi-00 Completed +1999-03-02 09:44:33 10HmaX-0005vi-00 malware acl condition: avast: invalid response from scanner: 'blah [E]' +1999-03-02 09:44:33 10HmaX-0005vi-00 U=CALLER F= temporarily rejected after DATA +1999-03-02 09:44:33 10HmbA-0005vi-00 malware_name VNAME +1999-03-02 09:44:33 10HmbA-0005vi-00 U=CALLER F= rejected after DATA +1999-03-02 09:44:33 10HmaY-0005vi-00 malware acl condition: avast: timeout from scanner +1999-03-02 09:44:33 10HmaY-0005vi-00 U=CALLER F= temporarily rejected after DATA +1999-03-02 09:44:33 10HmaZ-0005vi-00 malware acl condition: avast: timeout from scanner +1999-03-02 09:44:33 10HmaZ-0005vi-00 <= CALLER@myhost.test.ex U=CALLER P=local-esmtp S=sss T="message should be accepted despite timeout" 1999-03-02 09:44:33 10HmaZ-0005vi-00 => :blackhole: R=r 1999-03-02 09:44:33 10HmaZ-0005vi-00 Completed -1999-03-02 09:44:33 10HmaX-0005vi-00 malware acl condition: avast: invalid response from scanner: blah [E] - -1999-03-02 09:44:33 10HmaX-0005vi-00 U=CALLER F= temporarily rejected after DATA -1999-03-02 09:44:33 10HmaY-0005vi-00 malware_name VNAME -1999-03-02 09:44:33 10HmaY-0005vi-00 U=CALLER F= rejected after DATA diff --git a/test/log/4007 b/test/log/4007 index 1425f521e..f78b4be3a 100644 --- a/test/log/4007 +++ b/test/log/4007 @@ -1,5 +1,11 @@ -1999-03-02 09:44:33 10HmaY-0005vi-00 <= CALLER@myhost.test.ex U=CALLER P=local-esmtp S=sss +1999-03-02 09:44:33 10HmbA-0005vi-00 <= CALLER@myhost.test.ex U=CALLER P=local-esmtp S=sss T="message should be accepted" +1999-03-02 09:44:33 10HmbA-0005vi-00 => :blackhole: R=r +1999-03-02 09:44:33 10HmbA-0005vi-00 Completed +1999-03-02 09:44:33 10HmaZ-0005vi-00 malware_name VNAME +1999-03-02 09:44:33 10HmaZ-0005vi-00 U=CALLER F= rejected after DATA +1999-03-02 09:44:33 10HmaX-0005vi-00 malware acl condition: cmdline: unable to read from scanner (TESTSUITE/aux-fixed/4007.script -o pause3 TESTSUITE/spool/scan/10HmaX-0005vi-00 2>&1): Connection timed out +1999-03-02 09:44:33 10HmaX-0005vi-00 U=CALLER F= temporarily rejected after DATA +1999-03-02 09:44:33 10HmaY-0005vi-00 malware acl condition: cmdline: unable to read from scanner (TESTSUITE/aux-fixed/4007.script -o pause3 TESTSUITE/spool/scan/10HmaY-0005vi-00 2>&1): Connection timed out +1999-03-02 09:44:33 10HmaY-0005vi-00 <= CALLER@myhost.test.ex U=CALLER P=local-esmtp S=sss T="message should be accepted despite a timeout" 1999-03-02 09:44:33 10HmaY-0005vi-00 => :blackhole: R=r 1999-03-02 09:44:33 10HmaY-0005vi-00 Completed -1999-03-02 09:44:33 10HmaX-0005vi-00 malware_name VNAME -1999-03-02 09:44:33 10HmaX-0005vi-00 U=CALLER F= rejected after DATA diff --git a/test/paniclog/4002 b/test/paniclog/4002 index 0fd411cb6..8d6b6f1c4 100644 --- a/test/paniclog/4002 +++ b/test/paniclog/4002 @@ -1 +1,4 @@ 1999-03-02 09:44:33 10HmaX-0005vi-00 malware acl condition: aveserver: unable to scan file TESTSUITE/spool/scan/10HmaX-0005vi-00/10HmaX-0005vi-00.eml (Responded: 5xx defer). +1999-03-02 09:44:33 10HmaY-0005vi-00 malware acl condition: aveserver: unavailable (Responded: nothing). +1999-03-02 09:44:33 10HmaZ-0005vi-00 malware acl condition: aveserver: unavailable (Responded: nothing). +1999-03-02 09:44:33 10HmbA-0005vi-00 malware acl condition: aveserver: unable to scan file TESTSUITE/spool/scan/10HmbA-0005vi-00/10HmbA-0005vi-00.eml (Responded: 5xx defer). diff --git a/test/paniclog/4003 b/test/paniclog/4003 new file mode 100644 index 000000000..aff2aff3f --- /dev/null +++ b/test/paniclog/4003 @@ -0,0 +1,2 @@ +1999-03-02 09:44:33 10HmaX-0005vi-00 malware acl condition: fsecure: unable to read answer 0 (Connection timed out) +1999-03-02 09:44:33 10HmaY-0005vi-00 malware acl condition: fsecure: unable to read answer 0 (Connection timed out) diff --git a/test/paniclog/4004 b/test/paniclog/4004 index 1b8d84454..3a5b4eceb 100644 --- a/test/paniclog/4004 +++ b/test/paniclog/4004 @@ -1 +1,3 @@ 1999-03-02 09:44:33 10HmaX-0005vi-00 malware acl condition: sophie: scanner reported error +1999-03-02 09:44:33 10HmaY-0005vi-00 malware acl condition: sophie: unable to read from UNIX socket (TESTSUITE/eximdir/sophie_sock) +1999-03-02 09:44:33 10HmaZ-0005vi-00 malware acl condition: sophie: unable to read from UNIX socket (TESTSUITE/eximdir/sophie_sock) diff --git a/test/paniclog/4005 b/test/paniclog/4005 index 12b19bc57..dbaf01e3a 100644 --- a/test/paniclog/4005 +++ b/test/paniclog/4005 @@ -1 +1,3 @@ 1999-03-02 09:44:33 10HmaX-0005vi-00 malware acl condition: clamd: ClamAV returned: scanned_file_name: 666 ERROR +1999-03-02 09:44:33 10HmaY-0005vi-00 malware acl condition: clamd: unable to read from socket (Connection timed out) +1999-03-02 09:44:33 10HmaZ-0005vi-00 malware acl condition: clamd: unable to read from socket (Connection timed out) diff --git a/test/paniclog/4006 b/test/paniclog/4006 index 24ab94808..19caa79fe 100644 --- a/test/paniclog/4006 +++ b/test/paniclog/4006 @@ -1,2 +1,3 @@ -1999-03-02 09:44:33 10HmaX-0005vi-00 malware acl condition: avast: invalid response from scanner: blah [E] - +1999-03-02 09:44:33 10HmaX-0005vi-00 malware acl condition: avast: invalid response from scanner: 'blah [E]' +1999-03-02 09:44:33 10HmaY-0005vi-00 malware acl condition: avast: timeout from scanner +1999-03-02 09:44:33 10HmaZ-0005vi-00 malware acl condition: avast: timeout from scanner diff --git a/test/paniclog/4007 b/test/paniclog/4007 new file mode 100644 index 000000000..156815be5 --- /dev/null +++ b/test/paniclog/4007 @@ -0,0 +1,2 @@ +1999-03-02 09:44:33 10HmaX-0005vi-00 malware acl condition: cmdline: unable to read from scanner (TESTSUITE/aux-fixed/4007.script -o pause3 TESTSUITE/spool/scan/10HmaX-0005vi-00 2>&1): Connection timed out +1999-03-02 09:44:33 10HmaY-0005vi-00 malware acl condition: cmdline: unable to read from scanner (TESTSUITE/aux-fixed/4007.script -o pause3 TESTSUITE/spool/scan/10HmaY-0005vi-00 2>&1): Connection timed out diff --git a/test/rejectlog/4001 b/test/rejectlog/4001 index bd26ac2a4..8ec883c54 100644 --- a/test/rejectlog/4001 +++ b/test/rejectlog/4001 @@ -10,3 +10,15 @@ P Received: from CALLER (helo=test.ex) Subject: message should be rejected I Message-Id: F From: CALLER_NAME +1999-03-02 09:44:33 10HmaY-0005vi-00 U=CALLER F= temporarily rejected after DATA +Envelope-from: +Envelope-to: +P Received: from CALLER (helo=test.ex) + by myhost.test.ex with local-esmtp (Exim x.yz) + (envelope-from ) + id 10HmaY-0005vi-00 + for userx@test.ex; Tue, 2 Mar 1999 09:44:33 +0000 + Date: Tue, 2 Mar 1999 09:44:33 +0000 + Subject: message should be deferred due to timeout +I Message-Id: +F From: CALLER_NAME diff --git a/test/rejectlog/4002 b/test/rejectlog/4002 index 2054abdb0..16c708986 100644 --- a/test/rejectlog/4002 +++ b/test/rejectlog/4002 @@ -7,10 +7,22 @@ P Received: from CALLER (helo=test.ex) id 10HmaX-0005vi-00 for userx@test.ex; Tue, 2 Mar 1999 09:44:33 +0000 Date: Tue, 2 Mar 1999 09:44:33 +0000 - Subject: defer this one + Subject: defer this one (daemon tmp-err) I Message-Id: F From: CALLER_NAME -1999-03-02 09:44:33 10HmaY-0005vi-00 U=CALLER F= rejected after DATA +1999-03-02 09:44:33 10HmbB-0005vi-00 U=CALLER F= rejected after DATA +Envelope-from: +Envelope-to: +P Received: from CALLER (helo=test.ex) + by myhost.test.ex with local-esmtp (Exim x.yz) + (envelope-from ) + id 10HmbB-0005vi-00 + for userx@test.ex; Tue, 2 Mar 1999 09:44:33 +0000 + Date: Tue, 2 Mar 1999 09:44:33 +0000 + Subject: message should be rejected (virus found) +I Message-Id: +F From: CALLER_NAME +1999-03-02 09:44:33 10HmaY-0005vi-00 U=CALLER F= temporarily rejected after DATA Envelope-from: Envelope-to: P Received: from CALLER (helo=test.ex) @@ -19,6 +31,18 @@ P Received: from CALLER (helo=test.ex) id 10HmaY-0005vi-00 for userx@test.ex; Tue, 2 Mar 1999 09:44:33 +0000 Date: Tue, 2 Mar 1999 09:44:33 +0000 - Subject: message should be rejected + Subject: message should be deferred due to timeout I Message-Id: F From: CALLER_NAME +1999-03-02 09:44:33 10HmbC-0005vi-00 U=CALLER F= rejected after DATA +Envelope-from: +Envelope-to: +P Received: from CALLER (helo=test.ex) + by myhost.test.ex with local-esmtp (Exim x.yz) + (envelope-from ) + id 10HmbC-0005vi-00 + for userx@test.ex; Tue, 2 Mar 1999 09:44:33 +0000 + Date: Tue, 2 Mar 1999 09:44:33 +0000 + Subject: message should be rejected (virus found) +I Message-Id: +F From: CALLER_NAME diff --git a/test/rejectlog/4003 b/test/rejectlog/4003 index bd26ac2a4..fbe3b69f0 100644 --- a/test/rejectlog/4003 +++ b/test/rejectlog/4003 @@ -1,12 +1,36 @@ -1999-03-02 09:44:33 10HmaX-0005vi-00 U=CALLER F= rejected after DATA +1999-03-02 09:44:33 10HmaZ-0005vi-00 U=CALLER F= rejected after DATA Envelope-from: Envelope-to: P Received: from CALLER (helo=test.ex) by myhost.test.ex with local-esmtp (Exim x.yz) (envelope-from ) - id 10HmaX-0005vi-00 + id 10HmaZ-0005vi-00 for userx@test.ex; Tue, 2 Mar 1999 09:44:33 +0000 Date: Tue, 2 Mar 1999 09:44:33 +0000 Subject: message should be rejected +I Message-Id: +F From: CALLER_NAME +1999-03-02 09:44:33 10HmaX-0005vi-00 U=CALLER F= temporarily rejected after DATA +Envelope-from: +Envelope-to: +P Received: from CALLER (helo=test.ex) + by myhost.test.ex with local-esmtp (Exim x.yz) + (envelope-from ) + id 10HmaX-0005vi-00 + for userx@test.ex; Tue, 2 Mar 1999 09:44:33 +0000 + Date: Tue, 2 Mar 1999 09:44:33 +0000 + Subject: message should be tmp-rejected due to timeout I Message-Id: F From: CALLER_NAME +1999-03-02 09:44:33 10HmbA-0005vi-00 U=CALLER F= rejected after DATA +Envelope-from: +Envelope-to: +P Received: from CALLER (helo=test.ex) + by myhost.test.ex with local-esmtp (Exim x.yz) + (envelope-from ) + id 10HmbA-0005vi-00 + for userx@test.ex; Tue, 2 Mar 1999 09:44:33 +0000 + Date: Tue, 2 Mar 1999 09:44:33 +0000 + Subject: message should be rejected as the options are not relevant +I Message-Id: +F From: CALLER_NAME diff --git a/test/rejectlog/4004 b/test/rejectlog/4004 index 2054abdb0..5aad09639 100644 --- a/test/rejectlog/4004 +++ b/test/rejectlog/4004 @@ -10,15 +10,27 @@ P Received: from CALLER (helo=test.ex) Subject: defer this one I Message-Id: F From: CALLER_NAME -1999-03-02 09:44:33 10HmaY-0005vi-00 U=CALLER F= rejected after DATA +1999-03-02 09:44:33 10HmbA-0005vi-00 U=CALLER F= rejected after DATA Envelope-from: Envelope-to: P Received: from CALLER (helo=test.ex) by myhost.test.ex with local-esmtp (Exim x.yz) (envelope-from ) - id 10HmaY-0005vi-00 + id 10HmbA-0005vi-00 for userx@test.ex; Tue, 2 Mar 1999 09:44:33 +0000 Date: Tue, 2 Mar 1999 09:44:33 +0000 Subject: message should be rejected +I Message-Id: +F From: CALLER_NAME +1999-03-02 09:44:33 10HmaY-0005vi-00 U=CALLER F= temporarily rejected after DATA +Envelope-from: +Envelope-to: +P Received: from CALLER (helo=test.ex) + by myhost.test.ex with local-esmtp (Exim x.yz) + (envelope-from ) + id 10HmaY-0005vi-00 + for userx@test.ex; Tue, 2 Mar 1999 09:44:33 +0000 + Date: Tue, 2 Mar 1999 09:44:33 +0000 + Subject: message should be tmp-rejected due to timeout I Message-Id: F From: CALLER_NAME diff --git a/test/rejectlog/4005 b/test/rejectlog/4005 index 2054abdb0..f9626e35a 100644 --- a/test/rejectlog/4005 +++ b/test/rejectlog/4005 @@ -7,18 +7,30 @@ P Received: from CALLER (helo=test.ex) id 10HmaX-0005vi-00 for userx@test.ex; Tue, 2 Mar 1999 09:44:33 +0000 Date: Tue, 2 Mar 1999 09:44:33 +0000 - Subject: defer this one + Subject: defer this one due to scanner error I Message-Id: F From: CALLER_NAME -1999-03-02 09:44:33 10HmaY-0005vi-00 U=CALLER F= rejected after DATA +1999-03-02 09:44:33 10HmbA-0005vi-00 U=CALLER F= rejected after DATA Envelope-from: Envelope-to: P Received: from CALLER (helo=test.ex) by myhost.test.ex with local-esmtp (Exim x.yz) (envelope-from ) - id 10HmaY-0005vi-00 + id 10HmbA-0005vi-00 for userx@test.ex; Tue, 2 Mar 1999 09:44:33 +0000 Date: Tue, 2 Mar 1999 09:44:33 +0000 Subject: message should be rejected +I Message-Id: +F From: CALLER_NAME +1999-03-02 09:44:33 10HmaY-0005vi-00 U=CALLER F= temporarily rejected after DATA +Envelope-from: +Envelope-to: +P Received: from CALLER (helo=test.ex) + by myhost.test.ex with local-esmtp (Exim x.yz) + (envelope-from ) + id 10HmaY-0005vi-00 + for userx@test.ex; Tue, 2 Mar 1999 09:44:33 +0000 + Date: Tue, 2 Mar 1999 09:44:33 +0000 + Subject: defer this one due to timeout I Message-Id: F From: CALLER_NAME diff --git a/test/rejectlog/4006 b/test/rejectlog/4006 index 2054abdb0..6ba3a2659 100644 --- a/test/rejectlog/4006 +++ b/test/rejectlog/4006 @@ -7,18 +7,30 @@ P Received: from CALLER (helo=test.ex) id 10HmaX-0005vi-00 for userx@test.ex; Tue, 2 Mar 1999 09:44:33 +0000 Date: Tue, 2 Mar 1999 09:44:33 +0000 - Subject: defer this one + Subject: defer this one, the scanner had an error I Message-Id: F From: CALLER_NAME -1999-03-02 09:44:33 10HmaY-0005vi-00 U=CALLER F= rejected after DATA +1999-03-02 09:44:33 10HmbA-0005vi-00 U=CALLER F= rejected after DATA Envelope-from: Envelope-to: P Received: from CALLER (helo=test.ex) by myhost.test.ex with local-esmtp (Exim x.yz) (envelope-from ) - id 10HmaY-0005vi-00 + id 10HmbA-0005vi-00 for userx@test.ex; Tue, 2 Mar 1999 09:44:33 +0000 Date: Tue, 2 Mar 1999 09:44:33 +0000 Subject: message should be rejected +I Message-Id: +F From: CALLER_NAME +1999-03-02 09:44:33 10HmaY-0005vi-00 U=CALLER F= temporarily rejected after DATA +Envelope-from: +Envelope-to: +P Received: from CALLER (helo=test.ex) + by myhost.test.ex with local-esmtp (Exim x.yz) + (envelope-from ) + id 10HmaY-0005vi-00 + for userx@test.ex; Tue, 2 Mar 1999 09:44:33 +0000 + Date: Tue, 2 Mar 1999 09:44:33 +0000 + Subject: message should be tmp-rejected due to timeout I Message-Id: F From: CALLER_NAME diff --git a/test/rejectlog/4007 b/test/rejectlog/4007 index bd26ac2a4..2c4463ad0 100644 --- a/test/rejectlog/4007 +++ b/test/rejectlog/4007 @@ -1,12 +1,24 @@ -1999-03-02 09:44:33 10HmaX-0005vi-00 U=CALLER F= rejected after DATA +1999-03-02 09:44:33 10HmaZ-0005vi-00 U=CALLER F= rejected after DATA Envelope-from: Envelope-to: P Received: from CALLER (helo=test.ex) by myhost.test.ex with local-esmtp (Exim x.yz) (envelope-from ) - id 10HmaX-0005vi-00 + id 10HmaZ-0005vi-00 for userx@test.ex; Tue, 2 Mar 1999 09:44:33 +0000 Date: Tue, 2 Mar 1999 09:44:33 +0000 Subject: message should be rejected +I Message-Id: +F From: CALLER_NAME +1999-03-02 09:44:33 10HmaX-0005vi-00 U=CALLER F= temporarily rejected after DATA +Envelope-from: +Envelope-to: +P Received: from CALLER (helo=test.ex) + by myhost.test.ex with local-esmtp (Exim x.yz) + (envelope-from ) + id 10HmaX-0005vi-00 + for userx@test.ex; Tue, 2 Mar 1999 09:44:33 +0000 + Date: Tue, 2 Mar 1999 09:44:33 +0000 + Subject: message should be tmp-rejected due to timeout I Message-Id: F From: CALLER_NAME diff --git a/test/scripts/4000-scanning/4001 b/test/scripts/4000-scanning/4001 index 0234a3d5d..25a676e04 100644 --- a/test/scripts/4000-scanning/4001 +++ b/test/scripts/4000-scanning/4001 @@ -9,7 +9,7 @@ server PORT_S # # # -exim -odi -bs +exim -odi -bs -DOPT= ehlo test.ex mail from:<> rcpt to: @@ -37,7 +37,7 @@ server PORT_S # # # -exim -odi -bs +exim -odi -bs -DOPT= ehlo test.ex mail from:<> rcpt to: @@ -45,6 +45,49 @@ data Date: Fri, 17 Dec 2004 14:35:01 +0100 Subject: message should be rejected +due to the server response (above) +. +quit +**** +# +# +# +server PORT_S + +rcpt to: +data +Date: Fri, 17 Dec 2004 14:35:01 +0100 +Subject: message should be deferred due to timeout + +. +quit +**** +# +# +# +server PORT_S + +rcpt to: +data +Date: Fri, 17 Dec 2004 14:35:01 +0100 +Subject: message should be accepted despite timeout + . quit **** diff --git a/test/scripts/4000-scanning/4002 b/test/scripts/4000-scanning/4002 index 201fdf197..4754609c0 100644 --- a/test/scripts/4000-scanning/4002 +++ b/test/scripts/4000-scanning/4002 @@ -11,7 +11,7 @@ server DIR/eximdir/aveserver_sock # # # -exim -odi -bs +exim -odi -bs -DOPT= ehlo test.ex mail from:<> rcpt to: @@ -36,13 +36,13 @@ server DIR/eximdir/aveserver_sock # # # -exim -odi -bs +exim -odi -bs -DOPT= ehlo test.ex mail from:<> rcpt to: data Date: Fri, 17 Dec 2004 14:35:01 +0100 -Subject: defer this one +Subject: defer this one (daemon tmp-err) . quit @@ -62,13 +62,106 @@ server DIR/eximdir/aveserver_sock # # # -exim -odi -bs +exim -odi -bs -DOPT= ehlo test.ex mail from:<> rcpt to: data Date: Fri, 17 Dec 2004 14:35:01 +0100 -Subject: message should be rejected +Subject: message should be rejected (virus found) + +. +quit +**** +# +# +# +server DIR/eximdir/aveserver_sock +*sleep 3 +**** +# +# +# +exim -odi -bs -DOPT="/tmo=2s" +ehlo test.ex +mail from:<> +rcpt to: +data +Date: Fri, 17 Dec 2004 14:35:01 +0100 +Subject: message should be deferred due to timeout + +. +quit +**** +# +# +# +server DIR/eximdir/aveserver_sock +*sleep 3 +**** +# +# +# +exim -odi -bs -DOPT="/tmo=2s / defer_ok" +ehlo test.ex +mail from:<> +rcpt to: +data +Date: Fri, 17 Dec 2004 14:35:01 +0100 +Subject: message should be accepted despite timeout + +. +quit +**** +# +# +# +server DIR/eximdir/aveserver_sock +>2xx ready +5xx defer +2xx ready +*eof +**** +# +# +# +exim -odi -bs -DOPT=" / defer_ok" +ehlo test.ex +mail from:<> +rcpt to: +data +Date: Fri, 17 Dec 2004 14:35:01 +0100 +Subject: accept this, overriding the scan daemon temp-error + +. +quit +**** +# +# +# +# Recheck that defer_ok option does not affect a malware detection +# +server DIR/eximdir/aveserver_sock +>2xx ready +322 VNAME found +>2xx ready +2xx ready +*eof +**** +# +# +# +exim -odi -bs -DOPT="/defer_ok" +ehlo test.ex +mail from:<> +rcpt to: +data +Date: Fri, 17 Dec 2004 14:35:01 +0100 +Subject: message should be rejected (virus found) . quit diff --git a/test/scripts/4000-scanning/4003 b/test/scripts/4000-scanning/4003 index 0bfa14e9e..49d331bf1 100644 --- a/test/scripts/4000-scanning/4003 +++ b/test/scripts/4000-scanning/4003 @@ -1,4 +1,5 @@ # content scan interface: fsecure +# also testing alternate list separator for malware= args # server DIR/eximdir/fsec_sock rcpt to: @@ -49,7 +50,7 @@ server DIR/eximdir/fsec_sock # # # -exim -odi -bs +exim -odi -bs -DOPT= ehlo test.ex mail from:<> rcpt to: @@ -60,3 +61,74 @@ Subject: message should be rejected . quit **** +# +# +# +server DIR/eximdir/fsec_sock +*sleep 3 +**** +# +# +# +exim -odi -bs -DOPT=";tmo=2s" +ehlo test.ex +mail from:<> +rcpt to: +data +Date: Fri, 17 Dec 2004 14:35:01 +0100 +Subject: message should be tmp-rejected due to timeout + +. +quit +**** +# +# +# +server DIR/eximdir/fsec_sock +*sleep 3 +**** +# +# +# +exim -odi -bs -DOPT=";defer_ok;tmo=2s" +ehlo test.ex +mail from:<> +rcpt to: +data +Date: Fri, 17 Dec 2004 14:35:01 +0100 +Subject: message should be accepted despite timeout + +. +quit +**** +# +# +# +server DIR/eximdir/fsec_sock +ignored_response +ignored_response +ignored_response +ignored_response +LF>xxxINFECTED blah VNAME blah +>LF>OK Scan ok. +*eof +**** +# +# +# +exim -odi -bs -DOPT="; defer_ok ; tmo=5h" +ehlo test.ex +mail from:<> +rcpt to: +data +Date: Fri, 17 Dec 2004 14:35:01 +0100 +Subject: message should be rejected as the options are not relevant + +. +quit +**** diff --git a/test/scripts/4000-scanning/4004 b/test/scripts/4000-scanning/4004 index e6a142930..956f04507 100644 --- a/test/scripts/4000-scanning/4004 +++ b/test/scripts/4000-scanning/4004 @@ -8,7 +8,7 @@ server DIR/eximdir/sophie_sock # # # -exim -odi -bs +exim -odi -bs -DOPT= ehlo test.ex mail from:<> rcpt to: @@ -30,7 +30,7 @@ server DIR/eximdir/sophie_sock # # # -exim -odi -bs +exim -odi -bs -DOPT= ehlo test.ex mail from:<> rcpt to: @@ -52,7 +52,7 @@ server DIR/eximdir/sophie_sock # # # -exim -odi -bs +exim -odi -bs -DOPT=/defer_ok ehlo test.ex mail from:<> rcpt to: @@ -63,3 +63,43 @@ Subject: message should be rejected . quit **** +# +# +# +server DIR/eximdir/sophie_sock +*sleep 3 +**** +# +# +# +exim -odi -bs -DOPT=/tmo=2s +ehlo test.ex +mail from:<> +rcpt to: +data +Date: Fri, 17 Dec 2004 14:35:01 +0100 +Subject: message should be tmp-rejected due to timeout + +. +quit +**** +# +# +# +server DIR/eximdir/sophie_sock +*sleep 3 +**** +# +# +# +exim -odi -bs -DOPT=/tmo=2s/defer_ok +ehlo test.ex +mail from:<> +rcpt to: +data +Date: Fri, 17 Dec 2004 14:35:01 +0100 +Subject: message should be accepted destpite timeout + +. +quit +**** diff --git a/test/scripts/4000-scanning/4005 b/test/scripts/4000-scanning/4005 index 32a281b03..0095157d1 100644 --- a/test/scripts/4000-scanning/4005 +++ b/test/scripts/4000-scanning/4005 @@ -10,7 +10,7 @@ server DIR/eximdir/clam_sock # # # -exim -odi -bs +exim -odi -bs -DOPT= ehlo test.ex mail from:<> rcpt to: @@ -32,13 +32,13 @@ server DIR/eximdir/clam_sock # # # -exim -odi -bs +exim -odi -bs -DOPT= ehlo test.ex mail from:<> rcpt to: data Date: Fri, 17 Dec 2004 14:35:01 +0100 -Subject: defer this one +Subject: defer this one due to scanner error . quit @@ -54,7 +54,7 @@ server DIR/eximdir/clam_sock # # # -exim -odi -bs +exim -odi -bs -DOPT=/defer_ok ehlo test.ex mail from:<> rcpt to: @@ -65,3 +65,46 @@ Subject: message should be rejected . quit **** +# +# +# +server DIR/eximdir/clam_sock +*sleep 3 +**** +# +# +# +exim -odi -bs -DOPT="/ tmo=2s" +ehlo test.ex +mail from:<> +rcpt to: +data +Date: Fri, 17 Dec 2004 14:35:01 +0100 +Subject: defer this one due to timeout + +. +quit +**** +# +# +# +server DIR/eximdir/clam_sock +*sleep 3 +**** +# +# +# +exim -odi -bs -DOPT="/ tmo=2s / defer_ok" +ehlo test.ex +mail from:<> +rcpt to: +data +Date: Fri, 17 Dec 2004 14:35:01 +0100 +Subject: accept this one despite timeout + +. +quit +**** +# +# +# Need to additionally test the timeout / defer_ok case diff --git a/test/scripts/4000-scanning/4006 b/test/scripts/4000-scanning/4006 index af9af4964..1adf8b136 100644 --- a/test/scripts/4000-scanning/4006 +++ b/test/scripts/4000-scanning/4006 @@ -15,7 +15,7 @@ server DIR/eximdir/avast_sock # # # -exim -odi -bs -DOPTION="FLAGS -fullfiles" +exim -odi -bs -DOPTION="FLAGS -fullfiles" -DINSERT= ehlo test.ex mail from:<> rcpt to: @@ -41,13 +41,13 @@ server DIR/eximdir/avast_sock # # # -exim -odi -bs -DOPTION= +exim -odi -bs -DOPTION= -DINSERT= ehlo test.ex mail from:<> rcpt to: data Date: Fri, 17 Dec 2004 14:35:01 +0100 -Subject: defer this one +Subject: defer this one, the scanner had an error . quit @@ -67,7 +67,7 @@ server DIR/eximdir/avast_sock # # # -exim -odi -bs -DOPTION= +exim -odi -bs -DOPTION= -DINSERT="/defer_ok" ehlo test.ex mail from:<> rcpt to: @@ -78,3 +78,43 @@ Subject: message should be rejected . quit **** +# +# +# +server DIR/eximdir/avast_sock +*sleep 3 +**** +# +# +# +exim -odi -bs -DOPTION= -DINSERT="/tmo=2s" +ehlo test.ex +mail from:<> +rcpt to: +data +Date: Fri, 17 Dec 2004 14:35:01 +0100 +Subject: message should be tmp-rejected due to timeout + +. +quit +**** +# +# +# +server DIR/eximdir/avast_sock +*sleep 3 +**** +# +# +# +exim -odi -bs -DOPTION= -DINSERT="/tmo=2s/defer_ok" +ehlo test.ex +mail from:<> +rcpt to: +data +Date: Fri, 17 Dec 2004 14:35:01 +0100 +Subject: message should be accepted despite timeout + +. +quit +**** diff --git a/test/scripts/4000-scanning/4007 b/test/scripts/4000-scanning/4007 index 7bf6aca34..9684056a0 100644 --- a/test/scripts/4000-scanning/4007 +++ b/test/scripts/4000-scanning/4007 @@ -1,7 +1,7 @@ # content scan interface: cmdline # # -exim -DOPT=accept -odi -bs +exim -DOPT=accept -odi -bs -DINSERT= ehlo test.ex mail from:<> rcpt to: @@ -14,8 +14,9 @@ quit **** # # +# the defer_ok should not prevent rejection # -exim -DOPT=reject -odi -bs +exim -DOPT=reject -odi -bs -DINSERT="/defer_ok" ehlo test.ex mail from:<> rcpt to: @@ -26,3 +27,31 @@ Subject: message should be rejected . quit **** +# +# +# +exim -DOPT=pause3 -odi -bs -DINSERT="/tmo=2s" +ehlo test.ex +mail from:<> +rcpt to: +data +Date: Fri, 17 Dec 2004 14:35:01 +0100 +Subject: message should be tmp-rejected due to timeout + +. +quit +**** +# +# +# +exim -DOPT=pause3 -odi -bs -DINSERT="/tmo=2s/defer_ok" +ehlo test.ex +mail from:<> +rcpt to: +data +Date: Fri, 17 Dec 2004 14:35:01 +0100 +Subject: message should be accepted despite a timeout + +. +quit +**** diff --git a/test/stderr/4002 b/test/stderr/4002 index 0fd411cb6..8d6b6f1c4 100644 --- a/test/stderr/4002 +++ b/test/stderr/4002 @@ -1 +1,4 @@ 1999-03-02 09:44:33 10HmaX-0005vi-00 malware acl condition: aveserver: unable to scan file TESTSUITE/spool/scan/10HmaX-0005vi-00/10HmaX-0005vi-00.eml (Responded: 5xx defer). +1999-03-02 09:44:33 10HmaY-0005vi-00 malware acl condition: aveserver: unavailable (Responded: nothing). +1999-03-02 09:44:33 10HmaZ-0005vi-00 malware acl condition: aveserver: unavailable (Responded: nothing). +1999-03-02 09:44:33 10HmbA-0005vi-00 malware acl condition: aveserver: unable to scan file TESTSUITE/spool/scan/10HmbA-0005vi-00/10HmbA-0005vi-00.eml (Responded: 5xx defer). diff --git a/test/stderr/4003 b/test/stderr/4003 new file mode 100644 index 000000000..aff2aff3f --- /dev/null +++ b/test/stderr/4003 @@ -0,0 +1,2 @@ +1999-03-02 09:44:33 10HmaX-0005vi-00 malware acl condition: fsecure: unable to read answer 0 (Connection timed out) +1999-03-02 09:44:33 10HmaY-0005vi-00 malware acl condition: fsecure: unable to read answer 0 (Connection timed out) diff --git a/test/stderr/4004 b/test/stderr/4004 index 1b8d84454..3a5b4eceb 100644 --- a/test/stderr/4004 +++ b/test/stderr/4004 @@ -1 +1,3 @@ 1999-03-02 09:44:33 10HmaX-0005vi-00 malware acl condition: sophie: scanner reported error +1999-03-02 09:44:33 10HmaY-0005vi-00 malware acl condition: sophie: unable to read from UNIX socket (TESTSUITE/eximdir/sophie_sock) +1999-03-02 09:44:33 10HmaZ-0005vi-00 malware acl condition: sophie: unable to read from UNIX socket (TESTSUITE/eximdir/sophie_sock) diff --git a/test/stderr/4005 b/test/stderr/4005 index 12b19bc57..dbaf01e3a 100644 --- a/test/stderr/4005 +++ b/test/stderr/4005 @@ -1 +1,3 @@ 1999-03-02 09:44:33 10HmaX-0005vi-00 malware acl condition: clamd: ClamAV returned: scanned_file_name: 666 ERROR +1999-03-02 09:44:33 10HmaY-0005vi-00 malware acl condition: clamd: unable to read from socket (Connection timed out) +1999-03-02 09:44:33 10HmaZ-0005vi-00 malware acl condition: clamd: unable to read from socket (Connection timed out) diff --git a/test/stderr/4006 b/test/stderr/4006 index 24ab94808..19caa79fe 100644 --- a/test/stderr/4006 +++ b/test/stderr/4006 @@ -1,2 +1,3 @@ -1999-03-02 09:44:33 10HmaX-0005vi-00 malware acl condition: avast: invalid response from scanner: blah [E] - +1999-03-02 09:44:33 10HmaX-0005vi-00 malware acl condition: avast: invalid response from scanner: 'blah [E]' +1999-03-02 09:44:33 10HmaY-0005vi-00 malware acl condition: avast: timeout from scanner +1999-03-02 09:44:33 10HmaZ-0005vi-00 malware acl condition: avast: timeout from scanner diff --git a/test/stderr/4007 b/test/stderr/4007 new file mode 100644 index 000000000..156815be5 --- /dev/null +++ b/test/stderr/4007 @@ -0,0 +1,2 @@ +1999-03-02 09:44:33 10HmaX-0005vi-00 malware acl condition: cmdline: unable to read from scanner (TESTSUITE/aux-fixed/4007.script -o pause3 TESTSUITE/spool/scan/10HmaX-0005vi-00 2>&1): Connection timed out +1999-03-02 09:44:33 10HmaY-0005vi-00 malware acl condition: cmdline: unable to read from scanner (TESTSUITE/aux-fixed/4007.script -o pause3 TESTSUITE/spool/scan/10HmaY-0005vi-00 2>&1): Connection timed out diff --git a/test/stdout/4001 b/test/stdout/4001 index 2e84f9860..40cf7a9d5 100644 --- a/test/stdout/4001 +++ b/test/stdout/4001 @@ -7,7 +7,7 @@ 250 OK 250 Accepted 354 Enter message, ending with "." on a line by itself -250 OK id=10HmaY-0005vi-00 +250 OK id=10HmaZ-0005vi-00 221 myhost.test.ex closing connection 220 myhost.test.ex ESMTP Exim x.yz Tue, 2 Mar 1999 09:44:33 +0000 250-myhost.test.ex Hello CALLER at test.ex @@ -20,11 +20,33 @@ 354 Enter message, ending with "." on a line by itself 550 Administrative prohibition 221 myhost.test.ex closing connection +220 myhost.test.ex ESMTP Exim x.yz Tue, 2 Mar 1999 09:44:33 +0000 +250-myhost.test.ex Hello CALLER at test.ex +250-SIZE 52428800 +250-8BITMIME +250-PIPELINING +250 HELP +250 OK +250 Accepted +354 Enter message, ending with "." on a line by itself +451 Temporary local problem - please try later +221 myhost.test.ex closing connection +220 myhost.test.ex ESMTP Exim x.yz Tue, 2 Mar 1999 09:44:33 +0000 +250-myhost.test.ex Hello CALLER at test.ex +250-SIZE 52428800 +250-8BITMIME +250-PIPELINING +250 HELP +250 OK +250 Accepted +354 Enter message, ending with "." on a line by itself +250 OK id=10HmbA-0005vi-00 +221 myhost.test.ex closing connection ******** SERVER ******** Listening on port 1224 ... Connection request from [127.0.0.1] - >*eof @@ -41,3 +63,13 @@ Connection request from [127.0.0.1] > >*eof End of script +Listening on port 1224 ... +Connection request from [127.0.0.1] +200 ready -200 done 200 ready @@ -54,7 +98,34 @@ End of script Listening on TESTSUITE/eximdir/aveserver_sock ... Connection request >2xx ready -322 VNAME found +>2xx ready +2xx ready +Expected EOF read from client +End of script +Listening on TESTSUITE/eximdir/aveserver_sock ... +Connection request +*sleep 3 +End of script +Listening on TESTSUITE/eximdir/aveserver_sock ... +Connection request +*sleep 3 +End of script +Listening on TESTSUITE/eximdir/aveserver_sock ... +Connection request +>2xx ready +5xx defer +2xx ready +Expected EOF read from client +End of script +Listening on TESTSUITE/eximdir/aveserver_sock ... +Connection request +>2xx ready +322 VNAME found >2xx ready ignored_response ignored_response -LF>random ignored line >LF>random ignored line 2 >LF>OK Scan ok. @@ -48,7 +81,30 @@ Connection request >ignored_response ignored_response -LF>xxxINFECTED blah VNAME blah +>LF>OK Scan ok. +Expected EOF read from client +End of script +Listening on TESTSUITE/eximdir/fsec_sock ... +Connection request +*sleep 3 +End of script +Listening on TESTSUITE/eximdir/fsec_sock ... +Connection request +*sleep 3 +End of script +Listening on TESTSUITE/eximdir/fsec_sock ... +Connection request +ignored_response +ignored_response +ignored_response +ignored_response +LF>xxxINFECTED blah VNAME blah >LF>OK Scan ok. Expected EOF read from client diff --git a/test/stdout/4004 b/test/stdout/4004 index ceeb9e2a2..a23806e00 100644 --- a/test/stdout/4004 +++ b/test/stdout/4004 @@ -7,7 +7,7 @@ 250 OK 250 Accepted 354 Enter message, ending with "." on a line by itself -250 OK id=10HmaZ-0005vi-00 +250 OK id=10HmbB-0005vi-00 221 myhost.test.ex closing connection 220 myhost.test.ex ESMTP Exim x.yz Tue, 2 Mar 1999 09:44:33 +0000 250-myhost.test.ex Hello CALLER at test.ex @@ -31,11 +31,33 @@ 354 Enter message, ending with "." on a line by itself 550 Administrative prohibition 221 myhost.test.ex closing connection +220 myhost.test.ex ESMTP Exim x.yz Tue, 2 Mar 1999 09:44:33 +0000 +250-myhost.test.ex Hello CALLER at test.ex +250-SIZE 52428800 +250-8BITMIME +250-PIPELINING +250 HELP +250 OK +250 Accepted +354 Enter message, ending with "." on a line by itself +451 Temporary local problem - please try later +221 myhost.test.ex closing connection +220 myhost.test.ex ESMTP Exim x.yz Tue, 2 Mar 1999 09:44:33 +0000 +250-myhost.test.ex Hello CALLER at test.ex +250-SIZE 52428800 +250-8BITMIME +250-PIPELINING +250 HELP +250 OK +250 Accepted +354 Enter message, ending with "." on a line by itself +250 OK id=10HmaZ-0005vi-00 +221 myhost.test.ex closing connection ******** SERVER ******** Listening on TESTSUITE/eximdir/sophie_sock ... Connection request -0 no problems mate Expected EOF read from client End of script @@ -47,7 +69,15 @@ Expected EOF read from client End of script Listening on TESTSUITE/eximdir/sophie_sock ... Connection request -LF>1 VNAME Expected EOF read from client End of script +Listening on TESTSUITE/eximdir/sophie_sock ... +Connection request +*sleep 3 +End of script +Listening on TESTSUITE/eximdir/sophie_sock ... +Connection request +*sleep 3 +End of script diff --git a/test/stdout/4005 b/test/stdout/4005 index 7b7dbdcb6..0a8a5ea9a 100644 --- a/test/stdout/4005 +++ b/test/stdout/4005 @@ -7,7 +7,7 @@ 250 OK 250 Accepted 354 Enter message, ending with "." on a line by itself -250 OK id=10HmaZ-0005vi-00 +250 OK id=10HmbB-0005vi-00 221 myhost.test.ex closing connection 220 myhost.test.ex ESMTP Exim x.yz Tue, 2 Mar 1999 09:44:33 +0000 250-myhost.test.ex Hello CALLER at test.ex @@ -31,11 +31,33 @@ 354 Enter message, ending with "." on a line by itself 550 Administrative prohibition 221 myhost.test.ex closing connection +220 myhost.test.ex ESMTP Exim x.yz Tue, 2 Mar 1999 09:44:33 +0000 +250-myhost.test.ex Hello CALLER at test.ex +250-SIZE 52428800 +250-8BITMIME +250-PIPELINING +250 HELP +250 OK +250 Accepted +354 Enter message, ending with "." on a line by itself +451 Temporary local problem - please try later +221 myhost.test.ex closing connection +220 myhost.test.ex ESMTP Exim x.yz Tue, 2 Mar 1999 09:44:33 +0000 +250-myhost.test.ex Hello CALLER at test.ex +250-SIZE 52428800 +250-8BITMIME +250-PIPELINING +250 HELP +250 OK +250 Accepted +354 Enter message, ending with "." on a line by itself +250 OK id=10HmaZ-0005vi-00 +221 myhost.test.ex closing connection ******** SERVER ******** Listening on TESTSUITE/eximdir/clam_sock ... Connection request -LF>scanned_file_name: OK Unexpected EOF read from client End of script @@ -47,7 +69,15 @@ Unexpected EOF read from client End of script Listening on TESTSUITE/eximdir/clam_sock ... Connection request -LF>scanned_file_name: VNAME FOUND Unexpected EOF read from client End of script +Listening on TESTSUITE/eximdir/clam_sock ... +Connection request +*sleep 3 +End of script +Listening on TESTSUITE/eximdir/clam_sock ... +Connection request +*sleep 3 +End of script diff --git a/test/stdout/4006 b/test/stdout/4006 index 602afa64c..bd18c9562 100644 --- a/test/stdout/4006 +++ b/test/stdout/4006 @@ -7,7 +7,7 @@ 250 OK 250 Accepted 354 Enter message, ending with "." on a line by itself -250 OK id=10HmaZ-0005vi-00 +250 OK id=10HmbB-0005vi-00 221 myhost.test.ex closing connection 220 myhost.test.ex ESMTP Exim x.yz Tue, 2 Mar 1999 09:44:33 +0000 250-myhost.test.ex Hello CALLER at test.ex @@ -31,6 +31,28 @@ 354 Enter message, ending with "." on a line by itself 550 Administrative prohibition 221 myhost.test.ex closing connection +220 myhost.test.ex ESMTP Exim x.yz Tue, 2 Mar 1999 09:44:33 +0000 +250-myhost.test.ex Hello CALLER at test.ex +250-SIZE 52428800 +250-8BITMIME +250-PIPELINING +250 HELP +250 OK +250 Accepted +354 Enter message, ending with "." on a line by itself +451 Temporary local problem - please try later +221 myhost.test.ex closing connection +220 myhost.test.ex ESMTP Exim x.yz Tue, 2 Mar 1999 09:44:33 +0000 +250-myhost.test.ex Hello CALLER at test.ex +250-SIZE 52428800 +250-8BITMIME +250-PIPELINING +250 HELP +250 OK +250 Accepted +354 Enter message, ending with "." on a line by itself +250 OK id=10HmaZ-0005vi-00 +221 myhost.test.ex closing connection ******** SERVER ******** Listening on TESTSUITE/eximdir/avast_sock ... @@ -39,7 +61,7 @@ Connection request LF>210 FLAGS DATA >LF>200 FLAGS OK -LF>210 SCAN DATA >LF>blah [+] >LF>200 SCAN OK @@ -57,8 +79,16 @@ Unexpected EOF read from client Listening on TESTSUITE/eximdir/avast_sock ... Connection request >LF>220 ready -LF>210 SCAN DATA >LF>b\ l\ a\ h [L]9.9 9 VNAME >LF>200 SCAN OK Unexpected EOF read from client +Listening on TESTSUITE/eximdir/avast_sock ... +Connection request +*sleep 3 +End of script +Listening on TESTSUITE/eximdir/avast_sock ... +Connection request +*sleep 3 +End of script diff --git a/test/stdout/4007 b/test/stdout/4007 index 35527951b..c72e085ce 100644 --- a/test/stdout/4007 +++ b/test/stdout/4007 @@ -7,7 +7,7 @@ 250 OK 250 Accepted 354 Enter message, ending with "." on a line by itself -250 OK id=10HmaY-0005vi-00 +250 OK id=10HmbA-0005vi-00 221 myhost.test.ex closing connection 220 myhost.test.ex ESMTP Exim x.yz Tue, 2 Mar 1999 09:44:33 +0000 250-myhost.test.ex Hello CALLER at test.ex @@ -20,3 +20,25 @@ 354 Enter message, ending with "." on a line by itself 550 Administrative prohibition 221 myhost.test.ex closing connection +220 myhost.test.ex ESMTP Exim x.yz Tue, 2 Mar 1999 09:44:33 +0000 +250-myhost.test.ex Hello CALLER at test.ex +250-SIZE 52428800 +250-8BITMIME +250-PIPELINING +250 HELP +250 OK +250 Accepted +354 Enter message, ending with "." on a line by itself +451 Temporary local problem - please try later +221 myhost.test.ex closing connection +220 myhost.test.ex ESMTP Exim x.yz Tue, 2 Mar 1999 09:44:33 +0000 +250-myhost.test.ex Hello CALLER at test.ex +250-SIZE 52428800 +250-8BITMIME +250-PIPELINING +250 HELP +250 OK +250 Accepted +354 Enter message, ending with "." on a line by itself +250 OK id=10HmaY-0005vi-00 +221 myhost.test.ex closing connection