Merge from EXISCAN branch.
[exim.git] / src / src / malware.c
1 /* $Cambridge: exim/src/src/malware.c,v 1.2 2004/12/16 15:11:47 tom 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 int mksd_scan_packed(int sock);
17
18 /* SHUT_WR seems to be undefined on Unixware? */
19 #ifndef SHUT_WR
20 #define SHUT_WR 1
21 #endif
22
23 #define DRWEBD_SCAN_CMD             (1)     /* scan file, buffer or diskfile */
24 #define DRWEBD_RETURN_VIRUSES       (1<<0)   /* ask daemon return to us viruses names from report */
25 #define DRWEBD_IS_MAIL              (1<<19)  /* say to daemon that format is "archive MAIL" */
26
27 /* Routine to check whether a system is big- or litte-endian. 
28    Ripped from http://www.faqs.org/faqs/graphics/fileformats-faq/part4/section-7.html
29    Needed for proper kavdaemon implementation. Sigh. */
30 #define BIG_MY_ENDIAN      0
31 #define LITTLE_MY_ENDIAN   1
32 int test_byte_order(void);
33 int test_byte_order() {
34       short int word = 0x0001;
35       char *byte = (char *) &word;
36       return(byte[0] ? LITTLE_MY_ENDIAN : BIG_MY_ENDIAN);
37 }
38
39 uschar malware_name_buffer[256];
40 int malware_ok = 0;
41
42 int malware(uschar **listptr) {
43   int sep = 0;
44   uschar *list = *listptr;
45   uschar *av_scanner_work = av_scanner;
46   uschar *scanner_name;
47   uschar scanner_name_buffer[16];
48   uschar *malware_regex;
49   uschar malware_regex_buffer[64];
50   uschar malware_regex_default[] = ".+";
51   unsigned long long mbox_size;
52   FILE *mbox_file;
53   int roffset;
54   const pcre *re;
55   const uschar *rerror;
56   
57   /* make sure the eml mbox file is spooled up */
58   mbox_file = spool_mbox(&mbox_size);
59   if (mbox_file == NULL) {
60     /* error while spooling */
61     log_write(0, LOG_MAIN|LOG_PANIC,
62            "malware acl condition: error while creating mbox spool file");
63     return DEFER;  
64   };
65   /* none of our current scanners need the mbox
66      file as a stream, so we can close it right away */
67   fclose(mbox_file);
68   
69   /* extract the malware regex to match against from the option list */
70   if ((malware_regex = string_nextinlist(&list, &sep,
71                                          malware_regex_buffer,
72                                          sizeof(malware_regex_buffer))) != NULL) {
73     
74     /* parse 1st option */
75     if ( (strcmpic(malware_regex,US"false") == 0) || 
76          (Ustrcmp(malware_regex,"0") == 0) ) {
77       /* explicitly no matching */
78       return FAIL;
79     };
80     
81     /* special cases (match anything except empty) */
82     if ( (strcmpic(malware_regex,US"true") == 0) || 
83          (Ustrcmp(malware_regex,"*") == 0) || 
84          (Ustrcmp(malware_regex,"1") == 0) ) {
85       malware_regex = malware_regex_default;
86     };
87   }
88   else {
89     /* empty means "don't match anything" */
90     return FAIL;
91   };
92
93   /* compile the regex, see if it works */
94   re = pcre_compile(CS malware_regex, PCRE_COPT, (const char **)&rerror, &roffset, NULL);
95   if (re == NULL) {
96     log_write(0, LOG_MAIN|LOG_PANIC,
97              "malware acl condition: regular expression error in '%s': %s at offset %d", malware_regex, rerror, roffset);
98     return DEFER;
99   };
100
101   /* if av_scanner starts with a dollar, expand it first */
102   if (*av_scanner == '$') {
103     av_scanner_work = expand_string(av_scanner);
104     if (av_scanner_work == NULL) {
105       log_write(0, LOG_MAIN|LOG_PANIC,
106            "malware acl condition: av_scanner starts with $, but expansion failed: %s", expand_string_message);
107       return DEFER;
108     }
109     else {
110       debug_printf("Expanded av_scanner global: %s\n", av_scanner_work);
111       /* disable result caching in this case */
112       malware_name = NULL;
113       malware_ok = 0;
114     };
115   }
116
117   /* Do not scan twice. */
118   if (malware_ok == 0) {
119
120     /* find the scanner type from the av_scanner option */
121     if ((scanner_name = string_nextinlist(&av_scanner_work, &sep,
122                                           scanner_name_buffer,
123                                           sizeof(scanner_name_buffer))) == NULL) {
124       /* no scanner given */
125       log_write(0, LOG_MAIN|LOG_PANIC,
126              "malware acl condition: av_scanner configuration variable is empty");
127       return DEFER;
128     };
129     
130         /* "drweb" scanner type ----------------------------------------------- */
131         /* v0.1 - added support for tcp sockets                                 */
132         /* v0.0 - initial release -- support for unix sockets                   */
133         if (strcmpic(scanner_name,US"drweb") == 0) {
134                 uschar *drweb_options;
135                 uschar drweb_options_buffer[1024];
136                 uschar drweb_options_default[] = "/usr/local/drweb/run/drwebd.sock";
137                 struct sockaddr_un server;
138                 int sock, port, result, ovector[30];
139                 unsigned int fsize;
140                 uschar tmpbuf[1024], *drweb_fbuf;
141                 uschar scanrequest[1024];
142                 uschar drweb_match_string[128];
143                 int drweb_rc, drweb_cmd, drweb_flags = 0x0000, drweb_fd,
144                     drweb_vnum, drweb_slen, drweb_fin = 0x0000;
145                 unsigned long bread;
146                 uschar hostname[256];
147                 struct hostent *he;
148                 struct in_addr in;
149                 pcre *drweb_re;
150       
151                 if ((drweb_options = string_nextinlist(&av_scanner_work, &sep,
152                         drweb_options_buffer, sizeof(drweb_options_buffer))) == NULL) {
153                         /* no options supplied, use default options */
154                         drweb_options = drweb_options_default;
155                 };
156   
157                 if (*drweb_options != '/') {
158       
159                         /* extract host and port part */
160                         if( sscanf(CS drweb_options, "%s %u", hostname, &port) != 2 ) {
161                                 log_write(0, LOG_MAIN|LOG_PANIC,
162                                         "malware acl condition: drweb: invalid socket '%s'", drweb_options);
163                                 return DEFER;
164                         }
165       
166                         /* Lookup the host */
167                         if((he = gethostbyname(CS hostname)) == 0) {
168                                 log_write(0, LOG_MAIN|LOG_PANIC,
169                                         "malware acl condition: drweb: failed to lookup host '%s'", hostname);
170                                 return DEFER;
171                         }
172       
173                         in = *(struct in_addr *) he->h_addr_list[0];
174       
175                         /* Open the drwebd TCP socket */
176                         if ( (sock = ip_socket(SOCK_STREAM, AF_INET)) < 0) {
177                                 log_write(0, LOG_MAIN|LOG_PANIC,
178                                         "malware acl condition: drweb: unable to acquire socket (%s)",
179                                         strerror(errno));
180                                 return DEFER;
181                         }
182       
183                         if (ip_connect(sock, AF_INET, (uschar*)inet_ntoa(in), port, 5) < 0) {
184                                 close(sock); 
185                                 log_write(0, LOG_MAIN|LOG_PANIC,
186                                         "malware acl condition: drweb: connection to %s, port %u failed (%s)",
187                                         inet_ntoa(in), port, strerror(errno));
188                                 return DEFER;
189                         }
190   
191                         /* prepare variables */
192                         drweb_cmd = htonl(DRWEBD_SCAN_CMD);
193                         drweb_flags = htonl(DRWEBD_RETURN_VIRUSES | DRWEBD_IS_MAIL);
194                         snprintf(CS scanrequest, 1024,CS"%s/scan/%s/%s.eml", 
195                                     spool_directory, message_id, message_id);
196   
197                         /* calc file size */
198                         drweb_fd = open(CS scanrequest, O_RDONLY);
199                         if (drweb_fd == -1) {
200                                 log_write(0, LOG_MAIN|LOG_PANIC,
201                                         "malware acl condition: drweb: can't open spool file %s: %s", 
202                                         scanrequest, strerror(errno));
203                                 return DEFER; 
204                         }
205                         fsize = lseek(drweb_fd, 0, SEEK_END);
206                         if (fsize == -1) {
207                                 log_write(0, LOG_MAIN|LOG_PANIC,
208                                         "malware acl condition: drweb: can't seek spool file %s: %s", 
209                                         scanrequest, strerror(errno));
210                                 return DEFER; 
211                         }
212                         drweb_slen = htonl(fsize);
213                         lseek(drweb_fd, 0, SEEK_SET);
214   
215                         /* send scan request */
216                         if ((send(sock, &drweb_cmd, sizeof(drweb_cmd), 0) < 0) || 
217                             (send(sock, &drweb_flags, sizeof(drweb_flags), 0) < 0) ||
218                             (send(sock, &drweb_fin, sizeof(drweb_fin), 0) < 0) ||
219                             (send(sock, &drweb_slen, sizeof(drweb_slen), 0) < 0)) {
220                                 close(sock); 
221                                 close(drweb_fd);
222                                 log_write(0, LOG_MAIN|LOG_PANIC,
223                                         "malware acl condition: drweb: unable to send commands to socket (%s)", drweb_options);
224                                 return DEFER;
225                         }
226   
227                         drweb_fbuf = (uschar *) malloc (fsize);
228                         if (!drweb_fbuf) {
229                                 close(sock);
230                                 close(drweb_fd);
231                                 log_write(0, LOG_MAIN|LOG_PANIC,
232                                         "malware acl condition: drweb: unable to allocate memory %u for file (%s)", 
233                                         fsize, scanrequest);
234                                 return DEFER;
235                         }
236   
237                         result = read (drweb_fd, drweb_fbuf, fsize);
238                         if (result == -1) {
239                                 close(sock);
240                                 close(drweb_fd);
241                                 log_write(0, LOG_MAIN|LOG_PANIC,
242                                         "malware acl condition: drweb: can't read spool file %s: %s",
243                                         scanrequest, strerror(errno));
244                                 return DEFER; 
245                         }
246                         
247                         /* send file body to socket */
248                         if (send(sock, drweb_fbuf, fsize, 0) < 0) {
249                                 close(sock);
250                                 log_write(0, LOG_MAIN|LOG_PANIC,
251                                         "malware acl condition: drweb: unable to send file body to socket (%s)", drweb_options);
252                                 return DEFER;
253                         }
254                         close(drweb_fd);
255                         free(drweb_fbuf);
256                 }
257                 else {
258                         /* open the drwebd UNIX socket */
259                         sock = socket(AF_UNIX, SOCK_STREAM, 0);
260                         if (sock < 0) {
261                                 log_write(0, LOG_MAIN|LOG_PANIC,
262                                         "malware acl condition: drweb: can't open UNIX socket");
263                                 return DEFER; 
264                         }
265                         server.sun_family = AF_UNIX;
266                         Ustrcpy(server.sun_path, drweb_options);
267                         if (connect(sock, (struct sockaddr *) &server, sizeof(struct sockaddr_un)) < 0) {
268                                 close(sock);
269                                 log_write(0, LOG_MAIN|LOG_PANIC,
270                                         "malware acl condition: drweb: unable to connect to socket (%s). errno=%d", drweb_options, errno);
271                                 return DEFER;
272                         }
273         
274                         /* prepare variables */
275                         drweb_cmd = htonl(DRWEBD_SCAN_CMD);
276                         drweb_flags = htonl(DRWEBD_RETURN_VIRUSES | DRWEBD_IS_MAIL);
277                         snprintf(CS scanrequest, 1024,CS"%s/scan/%s/%s.eml", spool_directory, message_id, message_id);
278                         drweb_slen = htonl(Ustrlen(scanrequest));
279   
280                         /* send scan request */
281                         if ((send(sock, &drweb_cmd, sizeof(drweb_cmd), 0) < 0) || 
282                             (send(sock, &drweb_flags, sizeof(drweb_flags), 0) < 0) ||
283                             (send(sock, &drweb_slen, sizeof(drweb_slen), 0) < 0) ||
284                             (send(sock, scanrequest, Ustrlen(scanrequest), 0) < 0) ||
285                             (send(sock, &drweb_fin, sizeof(drweb_fin), 0) < 0)) {
286                                 close(sock);
287                                 log_write(0, LOG_MAIN|LOG_PANIC,
288                                         "malware acl condition: drweb: unable to send commands to socket (%s)", drweb_options);
289                                 return DEFER;
290                         }
291                 }
292   
293                 /* wait for result */
294                 if ((bread = recv(sock, &drweb_rc, sizeof(drweb_rc), 0) != sizeof(drweb_rc))) {
295                         close(sock);
296                         log_write(0, LOG_MAIN|LOG_PANIC,
297                                 "malware acl condition: drweb: unable to read return code");
298                         return DEFER;
299                 }
300                 drweb_rc = ntohl(drweb_rc);
301       
302                 if ((bread = recv(sock, &drweb_vnum, sizeof(drweb_vnum), 0) != sizeof(drweb_vnum))) {
303                         close(sock);
304                         log_write(0, LOG_MAIN|LOG_PANIC,
305                                 "malware acl condition: drweb: unable to read the number of viruses");
306                         return DEFER;
307                 }
308                 drweb_vnum = ntohl(drweb_vnum);
309                 
310                 /* "virus(es) found" if virus number is > 0 */
311                 if (drweb_vnum)
312                 {
313                         int i;
314                         uschar pre_malware_nb[256];
315                         
316                         malware_name = malware_name_buffer;
317                         
318                         /* setup default virus name */
319                         Ustrcpy(malware_name_buffer,"unknown");
320                         
321                         /* read and concatenate virus names into one string */
322                         for (i=0;i<drweb_vnum;i++)
323                         {
324                                 /* read the size of report */
325                                 if ((bread = recv(sock, &drweb_slen, sizeof(drweb_slen), 0) != sizeof(drweb_slen))) {
326                                         close(sock);
327                                         log_write(0, LOG_MAIN|LOG_PANIC,
328                                                 "malware acl condition: drweb: cannot read report size");
329                                         return DEFER;
330                                 };
331                                 drweb_slen = ntohl(drweb_slen);
332                         
333                                 /* read report body */
334                                 if ((bread = recv(sock, tmpbuf, drweb_slen, 0)) != drweb_slen) {
335                                         close(sock);
336                                         log_write(0, LOG_MAIN|LOG_PANIC,
337                                                 "malware acl condition: drweb: cannot read report string");
338                                         return DEFER;
339                                 };
340                                 tmpbuf[drweb_slen] = '\0';
341   
342                                 /* set up match regex, depends on retcode */
343                                 Ustrcpy(drweb_match_string, "infected\\swith\\s*(.+?)$");
344   
345                                 drweb_re = pcre_compile( CS drweb_match_string,
346                                         PCRE_COPT,
347                                         (const char **)&rerror,
348                                         &roffset,
349                                         NULL );
350               
351                                 /* try matcher on the line, grab substring */
352                                 result = pcre_exec(drweb_re, NULL, CS tmpbuf, Ustrlen(tmpbuf), 0, 0, ovector, 30);
353                                 if (result >= 2) {
354                                         pcre_copy_substring(CS tmpbuf, ovector, result, 1, CS pre_malware_nb, 255);
355                                 }
356                                 /* the first name we just copy to malware_name */
357                                 if (i==0)
358                                         Ustrcpy(CS malware_name_buffer, CS pre_malware_nb);
359                                 else {
360                                         /* concatenate each new virus name to previous */
361                                         int slen = Ustrlen(malware_name_buffer);
362                                         if (slen < (slen+Ustrlen(pre_malware_nb))) {
363                                                 Ustrcat(malware_name_buffer, "/");
364                                                 Ustrcat(malware_name_buffer, pre_malware_nb);
365                                         }
366                                 }
367                         }
368                 }
369                 else {
370                         /* no virus found */
371                         malware_name = NULL;
372                 };
373                 close(sock);
374         }
375         /* ----------------------------------------------------------------------- */
376     else if (strcmpic(scanner_name,US"aveserver") == 0) {
377       uschar *kav_options;
378       uschar kav_options_buffer[1024];
379       uschar kav_options_default[] = "/var/run/aveserver";
380       uschar buf[32768];
381       uschar *p;
382       struct sockaddr_un server;
383       int sock;
384     
385       if ((kav_options = string_nextinlist(&av_scanner_work, &sep,
386                                            kav_options_buffer,
387                                            sizeof(kav_options_buffer))) == NULL) {
388         /* no options supplied, use default options */
389         kav_options = kav_options_default;
390       };
391     
392       /* open the aveserver socket */
393       sock = socket(AF_UNIX, SOCK_STREAM, 0);
394       if (sock < 0) {
395         log_write(0, LOG_MAIN|LOG_PANIC,
396              "malware acl condition: can't open UNIX socket.");
397         return DEFER; 
398       }
399       server.sun_family = AF_UNIX;
400       Ustrcpy(server.sun_path, kav_options);
401       if (connect(sock, (struct sockaddr *) &server, sizeof(struct sockaddr_un)) < 0) {
402         close(sock);
403         log_write(0, LOG_MAIN|LOG_PANIC,
404              "malware acl condition: unable to connect to aveserver UNIX socket (%s). errno=%d", kav_options, errno);
405         return DEFER;
406       }
407     
408       /* read aveserver's greeting and see if it is ready (2xx greeting) */
409       recv_line(sock, buf, 32768);
410
411       if (buf[0] != '2') {
412         /* aveserver is having problems */
413         close(sock);
414         log_write(0, LOG_MAIN|LOG_PANIC,
415              "malware acl condition: aveserver is unavailable (Responded: %s).", ((buf[0] != 0) ? buf : (uschar *)"nothing") );
416         return DEFER;
417       };
418       
419       /* prepare our command */
420       snprintf(CS buf, 32768, "SCAN bPQRSTUW %s/scan/%s/%s.eml\r\n", spool_directory, message_id, message_id);
421       
422       /* and send it */
423       if (send(sock, buf, Ustrlen(buf), 0) < 0) {
424         close(sock);
425         log_write(0, LOG_MAIN|LOG_PANIC,
426              "malware acl condition: unable to write to aveserver UNIX socket (%s)", kav_options);
427         return DEFER;
428       }
429       
430       malware_name = NULL;
431       /* read response lines, find malware name and final response */
432       while (recv_line(sock, buf, 32768) > 0) {
433         debug_printf("aveserver: %s\n", buf);
434         if (buf[0] == '2') break;
435         if (Ustrncmp(buf,"322",3) == 0) {
436           uschar *p = Ustrchr(&buf[4],' ');
437           *p = '\0';
438           Ustrcpy(malware_name_buffer,&buf[4]);
439           malware_name = malware_name_buffer;
440         };
441       }
442
443       close(sock);
444     }
445     /* "fsecure" scanner type ------------------------------------------------- */
446     else if (strcmpic(scanner_name,US"fsecure") == 0) {
447       uschar *fsecure_options;
448       uschar fsecure_options_buffer[1024];
449       uschar fsecure_options_default[] = "/var/run/.fsav";
450       struct sockaddr_un server;
451       int sock, i, j, bread = 0;
452       uschar file_name[1024];
453       uschar av_buffer[1024];
454       pcre *fs_inf;
455       static uschar *cmdoptions[] = { "CONFIGURE\tARCHIVE\t1\n","CONFIGURE\tTIMEOUT\t0\n","CONFIGURE\tMAXARCH\t5\n","CONFIGURE\tMIME\t1\n" };
456       
457       malware_name = NULL;
458       if ((fsecure_options = string_nextinlist(&av_scanner_work, &sep,
459                                                fsecure_options_buffer,
460                                                sizeof(fsecure_options_buffer))) == NULL) { 
461          /* no options supplied, use default options */
462          fsecure_options = fsecure_options_default;
463       };
464      
465       /* open the fsecure socket */
466       sock = socket(AF_UNIX, SOCK_STREAM, 0);
467       if (sock < 0) {
468         log_write(0, LOG_MAIN|LOG_PANIC,
469                   "malware acl condition: unable to open fsecure socket %s (%s)",
470                   fsecure_options, strerror(errno));
471         return DEFER;
472       }
473       server.sun_family = AF_UNIX;
474       Ustrcpy(server.sun_path, fsecure_options);
475       if (connect(sock, (struct sockaddr *) &server, sizeof(struct sockaddr_un)) < 0) {
476         close(sock);
477         log_write(0, LOG_MAIN|LOG_PANIC,
478                   "malware acl condition: unable to connect to fsecure socket %s (%s)",
479                   fsecure_options, strerror(errno));
480         return DEFER;
481       }
482        
483       /* pass options */
484       memset(av_buffer, 0, sizeof(av_buffer));
485       for (i=0; i != 4; i++) {
486         /* debug_printf("send option \"%s\"",cmdoptions[i]); */
487         if (write(sock, cmdoptions[i], Ustrlen(cmdoptions[i])) < 0) {
488           close(sock);
489           log_write(0, LOG_MAIN|LOG_PANIC,
490                     "malware acl condition: unable to write fsecure option %d to %s (%s)",
491                     i, fsecure_options, strerror(errno));
492           return DEFER; 
493         };
494        
495         bread = read(sock, av_buffer, sizeof(av_buffer));
496         if (bread >0) av_buffer[bread]='\0';
497         if (bread < 0) {
498           close(sock);
499           log_write(0, LOG_MAIN|LOG_PANIC,
500                     "malware acl condition: unable to read fsecure answer %d (%s)", i, strerror(errno));
501           return DEFER;
502         };
503         for (j=0;j<bread;j++) if((av_buffer[j]=='\r')||(av_buffer[j]=='\n')) av_buffer[j] ='@';
504         /* debug_printf("read answer %d read=%d \"%s\"\n", i, bread, av_buffer ); */
505         /* while (Ustrstr(av_buffer, "OK\tServer configured.@") == NULL); */
506       };
507  
508       /* pass the mailfile to fsecure */
509       snprintf(CS file_name,1024,"SCAN\t%s/scan/%s/%s.eml\n", spool_directory, message_id, message_id);
510       /* debug_printf("send scan %s",file_name); */
511       if (write(sock, file_name, Ustrlen(file_name)) < 0) {
512         close(sock);
513         log_write(0, LOG_MAIN|LOG_PANIC,
514                   "malware acl condition: unable to write fsecure scan to %s (%s)",
515                   fsecure_options, strerror(errno));
516         return DEFER;
517       };
518        
519       /* set up match */
520       /* todo also SUSPICION\t */
521       fs_inf = pcre_compile("\\S{0,5}INFECTED\\t[^\\t]*\\t([^\\t]+)\\t\\S*$", PCRE_COPT, (const char **)&rerror, &roffset, NULL);
522  
523       /* read report, linewise */
524       do {
525         int ovector[30];
526         i = 0;
527         memset(av_buffer, 0, sizeof(av_buffer));
528         do {
529           bread=read(sock, &av_buffer[i], 1);
530           if (bread < 0) {
531             close(sock);
532             log_write(0, LOG_MAIN|LOG_PANIC,
533                       "malware acl condition: unable to read fsecure result (%s)", strerror(errno));
534             return DEFER;
535           };
536           i++;
537         }
538         while ((i < sizeof(av_buffer)-1 ) && (av_buffer[i-1] != '\n'));
539         av_buffer[i-1] = '\0';
540         /* debug_printf("got line \"%s\"\n",av_buffer); */
541          
542         /* Really search for virus again? */
543         if (malware_name == NULL) {
544           /* try matcher on the line, grab substring */
545           i = pcre_exec(fs_inf, NULL, CS av_buffer, Ustrlen(av_buffer), 0, 0, ovector, 30);
546           if (i >= 2) {
547             /* Got it */
548             pcre_copy_substring(CS av_buffer, ovector, i, 1, CS malware_name_buffer, 255);
549             malware_name = malware_name_buffer;
550           };
551         };
552       }
553       while (Ustrstr(av_buffer, "OK\tScan ok.") == NULL);
554       close(sock);      
555     }
556     /* ----------------------------------------------------------------------- */
557
558     /* "kavdaemon" scanner type ------------------------------------------------ */
559     else if (strcmpic(scanner_name,US"kavdaemon") == 0) {
560       uschar *kav_options;
561       uschar kav_options_buffer[1024];
562       uschar kav_options_default[] = "/var/run/AvpCtl";
563       struct sockaddr_un server;
564       int sock;
565       time_t t;
566       uschar tmpbuf[1024];
567       uschar scanrequest[1024];
568       uschar kav_match_string[128];
569       int kav_rc;
570       unsigned long kav_reportlen, bread;
571       pcre *kav_re;
572     
573       if ((kav_options = string_nextinlist(&av_scanner_work, &sep,
574                                            kav_options_buffer,
575                                            sizeof(kav_options_buffer))) == NULL) {
576         /* no options supplied, use default options */
577         kav_options = kav_options_default;
578       };
579     
580       /* open the kavdaemon socket */
581       sock = socket(AF_UNIX, SOCK_STREAM, 0);
582       if (sock < 0) {
583         log_write(0, LOG_MAIN|LOG_PANIC,
584              "malware acl condition: can't open UNIX socket.");
585         return DEFER; 
586       }
587       server.sun_family = AF_UNIX;
588       Ustrcpy(server.sun_path, kav_options);
589       if (connect(sock, (struct sockaddr *) &server, sizeof(struct sockaddr_un)) < 0) {
590         close(sock);
591         log_write(0, LOG_MAIN|LOG_PANIC,
592              "malware acl condition: unable to connect to kavdaemon UNIX socket (%s). errno=%d", kav_options, errno);
593         return DEFER;
594       }
595       
596       /* get current date and time, build scan request */
597       time(&t);
598       strftime(CS tmpbuf, sizeof(tmpbuf), "<0>%d %b %H:%M:%S:%%s/scan/%%s", localtime(&t));
599       snprintf(CS scanrequest, 1024,CS tmpbuf, spool_directory, message_id);
600       
601       /* send scan request */
602       if (send(sock, scanrequest, Ustrlen(scanrequest)+1, 0) < 0) {
603         close(sock);
604         log_write(0, LOG_MAIN|LOG_PANIC,
605              "malware acl condition: unable to write to kavdaemon UNIX socket (%s)", kav_options);
606         return DEFER;
607       }
608       
609       /* wait for result */
610       if ((bread = recv(sock, tmpbuf, 2, 0) != 2)) {
611         close(sock);
612         log_write(0, LOG_MAIN|LOG_PANIC,
613              "malware acl condition: unable to read 2 bytes from kavdaemon socket.");
614         return DEFER;
615       }
616     
617       /* get errorcode from one nibble */
618       if (test_byte_order() == LITTLE_MY_ENDIAN) {
619         kav_rc = tmpbuf[0] & 0x0F;
620       }
621       else {
622         kav_rc = tmpbuf[1] & 0x0F;
623       };
624     
625       /* improper kavdaemon configuration */
626       if ( (kav_rc == 5) || (kav_rc == 6) ) {
627         close(sock);
628         log_write(0, LOG_MAIN|LOG_PANIC,
629              "malware acl condition: please reconfigure kavdaemon to NOT disinfect or remove infected files.");
630         return DEFER;
631       };
632       
633       if (kav_rc == 1) {
634         close(sock);
635         log_write(0, LOG_MAIN|LOG_PANIC,
636              "malware acl condition: kavdaemon reported 'scanning not completed' (code 1).");
637         return DEFER;
638       };
639     
640       if (kav_rc == 7) {
641         close(sock);
642         log_write(0, LOG_MAIN|LOG_PANIC,
643              "malware acl condition: kavdaemon reported 'kavdaemon damaged' (code 7).");
644         return DEFER;
645       };
646     
647       /* code 8 is not handled, since it is ambigous. It appears mostly on
648       bounces where part of a file has been cut off */
649     
650       /* "virus found" return codes (2-4) */
651       if ((kav_rc > 1) && (kav_rc < 5)) {
652         int report_flag = 0;
653         
654         /* setup default virus name */
655         Ustrcpy(malware_name_buffer,"unknown");
656         malware_name = malware_name_buffer;
657       
658         if (test_byte_order() == LITTLE_MY_ENDIAN) {
659           report_flag = tmpbuf[1];
660         }
661         else {
662           report_flag = tmpbuf[0];
663         };
664       
665         /* read the report, if available */
666         if( report_flag == 1 ) {
667           /* read report size */
668           if ((bread = recv(sock, &kav_reportlen, 4, 0)) != 4) {
669             close(sock);
670             log_write(0, LOG_MAIN|LOG_PANIC,
671                   "malware acl condition: cannot read report size from kavdaemon");
672             return DEFER;
673           };
674   
675           /* it's possible that avp returns av_buffer[1] == 1 but the
676           reportsize is 0 (!?) */
677           if (kav_reportlen > 0) {
678             /* set up match regex, depends on retcode */
679             if( kav_rc == 3 )
680               Ustrcpy(kav_match_string, "suspicion:\\s*(.+?)\\s*$");
681             else
682               Ustrcpy(kav_match_string, "infected:\\s*(.+?)\\s*$");
683   
684             kav_re = pcre_compile( CS kav_match_string,
685                                    PCRE_COPT,
686                                    (const char **)&rerror,
687                                    &roffset,
688                                    NULL );
689             
690             /* read report, linewise */  
691             while (kav_reportlen > 0) {
692               int result = 0;
693               int ovector[30];
694               
695               bread = 0;
696               while ( recv(sock, &tmpbuf[bread], 1, 0) == 1 ) {
697                 kav_reportlen--;
698                 if ( (tmpbuf[bread] == '\n') || (bread > 1021) ) break;
699                 bread++;
700               };
701               bread++;
702               tmpbuf[bread] = '\0';
703               
704               /* try matcher on the line, grab substring */
705               result = pcre_exec(kav_re, NULL, CS tmpbuf, Ustrlen(tmpbuf), 0, 0, ovector, 30);
706               if (result >= 2) {
707                 pcre_copy_substring(CS tmpbuf, ovector, result, 1, CS malware_name_buffer, 255);
708                 break;
709               };
710             };
711           };
712         };
713       }
714       else {
715         /* no virus found */
716         malware_name = NULL;
717       };
718     
719       close(sock);
720     }
721     /* ----------------------------------------------------------------------- */
722     
723     
724     /* "cmdline" scanner type ------------------------------------------------ */
725     else if (strcmpic(scanner_name,US"cmdline") == 0) {
726       uschar *cmdline_scanner;
727       uschar cmdline_scanner_buffer[1024];
728       uschar *cmdline_trigger;
729       uschar cmdline_trigger_buffer[1024];
730       const pcre *cmdline_trigger_re;
731       uschar *cmdline_regex;
732       uschar cmdline_regex_buffer[1024];
733       const pcre *cmdline_regex_re;
734       uschar file_name[1024];
735       uschar commandline[1024];
736       void (*eximsigchld)(int);
737       void (*eximsigpipe)(int);
738       FILE *scanner_out = NULL;
739       FILE *scanner_record = NULL;
740       uschar linebuffer[32767];
741       int trigger = 0;
742       int result;
743       int ovector[30];
744       
745       /* find scanner command line */
746       if ((cmdline_scanner = string_nextinlist(&av_scanner_work, &sep,
747                                           cmdline_scanner_buffer,
748                                           sizeof(cmdline_scanner_buffer))) == NULL) {
749         /* no command line supplied */
750         log_write(0, LOG_MAIN|LOG_PANIC,
751              "malware acl condition: missing commandline specification for cmdline scanner type.");
752         return DEFER; 
753       };
754     
755       /* find scanner output trigger */
756       if ((cmdline_trigger = string_nextinlist(&av_scanner_work, &sep,
757                                           cmdline_trigger_buffer,
758                                           sizeof(cmdline_trigger_buffer))) == NULL) {
759         /* no trigger regex supplied */
760         log_write(0, LOG_MAIN|LOG_PANIC,
761              "malware acl condition: missing trigger specification for cmdline scanner type.");
762         return DEFER; 
763       };
764     
765       /* precompile trigger regex */
766       cmdline_trigger_re = pcre_compile(CS cmdline_trigger, PCRE_COPT, (const char **)&rerror, &roffset, NULL);
767       if (cmdline_trigger_re == NULL) {
768         log_write(0, LOG_MAIN|LOG_PANIC,
769                  "malware acl condition: regular expression error in '%s': %s at offset %d", cmdline_trigger_re, rerror, roffset);
770         return DEFER;
771       };
772     
773       /* find scanner name regex */
774       if ((cmdline_regex = string_nextinlist(&av_scanner_work, &sep,
775                                              cmdline_regex_buffer,
776                                              sizeof(cmdline_regex_buffer))) == NULL) {
777         /* no name regex supplied */
778         log_write(0, LOG_MAIN|LOG_PANIC,
779              "malware acl condition: missing virus name regex specification for cmdline scanner type.");
780         return DEFER; 
781       };
782     
783       /* precompile name regex */
784       cmdline_regex_re = pcre_compile(CS cmdline_regex, PCRE_COPT, (const char **)&rerror, &roffset, NULL);
785       if (cmdline_regex_re == NULL) {
786         log_write(0, LOG_MAIN|LOG_PANIC,
787                  "malware acl condition: regular expression error in '%s': %s at offset %d", cmdline_regex_re, rerror, roffset);
788         return DEFER;
789       };
790     
791       /* prepare scanner call */
792       snprintf(CS file_name,1024,"%s/scan/%s", spool_directory, message_id);
793       snprintf(CS commandline,1024, CS cmdline_scanner,file_name);
794       /* redirect STDERR too */
795       Ustrcat(commandline," 2>&1");
796       
797       /* store exims signal handlers */
798       eximsigchld = signal(SIGCHLD,SIG_DFL);
799       eximsigpipe = signal(SIGPIPE,SIG_DFL);
800       
801       scanner_out = popen(CS commandline,"r");
802       if (scanner_out == NULL) {
803         log_write(0, LOG_MAIN|LOG_PANIC,
804                  "malware acl condition: calling cmdline scanner (%s) failed: %s.", commandline, strerror(errno));
805         signal(SIGCHLD,eximsigchld);
806         signal(SIGPIPE,eximsigpipe);
807         return DEFER;
808       };
809       
810       snprintf(CS file_name,1024,"%s/scan/%s/%s_scanner_output", spool_directory, message_id, message_id);
811       scanner_record = fopen(CS file_name,"w");
812       
813       if (scanner_record == NULL) {
814         log_write(0, LOG_MAIN|LOG_PANIC,
815                  "malware acl condition: opening scanner output file (%s) failed: %s.", file_name, strerror(errno));
816         pclose(scanner_out);
817         signal(SIGCHLD,eximsigchld);
818         signal(SIGPIPE,eximsigpipe);
819         return DEFER;
820       };
821       
822       /* look for trigger while recording output */
823       while(fgets(CS linebuffer,32767,scanner_out) != NULL) {
824         if ( Ustrlen(linebuffer) > fwrite(linebuffer, 1, Ustrlen(linebuffer), scanner_record) ) {
825           /* short write */
826           log_write(0, LOG_MAIN|LOG_PANIC,
827                  "malware acl condition: short write on scanner output file (%s).", file_name);
828           pclose(scanner_out);
829           signal(SIGCHLD,eximsigchld);
830           signal(SIGPIPE,eximsigpipe);
831           return DEFER;
832         };
833         /* try trigger match */
834         if (!trigger && regex_match_and_setup(cmdline_trigger_re, linebuffer, 0, -1))
835           trigger = 1;
836       };
837       
838       fclose(scanner_record);
839       pclose(scanner_out);
840       signal(SIGCHLD,eximsigchld);
841       signal(SIGPIPE,eximsigpipe);
842       
843       if (trigger) {
844         /* setup default virus name */
845         Ustrcpy(malware_name_buffer,"unknown");
846         malware_name = malware_name_buffer;
847         
848         /* re-open the scanner output file, look for name match */
849         scanner_record = fopen(CS file_name,"r");
850         while(fgets(CS linebuffer,32767,scanner_record) != NULL) {
851           /* try match */
852           result = pcre_exec(cmdline_regex_re, NULL, CS linebuffer, Ustrlen(linebuffer), 0, 0, ovector, 30);
853           if (result >= 2) {
854             pcre_copy_substring(CS linebuffer, ovector, result, 1, CS malware_name_buffer, 255);
855           };
856         };
857         fclose(scanner_record);
858       }
859       else {
860         /* no virus found */
861         malware_name = NULL;
862       };
863     }
864     /* ----------------------------------------------------------------------- */
865     
866     
867     /* "sophie" scanner type ------------------------------------------------- */
868     else if (strcmpic(scanner_name,US"sophie") == 0) {
869       uschar *sophie_options;
870       uschar sophie_options_buffer[1024];
871       uschar sophie_options_default[] = "/var/run/sophie";
872       int bread = 0;
873       struct sockaddr_un server;
874       int sock;
875       uschar file_name[1024];
876       uschar av_buffer[1024];
877       
878       if ((sophie_options = string_nextinlist(&av_scanner_work, &sep,
879                                           sophie_options_buffer,
880                                           sizeof(sophie_options_buffer))) == NULL) {
881         /* no options supplied, use default options */
882         sophie_options = sophie_options_default;
883       };
884     
885       /* open the sophie socket */
886       sock = socket(AF_UNIX, SOCK_STREAM, 0);
887       if (sock < 0) {
888         log_write(0, LOG_MAIN|LOG_PANIC,
889              "malware acl condition: can't open UNIX socket.");
890         return DEFER; 
891       }
892       server.sun_family = AF_UNIX;
893       Ustrcpy(server.sun_path, sophie_options);
894       if (connect(sock, (struct sockaddr *) &server, sizeof(struct sockaddr_un)) < 0) {
895         close(sock);
896         log_write(0, LOG_MAIN|LOG_PANIC,
897              "malware acl condition: unable to connect to sophie UNIX socket (%s). errno=%d", sophie_options, errno);
898         return DEFER;
899       }
900       
901       /* pass the scan directory to sophie */
902       snprintf(CS file_name,1024,"%s/scan/%s", spool_directory, message_id);
903       if (write(sock, file_name, Ustrlen(file_name)) < 0) {
904         close(sock);
905         log_write(0, LOG_MAIN|LOG_PANIC,
906              "malware acl condition: unable to write to sophie UNIX socket (%s)", sophie_options);
907         return DEFER; 
908       };
909       
910       write(sock, "\n", 1);
911       
912       /* wait for result */
913       memset(av_buffer, 0, sizeof(av_buffer));
914       if ((!(bread = read(sock, av_buffer, sizeof(av_buffer))) > 0)) {
915         close(sock);
916         log_write(0, LOG_MAIN|LOG_PANIC,
917              "malware acl condition: unable to read from sophie UNIX socket (%s)", sophie_options);
918         return DEFER;
919       };
920     
921       close(sock);
922     
923       /* infected ? */
924       if (av_buffer[0] == '1') {
925         if (Ustrchr(av_buffer, '\n')) *Ustrchr(av_buffer, '\n') = '\0';
926         Ustrcpy(malware_name_buffer,&av_buffer[2]);
927         malware_name = malware_name_buffer;
928       }
929       else if (!strncmp(CS av_buffer, "-1", 2)) {
930         log_write(0, LOG_MAIN|LOG_PANIC,
931              "malware acl condition: malware acl condition: sophie reported error");
932         return DEFER;
933       }
934       else {
935         /* all ok, no virus */
936         malware_name = NULL;
937       };
938     }
939     /* ----------------------------------------------------------------------- */
940     
941
942     /* "clamd" scanner type ------------------------------------------------- */
943     /* This code was contributed by David Saez <david@ols.es> */
944     else if (strcmpic(scanner_name,US"clamd") == 0) {
945       uschar *clamd_options;
946       uschar clamd_options_buffer[1024];
947       uschar clamd_options_default[] = "/tmp/clamd";
948       uschar *p,*vname;
949       struct sockaddr_un server;
950       int sock,port,bread=0;
951       uschar file_name[1024];
952       uschar av_buffer[1024];
953       uschar hostname[256];
954       struct hostent *he;
955       struct in_addr in;
956
957       if ((clamd_options = string_nextinlist(&av_scanner_work, &sep,
958                                              clamd_options_buffer,
959                                              sizeof(clamd_options_buffer))) == NULL) {
960         /* no options supplied, use default options */
961         clamd_options = clamd_options_default;
962       }
963     
964       /* socket does not start with '/' -> network socket */
965       if (*clamd_options != '/') {
966     
967         /* extract host and port part */
968         if( sscanf(CS clamd_options, "%s %u", hostname, &port) != 2 ) {
969           log_write(0, LOG_MAIN|LOG_PANIC,
970                     "malware acl condition: clamd: invalid socket '%s'", clamd_options);
971           return DEFER;
972         };
973     
974         /* Lookup the host */
975         if((he = gethostbyname(CS hostname)) == 0) {
976           log_write(0, LOG_MAIN|LOG_PANIC,
977                     "malware acl condition: clamd: failed to lookup host '%s'", hostname);
978           return DEFER;
979         }
980     
981         in = *(struct in_addr *) he->h_addr_list[0];
982     
983         /* Open the ClamAV Socket */
984         if ( (sock = ip_socket(SOCK_STREAM, AF_INET)) < 0) {
985           log_write(0, LOG_MAIN|LOG_PANIC,
986                     "malware acl condition: clamd: unable to acquire socket (%s)",
987                     strerror(errno));
988           return DEFER;
989         }
990     
991         if (ip_connect(sock, AF_INET, (uschar*)inet_ntoa(in), port, 5) < 0) {
992           close(sock); 
993           log_write(0, LOG_MAIN|LOG_PANIC,
994                     "malware acl condition: clamd: connection to %s, port %u failed (%s)",
995                     inet_ntoa(in), port, strerror(errno));
996           return DEFER;
997         }
998       }
999       else {
1000         /* open the local socket */
1001         if ((sock = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) {
1002           log_write(0, LOG_MAIN|LOG_PANIC,
1003                     "malware acl condition: clamd: unable to acquire socket (%s)",
1004                     strerror(errno));
1005           return DEFER;
1006         }
1007     
1008         server.sun_family = AF_UNIX;
1009         Ustrcpy(server.sun_path, clamd_options);
1010     
1011         if (connect(sock, (struct sockaddr *) &server, sizeof(struct sockaddr_un)) < 0) {
1012           close(sock);
1013           log_write(0, LOG_MAIN|LOG_PANIC,
1014                     "malware acl condition: clamd: unable to connect to UNIX socket %s (%s)",
1015                     clamd_options, strerror(errno) );
1016           return DEFER;
1017         }
1018       }
1019     
1020       /* Pass the string to ClamAV (7 = "SCAN \n" + \0) */
1021     
1022       snprintf(CS file_name,1024,"SCAN %s/scan/%s\n", spool_directory, message_id);
1023     
1024       if (send(sock, file_name, Ustrlen(file_name), 0) < 0) {
1025         close(sock);
1026         log_write(0, LOG_MAIN|LOG_PANIC,"malware acl condition: clamd: unable to write to socket (%s)",
1027                   strerror(errno));
1028         return DEFER;
1029       }
1030     
1031       /* 
1032         We're done sending, close socket for writing.
1033         
1034         One user reported that clamd 0.70 does not like this any more ...
1035         
1036       */
1037       
1038       /* shutdown(sock, SHUT_WR); */
1039     
1040       /* Read the result */
1041       memset(av_buffer, 0, sizeof(av_buffer));
1042       bread = read(sock, av_buffer, sizeof(av_buffer));
1043       close(sock);
1044
1045       if (!(bread  > 0)) {
1046         log_write(0, LOG_MAIN|LOG_PANIC,
1047                   "malware acl condition: clamd: unable to read from socket (%s)",
1048                   strerror(errno));
1049         return DEFER;
1050       }
1051     
1052       if (bread == sizeof(av_buffer)) {
1053         log_write(0, LOG_MAIN|LOG_PANIC,
1054                   "malware acl condition: clamd: buffer too small");
1055         return DEFER;
1056       }
1057     
1058       /* Check the result. ClamAV Returns
1059          infected: -> "<filename>: <virusname> FOUND"
1060          not-infected: -> "<filename>: OK"
1061                 error: -> "<filename>: <errcode> ERROR */
1062          
1063       if (!(*av_buffer)) {
1064         log_write(0, LOG_MAIN|LOG_PANIC,
1065                   "malware acl condition: clamd: ClamAV returned null");
1066         return DEFER;
1067       }
1068     
1069       /* colon in returned output? */
1070       if((p = Ustrrchr(av_buffer,':')) == NULL) {
1071         log_write(0, LOG_MAIN|LOG_PANIC,
1072                   "malware acl condition: clamd: ClamAV returned malformed result: %s",
1073                   av_buffer);  
1074         return DEFER;
1075       }
1076     
1077       /* strip filename strip CR at the end */
1078       ++p;
1079       while (*p == ' ') ++p;
1080       vname = p;
1081       p = vname + Ustrlen(vname) - 1;
1082       if( *p == '\n' ) *p = '\0';
1083
1084       if ((p = Ustrstr(vname, "FOUND"))!=NULL) {
1085            *p=0;
1086            for (--p;p>vname && *p<=32;p--) *p=0;
1087            for (;*vname==32;vname++);
1088            Ustrcpy(malware_name_buffer,vname);
1089            malware_name = malware_name_buffer;
1090       }
1091       else {
1092            if (Ustrstr(vname, "ERROR")!=NULL) {
1093               /* ClamAV reports ERROR
1094               Find line start */
1095               for (;*vname!='\n' && vname>av_buffer; vname--);
1096               if (*vname=='\n') vname++;
1097
1098               log_write(0, LOG_MAIN|LOG_PANIC,
1099                      "malware acl condition: clamd: ClamAV returned %s",vname);
1100               return DEFER;
1101            }
1102            else {
1103               /* Everything should be OK */
1104               malware_name = NULL;
1105            }
1106       }
1107     }
1108     /* ----------------------------------------------------------------------- */
1109     
1110     
1111     /* "mksd" scanner type --------------------------------------------------- */
1112     else if (strcmpic(scanner_name,US"mksd") == 0) {
1113       uschar *mksd_options;
1114       char *mksd_options_end;
1115       uschar mksd_options_buffer[32];
1116       int mksd_maxproc = 1;  /* default, if no option supplied */
1117       struct sockaddr_un server;
1118       int sock;
1119       int retval;
1120       
1121       if ((mksd_options = string_nextinlist(&av_scanner_work, &sep,
1122                                             mksd_options_buffer,
1123                                             sizeof(mksd_options_buffer))) != NULL) {
1124         mksd_maxproc = (int) strtol(CS mksd_options, &mksd_options_end, 10);
1125               if ((*mksd_options == '\0') || (*mksd_options_end != '\0') ||
1126                   (mksd_maxproc < 1) || (mksd_maxproc > 32)) {
1127           log_write(0, LOG_MAIN|LOG_PANIC,
1128                     "malware acl condition: mksd: invalid option '%s'", mksd_options);
1129           return DEFER;
1130         }
1131       }
1132       
1133       /* open the mksd socket */
1134       sock = socket(AF_UNIX, SOCK_STREAM, 0);
1135       if (sock < 0) {
1136         log_write(0, LOG_MAIN|LOG_PANIC,
1137              "malware acl condition: can't open UNIX socket.");
1138         return DEFER; 
1139       }
1140       server.sun_family = AF_UNIX;
1141       Ustrcpy(server.sun_path, "/var/run/mksd/socket");
1142       if (connect(sock, (struct sockaddr *) &server, sizeof(struct sockaddr_un)) < 0) {
1143         close(sock);
1144         log_write(0, LOG_MAIN|LOG_PANIC,
1145              "malware acl condition: unable to connect to mksd UNIX socket (/var/run/mksd/socket). errno=%d", errno);
1146         return DEFER;
1147       }
1148       
1149       malware_name = NULL;
1150       
1151       retval = mksd_scan_packed(sock);
1152       
1153       if (retval != OK)
1154         return retval;
1155     }
1156     /* ----------------------------------------------------------------------- */
1157
1158     /* "unknown" scanner type ------------------------------------------------- */
1159     else {
1160       log_write(0, LOG_MAIN|LOG_PANIC,
1161              "malware condition: unknown scanner type '%s'", scanner_name);
1162       return DEFER;
1163     };
1164     /* ----------------------------------------------------------------------- */
1165   
1166     /* set "been here, done that" marker */
1167     malware_ok = 1;
1168   };
1169
1170   /* match virus name against pattern (caseless ------->----------v) */
1171   if ( (malware_name != NULL) &&
1172        (regex_match_and_setup(re, malware_name, 0, -1)) ) {
1173     return OK;
1174   }
1175   else {
1176     return FAIL;
1177   };
1178 }
1179
1180
1181 /* simple wrapper for reading lines from sockets */
1182 int recv_line(int sock, uschar *buffer, int size) {
1183   uschar *p = buffer;
1184
1185   memset(buffer,0,size);
1186   /* read until \n */
1187   while(recv(sock,p,1,0) > -1) {
1188     if ((p-buffer) > (size-2)) break;
1189     if (*p == '\n') break;
1190     if (*p != '\r') p++;
1191   };
1192   *p = '\0';
1193
1194   return (p-buffer);
1195 }
1196
1197
1198 /* ============= private routines for the "mksd" scanner type ============== */
1199
1200 #include <sys/uio.h>
1201
1202 int mksd_writev (int sock, struct iovec *iov, int iovcnt)
1203 {
1204   int i;
1205   
1206   for (;;) {
1207     do
1208       i = writev (sock, iov, iovcnt);
1209     while ((i < 0) && (errno == EINTR));
1210     if (i <= 0) {
1211       close (sock);
1212       log_write(0, LOG_MAIN|LOG_PANIC,
1213                 "malware acl condition: unable to write to mksd UNIX socket (/var/run/mksd/socket)");
1214       return -1;
1215     }
1216     
1217     for (;;)
1218       if (i >= iov->iov_len) {
1219         if (--iovcnt == 0)
1220           return 0;
1221         i -= iov->iov_len;
1222         iov++;
1223       } else {
1224         iov->iov_len -= i;
1225         iov->iov_base = CS iov->iov_base + i;
1226         break;
1227       }
1228   }
1229 }
1230
1231 int mksd_read_lines (int sock, uschar *av_buffer, int av_buffer_size)
1232 {
1233   int offset = 0;
1234   int i;
1235   
1236   do {
1237     if ((i = recv (sock, av_buffer+offset, av_buffer_size-offset, 0)) <= 0) {
1238       close (sock);
1239       log_write(0, LOG_MAIN|LOG_PANIC,
1240                 "malware acl condition: unable to read from mksd UNIX socket (/var/run/mksd/socket)");
1241       return -1;
1242     }
1243     
1244     offset += i;
1245     /* offset == av_buffer_size -> buffer full */
1246     if (offset == av_buffer_size) {
1247       close (sock);
1248       log_write(0, LOG_MAIN|LOG_PANIC,
1249                 "malware acl condition: malformed reply received from mksd");
1250       return -1;
1251     }
1252   } while (av_buffer[offset-1] != '\n');
1253   
1254   av_buffer[offset] = '\0';
1255   return offset;
1256 }
1257
1258 int mksd_parse_line (char *line)
1259 {
1260   char *p;
1261   
1262   switch (*line) {
1263     case 'O':
1264       /* OK */
1265       return OK;
1266     case 'E':
1267     case 'A':
1268       /* ERR */
1269       if ((p = strchr (line, '\n')) != NULL)
1270         (*p) = '\0';
1271       log_write(0, LOG_MAIN|LOG_PANIC,
1272                 "malware acl condition: mksd scanner failed: %s", line);
1273       return DEFER;
1274     default:
1275       /* VIR */
1276       if ((p = strchr (line, '\n')) != NULL) {
1277         (*p) = '\0';
1278         if (((p-line) > 5) && ((p-line) < sizeof (malware_name_buffer)) && (line[3] == ' '))
1279           if (((p = strchr (line+4, ' ')) != NULL) && ((p-line) > 4)) {
1280             (*p) = '\0';
1281             Ustrcpy (malware_name_buffer, line+4);
1282             malware_name = malware_name_buffer;
1283             return OK;
1284           }
1285       }
1286       log_write(0, LOG_MAIN|LOG_PANIC,
1287                 "malware acl condition: malformed reply received from mksd: %s", line);
1288       return DEFER;
1289   }
1290 }
1291
1292 int mksd_scan_packed (int sock)
1293 {
1294   struct iovec iov[7];
1295   char *cmd = "MSQ/scan/.eml\n";
1296   uschar av_buffer[1024];
1297   
1298   iov[0].iov_base = cmd;
1299   iov[0].iov_len = 3;
1300   iov[1].iov_base = CS spool_directory;
1301   iov[1].iov_len = Ustrlen (spool_directory);
1302   iov[2].iov_base = cmd + 3;
1303   iov[2].iov_len = 6;
1304   iov[3].iov_base = iov[5].iov_base = CS message_id;
1305   iov[3].iov_len = iov[5].iov_len = Ustrlen (message_id);
1306   iov[4].iov_base = cmd + 3;
1307   iov[4].iov_len = 1;
1308   iov[6].iov_base = cmd + 9;
1309   iov[6].iov_len = 5;
1310   
1311   if (mksd_writev (sock, iov, 7) < 0)
1312     return DEFER;
1313   
1314   if (mksd_read_lines (sock, av_buffer, sizeof (av_buffer)) < 0)
1315     return DEFER;
1316   
1317   close (sock);
1318   
1319   return mksd_parse_line (CS av_buffer);
1320 }
1321
1322 #endif