} clamd_address_container;
/* declaration of private routines */
-static int mksd_scan_packed(struct scan * scanent, int sock, uschar *scan_filename);
+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
return cre;
}
+/*
+ Simple though inefficient wrapper for reading a line. Drop CRs and the
+ 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
+*/
+static int
+recv_line(int fd, uschar * buffer, int bsize, int tmo)
+{
+uschar * p = buffer;
+ssize_t rcv;
+
+if (!fd_ready(fd, tmo-time(NULL)))
+ return -1;
+
+ /*XXX tmo handling assumes we always get a whole line */
+/* read until \n */
+while ((rcv = read(fd, p, 1)) > 0)
+ {
+ if (p-buffer > bsize-2) break;
+ if (*p == '\n') break;
+ if (*p != '\r') p++;
+ }
+*p = '\0';
+
+DEBUG(D_acl) debug_printf("Malware scan: read '%s'\n", buffer);
+return p - buffer;
+}
+
+/* return TRUE iff size as requested */
+static BOOL
+recv_len(int sock, void * buf, int size, int tmo)
+{
+return fd_ready(sock, tmo-time(NULL))
+ ? recv(sock, buf, size, 0) == size
+ : FALSE;
+}
+
+
/*************************************************
* Scan content for malware *
*************************************************/
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)))
/* 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)
}
DEBUG(D_lookup) debug_printf("Malware scan: %s\n", scanner_name);
- switch (scanent->scancode) {
- case M_FPROTD: /* "f-protd" scanner type -------------------------------- */
+ switch (scanent->scancode)
{
+ 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;
- DEBUG(D_acl) debug_printf("Malware scan: issuing %s GET\n", scanner_name);
scanrequest = string_sprintf("GET %s", eml_filename);
while ((fp_scan_option = string_nextinlist(&av_scanner_work, &sep,
- NULL, 0))) {
+ 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);
- /* We get a lot of empty lines, so we need this hack to check for any data at all */
- while( recv(sock, buf, 1, MSG_PEEK) > 0 ) {
- if ( recv_line(sock, buf, sizeof(buf)) > 0) {
- if ( Ustrstr(buf, US"<detected type=\"") != NULL )
+ while ((len = recv_line(sock, buf, sizeof(buf), tmo)) >= 0)
+ if (len > 0)
+ {
+ if (Ustrstr(buf, US"<detected type=\"") != NULL)
detected = 1;
- else if ( detected && (strhelper = Ustrstr(buf, US"<name>")) ) {
- if ((strhelper2 = Ustrstr(buf, US"</name>")) != NULL) {
+ else if (detected && (strhelper = Ustrstr(buf, US"<name>")))
+ {
+ if ((strhelper2 = Ustrstr(buf, US"</name>")) != NULL)
+ {
*strhelper2 = '\0';
malware_name_internal = string_copy(strhelper+6);
+ }
}
- } else if ( Ustrstr(buf, US"<summary code=\"") )
- malware_name = Ustrstr(buf, US"<summary code=\"11\">")
+ else if (Ustrstr(buf, US"<summary code=\""))
+ {
+ malware_name = Ustrstr(buf, US"<summary code=\"11\">")
? malware_name_internal : NULL;
- }
- }
+ break;
+ }
+ }
break;
- } /* f-protd */
+ } /* f-protd */
- case M_DRWEB: /* "drweb" scanner type ----------------------------------- */
+ 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;
- unsigned long bread;
const pcre *drweb_re;
/* prepare variables */
drweb_cmd = htonl(DRWEBD_SCAN_CMD);
drweb_flags = htonl(DRWEBD_RETURN_VIRUSES | DRWEBD_IS_MAIL);
- if (*scanner_options != '/') {
-
+ if (*scanner_options != '/')
+ {
/* calc file size */
if ((drweb_fd = open(CS eml_filename, O_RDONLY)) == -1)
return m_errlog_defer_3(scanent,
eml_filename, strerror(errno)),
sock);
- if ((fsize = lseek(drweb_fd, 0, SEEK_END)) == -1) {
+ 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) {
+ 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);
- }
+ }
drweb_slen = htonl(fsize);
lseek(drweb_fd, 0, SEEK_SET);
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)) {
+ (send(sock, &drweb_slen, sizeof(drweb_slen), 0) < 0))
+ {
(void)close(drweb_fd);
- return m_errlog_defer_3(scanent,
- string_sprintf("unable to send commands to socket (%s)", scanner_options),
+ return m_errlog_defer_3(scanent, string_sprintf(
+ "unable to send commands to socket (%s)", scanner_options),
sock);
- }
+ }
- if (!(drweb_fbuf = (uschar *) malloc (fsize_uint))) {
+ 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);
- }
+ }
- if ((result = read (drweb_fd, drweb_fbuf, fsize)) == -1) {
+ if ((result = read (drweb_fd, drweb_fbuf, fsize)) == -1)
+ {
int err = errno;
(void)close(drweb_fd);
free(drweb_fbuf);
string_sprintf("can't read spool file %s: %s",
eml_filename, strerror(err)),
sock);
- }
+ }
(void)close(drweb_fd);
/* send file body to socket */
- if (send(sock, drweb_fbuf, fsize, 0) < 0) {
+ 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(
+ "unable to send file body to socket (%s)", scanner_options),
+ sock);
+ }
(void)close(drweb_fd);
-
- } else {
-
+ }
+ else
+ {
drweb_slen = htonl(Ustrlen(eml_filename));
DEBUG(D_acl) debug_printf("Malware scan: issuing %s local scan [%s]\n",
(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),
+ return m_errlog_defer_3(scanent, string_sprintf(
+ "unable to send commands to socket (%s)", scanner_options),
sock);
- }
+ }
/* wait for result */
- if ((bread = recv(sock, &drweb_rc, sizeof(drweb_rc), 0) != sizeof(drweb_rc)))
+ 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 ((bread = recv(sock, &drweb_vnum, sizeof(drweb_vnum), 0) != sizeof(drweb_vnum)))
+ 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) {
+ if (drweb_vnum)
+ {
int i;
/* setup default virus name */
drweb_re = m_pcre_compile(US"infected\\swith\\s*(.+?)$", &errstr);
/* read and concatenate virus names into one string */
- for (i=0;i<drweb_vnum;i++)
- {
+ for (i = 0; i < drweb_vnum; i++)
+ {
int size = 0, off = 0, ovector[10*3];
/* read the size of report */
- if ((bread = recv(sock, &drweb_slen, sizeof(drweb_slen), 0) != sizeof(drweb_slen)))
+ 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 ((bread = recv(sock, tmpbuf, drweb_slen, 0)) != drweb_slen)
+ 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) {
+ if (result >= 2)
+ {
const char * pre_malware_nb;
pcre_get_substring(CS tmpbuf, ovector, result, 1, &pre_malware_nb);
2, "/", pre_malware_nb);
pcre_free_substring(pre_malware_nb);
+ }
}
}
- }
- else {
+ else
+ {
const char *drweb_s = NULL;
if (drweb_rc & DERR_READ_ERR) drweb_s = "read error";
/* no virus found */
malware_name = NULL;
- }
+ }
break;
- } /* drweb */
+ } /* drweb */
case M_AVES: /* "aveserver" scanner type -------------------------------- */
{
int result;
/* read aveserver's greeting and see if it is ready (2xx greeting) */
- recv_line(sock, buf, sizeof(buf));
+ recv_line(sock, buf, sizeof(buf), tmo);
if (buf[0] != '2') /* aveserver is having problems */
return m_errlog_defer_3(scanent,
malware_name = NULL;
result = 0;
/* read response lines, find malware name and final response */
- while (recv_line(sock, buf, sizeof(buf)) > 0) {
+ 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 */
+ 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;
- } else if (Ustrncmp(buf,"322",3) == 0) {
- uschar *p = Ustrchr(&buf[4],' ');
+ }
+ if (Ustrncmp(buf,"322",3) == 0)
+ {
+ uschar *p = Ustrchr(&buf[4], ' ');
*p = '\0';
malware_name = string_copy(&buf[4]);
+ }
}
- }
/* and send it */
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) */
- recv_line(sock, buf, sizeof(buf));
+ recv_line(sock, buf, sizeof(buf), tmo);
if (buf[0] != '2') /* aveserver is having problems */
return m_errlog_defer_3(scanent,
((buf[0] != 0) ? buf : (uschar *)"nothing") ),
sock);
- if (result == DEFER) {
+ if (result == DEFER)
+ {
(void)close(sock);
return DEFER;
- }
+ }
break;
} /* aveserver */
US"CONFIGURE\tTIMEOUT\t0\n",
US"CONFIGURE\tMAXARCH\t5\n",
US"CONFIGURE\tMIME\t1\n" };
- time_t tmo;
malware_name = NULL;
DEBUG(D_acl) debug_printf("Malware scan: issuing %s scan [%s]\n",
scanner_name, scanner_options);
- tmo = time(NULL) + MALWARE_TIMEOUT;
/* pass options */
memset(av_buffer, 0, sizeof(av_buffer));
- for (i=0; i != nelements(cmdopt); i++) {
+ 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);
- bread = ip_recv(sock, av_buffer, sizeof(av_buffer), MALWARE_TIMEOUT);
- if (bread >0) av_buffer[bread]='\0';
+ 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'))
+ 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);
for (;;)
{
- int t = tmo - time(NULL);
-
errno = ETIME;
i = av_buffer+sizeof(av_buffer)-p;
- if ( t <= 0
- || (bread= ip_recv(sock, p, i-1, t)) < 0
- )
+ 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);
return m_errlog_defer(scanent, errstr);
/* wait for result */
- if ((bread = recv(sock, tmpbuf, 2, 0) != 2))
+ if (!recv_len(sock, tmpbuf, 2, tmo))
return m_errlog_defer_3(scanent,
US"unable to read 2 bytes from socket.", sock);
bounces where part of a file has been cut off */
/* "virus found" return codes (2-4) */
- if ((kav_rc > 1) && (kav_rc < 5)) {
+ if (kav_rc > 1 && kav_rc < 5)
+ {
int report_flag = 0;
/* setup default virus name */
report_flag = tmpbuf[ test_byte_order() == LITTLE_MY_ENDIAN ? 1 : 0 ];
/* read the report, if available */
- if( report_flag == 1 ) {
+ if (report_flag == 1)
+ {
/* read report size */
- if ((bread = recv(sock, &kav_reportlen, 4, 0)) != 4)
+ 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) {
+ if (kav_reportlen > 0)
+ {
/* set up match regex, depends on retcode */
kav_re = m_pcre_compile( kav_rc == 3
? US"suspicion:\\s*(.+?)\\s*$"
&errstr );
/* read report, linewise */
- while (kav_reportlen > 0) {
- bread = 0;
- while ( recv(sock, &tmpbuf[bread], 1, 0) == 1 ) {
- kav_reportlen--;
- if ( (tmpbuf[bread] == '\n') || (bread > 1021) ) break;
- bread++;
- }
- bread++;
- tmpbuf[bread] = '\0';
+ 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;
+ }
}
}
}
- }
else /* no virus found */
malware_name = NULL;
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;
/* 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);
- if (!(scanner_out = popen(CS commandline,"r"))) {
+ 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);
- scanner_record = modefopen(file_name, "wb", SPOOL_MODE);
- if (scanner_record == NULL) {
+ 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.",
+ 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(fgets(CS linebuffer, sizeof(linebuffer), scanner_out)) {
- if ( Ustrlen(linebuffer) > fwrite(linebuffer, 1, Ustrlen(linebuffer), scanner_record) ) {
+ while ((rcnt = recv_line(scanner_fd, linebuffer,
+ sizeof(linebuffer), tmo)))
+ {
+ if (rcnt < 0)
+ {
+ (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));
+ }
+
+ 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));
- }
+ 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))
+ if ( !trigger
+ && regex_match_and_setup(cmdline_trigger_re, linebuffer, 0, -1)
+ )
trigger = 1;
- }
+ }
(void)fclose(scanner_record);
sep = pclose(scanner_out);
? string_sprintf("running scanner failed: %s", strerror(sep))
: string_sprintf("scanner returned error code: %d", sep));
- if (trigger) {
+ 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)) {
+ 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);
- }
+ }
else /* no virus found */
malware_name = NULL;
break;
/* wait for result */
memset(av_buffer, 0, sizeof(av_buffer));
- if ((!(bread = ip_recv(sock, av_buffer, sizeof(av_buffer), MALWARE_TIMEOUT)) > 0))
+ 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);
/* 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 {
+ else
+ {
const uschar *address = scanner_options;
uschar address_buffer[MAX_CLAMD_ADDRESS_LENGTH + 20];
* 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 {
+ do
+ {
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) {
+ if (strcmpic(address,US"local") == 0)
+ {
use_scan_command = TRUE;
continue;
- }
+ }
/* XXX: If unsuccessful we should free this memory */
this_clamd =
/* 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 ) {
+ 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) {
+ 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,
+ }
+ } while ((address = string_nextinlist(&av_scanner_work, &sep,
address_buffer,
sizeof(address_buffer))) != NULL);
if (!num_servers)
return m_errlog_defer(scanent,
US"no useable server addresses in malware configuration option.");
- }
+ }
- /* See the discussion of response formats below to see why we really don't
- like colons in filenames when passing filenames to ClamAV. */
+ /* 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));
/* We have some network servers specified */
- if (num_servers) {
-
+ 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. */
- while ( num_servers > 0 ) {
+ while (num_servers > 0)
+ {
+ int i;
/* Randomly pick a server to start with */
current_server = random_number( num_servers );
sock= m_tcpsocket(clamd_address_vector[current_server]->tcp_addr,
clamd_address_vector[current_server]->tcp_port,
&connhost, &errstr);
- if (sock >= 0) {
+ if (sock >= 0)
+ {
/* Connection successfully established with a server */
hostname = clamd_address_vector[current_server]->tcp_addr;
break;
- }
+ }
(void) m_errlog_defer(scanent, errstr);
/* Remove the server from the list. XXX We should free the memory */
num_servers--;
- int i;
- for( i = current_server; i < num_servers; i++ )
+ for (i = current_server; i < num_servers; i++)
clamd_address_vector[i] = clamd_address_vector[i+1];
- }
+ }
- if ( num_servers == 0 )
+ if (num_servers == 0)
return m_errlog_defer(scanent, US"all servers failed");
-
- } else {
+ }
+ else
+ {
if ((sock = m_unixsocket(scanner_options, &errstr)) < 0)
return m_errlog_defer(scanent, errstr);
- }
+ }
/* 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
+ if (!use_scan_command)
+ {
+#ifdef WITH_OLD_CLAMAV_STREAM
/* "STREAM\n" command, get back a "PORT <N>\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. */
return m_errlog_defer(scanent, errstr);
memset(av_buffer2, 0, sizeof(av_buffer2));
- bread = ip_recv(sock, av_buffer2, sizeof(av_buffer2), MALWARE_TIMEOUT);
+ bread = ip_recv(sock, av_buffer2, sizeof(av_buffer2), tmo-time(NULL));
if (bread < 0)
return m_errlog_defer_3(scanent,
if (sockData < 0)
return m_errlog_defer_3(scanent, errstr, sock);
- #define CLOSE_SOCKDATA (void)close(sockData)
- #else /* WITH_OLD_CLAMAV_STREAM not defined */
+# define CLOSE_SOCKDATA (void)close(sockData)
+#else /* WITH_OLD_CLAMAV_STREAM not defined */
/* New protocol: "zINSTREAM\n" followed by a sequence of <length><data>
chunks, <n> a 4-byte number (network order), terminated by a zero-length
chunk. */
strerror(errno)),
sock);
- #define CLOSE_SOCKDATA /**/
- #endif
+# define CLOSE_SOCKDATA /**/
+#endif
/* calc file size */
- if ((clam_fd = open(CS eml_filename, O_RDONLY)) < 0) {
+ 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) {
+ }
+ 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) {
+ 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))) {
+ 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) {
+ 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 */
- #ifdef WITH_OLD_CLAMAV_STREAM
- if (send(sockData, clamav_fbuf, fsize_uint, 0) < 0) {
+#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);
- }
- #else
+ }
+#else
send_size = htonl(fsize_uint);
send_final_zeroblock = 0;
if ((send(sock, &send_size, sizeof(send_size), 0) < 0) ||
string_sprintf("unable to send file body to socket (%s)", hostname),
sock);
}
- #endif
+#endif
free(clamav_fbuf);
CLOSE_SOCKDATA;
- #undef CLOSE_SOCKDATA
-
- } else { /* use scan command */
+#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 */
/* 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), MALWARE_TIMEOUT);
+ bread = ip_recv(sock, av_buffer, sizeof(av_buffer), tmo-time(NULL));
(void)close(sock);
sock = -1;
- if (!(bread > 0))
+ if (bread <= 0)
return m_errlog_defer(scanent,
string_sprintf("unable to read from socket (%s)", strerror(errno)));
/* colon in returned output? */
if((p = Ustrchr(av_buffer,':')) == NULL)
- return m_errlog_defer(scanent,
- string_sprintf("ClamAV returned malformed result (missing colon): %s",
+ return m_errlog_defer(scanent, string_sprintf(
+ "ClamAV returned malformed result (missing colon): %s",
av_buffer));
/* strip filename */
p = Ustrrchr(vname, ' ');
result_tag = p ? p+1 : vname;
- if (Ustrcmp(result_tag, "FOUND") == 0) {
+ 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 == ')') {
+ 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);
- } else if (Ustrcmp(result_tag, "ERROR") == 0)
+ }
+ else if (Ustrcmp(result_tag, "ERROR") == 0)
return m_errlog_defer(scanent,
string_sprintf("ClamAV returned: %s", av_buffer));
- else if (Ustrcmp(result_tag, "OK") == 0) {
+ else if (Ustrcmp(result_tag, "OK") == 0)
+ {
/* Everything should be OK */
malware_name = NULL;
DEBUG(D_acl) debug_printf("Malware not found\n");
- } else
+ }
+ else
return m_errlog_defer(scanent,
string_sprintf("unparseable response from ClamAV: {%s}", av_buffer));
return m_errlog_defer(scanent, errstr);
/* Read the result */
- memset(av_buffer, 0, sizeof(av_buffer));
- bread = read(sock, av_buffer, sizeof(av_buffer));
+ bread = ip_recv(sock, av_buffer, sizeof(av_buffer), tmo-time(NULL));
- if (!(bread > 0))
+ 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);
/* try trigger match */
{
char *mksd_options_end;
int mksd_maxproc = 1; /* default, if no option supplied */
- int sock;
int retval;
- if (scanner_options) {
+ if (scanner_options)
+ {
mksd_maxproc = (int)strtol(CS scanner_options, &mksd_options_end, 10);
if ( *scanner_options == '\0'
|| *mksd_options_end != '\0'
)
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);
DEBUG(D_acl) debug_printf("Malware scan: issuing %s scan\n", scanner_name);
- if ((retval = mksd_scan_packed(scanent, sock, eml_filename)) != OK) {
+ if ((retval = mksd_scan_packed(scanent, sock, eml_filename, tmo)) != OK)
+ {
close (sock);
return retval;
- }
+ }
break;
}
case M_AVAST: /* "avast" scanner type ----------------------------------- */
return malware_errlog_defer(errstr);
/* wait for result */
- for (avast_stage = AVA_HELO; recv_line(sock, buf, sizeof(buf)) > 0; )
+ for (avast_stage = AVA_HELO; recv_line(sock, buf, sizeof(buf), tmo) > 0; )
{
int slen = Ustrlen(buf);
if (slen >= 1)
}
-/* simple wrapper for reading lines from sockets */
-int
-recv_line(int sock, uschar *buffer, int size)
-{
- uschar *p = buffer;
-
- memset(buffer,0,size);
- /* read until \n */
- while(recv(sock,p,1,0) > -1) {
- if ((p-buffer) > (size-2)) break;
- if (*p == '\n') break;
- if (*p != '\r') p++;
- }
- *p = '\0';
-
- return (p-buffer);
-}
-
-
/* ============= private routines for the "mksd" scanner type ============== */
#include <sys/uio.h>
}
static inline int
-mksd_read_lines (int sock, uschar *av_buffer, int av_buffer_size)
+mksd_read_lines (int sock, uschar *av_buffer, int av_buffer_size, int tmo)
{
int offset = 0;
int i;
- do {
- if ((i = recv (sock, av_buffer+offset, av_buffer_size-offset, 0)) <= 0) {
+ 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) {
+ if (offset == av_buffer_size)
+ {
(void) malware_errlog_defer(US"malformed reply received from mksd");
return -1;
- }
- } while (av_buffer[offset-1] != '\n');
+ }
+ } while (av_buffer[offset-1] != '\n');
av_buffer[offset] = '\0';
return offset;
}
static int
-mksd_scan_packed(struct scan * scanent, int sock, uschar *scan_filename)
+mksd_scan_packed(struct scan * scanent, int sock, uschar *scan_filename, int tmo)
{
struct iovec iov[3];
const char *cmd = "MSQ\n";
if (mksd_writev (sock, iov, 3) < 0)
return DEFER;
- if (mksd_read_lines (sock, av_buffer, sizeof (av_buffer)) < 0)
+ if (mksd_read_lines (sock, av_buffer, sizeof (av_buffer), tmo) < 0)
return DEFER;
return mksd_parse_line (scanent, CS av_buffer);