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