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