Log/return-defer: de-repeat strings
[users/jgh/exim.git] / src / src / malware.c
1 /*************************************************
2 *     Exim - an Internet mail transport agent    *
3 *************************************************/
4
5 /* Copyright (c) Tom Kistner <tom@duncanthrax.net> 2003-2013 */
6 /* License: GPL */
7
8 /* Code for calling virus (malware) scanners. Called from acl.c. */
9
10 #include "exim.h"
11 #ifdef WITH_CONTENT_SCAN
12
13 /* The maximum number of clamd servers that are supported in the configuration */
14 #define MAX_CLAMD_SERVERS 32
15 #define MAX_CLAMD_SERVERS_S "32"
16 /* Maximum length of the hostname that can be specified in the clamd address list */
17 #define MAX_CLAMD_ADDRESS_LENGTH 64
18 #define MAX_CLAMD_ADDRESS_LENGTH_S "64"
19
20 typedef struct clamd_address_container {
21   uschar tcp_addr[MAX_CLAMD_ADDRESS_LENGTH];
22   unsigned int tcp_port;
23 } clamd_address_container;
24
25 /* declaration of private routines */
26 static int mksd_scan_packed(int sock, uschar *scan_filename);
27 static int malware_internal(uschar **listptr, uschar *eml_filename, BOOL faking);
28
29 /* SHUT_WR seems to be undefined on Unixware? */
30 #ifndef SHUT_WR
31 #define SHUT_WR 1
32 #endif
33
34
35 #define        MALWARE_TIMEOUT             120
36
37
38 #define DRWEBD_SCAN_CMD             (1)     /* scan file, buffer or diskfile */
39 #define DRWEBD_RETURN_VIRUSES       (1<<0)   /* ask daemon return to us viruses names from report */
40 #define DRWEBD_IS_MAIL              (1<<19)  /* say to daemon that format is "archive MAIL" */
41
42 #define DERR_READ_ERR               (1<<0)   /* read error */
43 #define DERR_NOMEMORY               (1<<2)   /* no memory */
44 #define DERR_TIMEOUT                (1<<9)   /* scan timeout has run out */
45 #define DERR_BAD_CALL               (1<<15)  /* wrong command */
46
47 /* Routine to check whether a system is big- or litte-endian.
48    Ripped from http://www.faqs.org/faqs/graphics/fileformats-faq/part4/section-7.html
49    Needed for proper kavdaemon implementation. Sigh. */
50 #define BIG_MY_ENDIAN      0
51 #define LITTLE_MY_ENDIAN   1
52 int test_byte_order(void);
53 int
54 test_byte_order()
55 {
56   short int word = 0x0001;
57   char *byte = (char *) &word;
58   return(byte[0] ? LITTLE_MY_ENDIAN : BIG_MY_ENDIAN);
59 }
60
61 static uschar * malware_name_internal = NULL;
62 int malware_ok = 0;
63
64 /* Gross hacks for the -bmalware option; perhaps we should just create
65 the scan directory normally for that case, but look into rigging up the
66 needed header variables if not already set on the command-line? */
67 extern int spool_mbox_ok;
68 extern uschar spooled_message_id[17];
69
70 /*************************************************
71 *          Scan an email for malware             *
72 *************************************************/
73
74 /* This is the normal interface for scanning an email, which doesn't need a
75 filename; it's a wrapper around the malware_file function.
76
77 Arguments:
78   listptr     the list of options to the "malware = ..." ACL condition
79
80 Returns:      Exim message processing code (OK, FAIL, DEFER, ...)
81               where true means malware was found (condition applies)
82 */
83 int
84 malware(uschar **listptr)
85 {
86   uschar scan_filename[1024];
87   BOOL fits;
88   int ret;
89
90   fits = string_format(scan_filename, sizeof(scan_filename),
91       CS"%s/scan/%s/%s.eml", spool_directory, message_id, message_id);
92   if (!fits)
93     {
94     av_failed = TRUE;
95     log_write(0, LOG_MAIN|LOG_PANIC,
96         "malware filename does not fit in buffer [malware()]");
97     return DEFER;
98   }
99
100   ret = malware_internal(listptr, scan_filename, FALSE);
101   if (ret == DEFER) av_failed = TRUE;
102
103   return ret;
104 }
105
106
107 /*************************************************
108 *          Scan a file for malware               *
109 *************************************************/
110
111 /* This is a test wrapper for scanning an email, which is not used in
112 normal processing.  Scan any file, using the Exim scanning interface.
113 This function tampers with various global variables so is unsafe to use
114 in any other context.
115
116 Arguments:
117   eml_filename  a file holding the message to be scanned
118
119 Returns:        Exim message processing code (OK, FAIL, DEFER, ...)
120                 where true means malware was found (condition applies)
121 */
122 int
123 malware_in_file(uschar *eml_filename)
124 {
125   uschar *scan_options[2];
126   uschar message_id_buf[64];
127   int ret;
128
129   scan_options[0] = US"*";
130   scan_options[1] = NULL;
131
132   /* spool_mbox() assumes various parameters exist, when creating
133   the relevant directory and the email within */
134   (void) string_format(message_id_buf, sizeof(message_id_buf),
135       "dummy-%d", vaguely_random_number(INT_MAX));
136   message_id = message_id_buf;
137   sender_address = US"malware-sender@example.net";
138   return_path = US"";
139   recipients_list = NULL;
140   receive_add_recipient(US"malware-victim@example.net", -1);
141   enable_dollar_recipients = TRUE;
142
143   ret = malware_internal(scan_options, eml_filename, TRUE);
144
145   Ustrncpy(spooled_message_id, message_id, sizeof(spooled_message_id));
146   spool_mbox_ok = 1;
147   /* don't set no_mbox_unspool; at present, there's no way for it to become
148   set, but if that changes, then it should apply to these tests too */
149   unspool_mbox();
150
151   /* silence static analysis tools */
152   message_id = NULL;
153
154   return ret;
155 }
156
157
158 static void
159 malware_errlog(const uschar * str)
160 {
161   log_write(0, LOG_MAIN|LOG_PANIC, "malware acl condition: %s", str);
162 }
163 static int
164 malware_errlog_defer(const uschar * str)
165 {
166   malware_errlog(str);
167   return DEFER;
168 }
169
170 static int
171 m_scanner_errlog_defer(const uschar * scanner, const uschar * str)
172 {
173   return malware_errlog_defer(string_sprintf("%s: %s", scanner, str));
174 }
175
176 static int
177 fprotd_errlog_defer(const uschar * str)
178 {
179   return m_scanner_errlog_defer("f-protd", str);
180 }
181 static int
182 drweb_errlog_defer(const uschar * str)
183 {
184   return m_scanner_errlog_defer("drweb", str);
185 }
186 static int
187 aves_errlog_defer(const uschar * str)
188 {
189   return m_scanner_errlog_defer("aveserver", str);
190 }
191 static int
192 fsec_errlog_defer(const uschar * str)
193 {
194   return m_scanner_errlog_defer("fsecure", str);
195 }
196 static int
197 kavd_errlog_defer(const uschar * str)
198 {
199   return m_scanner_errlog_defer("kavdaemon", str);
200 }
201 static int
202 cmdl_errlog_defer(const uschar * str)
203 {
204   return m_scanner_errlog_defer("commandline", str);
205 }
206 static int
207 soph_errlog_defer(const uschar * str)
208 {
209   return m_scanner_errlog_defer("sophie", str);
210 }
211 static int
212 clmd_errlog_defer(const uschar * str)
213 {
214   return m_scanner_errlog_defer("clamd", str);
215 }
216 static int
217 mksd_errlog_defer(const uschar * str)
218 {
219   return m_scanner_errlog_defer("mksd", str);
220 }
221
222 static void
223 clmd_errlog(const uschar * str)
224 {
225   log_write(0, LOG_MAIN|LOG_PANIC, "malware acl condition: clamd: %s", str);
226 }
227
228 /*************************************************
229 *          Scan content for malware              *
230 *************************************************/
231
232 /* This is an internal interface for scanning an email; the normal interface
233 is via malware(), or there's malware_in_file() used for testing/debugging.
234
235 Arguments:
236   listptr       the list of options to the "malware = ..." ACL condition
237   eml_filename  the file holding the email to be scanned
238   faking        whether or not we're faking this up for the -bmalware test
239
240 Returns:        Exim message processing code (OK, FAIL, DEFER, ...)
241                 where true means malware was found (condition applies)
242 */
243 static int
244 malware_internal(uschar **listptr, uschar *eml_filename, BOOL faking)
245 {
246   int sep = 0;
247   uschar *list = *listptr;
248   uschar *av_scanner_work = av_scanner;
249   uschar *scanner_name;
250   uschar scanner_name_buffer[16];
251   uschar *malware_regex;
252   uschar malware_regex_buffer[64];
253   uschar malware_regex_default[] = ".+";
254   unsigned long mbox_size;
255   FILE *mbox_file;
256   int roffset;
257   const pcre *re;
258   const uschar *rerror;
259
260   /* make sure the eml mbox file is spooled up */
261   mbox_file = spool_mbox(&mbox_size, faking ? eml_filename : NULL);
262   if (mbox_file == NULL)  /* error while spooling */
263     return malware_errlog_defer("error while creating mbox spool file");
264
265   /* none of our current scanners need the mbox
266      file as a stream, so we can close it right away */
267   (void)fclose(mbox_file);
268
269   /* extract the malware regex to match against from the option list */
270   if ((malware_regex = string_nextinlist(&list, &sep,
271                                          malware_regex_buffer,
272                                          sizeof(malware_regex_buffer))) != NULL) {
273
274     /* parse 1st option */
275     if ( (strcmpic(malware_regex,US"false") == 0) ||
276          (Ustrcmp(malware_regex,"0") == 0) ) {
277       /* explicitly no matching */
278       return FAIL;
279     };
280
281     /* special cases (match anything except empty) */
282     if ( (strcmpic(malware_regex,US"true") == 0) ||
283          (Ustrcmp(malware_regex,"*") == 0) ||
284          (Ustrcmp(malware_regex,"1") == 0) ) {
285       malware_regex = malware_regex_default;
286     };
287   }
288   else {
289     /* empty means "don't match anything" */
290     return FAIL;
291   };
292
293   /* Reset sep that is set by previous string_nextinlist() call */
294   sep = 0;
295
296   /* compile the regex, see if it works */
297   re = pcre_compile(CS malware_regex, PCRE_COPT, (const char **)&rerror, &roffset, NULL);
298   if (!re)
299     return malware_errlog_defer(
300          string_sprintf("regular expression error in '%s': %s at offset %d",
301              malware_regex, rerror, roffset));
302
303   /* if av_scanner starts with a dollar, expand it first */
304   if (*av_scanner == '$') {
305     av_scanner_work = expand_string(av_scanner);
306     if (!av_scanner_work)
307       return malware_errlog_defer(
308            string_sprintf("av_scanner starts with $, but expansion failed: %s",
309            expand_string_message));
310     else {
311       debug_printf("Expanded av_scanner global: %s\n", av_scanner_work);
312       /* disable result caching in this case */
313       malware_name = NULL;
314       malware_ok = 0;
315     };
316   }
317
318   /* Do not scan twice. */
319   if (malware_ok == 0) {
320
321     /* find the scanner type from the av_scanner option */
322     if ((scanner_name = string_nextinlist(&av_scanner_work, &sep,
323                                       scanner_name_buffer,
324                                       sizeof(scanner_name_buffer))) == NULL) {
325       /* no scanner given */
326       return malware_errlog_defer("av_scanner configuration variable is empty");
327     }
328
329   /* "f-protd" scanner type ----------------------------------------------- */
330   if (strcmpic(scanner_name, US"f-protd") == 0) {
331     uschar *fp_options, *fp_scan_option;
332     uschar fp_scan_option_buffer[1024];
333     uschar fp_options_buffer[1024];
334     uschar fp_options_default[] = "localhost 10200-10204";
335     uschar hostname[256];
336     unsigned int port, portlow, porthigh, connect_ok=0, detected=0, par_count = 0;
337     struct hostent *he;
338     struct in_addr in;
339     int sock;
340     uschar scanrequest[2048], buf[32768], *strhelper, *strhelper2;
341
342     if ((fp_options = string_nextinlist(&av_scanner_work, &sep,
343       fp_options_buffer, sizeof(fp_options_buffer))) == NULL) {
344       /* no options supplied, use default options */
345       fp_options = fp_options_default;
346     };
347
348     /* extract host and port part */
349     if ( sscanf(CS fp_options, "%s %u-%u", hostname, &portlow, &porthigh) != 3 ) {
350       if ( sscanf(CS fp_options, "%s %u", hostname, &portlow) != 2 )
351         return fprotd_errlog_defer(
352           string_sprintf("invalid socket '%s'", fp_options));
353       porthigh = portlow;
354     }
355
356     /* Lookup the host */
357     if((he = gethostbyname(CS hostname)) == 0)
358       return fprotd_errlog_defer(
359         string_sprintf("failed to lookup host '%s'", hostname));
360
361     in = *(struct in_addr *) he->h_addr_list[0];
362     port = portlow;
363
364
365     /* Open the f-protd TCP socket */
366     if ( (sock = ip_socket(SOCK_STREAM, AF_INET)) < 0)
367       return fprotd_errlog_defer(
368         string_sprintf("unable to acquire socket (%s)", strerror(errno)));
369
370     /* Try to connect to all portslow-high until connection is established */
371     for (port = portlow; !connect_ok && port < porthigh; port++)
372       if (ip_connect(sock, AF_INET, (uschar*)inet_ntoa(in), port, 5) >= 0)
373         connect_ok = 1;
374
375     if ( !connect_ok ) {
376       int err = errno;
377       (void)close(sock);
378       return fprotd_errlog_defer(
379         string_sprintf("connection to %s, port %u-%u failed (%s)",
380         inet_ntoa(in), portlow, porthigh, strerror(err)));
381     }
382
383     DEBUG(D_acl) debug_printf("Malware scan: issuing %s GET\n", scanner_name);
384     (void)string_format(scanrequest, 1024, CS"GET %s", eml_filename);
385
386     while ((fp_scan_option = string_nextinlist(&av_scanner_work, &sep,
387       fp_scan_option_buffer, sizeof(fp_scan_option_buffer))) != NULL) {
388       if ( par_count ) {
389         Ustrcat(scanrequest, "%20");
390       } else {
391         Ustrcat(scanrequest, "?");
392       }
393       Ustrcat(scanrequest, fp_scan_option);
394       par_count++;
395     }
396     Ustrcat(scanrequest, " HTTP/1.0\r\n\r\n");
397
398     /* send scan request */
399     if (send(sock, &scanrequest, Ustrlen(scanrequest)+1, 0) < 0)
400       (void)close(sock);
401       return fprotd_errlog_defer(
402         string_sprintf("unable to send command to socket (%s)", scanrequest));
403
404     /* We get a lot of empty lines, so we need this hack to check for any data at all */
405     while( recv(sock, buf, 1, MSG_PEEK) > 0 ) {
406       if ( recv_line(sock, buf, 32768) > 0) {
407         if ( Ustrstr(buf, US"<detected type=\"") != NULL )
408           detected = 1;
409         else if ( detected && (strhelper = Ustrstr(buf, US"<name>")) ) {
410           if ((strhelper2 = Ustrstr(buf, US"</name>")) != NULL) {
411             *strhelper2 = '\0';
412             malware_name_internal = string_copy(strhelper+6);
413           }
414         } else if ( Ustrstr(buf, US"<summary code=\"") )
415           malware_name = Ustrstr(buf, US"<summary code=\"11\">")
416             ? malware_name_internal : NULL;
417       }
418     }
419     (void)close(sock);
420   }
421   /* "drweb" scanner type ----------------------------------------------- */
422   /* v0.1 - added support for tcp sockets          */
423   /* v0.0 - initial release -- support for unix sockets      */
424   else if (strcmpic(scanner_name,US"drweb") == 0) {
425     uschar *drweb_options;
426     uschar drweb_options_buffer[1024];
427     uschar drweb_options_default[] = "/usr/local/drweb/run/drwebd.sock";
428     struct sockaddr_un server;
429     int sock, result, ovector[30];
430     unsigned int port, fsize;
431     uschar tmpbuf[1024], *drweb_fbuf;
432     int drweb_rc, drweb_cmd, drweb_flags = 0x0000, drweb_fd,
433         drweb_vnum, drweb_slen, drweb_fin = 0x0000;
434     unsigned long bread;
435     uschar hostname[256];
436     struct hostent *he;
437     struct in_addr in;
438     pcre *drweb_re;
439
440     if ((drweb_options = string_nextinlist(&av_scanner_work, &sep,
441       drweb_options_buffer, sizeof(drweb_options_buffer))) == NULL) {
442       /* no options supplied, use default options */
443       drweb_options = drweb_options_default;
444     };
445
446     if (*drweb_options != '/') {
447
448       /* extract host and port part */
449       if( sscanf(CS drweb_options, "%s %u", hostname, &port) != 2 )
450         return drweb_errlog_defer(
451           string_sprintf("invalid socket '%s'", drweb_options));
452
453       /* Lookup the host */
454       if((he = gethostbyname(CS hostname)) == 0)
455         return drweb_errlog_defer(
456           string_sprintf("failed to lookup host '%s'", hostname));
457
458       in = *(struct in_addr *) he->h_addr_list[0];
459
460       /* Open the drwebd TCP socket */
461       if ( (sock = ip_socket(SOCK_STREAM, AF_INET)) < 0)
462         return drweb_errlog_defer(
463           string_sprintf("unable to acquire socket (%s)", strerror(errno)));
464
465       if (ip_connect(sock, AF_INET, (uschar*)inet_ntoa(in), port, 5) < 0) {
466         int err = errno;
467         (void)close(sock);
468         return drweb_errlog_defer(
469           string_sprintf("connection to %s, port %u failed (%s)",
470             inet_ntoa(in), port, strerror(err)));
471       }
472
473       /* prepare variables */
474       drweb_cmd = htonl(DRWEBD_SCAN_CMD);
475       drweb_flags = htonl(DRWEBD_RETURN_VIRUSES | DRWEBD_IS_MAIL);
476
477       /* calc file size */
478       drweb_fd = open(CS eml_filename, O_RDONLY);
479       if (drweb_fd == -1) {
480         int err = errno;
481         (void)close(sock);
482         return drweb_errlog_defer(
483           string_sprintf("can't open spool file %s: %s",
484             eml_filename, strerror(err)));
485       }
486       fsize = lseek(drweb_fd, 0, SEEK_END);
487       if (fsize == -1) {
488         int err = errno;
489         (void)close(sock);
490         (void)close(drweb_fd);
491         return drweb_errlog_defer(
492           string_sprintf("can't seek spool file %s: %s",
493             eml_filename, strerror(err)));
494       }
495       drweb_slen = htonl(fsize);
496       lseek(drweb_fd, 0, SEEK_SET);
497
498       DEBUG(D_acl) debug_printf("Malware scan: issuing %s remote scan [%s %u]\n",
499           scanner_name, hostname, port);
500
501       /* send scan request */
502       if ((send(sock, &drweb_cmd, sizeof(drweb_cmd), 0) < 0) ||
503           (send(sock, &drweb_flags, sizeof(drweb_flags), 0) < 0) ||
504           (send(sock, &drweb_fin, sizeof(drweb_fin), 0) < 0) ||
505           (send(sock, &drweb_slen, sizeof(drweb_slen), 0) < 0)) {
506         (void)close(sock);
507         (void)close(drweb_fd);
508         return drweb_errlog_defer(
509           string_sprintf("unable to send commands to socket (%s)", drweb_options));
510       }
511
512       drweb_fbuf = (uschar *) malloc (fsize);
513       if (!drweb_fbuf) {
514         (void)close(sock);
515         (void)close(drweb_fd);
516         return drweb_errlog_defer(
517           string_sprintf("unable to allocate memory %u for file (%s)",
518             fsize, eml_filename));
519       }
520
521       result = read (drweb_fd, drweb_fbuf, fsize);
522       if (result == -1) {
523         int err = errno;
524         (void)close(sock);
525         (void)close(drweb_fd);
526         free(drweb_fbuf);
527         return drweb_errlog_defer(
528           string_sprintf("can't read spool file %s: %s",
529             eml_filename, strerror(err)));
530       }
531       (void)close(drweb_fd);
532
533       /* send file body to socket */
534       if (send(sock, drweb_fbuf, fsize, 0) < 0) {
535         (void)close(sock);
536         free(drweb_fbuf);
537         return drweb_errlog_defer(
538           string_sprintf("unable to send file body to socket (%s)", drweb_options));
539       }
540       (void)close(drweb_fd);
541     }
542     else {
543       /* open the drwebd UNIX socket */
544       sock = socket(AF_UNIX, SOCK_STREAM, 0);
545       if (sock < 0)
546         return drweb_errlog_defer("can't open UNIX socket");
547       server.sun_family = AF_UNIX;
548       Ustrcpy(server.sun_path, drweb_options);
549       if (connect(sock, (struct sockaddr *) &server, sizeof(struct sockaddr_un)) < 0) {
550         int err = errno;
551         (void)close(sock);
552         return drweb_errlog_defer(
553           string_sprintf("unable to connect to socket (%s). errno=%d", drweb_options, err));
554       }
555
556       /* prepare variables */
557       drweb_cmd = htonl(DRWEBD_SCAN_CMD);
558       drweb_flags = htonl(DRWEBD_RETURN_VIRUSES | DRWEBD_IS_MAIL);
559       drweb_slen = htonl(Ustrlen(eml_filename));
560
561       DEBUG(D_acl) debug_printf("Malware scan: issuing %s local scan [%s]\n",
562           scanner_name, drweb_options);
563
564       /* send scan request */
565       if ((send(sock, &drweb_cmd, sizeof(drweb_cmd), 0) < 0) ||
566           (send(sock, &drweb_flags, sizeof(drweb_flags), 0) < 0) ||
567           (send(sock, &drweb_slen, sizeof(drweb_slen), 0) < 0) ||
568           (send(sock, eml_filename, Ustrlen(eml_filename), 0) < 0) ||
569           (send(sock, &drweb_fin, sizeof(drweb_fin), 0) < 0)) {
570         (void)close(sock);
571         return drweb_errlog_defer(
572           string_sprintf("unable to send commands to socket (%s)", drweb_options));
573       }
574     }
575
576     /* wait for result */
577     if ((bread = recv(sock, &drweb_rc, sizeof(drweb_rc), 0) != sizeof(drweb_rc))) {
578       (void)close(sock);
579       return drweb_errlog_defer("unable to read return code");
580     }
581     drweb_rc = ntohl(drweb_rc);
582
583     if ((bread = recv(sock, &drweb_vnum, sizeof(drweb_vnum), 0) != sizeof(drweb_vnum))) {
584       (void)close(sock);
585       return drweb_errlog_defer("unable to read the number of viruses");
586     }
587     drweb_vnum = ntohl(drweb_vnum);
588
589     /* "virus(es) found" if virus number is > 0 */
590     if (drweb_vnum)
591     {
592       int i;
593
594       /* setup default virus name */
595       malware_name_internal = "unknown";
596       malware_name = malware_name_internal;
597
598       /* set up match regex */
599       drweb_re = pcre_compile( "infected\\swith\\s*(.+?)$", PCRE_COPT,
600         (const char **)&rerror, &roffset, NULL );
601
602       /* read and concatenate virus names into one string */
603       for (i=0;i<drweb_vnum;i++)
604       {
605         int size = 0, off = 0;
606         /* read the size of report */
607         if ((bread = recv(sock, &drweb_slen, sizeof(drweb_slen), 0) != sizeof(drweb_slen))) {
608           (void)close(sock);
609           return drweb_errlog_defer("cannot read report size");
610         };
611         drweb_slen = ntohl(drweb_slen);
612
613         /* read report body */
614         if ((bread = recv(sock, tmpbuf, drweb_slen, 0)) != drweb_slen) {
615           (void)close(sock);
616           return drweb_errlog_defer("cannot read report string");
617         };
618         tmpbuf[drweb_slen] = '\0';
619
620         /* try matcher on the line, grab substring */
621         result = pcre_exec(drweb_re, NULL, CS tmpbuf, Ustrlen(tmpbuf), 0, 0, ovector, 30);
622         if (result >= 2) {
623           const char * pre_malware_nb;
624
625           pcre_get_substring(CS tmpbuf, ovector, result, 1, &pre_malware_nb);
626
627           /* the first name we just copy to malware_name */
628           if (i==0)
629             malware_name_internal = string_append(NULL, &size, &off,
630                                         1, pre_malware_nb);
631           else
632             /* concatenate each new virus name to previous */
633             malware_name_internal = string_append(malware_name_internal,
634                                         &size, &off, 2, "/", pre_malware_nb);
635
636           pcre_free_substring(pre_malware_nb);
637         }
638       }
639     }
640     else {
641       const char *drweb_s = NULL;
642
643       if (drweb_rc & DERR_READ_ERR) drweb_s = "read error";
644       if (drweb_rc & DERR_NOMEMORY) drweb_s = "no memory";
645       if (drweb_rc & DERR_TIMEOUT)  drweb_s = "timeout";
646       if (drweb_rc & DERR_BAD_CALL) drweb_s = "wrong command";
647       /* retcodes DERR_SYMLINK, DERR_NO_REGFILE, DERR_SKIPPED.
648        * DERR_TOO_BIG, DERR_TOO_COMPRESSED, DERR_SPAM,
649        * DERR_CRC_ERROR, DERR_READSOCKET, DERR_WRITE_ERR
650        * and others are ignored */
651       if (drweb_s) {
652         (void)close(sock);
653         return drweb_errlog_defer(
654           string_sprintf("drweb daemon retcode 0x%x (%s)", drweb_rc, drweb_s));
655       }
656       /* no virus found */
657       malware_name = NULL;
658     };
659     (void)close(sock);
660   }
661   /* ----------------------------------------------------------------------- */
662     else if (strcmpic(scanner_name,US"aveserver") == 0) {
663       uschar *kav_options;
664       uschar kav_options_buffer[1024];
665       uschar kav_options_default[] = "/var/run/aveserver";
666       uschar buf[32768];
667       struct sockaddr_un server;
668       int sock;
669       int result;
670
671       if ((kav_options = string_nextinlist(&av_scanner_work, &sep,
672                                            kav_options_buffer,
673                                            sizeof(kav_options_buffer))) == NULL) {
674         /* no options supplied, use default options */
675         kav_options = kav_options_default;
676       };
677
678       /* open the aveserver socket */
679       sock = socket(AF_UNIX, SOCK_STREAM, 0);
680       if (sock < 0)
681         return aves_errlog_defer("can't open UNIX socket.");
682
683       server.sun_family = AF_UNIX;
684       Ustrcpy(server.sun_path, kav_options);
685       if (connect(sock, (struct sockaddr *) &server, sizeof(struct sockaddr_un)) < 0) {
686         int err = errno;
687         (void)close(sock);
688         return aves_errlog_defer(
689           string_sprintf("unable to connect to UNIX socket (%s). errno=%d", kav_options, err));
690       }
691
692       /* read aveserver's greeting and see if it is ready (2xx greeting) */
693       recv_line(sock, buf, 32768);
694
695       if (buf[0] != '2') {
696         /* aveserver is having problems */
697         (void)close(sock);
698         return aves_errlog_defer(
699           string_sprintf("unavailable (Responded: %s).", ((buf[0] != 0) ? buf : (uschar *)"nothing") ));
700       }
701
702       /* prepare our command */
703       (void)string_format(buf, 32768, "SCAN bPQRSTUW %s\r\n", eml_filename);
704
705       DEBUG(D_acl) debug_printf("Malware scan: issuing %s SCAN\n", scanner_name);
706
707       /* and send it */
708       if (send(sock, buf, Ustrlen(buf), 0) < 0) {
709         (void)close(sock);
710         return aves_errlog_defer(
711           string_sprintf("unable to write to UNIX socket (%s)", kav_options));
712       }
713
714       malware_name = NULL;
715       result = 0;
716       /* read response lines, find malware name and final response */
717       while (recv_line(sock, buf, 32768) > 0) {
718         debug_printf("aveserver: %s\n", buf);
719         if (buf[0] == '2') {
720           break;
721         } else if (buf[0] == '5') {
722           /* aveserver is having problems */
723           log_write(0, LOG_MAIN|LOG_PANIC,
724              "malware acl condition: unable to scan file %s (Responded: %s).",
725              eml_filename, buf);
726           result = DEFER;
727           break;
728         } else if (Ustrncmp(buf,"322",3) == 0) {
729           uschar *p = Ustrchr(&buf[4],' ');
730           *p = '\0';
731           malware_name_internal = string_copy(&buf[4]);
732           malware_name = malware_name_internal;
733         }
734       }
735
736       /* prepare our command */
737       (void)string_format(buf, 32768, "quit\r\n");
738
739       /* and send it */
740       if (send(sock, buf, Ustrlen(buf), 0) < 0) {
741         (void)close(sock);
742         return aves_errlog_defer(
743           string_sprintf("unable to write to UNIX socket (%s)", kav_options));
744       }
745
746       /* read aveserver's greeting and see if it is ready (2xx greeting) */
747       recv_line(sock, buf, 32768);
748
749       if (buf[0] != '2') {
750         /* aveserver is having problems */
751         (void)close(sock);
752         return aves_errlog_defer(
753           string_sprintf("unable to quit dialogue (Responded: %s).", ((buf[0] != 0) ? buf : (uschar *)"nothing") ));
754       }
755
756       (void)close(sock);
757
758       if (result == DEFER) return DEFER;
759     }
760     /* "fsecure" scanner type ------------------------------------------------- */
761     else if (strcmpic(scanner_name,US"fsecure") == 0) {
762       uschar *fsecure_options;
763       uschar fsecure_options_buffer[1024];
764       uschar fsecure_options_default[] = "/var/run/.fsav";
765       struct sockaddr_un server;
766       int sock, i, j, bread = 0;
767       uschar file_name[1024];
768       uschar av_buffer[1024];
769       pcre *fs_inf;
770       static uschar *cmdoptions[] = { US"CONFIGURE\tARCHIVE\t1\n",
771                                       US"CONFIGURE\tTIMEOUT\t0\n",
772                                       US"CONFIGURE\tMAXARCH\t5\n",
773                                       US"CONFIGURE\tMIME\t1\n" };
774
775       malware_name = NULL;
776       if ((fsecure_options = string_nextinlist(&av_scanner_work, &sep,
777                                                fsecure_options_buffer,
778                                                sizeof(fsecure_options_buffer))) == NULL) {
779          /* no options supplied, use default options */
780          fsecure_options = fsecure_options_default;
781       }
782
783       /* open the fsecure socket */
784       sock = socket(AF_UNIX, SOCK_STREAM, 0);
785       if (sock < 0)
786         return fsec_errlog_defer(
787           string_sprintf("unable to open socket %s (%s)",
788                   fsecure_options, strerror(errno)));
789
790       server.sun_family = AF_UNIX;
791       Ustrcpy(server.sun_path, fsecure_options);
792       if (connect(sock, (struct sockaddr *) &server, sizeof(struct sockaddr_un)) < 0) {
793         int err = errno;
794         (void)close(sock);
795         return fsec_errlog_defer(
796           string_sprintf("unable to connect to socket %s (%s)",
797                   fsecure_options, strerror(err)));
798       }
799
800       DEBUG(D_acl) debug_printf("Malware scan: issuing %s scan [%s]\n",
801           scanner_name, fsecure_options);
802
803       /* pass options */
804       memset(av_buffer, 0, sizeof(av_buffer));
805       for (i=0; i != 4; i++) {
806         /* debug_printf("send option \"%s\"",cmdoptions[i]); */
807         if (write(sock, cmdoptions[i], Ustrlen(cmdoptions[i])) < 0) {
808           int err = errno;
809           (void)close(sock);
810           return fsec_errlog_defer(
811             string_sprintf("unable to write option %d to %s (%s)",
812                     i, fsecure_options, strerror(err)));
813         }
814
815         bread = ip_recv(sock, av_buffer, sizeof(av_buffer), MALWARE_TIMEOUT);
816         if (bread >0) av_buffer[bread]='\0';
817         if (bread < 0) {
818           int err = errno;
819           (void)close(sock);
820           return fsec_errlog_defer(
821             string_sprintf("unable to read answer %d (%s)", i, strerror(errno)));
822         }
823         for (j=0;j<bread;j++) if((av_buffer[j]=='\r')||(av_buffer[j]=='\n')) av_buffer[j] ='@';
824         /* debug_printf("read answer %d read=%d \"%s\"\n", i, bread, av_buffer ); */
825         /* while (Ustrstr(av_buffer, "OK\tServer configured.@") == NULL); */
826       };
827
828       /* pass the mailfile to fsecure */
829       (void)string_format(file_name,1024,"SCAN\t%s\n", eml_filename);
830       /* debug_printf("send scan %s",file_name); */
831       if (write(sock, file_name, Ustrlen(file_name)) < 0) {
832         int err = errno;
833         (void)close(sock);
834         return fsec_errlog_defer(
835           string_sprintf("unable to write scan to %s (%s)",
836                   fsecure_options, strerror(err)));
837       }
838
839       /* set up match */
840       /* todo also SUSPICION\t */
841       fs_inf = pcre_compile("\\S{0,5}INFECTED\\t[^\\t]*\\t([^\\t]+)\\t\\S*$", PCRE_COPT, (const char **)&rerror, &roffset, NULL);
842
843       /* read report, linewise */
844       do {
845         int ovector[30];
846         i = 0;
847         memset(av_buffer, 0, sizeof(av_buffer));
848         do {
849           bread=ip_recv(sock, &av_buffer[i], 1, MALWARE_TIMEOUT);
850           if (bread < 0) {
851             int err = errno;
852             (void)close(sock);
853             return fsec_errlog_defer(
854               string_sprintf("unable to read result (%s)", strerror(err)));
855           }
856           i++;
857         }
858         while ((i < sizeof(av_buffer)-1 ) && (av_buffer[i-1] != '\n'));
859         av_buffer[i-1] = '\0';
860         /* debug_printf("got line \"%s\"\n",av_buffer); */
861
862         /* Really search for virus again? */
863         if (malware_name == NULL) {
864           /* try matcher on the line, grab substring */
865           i = pcre_exec(fs_inf, NULL, CS av_buffer, Ustrlen(av_buffer), 0, 0, ovector, 30);
866           if (i >= 2) {
867             /* Got it */
868             pcre_get_substring(CS av_buffer, ovector, i, 1,
869                                 (const char **) &malware_name_internal);
870             malware_name = malware_name_internal;
871           };
872         };
873       }
874       while (Ustrstr(av_buffer, "OK\tScan ok.") == NULL);
875       (void)close(sock);
876     }
877     /* ----------------------------------------------------------------------- */
878
879     /* "kavdaemon" scanner type ------------------------------------------------ */
880     else if (strcmpic(scanner_name,US"kavdaemon") == 0) {
881       uschar *kav_options;
882       uschar kav_options_buffer[1024];
883       uschar kav_options_default[] = "/var/run/AvpCtl";
884       struct sockaddr_un server;
885       int sock;
886       time_t t;
887       uschar tmpbuf[1024];
888       uschar scanrequest[1024];
889       int kav_rc;
890       unsigned long kav_reportlen, bread;
891       pcre *kav_re;
892       uschar *p;
893       int fits;
894
895       if ((kav_options = string_nextinlist(&av_scanner_work, &sep,
896                                            kav_options_buffer,
897                                            sizeof(kav_options_buffer))) == NULL) {
898         /* no options supplied, use default options */
899         kav_options = kav_options_default;
900       };
901
902       /* open the kavdaemon socket */
903       sock = socket(AF_UNIX, SOCK_STREAM, 0);
904       if (sock < 0)
905         return kavd_errlog_defer("can't open UNIX socket.");
906
907       server.sun_family = AF_UNIX;
908       Ustrcpy(server.sun_path, kav_options);
909       if (connect(sock, (struct sockaddr *) &server, sizeof(struct sockaddr_un)) < 0) {
910         int err = errno;
911         (void)close(sock);
912         return kavd_errlog_defer(
913           string_sprintf("unable to connect to UNIX socket (%s). errno=%d", kav_options, err));
914       }
915
916       /* get current date and time, build scan request */
917       time(&t);
918       /* pdp note: before the eml_filename parameter, this scanned the
919       directory; not finding documentation, so we'll strip off the directory.
920       The side-effect is that the test framework scanning may end up in
921       scanning more than was requested, but for the normal interface, this is
922       fine. */
923       strftime(CS tmpbuf, sizeof(tmpbuf), "<0>%d %b %H:%M:%S:%%s", localtime(&t));
924       fits = string_format(scanrequest, 1024,CS tmpbuf, eml_filename);
925       if (!fits) {
926         (void)close(sock);
927         log_write(0, LOG_MAIN|LOG_PANIC,
928             "malware filename does not fit in buffer [malware_internal() kavdaemon]");
929       }
930       p = Ustrrchr(scanrequest, '/');
931       if (p)
932         *p = '\0';
933
934       DEBUG(D_acl) debug_printf("Malware scan: issuing %s scan [%s]\n",
935           scanner_name, kav_options);
936
937       /* send scan request */
938       if (send(sock, scanrequest, Ustrlen(scanrequest)+1, 0) < 0) {
939         (void)close(sock);
940         return kavd_errlog_defer(
941           string_sprintf("unable to write to UNIX socket (%s)", kav_options));
942       }
943
944       /* wait for result */
945       if ((bread = recv(sock, tmpbuf, 2, 0) != 2)) {
946         (void)close(sock);
947         return kavd_errlog_defer("unable to read 2 bytes from socket.");
948       }
949
950       /* get errorcode from one nibble */
951       kav_rc = tmpbuf[ test_byte_order()==LITTLE_MY_ENDIAN ? 0 : 1 ] & 0x0F;
952
953       /* improper kavdaemon configuration */
954       if ( (kav_rc == 5) || (kav_rc == 6) ) {
955         (void)close(sock);
956         return kavd_errlog_defer("please reconfigure kavdaemon to NOT disinfect or remove infected files.");
957       }
958
959       if (kav_rc == 1) {
960         (void)close(sock);
961         return kavd_errlog_defer("reported 'scanning not completed' (code 1).");
962       }
963
964       if (kav_rc == 7) {
965         (void)close(sock);
966         return kavd_errlog_defer("reported 'kavdaemon damaged' (code 7).");
967       }
968
969       /* code 8 is not handled, since it is ambigous. It appears mostly on
970       bounces where part of a file has been cut off */
971
972       /* "virus found" return codes (2-4) */
973       if ((kav_rc > 1) && (kav_rc < 5)) {
974         int report_flag = 0;
975
976         /* setup default virus name */
977         malware_name_internal = string_copy("unknown");
978         malware_name = malware_name_internal;
979
980         report_flag = tmpbuf[ test_byte_order() == LITTLE_MY_ENDIAN ? 1 : 0 ];
981
982         /* read the report, if available */
983         if( report_flag == 1 ) {
984           /* read report size */
985           if ((bread = recv(sock, &kav_reportlen, 4, 0)) != 4) {
986             (void)close(sock);
987             return kavd_errlog_defer("cannot read report size");
988           }
989
990           /* it's possible that avp returns av_buffer[1] == 1 but the
991           reportsize is 0 (!?) */
992           if (kav_reportlen > 0) {
993             /* set up match regex, depends on retcode */
994             kav_re = pcre_compile( kav_rc == 3
995                                    ? "suspicion:\\s*(.+?)\\s*$"
996                                    : "infected:\\s*(.+?)\\s*$",
997                                    PCRE_COPT,
998                                    (const char **)&rerror,
999                                    &roffset,
1000                                    NULL );
1001
1002             /* read report, linewise */
1003             while (kav_reportlen > 0) {
1004               int result = 0;
1005               int ovector[30];
1006
1007               bread = 0;
1008               while ( recv(sock, &tmpbuf[bread], 1, 0) == 1 ) {
1009                 kav_reportlen--;
1010                 if ( (tmpbuf[bread] == '\n') || (bread > 1021) ) break;
1011                 bread++;
1012               };
1013               bread++;
1014               tmpbuf[bread] = '\0';
1015
1016               /* try matcher on the line, grab substring */
1017               result = pcre_exec(kav_re, NULL, CS tmpbuf, Ustrlen(tmpbuf), 0, 0, ovector, 30);
1018               if (result >= 2) {
1019                 pcre_get_substring(CS tmpbuf, ovector, result, 1,
1020                                     (const char **) &malware_name_internal);
1021                 break;
1022               };
1023             };
1024           };
1025         };
1026       }
1027       else {
1028         /* no virus found */
1029         malware_name = NULL;
1030       };
1031
1032       (void)close(sock);
1033     }
1034     /* ----------------------------------------------------------------------- */
1035
1036
1037     /* "cmdline" scanner type ------------------------------------------------ */
1038     else if (strcmpic(scanner_name,US"cmdline") == 0) {
1039       uschar *cmdline_scanner;
1040       uschar cmdline_scanner_buffer[1024];
1041       uschar *cmdline_trigger;
1042       uschar cmdline_trigger_buffer[1024];
1043       const pcre *cmdline_trigger_re;
1044       uschar *cmdline_regex;
1045       uschar cmdline_regex_buffer[1024];
1046       const pcre *cmdline_regex_re;
1047       uschar file_name[1024];
1048       uschar commandline[1024];
1049       void (*eximsigchld)(int);
1050       void (*eximsigpipe)(int);
1051       FILE *scanner_out = NULL;
1052       FILE *scanner_record = NULL;
1053       uschar linebuffer[32767];
1054       int trigger = 0;
1055       int result;
1056       int ovector[30];
1057       uschar *p;
1058       BOOL fits;
1059
1060       /* find scanner command line */
1061       if ((cmdline_scanner = string_nextinlist(&av_scanner_work, &sep,
1062                                           cmdline_scanner_buffer,
1063                                           sizeof(cmdline_scanner_buffer))) == NULL)
1064         return cmdl_errlog_defer("missing commandline specification");
1065
1066       /* find scanner output trigger */
1067       if ((cmdline_trigger = string_nextinlist(&av_scanner_work, &sep,
1068                                           cmdline_trigger_buffer,
1069                                           sizeof(cmdline_trigger_buffer))) == NULL)
1070         return cmdl_errlog_defer("missing trigger specification");
1071
1072       /* precompile trigger regex */
1073       cmdline_trigger_re = pcre_compile(CS cmdline_trigger, PCRE_COPT, (const char **)&rerror, &roffset, NULL);
1074       if (cmdline_trigger_re == NULL)
1075         return cmdl_errlog_defer(
1076           string_sprintf("regular expression error in '%s': %s at offset %d",
1077             cmdline_trigger, rerror, roffset));
1078
1079       /* find scanner name regex */
1080       if ((cmdline_regex = string_nextinlist(&av_scanner_work, &sep,
1081                                              cmdline_regex_buffer,
1082                                              sizeof(cmdline_regex_buffer))) == NULL)
1083         return cmdl_errlog_defer("missing virus name regex specification");
1084
1085       /* precompile name regex */
1086       cmdline_regex_re = pcre_compile(CS cmdline_regex, PCRE_COPT, (const char **)&rerror, &roffset, NULL);
1087       if (cmdline_regex_re == NULL)
1088         return cmdl_errlog_defer(
1089           string_sprintf("regular expression error in '%s': %s at offset %d",
1090             cmdline_regex, rerror, roffset));
1091
1092       /* prepare scanner call; despite the naming, file_name holds a directory
1093       name which is documented as the value given to %s. */
1094       if (Ustrlen(eml_filename) > sizeof(file_name) - 1)
1095         return cmdl_errlog_defer("filename does not fit in buffer");
1096
1097       Ustrcpy(file_name, eml_filename);
1098       p = Ustrrchr(file_name, '/');
1099       if (p)
1100         *p = '\0';
1101       fits = string_format(commandline, sizeof(commandline), CS cmdline_scanner, file_name);
1102       if (!fits)
1103         return cmdl_errlog_defer("command-line does not fit in buffer");
1104
1105       /* redirect STDERR too */
1106       if (Ustrlen(commandline) + 5 > sizeof(commandline))
1107         return cmdl_errlog_defer("command-line does not fit in buffer (STDERR redirect)");
1108       Ustrcat(commandline," 2>&1");
1109
1110       DEBUG(D_acl) debug_printf("Malware scan: issuing %s scan [%s]\n", scanner_name, commandline);
1111
1112       /* store exims signal handlers */
1113       eximsigchld = signal(SIGCHLD,SIG_DFL);
1114       eximsigpipe = signal(SIGPIPE,SIG_DFL);
1115
1116       scanner_out = popen(CS commandline,"r");
1117       if (scanner_out == NULL) {
1118         int err = errno;
1119         signal(SIGCHLD,eximsigchld);
1120         signal(SIGPIPE,eximsigpipe);
1121         return cmdl_errlog_defer(
1122           string_sprintf("call (%s) failed: %s.", commandline, strerror(err)));
1123       }
1124
1125       (void)string_format(file_name,1024,"%s/scan/%s/%s_scanner_output", spool_directory, message_id, message_id);
1126       scanner_record = modefopen(file_name,"wb",SPOOL_MODE);
1127
1128       if (scanner_record == NULL) {
1129         int err = errno;
1130         pclose(scanner_out);
1131         signal(SIGCHLD,eximsigchld);
1132         signal(SIGPIPE,eximsigpipe);
1133         return cmdl_errlog_defer(
1134           string_sprintf("opening scanner output file (%s) failed: %s.",
1135             file_name, strerror(err)));
1136       }
1137
1138       /* look for trigger while recording output */
1139       while(fgets(CS linebuffer,32767,scanner_out) != NULL) {
1140         if ( Ustrlen(linebuffer) > fwrite(linebuffer, 1, Ustrlen(linebuffer), scanner_record) ) {
1141           /* short write */
1142           pclose(scanner_out);
1143           signal(SIGCHLD,eximsigchld);
1144           signal(SIGPIPE,eximsigpipe);
1145           return cmdl_errlog_defer(
1146             string_sprintf("short write on scanner output file (%s).", file_name));
1147         }
1148         /* try trigger match */
1149         if (!trigger && regex_match_and_setup(cmdline_trigger_re, linebuffer, 0, -1))
1150           trigger = 1;
1151       }
1152
1153       (void)fclose(scanner_record);
1154       pclose(scanner_out);
1155       signal(SIGCHLD,eximsigchld);
1156       signal(SIGPIPE,eximsigpipe);
1157
1158       if (trigger) {
1159         /* setup default virus name */
1160         malware_name = US"unknown";
1161
1162         /* re-open the scanner output file, look for name match */
1163         scanner_record = fopen(CS file_name,"rb");
1164         while(fgets(CS linebuffer,32767,scanner_record) != NULL) {
1165           /* try match */
1166           result = pcre_exec(cmdline_regex_re, NULL, CS linebuffer, Ustrlen(linebuffer), 0, 0, ovector, 30);
1167           if (result >= 2)
1168             pcre_get_substring(CS linebuffer, ovector, result, 1,
1169                                 (const char **) &malware_name_internal);
1170         }
1171         (void)fclose(scanner_record);
1172       }
1173       else /* no virus found */
1174         malware_name = NULL;
1175     }
1176     /* ----------------------------------------------------------------------- */
1177
1178
1179     /* "sophie" scanner type ------------------------------------------------- */
1180     else if (strcmpic(scanner_name,US"sophie") == 0) {
1181       uschar *sophie_options;
1182       uschar sophie_options_buffer[1024];
1183       uschar sophie_options_default[] = "/var/run/sophie";
1184       int bread = 0;
1185       struct sockaddr_un server;
1186       int sock, len;
1187       uschar *p;
1188       uschar file_name[1024];
1189       uschar av_buffer[1024];
1190
1191       if ((sophie_options = string_nextinlist(&av_scanner_work, &sep,
1192                                           sophie_options_buffer,
1193                                           sizeof(sophie_options_buffer))) == NULL) {
1194         /* no options supplied, use default options */
1195         sophie_options = sophie_options_default;
1196       }
1197
1198       /* open the sophie socket */
1199       sock = socket(AF_UNIX, SOCK_STREAM, 0);
1200       if (sock < 0)
1201         return soph_errlog_defer("can't open UNIX socket.");
1202
1203       server.sun_family = AF_UNIX;
1204       Ustrcpy(server.sun_path, sophie_options);
1205       if (connect(sock, (struct sockaddr *) &server, sizeof(struct sockaddr_un)) < 0) {
1206         int err = errno;
1207         (void)close(sock);
1208         return soph_errlog_defer(
1209           string_sprintf("unable to connect to UNIX socket (%s). errno=%d",
1210             sophie_options, err));
1211       }
1212
1213       /* pass the scan directory to sophie */
1214       len = Ustrlen(eml_filename) + 1;
1215       if (len > sizeof(file_name))
1216         {
1217         (void)close(sock);
1218         return soph_errlog_defer("malware filename does not fit in buffer");
1219         }
1220       memcpy(file_name, eml_filename, len);
1221       p = Ustrrchr(file_name, '/');
1222       if (p)
1223         *p = '\0';
1224
1225       DEBUG(D_acl) debug_printf("Malware scan: issuing %s scan [%s]\n",
1226           scanner_name, sophie_options);
1227
1228       if (  write(sock, file_name, Ustrlen(file_name)) < 0
1229          || write(sock, "\n", 1) != 1
1230          ) {
1231         (void)close(sock);
1232         return soph_errlog_defer(
1233           string_sprintf("unable to write to UNIX socket (%s)", sophie_options));
1234       }
1235
1236       /* wait for result */
1237       memset(av_buffer, 0, sizeof(av_buffer));
1238       if ((!(bread = ip_recv(sock, av_buffer, sizeof(av_buffer), MALWARE_TIMEOUT)) > 0)) {
1239         (void)close(sock);
1240         return soph_errlog_defer(
1241           string_sprintf("unable to read from UNIX socket (%s)", sophie_options));
1242       }
1243
1244       (void)close(sock);
1245
1246       /* infected ? */
1247       if (av_buffer[0] == '1') {
1248         if (Ustrchr(av_buffer, '\n')) *Ustrchr(av_buffer, '\n') = '\0';
1249         malware_name_internal = string_copy(&av_buffer[2]);
1250         malware_name = malware_name_internal;
1251       }
1252       else if (!strncmp(CS av_buffer, "-1", 2))
1253         return soph_errlog_defer("scanner reported error");
1254       else /* all ok, no virus */
1255         malware_name = NULL;
1256     }
1257     /* ----------------------------------------------------------------------- */
1258
1259
1260     /* "clamd" scanner type ------------------------------------------------- */
1261     /* This code was originally contributed by David Saez */
1262     /* There are three scanning methods available to us:
1263      *  (1) Use the SCAN command, pointing to a file in the filesystem
1264      *  (2) Use the STREAM command, send the data on a separate port
1265      *  (3) Use the zINSTREAM command, send the data inline
1266      * The zINSTREAM command was introduced with ClamAV 0.95, which marked
1267      * STREAM deprecated; see: http://wiki.clamav.net/bin/view/Main/UpgradeNotes095
1268      * In Exim, we use SCAN if using a Unix-domain socket or explicitly told that
1269      * the TCP-connected daemon is actually local; otherwise we use zINSTREAM unless
1270      * WITH_OLD_CLAMAV_STREAM is defined.
1271      * See Exim bug 926 for details.  */
1272     else if (strcmpic(scanner_name,US"clamd") == 0) {
1273       uschar *clamd_options = NULL;
1274       uschar clamd_options_buffer[1024];
1275       uschar clamd_options_default[] = "/tmp/clamd";
1276       uschar *p, *vname, *result_tag, *response_end;
1277       struct sockaddr_un server;
1278       int sock,bread=0;
1279       unsigned int port;
1280       uschar file_name[1024];
1281       uschar av_buffer[1024];
1282       uschar *hostname = "";
1283       struct hostent *he;
1284       struct in_addr in;
1285       uschar *clamav_fbuf;
1286       int clam_fd, result;
1287       unsigned int fsize;
1288       BOOL use_scan_command = FALSE, fits;
1289       clamd_address_container * clamd_address_vector[MAX_CLAMD_SERVERS];
1290       int current_server;
1291       int num_servers = 0;
1292 #ifdef WITH_OLD_CLAMAV_STREAM
1293       uschar av_buffer2[1024];
1294       int sockData;
1295 #else
1296       uint32_t send_size, send_final_zeroblock;
1297 #endif
1298
1299       if ((clamd_options = string_nextinlist(&av_scanner_work, &sep,
1300                                              clamd_options_buffer,
1301                                              sizeof(clamd_options_buffer))) == NULL) {
1302         /* no options supplied, use default options */
1303         clamd_options = clamd_options_default;
1304       }
1305
1306       if (*clamd_options == '/')
1307         /* Local file; so we def want to use_scan_command and don't want to try
1308          * passing IP/port combinations */
1309         use_scan_command = TRUE;
1310       else {
1311         uschar *address = clamd_options;
1312         uschar address_buffer[MAX_CLAMD_ADDRESS_LENGTH + 20];
1313
1314         /* Go through the rest of the list of host/port and construct an array
1315          * of servers to try. The first one is the bit we just passed from
1316          * clamd_options so process that first and then scan the remainder of
1317          * the address buffer */
1318         do {
1319           clamd_address_container *this_clamd;
1320
1321           /* The 'local' option means use the SCAN command over the network
1322            * socket (ie common file storage in use) */
1323           if (strcmpic(address,US"local") == 0) {
1324             use_scan_command = TRUE;
1325             continue;
1326           }
1327
1328           /* XXX: If unsuccessful we should free this memory */
1329           this_clamd =
1330               (clamd_address_container *)store_get(sizeof(clamd_address_container));
1331
1332           /* extract host and port part */
1333           if( sscanf(CS address, "%" MAX_CLAMD_ADDRESS_LENGTH_S "s %u", this_clamd->tcp_addr,
1334                                             &(this_clamd->tcp_port)) != 2 ) {
1335             clmd_errlog(string_sprintf("invalid address '%s'", address));
1336             continue;
1337           }
1338
1339           clamd_address_vector[num_servers] = this_clamd;
1340           num_servers++;
1341           if (num_servers >= MAX_CLAMD_SERVERS) {
1342             clmd_errlog("More than " MAX_CLAMD_SERVERS_S " clamd servers "
1343                   "specified; only using the first " MAX_CLAMD_SERVERS_S );
1344             break;
1345           }
1346         } while ((address = string_nextinlist(&av_scanner_work, &sep,
1347                                         address_buffer,
1348                                         sizeof(address_buffer))) != NULL);
1349
1350         /* check if we have at least one server */
1351         if (!num_servers)
1352           return clmd_errlog_defer("no useable server addresses in malware configuration option.");
1353       }
1354
1355       /* See the discussion of response formats below to see why we really don't
1356       like colons in filenames when passing filenames to ClamAV. */
1357       if (use_scan_command && Ustrchr(eml_filename, ':'))
1358         return clmd_errlog_defer(
1359           string_sprintf("local/SCAN mode incompatible with" \
1360             " : in path to email filename [%s]", eml_filename));
1361
1362       /* We have some network servers specified */
1363       if (num_servers) {
1364
1365         /* Confirmed in ClamAV source (0.95.3) that the TCPAddr option of clamd
1366          * only supports AF_INET, but we should probably be looking to the
1367          * future and rewriting this to be protocol-independent anyway. */
1368
1369         while ( num_servers > 0 ) {
1370           /* Randomly pick a server to start with */
1371           current_server = random_number( num_servers );
1372
1373           debug_printf("trying server name %s, port %u\n",
1374                        clamd_address_vector[current_server]->tcp_addr,
1375                        clamd_address_vector[current_server]->tcp_port);
1376
1377           /* Lookup the host. This is to ensure that we connect to the same IP
1378            * on both connections (as one host could resolve to multiple ips) */
1379           if((he = gethostbyname(CS clamd_address_vector[current_server]->tcp_addr))
1380                           == 0) {
1381             clmd_errlog(string_sprintf("failed to lookup host '%s'",
1382                     clamd_address_vector[current_server]->tcp_addr));
1383             goto try_next_server;
1384           }
1385
1386           in = *(struct in_addr *) he->h_addr_list[0];
1387
1388           /* Open the ClamAV Socket */
1389           if ( (sock = ip_socket(SOCK_STREAM, AF_INET)) < 0) {
1390             clmd_errlog(string_sprintf("unable to acquire socket (%s)",
1391                       strerror(errno)));
1392             goto try_next_server;
1393           }
1394
1395           if (ip_connect( sock,
1396                           AF_INET,
1397                           (uschar*)inet_ntoa(in),
1398                           clamd_address_vector[current_server]->tcp_port,
1399                           5 ) > -1) {
1400             /* Connection successfully established with a server */
1401             hostname = clamd_address_vector[current_server]->tcp_addr;
1402             break;
1403           } else {
1404             clmd_errlog(string_sprintf(
1405                "malware acl condition: clamd: connection to %s, port %u failed (%s)",
1406                clamd_address_vector[current_server]->tcp_addr,
1407                clamd_address_vector[current_server]->tcp_port,
1408                strerror(errno)));
1409
1410             (void)close(sock);
1411           }
1412
1413 try_next_server:
1414           /* Remove the server from the list. XXX We should free the memory */
1415           num_servers--;
1416           int i;
1417           for( i = current_server; i < num_servers; i++ )
1418             clamd_address_vector[i] = clamd_address_vector[i+1];
1419         }
1420
1421         if ( num_servers == 0 )
1422           return clmd_errlog_defer("all servers failed");
1423
1424       } else {
1425         /* open the local socket */
1426         if ((sock = socket(AF_UNIX, SOCK_STREAM, 0)) < 0)
1427           return clmd_errlog_defer(
1428             string_sprintf("unable to acquire socket (%s)", strerror(errno)));
1429
1430         server.sun_family = AF_UNIX;
1431         Ustrcpy(server.sun_path, clamd_options);
1432
1433         if (connect(sock, (struct sockaddr *) &server, sizeof(struct sockaddr_un)) < 0) {
1434           int err = errno;
1435           (void)close(sock);
1436           return clmd_errlog_defer(
1437             string_sprintf("unable to connect to UNIX socket %s (%s)",
1438                     clamd_options, strerror(err) ));
1439         }
1440       }
1441
1442       /* have socket in variable "sock"; command to use is semi-independent of
1443        * the socket protocol.  We use SCAN if is local (either Unix/local
1444        * domain socket, or explicitly told local) else we stream the data.
1445        * How we stream the data depends upon how we were built.  */
1446
1447       if (!use_scan_command) {
1448
1449 #ifdef WITH_OLD_CLAMAV_STREAM
1450         /* "STREAM\n" command, get back a "PORT <N>\n" response, send data to
1451          * that port on a second connection; then in the scan-method-neutral
1452          * part, read the response back on the original connection. */
1453
1454         DEBUG(D_acl) debug_printf("Malware scan: issuing %s old-style remote scan (PORT)\n",
1455             scanner_name);
1456
1457         /* Pass the string to ClamAV (7 = "STREAM\n") */
1458         if (send(sock, "STREAM\n", 7, 0) < 0) {
1459           int err = errno;
1460           (void)close(sock);
1461           return clmd_errlog_defer(
1462             string_sprintf("unable to write to socket (%s)", strerror(err)));
1463         }
1464         memset(av_buffer2, 0, sizeof(av_buffer2));
1465         bread = ip_recv(sock, av_buffer2, sizeof(av_buffer2), MALWARE_TIMEOUT);
1466
1467         if (bread < 0) {
1468           int err = errno;
1469           (void)close(sock);
1470           return clmd_errlog_defer(
1471             string_sprintf("unable to read PORT from socket (%s)",
1472                 strerror(err)));
1473         }
1474
1475         if (bread == sizeof(av_buffer)) {
1476           (void)close(sock);
1477           return clmd_errlog_defer("buffer too small");
1478         }
1479
1480         if (!(*av_buffer2)) {
1481           (void)close(sock);
1482           return clmd_errlog_defer("ClamAV returned null");
1483         }
1484
1485         av_buffer2[bread] = '\0';
1486         if( sscanf(CS av_buffer2, "PORT %u\n", &port) != 1 ) {
1487           (void)close(sock);
1488           return clmd_errlog_defer(
1489             string_sprintf("Expected port information from clamd, got '%s'",
1490               av_buffer2));
1491         }
1492
1493         if ( (sockData = ip_socket(SOCK_STREAM, AF_INET)) < 0) {
1494           int err = errno;
1495           (void)close(sock);
1496           return clmd_errlog_defer(
1497             string_sprintf("unable to acquire socket (%s)", strerror(err)));
1498         }
1499
1500         if (ip_connect(sockData, AF_INET, (uschar*)inet_ntoa(in), port, 5) < 0) {
1501           int err = errno;
1502           (void)close(sockData); (void)close(sock);
1503           return clmd_errlog_defer(
1504             string_sprintf("connection to %s, port %u failed (%s)",
1505                   inet_ntoa(in), port, strerror(err)));
1506         }
1507
1508 #define CLOSE_SOCKDATA (void)close(sockData)
1509 #else /* WITH_OLD_CLAMAV_STREAM not defined */
1510         /* New protocol: "zINSTREAM\n" followed by a sequence of <length><data>
1511         chunks, <n> a 4-byte number (network order), terminated by a zero-length
1512         chunk. */
1513
1514         DEBUG(D_acl) debug_printf("Malware scan: issuing %s new-style remote scan (zINSTREAM)\n",
1515             scanner_name);
1516
1517         /* Pass the string to ClamAV (10 = "zINSTREAM\0") */
1518         if (send(sock, "zINSTREAM", 10, 0) < 0) {
1519           int err = errno;
1520           (void)close(sock);
1521           return clmd_errlog_defer(
1522             string_sprintf("unable to send zINSTREAM to socket (%s)",
1523               strerror(err)));
1524         }
1525
1526 #define CLOSE_SOCKDATA /**/
1527 #endif
1528
1529         /* calc file size */
1530         clam_fd = open(CS eml_filename, O_RDONLY);
1531         if (clam_fd == -1) {
1532           int err = errno;
1533           CLOSE_SOCKDATA; (void)close(sock);
1534           return clmd_errlog_defer(
1535             string_sprintf("can't open spool file %s: %s",
1536               eml_filename, strerror(err)));
1537         }
1538         fsize = lseek(clam_fd, 0, SEEK_END);
1539         if (fsize == -1) {
1540           int err = errno;
1541           CLOSE_SOCKDATA; (void)close(sock); (void)close(clam_fd);
1542           return clmd_errlog_defer(
1543             string_sprintf("can't seek spool file %s: %s",
1544               eml_filename, strerror(errno)));
1545         }
1546         lseek(clam_fd, 0, SEEK_SET);
1547
1548         clamav_fbuf = (uschar *) malloc (fsize);
1549         if (!clamav_fbuf) {
1550           CLOSE_SOCKDATA; (void)close(sock); (void)close(clam_fd);
1551           return clmd_errlog_defer(
1552             string_sprintf("unable to allocate memory %u for file (%s)",
1553               fsize, eml_filename));
1554         }
1555
1556         result = read (clam_fd, clamav_fbuf, fsize);
1557         if (result == -1) {
1558           int err = errno;
1559           CLOSE_SOCKDATA; (void)close(sock); (void)close(clam_fd);
1560           free(clamav_fbuf);
1561           return clmd_errlog_defer(
1562             string_sprintf("can't read spool file %s: %s",
1563               eml_filename, strerror(err)));
1564         }
1565         (void)close(clam_fd);
1566
1567         /* send file body to socket */
1568 #ifdef WITH_OLD_CLAMAV_STREAM
1569         if (send(sockData, clamav_fbuf, fsize, 0) < 0) {
1570           CLOSE_SOCKDATA; (void)close(sock);
1571           free(clamav_fbuf);
1572           return clmd_errlog_defer(
1573             string_sprintf("unable to send file body to socket (%s:%u)",
1574               hostname, port);
1575         }
1576 #else
1577         send_size = htonl(fsize);
1578         send_final_zeroblock = 0;
1579         if ((send(sock, &send_size, sizeof(send_size), 0) < 0) ||
1580             (send(sock, clamav_fbuf, fsize, 0) < 0) ||
1581             (send(sock, &send_final_zeroblock, sizeof(send_final_zeroblock), 0) < 0))
1582           {
1583           (void)close(sock);
1584           free(clamav_fbuf);
1585           return clmd_errlog_defer(
1586             string_sprintf("unable to send file body to socket (%s:%u)",
1587               hostname, port));
1588           }
1589 #endif
1590
1591         free(clamav_fbuf);
1592
1593         CLOSE_SOCKDATA;
1594 #undef CLOSE_SOCKDATA
1595
1596       } else { /* use scan command */
1597         /* Send a SCAN command pointing to a filename; then in the then in the
1598          * scan-method-neutral part, read the response back */
1599
1600 /* ================================================================= */
1601
1602         /* Prior to the reworking post-Exim-4.72, this scanned a directory,
1603         which dates to when ClamAV needed us to break apart the email into the
1604         MIME parts (eg, with the now deprecated demime condition coming first).
1605         Some time back, ClamAV gained the ability to deconstruct the emails, so
1606         doing this would actually have resulted in the mail attachments being
1607         scanned twice, in the broken out files and from the original .eml.
1608         Since ClamAV now handles emails (and has for quite some time) we can
1609         just use the email file itself. */
1610         /* Pass the string to ClamAV (7 = "SCAN \n" + \0) */
1611         fits = string_format(file_name, sizeof(file_name), "SCAN %s\n",
1612             eml_filename);
1613         if (!fits) {
1614           (void)close(sock);
1615           clmd_errlog("filename does not fit in buffer");
1616         }
1617
1618         DEBUG(D_acl) debug_printf("Malware scan: issuing %s local-path scan [%s]\n",
1619             scanner_name, clamd_options);
1620
1621         if (send(sock, file_name, Ustrlen(file_name), 0) < 0) {
1622           int err = errno;
1623           (void)close(sock);
1624           return clmd_errlog_defer(
1625             string_sprintf("unable to write to socket (%s)", strerror(err)));
1626         }
1627
1628         /* Do not shut down the socket for writing; a user report noted that
1629          * clamd 0.70 does not react well to this. */
1630       }
1631       /* Commands have been sent, no matter which scan method or connection
1632        * type we're using; now just read the result, independent of method. */
1633
1634       /* Read the result */
1635       memset(av_buffer, 0, sizeof(av_buffer));
1636       bread = ip_recv(sock, av_buffer, sizeof(av_buffer), MALWARE_TIMEOUT);
1637       (void)close(sock);
1638
1639       if (!(bread > 0))
1640         return clmd_errlog_defer(
1641           string_sprintf("unable to read from socket (%s)", strerror(errno)));
1642
1643       if (bread == sizeof(av_buffer))
1644         return clmd_errlog_defer("buffer too small");
1645       /* We're now assured of a NULL at the end of av_buffer */
1646
1647       /* Check the result. ClamAV returns one of two result formats.
1648       In the basic mode, the response is of the form:
1649         infected: -> "<filename>: <virusname> FOUND"
1650         not-infected: -> "<filename>: OK"
1651         error: -> "<filename>: <errcode> ERROR
1652       If the ExtendedDetectionInfo option has been turned on, then we get:
1653         "<filename>: <virusname>(<virushash>:<virussize>) FOUND"
1654       for the infected case.  Compare:
1655 /tmp/eicar.com: Eicar-Test-Signature FOUND
1656 /tmp/eicar.com: Eicar-Test-Signature(44d88612fea8a8f36de82e1278abb02f:68) FOUND
1657
1658       In the streaming case, clamd uses the filename "stream" which you should
1659       be able to verify with { ktrace clamdscan --stream /tmp/eicar.com }.  (The
1660       client app will replace "stream" with the original filename before returning
1661       results to stdout, but the trace shows the data).
1662
1663       We will assume that the pathname passed to clamd from Exim does not contain
1664       a colon.  We will have whined loudly above if the eml_filename does (and we're
1665       passing a filename to clamd). */
1666
1667       if (!(*av_buffer))
1668         return clmd_errlog_defer("ClamAV returned null");
1669
1670       /* strip newline at the end (won't be present for zINSTREAM)
1671       (also any trailing whitespace, which shouldn't exist, but we depend upon
1672       this below, so double-check) */
1673       p = av_buffer + Ustrlen(av_buffer) - 1;
1674       if (*p == '\n') *p = '\0';
1675
1676       DEBUG(D_acl) debug_printf("Malware response: %s\n", av_buffer);
1677
1678       while (isspace(*--p) && (p > av_buffer))
1679         *p = '\0';
1680       if (*p) ++p;
1681       response_end = p;
1682
1683       /* colon in returned output? */
1684       if((p = Ustrchr(av_buffer,':')) == NULL)
1685         return clmd_errlog_defer(
1686           string_sprintf("ClamAV returned malformed result (missing colon): %s",
1687                   av_buffer));
1688
1689       /* strip filename */
1690       while (*p && isspace(*++p)) /**/;
1691       vname = p;
1692
1693       /* It would be bad to encounter a virus with "FOUND" in part of the name,
1694       but we should at least be resistant to it. */
1695       p = Ustrrchr(vname, ' ');
1696       result_tag = p ? p+1 : vname;
1697
1698       if (Ustrcmp(result_tag, "FOUND") == 0) {
1699         /* p should still be the whitespace before the result_tag */
1700         while (isspace(*p)) --p;
1701         *++p = '\0';
1702         /* Strip off the extended information too, which will be in parens
1703         after the virus name, with no intervening whitespace. */
1704         if (*--p == ')') {
1705           /* "(hash:size)", so previous '(' will do; if not found, we have
1706           a curious virus name, but not an error. */
1707           p = Ustrrchr(vname, '(');
1708           if (p)
1709             *p = '\0';
1710         }
1711         malware_name_internal = string_copy(vname);
1712         malware_name = malware_name_internal;
1713         DEBUG(D_acl) debug_printf("Malware found, name \"%s\"\n", malware_name);
1714
1715       } else if (Ustrcmp(result_tag, "ERROR") == 0)
1716         return clmd_errlog_defer(
1717           string_sprintf("ClamAV returned: %s", av_buffer));
1718
1719       else if (Ustrcmp(result_tag, "OK") == 0) {
1720         /* Everything should be OK */
1721         malware_name = NULL;
1722         DEBUG(D_acl) debug_printf("Malware not found\n");
1723
1724       } else
1725         return clmd_errlog_defer(
1726           string_sprintf("unparseable response from ClamAV: {%s}", av_buffer));
1727
1728     } /* clamd */
1729
1730     /* ----------------------------------------------------------------------- */
1731
1732
1733     /* "mksd" scanner type --------------------------------------------------- */
1734     else if (strcmpic(scanner_name,US"mksd") == 0) {
1735       uschar *mksd_options;
1736       char *mksd_options_end;
1737       uschar mksd_options_buffer[32];
1738       int mksd_maxproc = 1;  /* default, if no option supplied */
1739       struct sockaddr_un server;
1740       int sock;
1741       int retval;
1742
1743       if ((mksd_options = string_nextinlist(&av_scanner_work, &sep,
1744                                             mksd_options_buffer,
1745                                             sizeof(mksd_options_buffer))) != NULL) {
1746         mksd_maxproc = (int) strtol(CS mksd_options, &mksd_options_end, 10);
1747         if ((*mksd_options == '\0') || (*mksd_options_end != '\0') ||
1748       (mksd_maxproc < 1) || (mksd_maxproc > 32))
1749           return mksd_errlog_defer(
1750             string_sprintf("invalid option '%s'", mksd_options));
1751       }
1752
1753       /* open the mksd socket */
1754       sock = socket(AF_UNIX, SOCK_STREAM, 0);
1755       if (sock < 0)
1756         return mksd_errlog_defer("can't open UNIX socket.");
1757
1758       server.sun_family = AF_UNIX;
1759       Ustrcpy(server.sun_path, "/var/run/mksd/socket");
1760       if (connect(sock, (struct sockaddr *) &server, sizeof(struct sockaddr_un)) < 0) {
1761         int err = errno;
1762         (void)close(sock);
1763         return mksd_errlog_defer(
1764           string_sprintf("unable to connect to mksd UNIX socket (/var/run/mksd/socket). errno=%d", err));
1765       }
1766
1767       malware_name = NULL;
1768
1769       DEBUG(D_acl) debug_printf("Malware scan: issuing %s scan\n", scanner_name);
1770
1771       retval = mksd_scan_packed(sock, eml_filename);
1772
1773       if (retval != OK)
1774         return retval;
1775     }
1776     /* ----------------------------------------------------------------------- */
1777
1778     /* "unknown" scanner type ------------------------------------------------- */
1779     else
1780       return malware_errlog_defer(string_sprintf("unknown scanner type '%s'",
1781         scanner_name));
1782     /* ----------------------------------------------------------------------- */
1783
1784     /* set "been here, done that" marker */
1785     malware_ok = 1;
1786   };
1787
1788   /* match virus name against pattern (caseless ------->----------v) */
1789   if ( (malware_name != NULL) &&
1790        (regex_match_and_setup(re, malware_name, 0, -1)) ) {
1791     DEBUG(D_acl) debug_printf("Matched regex to malware [%s] [%s]\n", malware_regex, malware_name);
1792     return OK;
1793   }
1794   else {
1795     return FAIL;
1796   };
1797 }
1798
1799
1800 /* simple wrapper for reading lines from sockets */
1801 int
1802 recv_line(int sock, uschar *buffer, int size)
1803 {
1804   uschar *p = buffer;
1805
1806   memset(buffer,0,size);
1807   /* read until \n */
1808   while(recv(sock,p,1,0) > -1) {
1809     if ((p-buffer) > (size-2)) break;
1810     if (*p == '\n') break;
1811     if (*p != '\r') p++;
1812   };
1813   *p = '\0';
1814
1815   return (p-buffer);
1816 }
1817
1818
1819 /* ============= private routines for the "mksd" scanner type ============== */
1820
1821 #include <sys/uio.h>
1822
1823 static int
1824 mksd_writev (int sock, struct iovec *iov, int iovcnt)
1825 {
1826   int i;
1827
1828   for (;;) {
1829     do
1830       i = writev (sock, iov, iovcnt);
1831     while ((i < 0) && (errno == EINTR));
1832     if (i <= 0) {
1833       close (sock);
1834       malware_errlog("unable to write to mksd UNIX socket (/var/run/mksd/socket)");
1835       return -1;
1836     }
1837
1838     for (;;)
1839       if (i >= iov->iov_len) {
1840         if (--iovcnt == 0)
1841           return 0;
1842         i -= iov->iov_len;
1843         iov++;
1844       } else {
1845         iov->iov_len -= i;
1846         iov->iov_base = CS iov->iov_base + i;
1847         break;
1848       }
1849   }
1850 }
1851
1852 static int
1853 mksd_read_lines (int sock, uschar *av_buffer, int av_buffer_size)
1854 {
1855   int offset = 0;
1856   int i;
1857
1858   do {
1859     if ((i = recv (sock, av_buffer+offset, av_buffer_size-offset, 0)) <= 0) {
1860       close (sock);
1861       malware_errlog("unable to read from mksd UNIX socket (/var/run/mksd/socket)");
1862       return -1;
1863     }
1864
1865     offset += i;
1866     /* offset == av_buffer_size -> buffer full */
1867     if (offset == av_buffer_size) {
1868       close (sock);
1869       malware_errlog("malformed reply received from mksd");
1870       return -1;
1871     }
1872   } while (av_buffer[offset-1] != '\n');
1873
1874   av_buffer[offset] = '\0';
1875   return offset;
1876 }
1877
1878 static int
1879 mksd_parse_line (char *line)
1880 {
1881   char *p;
1882
1883   switch (*line) {
1884     case 'O': /* OK */
1885       return OK;
1886
1887     case 'E':
1888     case 'A': /* ERR */
1889       if ((p = strchr (line, '\n')) != NULL)
1890         *p = '\0';
1891       return mksd_errlog_defer(string_sprintf("scanner failed: %s", line));
1892
1893     default: /* VIR */
1894       if ((p = strchr (line, '\n')) != NULL) {
1895         *p = '\0';
1896         if (((p-line) > 5) && (line[3] == ' '))
1897           if (((p = strchr (line+4, ' ')) != NULL) && ((p-line) > 4)) {
1898             *p = '\0';
1899             malware_name_internal = string_copy(line+4);
1900             malware_name = malware_name_internal;
1901             return OK;
1902           }
1903       }
1904       return mksd_errlog_defer(
1905         string_sprintf("malformed reply received from mksd: %s", line));
1906   }
1907 }
1908
1909 static int
1910 mksd_scan_packed(int sock, uschar *scan_filename)
1911 {
1912   struct iovec iov[3];
1913   const char *cmd = "MSQ\n";
1914   uschar av_buffer[1024];
1915
1916   iov[0].iov_base = (void *) cmd;
1917   iov[0].iov_len = 3;
1918   iov[1].iov_base = CS scan_filename;
1919   iov[1].iov_len = Ustrlen(scan_filename);
1920   iov[2].iov_base = (void *) (cmd + 3);
1921   iov[2].iov_len = 1;
1922
1923   if (mksd_writev (sock, iov, 3) < 0)
1924     return DEFER;
1925
1926   if (mksd_read_lines (sock, av_buffer, sizeof (av_buffer)) < 0)
1927     return DEFER;
1928
1929   close (sock);
1930
1931   return mksd_parse_line (CS av_buffer);
1932 }
1933
1934 #endif /*WITH_CONTENT_SCAN*/