Clean up compiler warnings from { gcc -Wall }, many of which I introduced with
[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       p = Ustrrchr(eml_filename, '/');
1102       if (p)
1103         *p = '\0';
1104       fits = string_format(commandline, sizeof(commandline), CS cmdline_scanner, file_name);
1105       if (!fits)
1106         {
1107         log_write(0, LOG_MAIN|LOG_PANIC,
1108             "cmdline scanner command-line does not fit in buffer");
1109         return DEFER;
1110         }
1111
1112       /* redirect STDERR too */
1113       if (Ustrlen(commandline) + 5 > sizeof(commandline))
1114         {
1115         log_write(0, LOG_MAIN|LOG_PANIC,
1116             "cmdline scanner command-line does not fit in buffer (STDERR redirect)");
1117         return DEFER;
1118         }
1119       Ustrcat(commandline," 2>&1");
1120
1121       DEBUG(D_acl) debug_printf("Malware scan: issuing %s scan [%s]\n", scanner_name, commandline);
1122
1123       /* store exims signal handlers */
1124       eximsigchld = signal(SIGCHLD,SIG_DFL);
1125       eximsigpipe = signal(SIGPIPE,SIG_DFL);
1126
1127       scanner_out = popen(CS commandline,"r");
1128       if (scanner_out == NULL) {
1129         log_write(0, LOG_MAIN|LOG_PANIC,
1130                  "malware acl condition: calling cmdline scanner (%s) failed: %s.", commandline, strerror(errno));
1131         signal(SIGCHLD,eximsigchld);
1132         signal(SIGPIPE,eximsigpipe);
1133         return DEFER;
1134       };
1135
1136       (void)string_format(file_name,1024,"%s/scan/%s/%s_scanner_output", spool_directory, message_id, message_id);
1137       scanner_record = modefopen(file_name,"wb",SPOOL_MODE);
1138
1139       if (scanner_record == NULL) {
1140         log_write(0, LOG_MAIN|LOG_PANIC,
1141                  "malware acl condition: opening scanner output file (%s) failed: %s.", file_name, strerror(errno));
1142         pclose(scanner_out);
1143         signal(SIGCHLD,eximsigchld);
1144         signal(SIGPIPE,eximsigpipe);
1145         return DEFER;
1146       };
1147
1148       /* look for trigger while recording output */
1149       while(fgets(CS linebuffer,32767,scanner_out) != NULL) {
1150         if ( Ustrlen(linebuffer) > fwrite(linebuffer, 1, Ustrlen(linebuffer), scanner_record) ) {
1151           /* short write */
1152           log_write(0, LOG_MAIN|LOG_PANIC,
1153                  "malware acl condition: short write on scanner output file (%s).", file_name);
1154           pclose(scanner_out);
1155           signal(SIGCHLD,eximsigchld);
1156           signal(SIGPIPE,eximsigpipe);
1157           return DEFER;
1158         };
1159         /* try trigger match */
1160         if (!trigger && regex_match_and_setup(cmdline_trigger_re, linebuffer, 0, -1))
1161           trigger = 1;
1162       };
1163
1164       (void)fclose(scanner_record);
1165       pclose(scanner_out);
1166       signal(SIGCHLD,eximsigchld);
1167       signal(SIGPIPE,eximsigpipe);
1168
1169       if (trigger) {
1170         /* setup default virus name */
1171         Ustrcpy(malware_name_buffer,"unknown");
1172         malware_name = malware_name_buffer;
1173
1174         /* re-open the scanner output file, look for name match */
1175         scanner_record = fopen(CS file_name,"rb");
1176         while(fgets(CS linebuffer,32767,scanner_record) != NULL) {
1177           /* try match */
1178           result = pcre_exec(cmdline_regex_re, NULL, CS linebuffer, Ustrlen(linebuffer), 0, 0, ovector, 30);
1179           if (result >= 2) {
1180             pcre_copy_substring(CS linebuffer, ovector, result, 1, CS malware_name_buffer, 255);
1181           };
1182         };
1183         (void)fclose(scanner_record);
1184       }
1185       else {
1186         /* no virus found */
1187         malware_name = NULL;
1188       };
1189     }
1190     /* ----------------------------------------------------------------------- */
1191
1192
1193     /* "sophie" scanner type ------------------------------------------------- */
1194     else if (strcmpic(scanner_name,US"sophie") == 0) {
1195       uschar *sophie_options;
1196       uschar sophie_options_buffer[1024];
1197       uschar sophie_options_default[] = "/var/run/sophie";
1198       int bread = 0;
1199       struct sockaddr_un server;
1200       int sock, len;
1201       uschar *p;
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 = Ustrrchr(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       int clam_fd, result;
1314       unsigned int fsize;
1315       BOOL use_scan_command, fits;
1316 #ifdef WITH_OLD_CLAMAV_STREAM
1317       uschar av_buffer2[1024];
1318       int sockData;
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