1. Tidies to get rid of compiler warnings from the merged Exiscan files.
[exim.git] / src / src / malware.c
1 /* $Cambridge: exim/src/src/malware.c,v 1.3 2004/12/17 14:52:44 ph10 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 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, result, ovector[30];
139                 unsigned int port, 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       struct sockaddr_un server;
382       int sock;
383     
384       if ((kav_options = string_nextinlist(&av_scanner_work, &sep,
385                                            kav_options_buffer,
386                                            sizeof(kav_options_buffer))) == NULL) {
387         /* no options supplied, use default options */
388         kav_options = kav_options_default;
389       };
390     
391       /* open the aveserver socket */
392       sock = socket(AF_UNIX, SOCK_STREAM, 0);
393       if (sock < 0) {
394         log_write(0, LOG_MAIN|LOG_PANIC,
395              "malware acl condition: can't open UNIX socket.");
396         return DEFER; 
397       }
398       server.sun_family = AF_UNIX;
399       Ustrcpy(server.sun_path, kav_options);
400       if (connect(sock, (struct sockaddr *) &server, sizeof(struct sockaddr_un)) < 0) {
401         close(sock);
402         log_write(0, LOG_MAIN|LOG_PANIC,
403              "malware acl condition: unable to connect to aveserver UNIX socket (%s). errno=%d", kav_options, errno);
404         return DEFER;
405       }
406     
407       /* read aveserver's greeting and see if it is ready (2xx greeting) */
408       recv_line(sock, buf, 32768);
409
410       if (buf[0] != '2') {
411         /* aveserver is having problems */
412         close(sock);
413         log_write(0, LOG_MAIN|LOG_PANIC,
414              "malware acl condition: aveserver is unavailable (Responded: %s).", ((buf[0] != 0) ? buf : (uschar *)"nothing") );
415         return DEFER;
416       };
417       
418       /* prepare our command */
419       snprintf(CS buf, 32768, "SCAN bPQRSTUW %s/scan/%s/%s.eml\r\n", spool_directory, message_id, message_id);
420       
421       /* and send it */
422       if (send(sock, buf, Ustrlen(buf), 0) < 0) {
423         close(sock);
424         log_write(0, LOG_MAIN|LOG_PANIC,
425              "malware acl condition: unable to write to aveserver UNIX socket (%s)", kav_options);
426         return DEFER;
427       }
428       
429       malware_name = NULL;
430       /* read response lines, find malware name and final response */
431       while (recv_line(sock, buf, 32768) > 0) {
432         debug_printf("aveserver: %s\n", buf);
433         if (buf[0] == '2') break;
434         if (Ustrncmp(buf,"322",3) == 0) {
435           uschar *p = Ustrchr(&buf[4],' ');
436           *p = '\0';
437           Ustrcpy(malware_name_buffer,&buf[4]);
438           malware_name = malware_name_buffer;
439         };
440       }
441
442       close(sock);
443     }
444     /* "fsecure" scanner type ------------------------------------------------- */
445     else if (strcmpic(scanner_name,US"fsecure") == 0) {
446       uschar *fsecure_options;
447       uschar fsecure_options_buffer[1024];
448       uschar fsecure_options_default[] = "/var/run/.fsav";
449       struct sockaddr_un server;
450       int sock, i, j, bread = 0;
451       uschar file_name[1024];
452       uschar av_buffer[1024];
453       pcre *fs_inf;
454       static uschar *cmdoptions[] = { US"CONFIGURE\tARCHIVE\t1\n",
455                                       US"CONFIGURE\tTIMEOUT\t0\n",
456                                       US"CONFIGURE\tMAXARCH\t5\n",
457                                       US"CONFIGURE\tMIME\t1\n" };
458       
459       malware_name = NULL;
460       if ((fsecure_options = string_nextinlist(&av_scanner_work, &sep,
461                                                fsecure_options_buffer,
462                                                sizeof(fsecure_options_buffer))) == NULL) { 
463          /* no options supplied, use default options */
464          fsecure_options = fsecure_options_default;
465       };
466      
467       /* open the fsecure socket */
468       sock = socket(AF_UNIX, SOCK_STREAM, 0);
469       if (sock < 0) {
470         log_write(0, LOG_MAIN|LOG_PANIC,
471                   "malware acl condition: unable to open fsecure socket %s (%s)",
472                   fsecure_options, strerror(errno));
473         return DEFER;
474       }
475       server.sun_family = AF_UNIX;
476       Ustrcpy(server.sun_path, fsecure_options);
477       if (connect(sock, (struct sockaddr *) &server, sizeof(struct sockaddr_un)) < 0) {
478         close(sock);
479         log_write(0, LOG_MAIN|LOG_PANIC,
480                   "malware acl condition: unable to connect to fsecure socket %s (%s)",
481                   fsecure_options, strerror(errno));
482         return DEFER;
483       }
484        
485       /* pass options */
486       memset(av_buffer, 0, sizeof(av_buffer));
487       for (i=0; i != 4; i++) {
488         /* debug_printf("send option \"%s\"",cmdoptions[i]); */
489         if (write(sock, cmdoptions[i], Ustrlen(cmdoptions[i])) < 0) {
490           close(sock);
491           log_write(0, LOG_MAIN|LOG_PANIC,
492                     "malware acl condition: unable to write fsecure option %d to %s (%s)",
493                     i, fsecure_options, strerror(errno));
494           return DEFER; 
495         };
496        
497         bread = read(sock, av_buffer, sizeof(av_buffer));
498         if (bread >0) av_buffer[bread]='\0';
499         if (bread < 0) {
500           close(sock);
501           log_write(0, LOG_MAIN|LOG_PANIC,
502                     "malware acl condition: unable to read fsecure answer %d (%s)", i, strerror(errno));
503           return DEFER;
504         };
505         for (j=0;j<bread;j++) if((av_buffer[j]=='\r')||(av_buffer[j]=='\n')) av_buffer[j] ='@';
506         /* debug_printf("read answer %d read=%d \"%s\"\n", i, bread, av_buffer ); */
507         /* while (Ustrstr(av_buffer, "OK\tServer configured.@") == NULL); */
508       };
509  
510       /* pass the mailfile to fsecure */
511       snprintf(CS file_name,1024,"SCAN\t%s/scan/%s/%s.eml\n", spool_directory, message_id, message_id);
512       /* debug_printf("send scan %s",file_name); */
513       if (write(sock, file_name, Ustrlen(file_name)) < 0) {
514         close(sock);
515         log_write(0, LOG_MAIN|LOG_PANIC,
516                   "malware acl condition: unable to write fsecure scan to %s (%s)",
517                   fsecure_options, strerror(errno));
518         return DEFER;
519       };
520        
521       /* set up match */
522       /* todo also SUSPICION\t */
523       fs_inf = pcre_compile("\\S{0,5}INFECTED\\t[^\\t]*\\t([^\\t]+)\\t\\S*$", PCRE_COPT, (const char **)&rerror, &roffset, NULL);
524  
525       /* read report, linewise */
526       do {
527         int ovector[30];
528         i = 0;
529         memset(av_buffer, 0, sizeof(av_buffer));
530         do {
531           bread=read(sock, &av_buffer[i], 1);
532           if (bread < 0) {
533             close(sock);
534             log_write(0, LOG_MAIN|LOG_PANIC,
535                       "malware acl condition: unable to read fsecure result (%s)", strerror(errno));
536             return DEFER;
537           };
538           i++;
539         }
540         while ((i < sizeof(av_buffer)-1 ) && (av_buffer[i-1] != '\n'));
541         av_buffer[i-1] = '\0';
542         /* debug_printf("got line \"%s\"\n",av_buffer); */
543          
544         /* Really search for virus again? */
545         if (malware_name == NULL) {
546           /* try matcher on the line, grab substring */
547           i = pcre_exec(fs_inf, NULL, CS av_buffer, Ustrlen(av_buffer), 0, 0, ovector, 30);
548           if (i >= 2) {
549             /* Got it */
550             pcre_copy_substring(CS av_buffer, ovector, i, 1, CS malware_name_buffer, 255);
551             malware_name = malware_name_buffer;
552           };
553         };
554       }
555       while (Ustrstr(av_buffer, "OK\tScan ok.") == NULL);
556       close(sock);      
557     }
558     /* ----------------------------------------------------------------------- */
559
560     /* "kavdaemon" scanner type ------------------------------------------------ */
561     else if (strcmpic(scanner_name,US"kavdaemon") == 0) {
562       uschar *kav_options;
563       uschar kav_options_buffer[1024];
564       uschar kav_options_default[] = "/var/run/AvpCtl";
565       struct sockaddr_un server;
566       int sock;
567       time_t t;
568       uschar tmpbuf[1024];
569       uschar scanrequest[1024];
570       uschar kav_match_string[128];
571       int kav_rc;
572       unsigned long kav_reportlen, bread;
573       pcre *kav_re;
574     
575       if ((kav_options = string_nextinlist(&av_scanner_work, &sep,
576                                            kav_options_buffer,
577                                            sizeof(kav_options_buffer))) == NULL) {
578         /* no options supplied, use default options */
579         kav_options = kav_options_default;
580       };
581     
582       /* open the kavdaemon socket */
583       sock = socket(AF_UNIX, SOCK_STREAM, 0);
584       if (sock < 0) {
585         log_write(0, LOG_MAIN|LOG_PANIC,
586              "malware acl condition: can't open UNIX socket.");
587         return DEFER; 
588       }
589       server.sun_family = AF_UNIX;
590       Ustrcpy(server.sun_path, kav_options);
591       if (connect(sock, (struct sockaddr *) &server, sizeof(struct sockaddr_un)) < 0) {
592         close(sock);
593         log_write(0, LOG_MAIN|LOG_PANIC,
594              "malware acl condition: unable to connect to kavdaemon UNIX socket (%s). errno=%d", kav_options, errno);
595         return DEFER;
596       }
597       
598       /* get current date and time, build scan request */
599       time(&t);
600       strftime(CS tmpbuf, sizeof(tmpbuf), "<0>%d %b %H:%M:%S:%%s/scan/%%s", localtime(&t));
601       snprintf(CS scanrequest, 1024,CS tmpbuf, spool_directory, message_id);
602       
603       /* send scan request */
604       if (send(sock, scanrequest, Ustrlen(scanrequest)+1, 0) < 0) {
605         close(sock);
606         log_write(0, LOG_MAIN|LOG_PANIC,
607              "malware acl condition: unable to write to kavdaemon UNIX socket (%s)", kav_options);
608         return DEFER;
609       }
610       
611       /* wait for result */
612       if ((bread = recv(sock, tmpbuf, 2, 0) != 2)) {
613         close(sock);
614         log_write(0, LOG_MAIN|LOG_PANIC,
615              "malware acl condition: unable to read 2 bytes from kavdaemon socket.");
616         return DEFER;
617       }
618     
619       /* get errorcode from one nibble */
620       if (test_byte_order() == LITTLE_MY_ENDIAN) {
621         kav_rc = tmpbuf[0] & 0x0F;
622       }
623       else {
624         kav_rc = tmpbuf[1] & 0x0F;
625       };
626     
627       /* improper kavdaemon configuration */
628       if ( (kav_rc == 5) || (kav_rc == 6) ) {
629         close(sock);
630         log_write(0, LOG_MAIN|LOG_PANIC,
631              "malware acl condition: please reconfigure kavdaemon to NOT disinfect or remove infected files.");
632         return DEFER;
633       };
634       
635       if (kav_rc == 1) {
636         close(sock);
637         log_write(0, LOG_MAIN|LOG_PANIC,
638              "malware acl condition: kavdaemon reported 'scanning not completed' (code 1).");
639         return DEFER;
640       };
641     
642       if (kav_rc == 7) {
643         close(sock);
644         log_write(0, LOG_MAIN|LOG_PANIC,
645              "malware acl condition: kavdaemon reported 'kavdaemon damaged' (code 7).");
646         return DEFER;
647       };
648     
649       /* code 8 is not handled, since it is ambigous. It appears mostly on
650       bounces where part of a file has been cut off */
651     
652       /* "virus found" return codes (2-4) */
653       if ((kav_rc > 1) && (kav_rc < 5)) {
654         int report_flag = 0;
655         
656         /* setup default virus name */
657         Ustrcpy(malware_name_buffer,"unknown");
658         malware_name = malware_name_buffer;
659       
660         if (test_byte_order() == LITTLE_MY_ENDIAN) {
661           report_flag = tmpbuf[1];
662         }
663         else {
664           report_flag = tmpbuf[0];
665         };
666       
667         /* read the report, if available */
668         if( report_flag == 1 ) {
669           /* read report size */
670           if ((bread = recv(sock, &kav_reportlen, 4, 0)) != 4) {
671             close(sock);
672             log_write(0, LOG_MAIN|LOG_PANIC,
673                   "malware acl condition: cannot read report size from kavdaemon");
674             return DEFER;
675           };
676   
677           /* it's possible that avp returns av_buffer[1] == 1 but the
678           reportsize is 0 (!?) */
679           if (kav_reportlen > 0) {
680             /* set up match regex, depends on retcode */
681             if( kav_rc == 3 )
682               Ustrcpy(kav_match_string, "suspicion:\\s*(.+?)\\s*$");
683             else
684               Ustrcpy(kav_match_string, "infected:\\s*(.+?)\\s*$");
685   
686             kav_re = pcre_compile( CS kav_match_string,
687                                    PCRE_COPT,
688                                    (const char **)&rerror,
689                                    &roffset,
690                                    NULL );
691             
692             /* read report, linewise */  
693             while (kav_reportlen > 0) {
694               int result = 0;
695               int ovector[30];
696               
697               bread = 0;
698               while ( recv(sock, &tmpbuf[bread], 1, 0) == 1 ) {
699                 kav_reportlen--;
700                 if ( (tmpbuf[bread] == '\n') || (bread > 1021) ) break;
701                 bread++;
702               };
703               bread++;
704               tmpbuf[bread] = '\0';
705               
706               /* try matcher on the line, grab substring */
707               result = pcre_exec(kav_re, NULL, CS tmpbuf, Ustrlen(tmpbuf), 0, 0, ovector, 30);
708               if (result >= 2) {
709                 pcre_copy_substring(CS tmpbuf, ovector, result, 1, CS malware_name_buffer, 255);
710                 break;
711               };
712             };
713           };
714         };
715       }
716       else {
717         /* no virus found */
718         malware_name = NULL;
719       };
720     
721       close(sock);
722     }
723     /* ----------------------------------------------------------------------- */
724     
725     
726     /* "cmdline" scanner type ------------------------------------------------ */
727     else if (strcmpic(scanner_name,US"cmdline") == 0) {
728       uschar *cmdline_scanner;
729       uschar cmdline_scanner_buffer[1024];
730       uschar *cmdline_trigger;
731       uschar cmdline_trigger_buffer[1024];
732       const pcre *cmdline_trigger_re;
733       uschar *cmdline_regex;
734       uschar cmdline_regex_buffer[1024];
735       const pcre *cmdline_regex_re;
736       uschar file_name[1024];
737       uschar commandline[1024];
738       void (*eximsigchld)(int);
739       void (*eximsigpipe)(int);
740       FILE *scanner_out = NULL;
741       FILE *scanner_record = NULL;
742       uschar linebuffer[32767];
743       int trigger = 0;
744       int result;
745       int ovector[30];
746       
747       /* find scanner command line */
748       if ((cmdline_scanner = string_nextinlist(&av_scanner_work, &sep,
749                                           cmdline_scanner_buffer,
750                                           sizeof(cmdline_scanner_buffer))) == NULL) {
751         /* no command line supplied */
752         log_write(0, LOG_MAIN|LOG_PANIC,
753              "malware acl condition: missing commandline specification for cmdline scanner type.");
754         return DEFER; 
755       };
756     
757       /* find scanner output trigger */
758       if ((cmdline_trigger = string_nextinlist(&av_scanner_work, &sep,
759                                           cmdline_trigger_buffer,
760                                           sizeof(cmdline_trigger_buffer))) == NULL) {
761         /* no trigger regex supplied */
762         log_write(0, LOG_MAIN|LOG_PANIC,
763              "malware acl condition: missing trigger specification for cmdline scanner type.");
764         return DEFER; 
765       };
766     
767       /* precompile trigger regex */
768       cmdline_trigger_re = pcre_compile(CS cmdline_trigger, PCRE_COPT, (const char **)&rerror, &roffset, NULL);
769       if (cmdline_trigger_re == NULL) {
770         log_write(0, LOG_MAIN|LOG_PANIC,
771                  "malware acl condition: regular expression error in '%s': %s at offset %d", cmdline_trigger_re, rerror, roffset);
772         return DEFER;
773       };
774     
775       /* find scanner name regex */
776       if ((cmdline_regex = string_nextinlist(&av_scanner_work, &sep,
777                                              cmdline_regex_buffer,
778                                              sizeof(cmdline_regex_buffer))) == NULL) {
779         /* no name regex supplied */
780         log_write(0, LOG_MAIN|LOG_PANIC,
781              "malware acl condition: missing virus name regex specification for cmdline scanner type.");
782         return DEFER; 
783       };
784     
785       /* precompile name regex */
786       cmdline_regex_re = pcre_compile(CS cmdline_regex, PCRE_COPT, (const char **)&rerror, &roffset, NULL);
787       if (cmdline_regex_re == NULL) {
788         log_write(0, LOG_MAIN|LOG_PANIC,
789                  "malware acl condition: regular expression error in '%s': %s at offset %d", cmdline_regex_re, rerror, roffset);
790         return DEFER;
791       };
792     
793       /* prepare scanner call */
794       snprintf(CS file_name,1024,"%s/scan/%s", spool_directory, message_id);
795       snprintf(CS commandline,1024, CS cmdline_scanner,file_name);
796       /* redirect STDERR too */
797       Ustrcat(commandline," 2>&1");
798       
799       /* store exims signal handlers */
800       eximsigchld = signal(SIGCHLD,SIG_DFL);
801       eximsigpipe = signal(SIGPIPE,SIG_DFL);
802       
803       scanner_out = popen(CS commandline,"r");
804       if (scanner_out == NULL) {
805         log_write(0, LOG_MAIN|LOG_PANIC,
806                  "malware acl condition: calling cmdline scanner (%s) failed: %s.", commandline, strerror(errno));
807         signal(SIGCHLD,eximsigchld);
808         signal(SIGPIPE,eximsigpipe);
809         return DEFER;
810       };
811       
812       snprintf(CS file_name,1024,"%s/scan/%s/%s_scanner_output", spool_directory, message_id, message_id);
813       scanner_record = fopen(CS file_name,"w");
814       
815       if (scanner_record == NULL) {
816         log_write(0, LOG_MAIN|LOG_PANIC,
817                  "malware acl condition: opening scanner output file (%s) failed: %s.", file_name, strerror(errno));
818         pclose(scanner_out);
819         signal(SIGCHLD,eximsigchld);
820         signal(SIGPIPE,eximsigpipe);
821         return DEFER;
822       };
823       
824       /* look for trigger while recording output */
825       while(fgets(CS linebuffer,32767,scanner_out) != NULL) {
826         if ( Ustrlen(linebuffer) > fwrite(linebuffer, 1, Ustrlen(linebuffer), scanner_record) ) {
827           /* short write */
828           log_write(0, LOG_MAIN|LOG_PANIC,
829                  "malware acl condition: short write on scanner output file (%s).", file_name);
830           pclose(scanner_out);
831           signal(SIGCHLD,eximsigchld);
832           signal(SIGPIPE,eximsigpipe);
833           return DEFER;
834         };
835         /* try trigger match */
836         if (!trigger && regex_match_and_setup(cmdline_trigger_re, linebuffer, 0, -1))
837           trigger = 1;
838       };
839       
840       fclose(scanner_record);
841       pclose(scanner_out);
842       signal(SIGCHLD,eximsigchld);
843       signal(SIGPIPE,eximsigpipe);
844       
845       if (trigger) {
846         /* setup default virus name */
847         Ustrcpy(malware_name_buffer,"unknown");
848         malware_name = malware_name_buffer;
849         
850         /* re-open the scanner output file, look for name match */
851         scanner_record = fopen(CS file_name,"r");
852         while(fgets(CS linebuffer,32767,scanner_record) != NULL) {
853           /* try match */
854           result = pcre_exec(cmdline_regex_re, NULL, CS linebuffer, Ustrlen(linebuffer), 0, 0, ovector, 30);
855           if (result >= 2) {
856             pcre_copy_substring(CS linebuffer, ovector, result, 1, CS malware_name_buffer, 255);
857           };
858         };
859         fclose(scanner_record);
860       }
861       else {
862         /* no virus found */
863         malware_name = NULL;
864       };
865     }
866     /* ----------------------------------------------------------------------- */
867     
868     
869     /* "sophie" scanner type ------------------------------------------------- */
870     else if (strcmpic(scanner_name,US"sophie") == 0) {
871       uschar *sophie_options;
872       uschar sophie_options_buffer[1024];
873       uschar sophie_options_default[] = "/var/run/sophie";
874       int bread = 0;
875       struct sockaddr_un server;
876       int sock;
877       uschar file_name[1024];
878       uschar av_buffer[1024];
879       
880       if ((sophie_options = string_nextinlist(&av_scanner_work, &sep,
881                                           sophie_options_buffer,
882                                           sizeof(sophie_options_buffer))) == NULL) {
883         /* no options supplied, use default options */
884         sophie_options = sophie_options_default;
885       };
886     
887       /* open the sophie socket */
888       sock = socket(AF_UNIX, SOCK_STREAM, 0);
889       if (sock < 0) {
890         log_write(0, LOG_MAIN|LOG_PANIC,
891              "malware acl condition: can't open UNIX socket.");
892         return DEFER; 
893       }
894       server.sun_family = AF_UNIX;
895       Ustrcpy(server.sun_path, sophie_options);
896       if (connect(sock, (struct sockaddr *) &server, sizeof(struct sockaddr_un)) < 0) {
897         close(sock);
898         log_write(0, LOG_MAIN|LOG_PANIC,
899              "malware acl condition: unable to connect to sophie UNIX socket (%s). errno=%d", sophie_options, errno);
900         return DEFER;
901       }
902       
903       /* pass the scan directory to sophie */
904       snprintf(CS file_name,1024,"%s/scan/%s", spool_directory, message_id);
905       if (write(sock, file_name, Ustrlen(file_name)) < 0) {
906         close(sock);
907         log_write(0, LOG_MAIN|LOG_PANIC,
908              "malware acl condition: unable to write to sophie UNIX socket (%s)", sophie_options);
909         return DEFER; 
910       };
911       
912       write(sock, "\n", 1);
913       
914       /* wait for result */
915       memset(av_buffer, 0, sizeof(av_buffer));
916       if ((!(bread = read(sock, av_buffer, sizeof(av_buffer))) > 0)) {
917         close(sock);
918         log_write(0, LOG_MAIN|LOG_PANIC,
919              "malware acl condition: unable to read from sophie UNIX socket (%s)", sophie_options);
920         return DEFER;
921       };
922     
923       close(sock);
924     
925       /* infected ? */
926       if (av_buffer[0] == '1') {
927         if (Ustrchr(av_buffer, '\n')) *Ustrchr(av_buffer, '\n') = '\0';
928         Ustrcpy(malware_name_buffer,&av_buffer[2]);
929         malware_name = malware_name_buffer;
930       }
931       else if (!strncmp(CS av_buffer, "-1", 2)) {
932         log_write(0, LOG_MAIN|LOG_PANIC,
933              "malware acl condition: malware acl condition: sophie reported error");
934         return DEFER;
935       }
936       else {
937         /* all ok, no virus */
938         malware_name = NULL;
939       };
940     }
941     /* ----------------------------------------------------------------------- */
942     
943
944     /* "clamd" scanner type ------------------------------------------------- */
945     /* This code was contributed by David Saez <david@ols.es> */
946     else if (strcmpic(scanner_name,US"clamd") == 0) {
947       uschar *clamd_options;
948       uschar clamd_options_buffer[1024];
949       uschar clamd_options_default[] = "/tmp/clamd";
950       uschar *p,*vname;
951       struct sockaddr_un server;
952       int sock,bread=0;
953       unsigned int port; 
954       uschar file_name[1024];
955       uschar av_buffer[1024];
956       uschar hostname[256];
957       struct hostent *he;
958       struct in_addr in;
959
960       if ((clamd_options = string_nextinlist(&av_scanner_work, &sep,
961                                              clamd_options_buffer,
962                                              sizeof(clamd_options_buffer))) == NULL) {
963         /* no options supplied, use default options */
964         clamd_options = clamd_options_default;
965       }
966     
967       /* socket does not start with '/' -> network socket */
968       if (*clamd_options != '/') {
969     
970         /* extract host and port part */
971         if( sscanf(CS clamd_options, "%s %u", hostname, &port) != 2 ) {
972           log_write(0, LOG_MAIN|LOG_PANIC,
973                     "malware acl condition: clamd: invalid socket '%s'", clamd_options);
974           return DEFER;
975         };
976     
977         /* Lookup the host */
978         if((he = gethostbyname(CS hostname)) == 0) {
979           log_write(0, LOG_MAIN|LOG_PANIC,
980                     "malware acl condition: clamd: failed to lookup host '%s'", hostname);
981           return DEFER;
982         }
983     
984         in = *(struct in_addr *) he->h_addr_list[0];
985     
986         /* Open the ClamAV Socket */
987         if ( (sock = ip_socket(SOCK_STREAM, AF_INET)) < 0) {
988           log_write(0, LOG_MAIN|LOG_PANIC,
989                     "malware acl condition: clamd: unable to acquire socket (%s)",
990                     strerror(errno));
991           return DEFER;
992         }
993     
994         if (ip_connect(sock, AF_INET, (uschar*)inet_ntoa(in), port, 5) < 0) {
995           close(sock); 
996           log_write(0, LOG_MAIN|LOG_PANIC,
997                     "malware acl condition: clamd: connection to %s, port %u failed (%s)",
998                     inet_ntoa(in), port, strerror(errno));
999           return DEFER;
1000         }
1001       }
1002       else {
1003         /* open the local socket */
1004         if ((sock = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) {
1005           log_write(0, LOG_MAIN|LOG_PANIC,
1006                     "malware acl condition: clamd: unable to acquire socket (%s)",
1007                     strerror(errno));
1008           return DEFER;
1009         }
1010     
1011         server.sun_family = AF_UNIX;
1012         Ustrcpy(server.sun_path, clamd_options);
1013     
1014         if (connect(sock, (struct sockaddr *) &server, sizeof(struct sockaddr_un)) < 0) {
1015           close(sock);
1016           log_write(0, LOG_MAIN|LOG_PANIC,
1017                     "malware acl condition: clamd: unable to connect to UNIX socket %s (%s)",
1018                     clamd_options, strerror(errno) );
1019           return DEFER;
1020         }
1021       }
1022     
1023       /* Pass the string to ClamAV (7 = "SCAN \n" + \0) */
1024     
1025       snprintf(CS file_name,1024,"SCAN %s/scan/%s\n", spool_directory, message_id);
1026     
1027       if (send(sock, file_name, Ustrlen(file_name), 0) < 0) {
1028         close(sock);
1029         log_write(0, LOG_MAIN|LOG_PANIC,"malware acl condition: clamd: unable to write to socket (%s)",
1030                   strerror(errno));
1031         return DEFER;
1032       }
1033     
1034       /* 
1035         We're done sending, close socket for writing.
1036         
1037         One user reported that clamd 0.70 does not like this any more ...
1038         
1039       */
1040       
1041       /* shutdown(sock, SHUT_WR); */
1042     
1043       /* Read the result */
1044       memset(av_buffer, 0, sizeof(av_buffer));
1045       bread = read(sock, av_buffer, sizeof(av_buffer));
1046       close(sock);
1047
1048       if (!(bread  > 0)) {
1049         log_write(0, LOG_MAIN|LOG_PANIC,
1050                   "malware acl condition: clamd: unable to read from socket (%s)",
1051                   strerror(errno));
1052         return DEFER;
1053       }
1054     
1055       if (bread == sizeof(av_buffer)) {
1056         log_write(0, LOG_MAIN|LOG_PANIC,
1057                   "malware acl condition: clamd: buffer too small");
1058         return DEFER;
1059       }
1060     
1061       /* Check the result. ClamAV Returns
1062          infected: -> "<filename>: <virusname> FOUND"
1063          not-infected: -> "<filename>: OK"
1064                 error: -> "<filename>: <errcode> ERROR */
1065          
1066       if (!(*av_buffer)) {
1067         log_write(0, LOG_MAIN|LOG_PANIC,
1068                   "malware acl condition: clamd: ClamAV returned null");
1069         return DEFER;
1070       }
1071     
1072       /* colon in returned output? */
1073       if((p = Ustrrchr(av_buffer,':')) == NULL) {
1074         log_write(0, LOG_MAIN|LOG_PANIC,
1075                   "malware acl condition: clamd: ClamAV returned malformed result: %s",
1076                   av_buffer);  
1077         return DEFER;
1078       }
1079     
1080       /* strip filename strip CR at the end */
1081       ++p;
1082       while (*p == ' ') ++p;
1083       vname = p;
1084       p = vname + Ustrlen(vname) - 1;
1085       if( *p == '\n' ) *p = '\0';
1086
1087       if ((p = Ustrstr(vname, "FOUND"))!=NULL) {
1088            *p=0;
1089            for (--p;p>vname && *p<=32;p--) *p=0;
1090            for (;*vname==32;vname++);
1091            Ustrcpy(malware_name_buffer,vname);
1092            malware_name = malware_name_buffer;
1093       }
1094       else {
1095            if (Ustrstr(vname, "ERROR")!=NULL) {
1096               /* ClamAV reports ERROR
1097               Find line start */
1098               for (;*vname!='\n' && vname>av_buffer; vname--);
1099               if (*vname=='\n') vname++;
1100
1101               log_write(0, LOG_MAIN|LOG_PANIC,
1102                      "malware acl condition: clamd: ClamAV returned %s",vname);
1103               return DEFER;
1104            }
1105            else {
1106               /* Everything should be OK */
1107               malware_name = NULL;
1108            }
1109       }
1110     }
1111     /* ----------------------------------------------------------------------- */
1112     
1113     
1114     /* "mksd" scanner type --------------------------------------------------- */
1115     else if (strcmpic(scanner_name,US"mksd") == 0) {
1116       uschar *mksd_options;
1117       char *mksd_options_end;
1118       uschar mksd_options_buffer[32];
1119       int mksd_maxproc = 1;  /* default, if no option supplied */
1120       struct sockaddr_un server;
1121       int sock;
1122       int retval;
1123       
1124       if ((mksd_options = string_nextinlist(&av_scanner_work, &sep,
1125                                             mksd_options_buffer,
1126                                             sizeof(mksd_options_buffer))) != NULL) {
1127         mksd_maxproc = (int) strtol(CS mksd_options, &mksd_options_end, 10);
1128               if ((*mksd_options == '\0') || (*mksd_options_end != '\0') ||
1129                   (mksd_maxproc < 1) || (mksd_maxproc > 32)) {
1130           log_write(0, LOG_MAIN|LOG_PANIC,
1131                     "malware acl condition: mksd: invalid option '%s'", mksd_options);
1132           return DEFER;
1133         }
1134       }
1135       
1136       /* open the mksd socket */
1137       sock = socket(AF_UNIX, SOCK_STREAM, 0);
1138       if (sock < 0) {
1139         log_write(0, LOG_MAIN|LOG_PANIC,
1140              "malware acl condition: can't open UNIX socket.");
1141         return DEFER; 
1142       }
1143       server.sun_family = AF_UNIX;
1144       Ustrcpy(server.sun_path, "/var/run/mksd/socket");
1145       if (connect(sock, (struct sockaddr *) &server, sizeof(struct sockaddr_un)) < 0) {
1146         close(sock);
1147         log_write(0, LOG_MAIN|LOG_PANIC,
1148              "malware acl condition: unable to connect to mksd UNIX socket (/var/run/mksd/socket). errno=%d", errno);
1149         return DEFER;
1150       }
1151       
1152       malware_name = NULL;
1153       
1154       retval = mksd_scan_packed(sock);
1155       
1156       if (retval != OK)
1157         return retval;
1158     }
1159     /* ----------------------------------------------------------------------- */
1160
1161     /* "unknown" scanner type ------------------------------------------------- */
1162     else {
1163       log_write(0, LOG_MAIN|LOG_PANIC,
1164              "malware condition: unknown scanner type '%s'", scanner_name);
1165       return DEFER;
1166     };
1167     /* ----------------------------------------------------------------------- */
1168   
1169     /* set "been here, done that" marker */
1170     malware_ok = 1;
1171   };
1172
1173   /* match virus name against pattern (caseless ------->----------v) */
1174   if ( (malware_name != NULL) &&
1175        (regex_match_and_setup(re, malware_name, 0, -1)) ) {
1176     return OK;
1177   }
1178   else {
1179     return FAIL;
1180   };
1181 }
1182
1183
1184 /* simple wrapper for reading lines from sockets */
1185 int recv_line(int sock, uschar *buffer, int size) {
1186   uschar *p = buffer;
1187
1188   memset(buffer,0,size);
1189   /* read until \n */
1190   while(recv(sock,p,1,0) > -1) {
1191     if ((p-buffer) > (size-2)) break;
1192     if (*p == '\n') break;
1193     if (*p != '\r') p++;
1194   };
1195   *p = '\0';
1196
1197   return (p-buffer);
1198 }
1199
1200
1201 /* ============= private routines for the "mksd" scanner type ============== */
1202
1203 #include <sys/uio.h>
1204
1205 int mksd_writev (int sock, struct iovec *iov, int iovcnt)
1206 {
1207   int i;
1208   
1209   for (;;) {
1210     do
1211       i = writev (sock, iov, iovcnt);
1212     while ((i < 0) && (errno == EINTR));
1213     if (i <= 0) {
1214       close (sock);
1215       log_write(0, LOG_MAIN|LOG_PANIC,
1216                 "malware acl condition: unable to write to mksd UNIX socket (/var/run/mksd/socket)");
1217       return -1;
1218     }
1219     
1220     for (;;)
1221       if (i >= iov->iov_len) {
1222         if (--iovcnt == 0)
1223           return 0;
1224         i -= iov->iov_len;
1225         iov++;
1226       } else {
1227         iov->iov_len -= i;
1228         iov->iov_base = CS iov->iov_base + i;
1229         break;
1230       }
1231   }
1232 }
1233
1234 int mksd_read_lines (int sock, uschar *av_buffer, int av_buffer_size)
1235 {
1236   int offset = 0;
1237   int i;
1238   
1239   do {
1240     if ((i = recv (sock, av_buffer+offset, av_buffer_size-offset, 0)) <= 0) {
1241       close (sock);
1242       log_write(0, LOG_MAIN|LOG_PANIC,
1243                 "malware acl condition: unable to read from mksd UNIX socket (/var/run/mksd/socket)");
1244       return -1;
1245     }
1246     
1247     offset += i;
1248     /* offset == av_buffer_size -> buffer full */
1249     if (offset == av_buffer_size) {
1250       close (sock);
1251       log_write(0, LOG_MAIN|LOG_PANIC,
1252                 "malware acl condition: malformed reply received from mksd");
1253       return -1;
1254     }
1255   } while (av_buffer[offset-1] != '\n');
1256   
1257   av_buffer[offset] = '\0';
1258   return offset;
1259 }
1260
1261 int mksd_parse_line (char *line)
1262 {
1263   char *p;
1264   
1265   switch (*line) {
1266     case 'O':
1267       /* OK */
1268       return OK;
1269     case 'E':
1270     case 'A':
1271       /* ERR */
1272       if ((p = strchr (line, '\n')) != NULL)
1273         (*p) = '\0';
1274       log_write(0, LOG_MAIN|LOG_PANIC,
1275                 "malware acl condition: mksd scanner failed: %s", line);
1276       return DEFER;
1277     default:
1278       /* VIR */
1279       if ((p = strchr (line, '\n')) != NULL) {
1280         (*p) = '\0';
1281         if (((p-line) > 5) && ((p-line) < sizeof (malware_name_buffer)) && (line[3] == ' '))
1282           if (((p = strchr (line+4, ' ')) != NULL) && ((p-line) > 4)) {
1283             (*p) = '\0';
1284             Ustrcpy (malware_name_buffer, line+4);
1285             malware_name = malware_name_buffer;
1286             return OK;
1287           }
1288       }
1289       log_write(0, LOG_MAIN|LOG_PANIC,
1290                 "malware acl condition: malformed reply received from mksd: %s", line);
1291       return DEFER;
1292   }
1293 }
1294
1295 int mksd_scan_packed (int sock)
1296 {
1297   struct iovec iov[7];
1298   char *cmd = "MSQ/scan/.eml\n";
1299   uschar av_buffer[1024];
1300   
1301   iov[0].iov_base = cmd;
1302   iov[0].iov_len = 3;
1303   iov[1].iov_base = CS spool_directory;
1304   iov[1].iov_len = Ustrlen (spool_directory);
1305   iov[2].iov_base = cmd + 3;
1306   iov[2].iov_len = 6;
1307   iov[3].iov_base = iov[5].iov_base = CS message_id;
1308   iov[3].iov_len = iov[5].iov_len = Ustrlen (message_id);
1309   iov[4].iov_base = cmd + 3;
1310   iov[4].iov_len = 1;
1311   iov[6].iov_base = cmd + 9;
1312   iov[6].iov_len = 5;
1313   
1314   if (mksd_writev (sock, iov, 7) < 0)
1315     return DEFER;
1316   
1317   if (mksd_read_lines (sock, av_buffer, sizeof (av_buffer)) < 0)
1318     return DEFER;
1319   
1320   close (sock);
1321   
1322   return mksd_parse_line (CS av_buffer);
1323 }
1324
1325 #endif