1 /*
2   Copyright (c) 2006  Morettoni Luca <luca@morettoni.net>
3   All rights reserved.
4 
5   Redistribution and use in source and binary forms, with or without
6   modification, are permitted provided that the following conditions
7   are met:
8   1. Redistributions of source code must retain the above copyright
9      notice, this list of conditions and the following disclaimer.
10   2. Redistributions in binary form must reproduce the above copyright
11      notice, this list of conditions and the following disclaimer in the
12      documentation and/or other materials provided with the distribution.
13 
14   THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15   ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16   IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17   ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18   FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19   DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20   OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21   HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22   LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23   OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24   SUCH DAMAGE.
25 
26   $Id: qmail-rblchk.c,v 1.8 2006/01/24 08:17:24 luca Exp $
27 
28   NOTE: functions ``buffer_copy, maildir_child and wait_pid'' plus all external
29         file (like dns.h and more) are developed by Dr. Dan Bernstein and are
30         free to use in the public domain.
31 */
32 
33 #include <unistd.h>
34 #include <signal.h>
35 #include <sys/types.h>
36 #include <sys/stat.h>
37 #include <sys/wait.h>
38 #include "alloc.h"
39 #include "dns.h"
40 #include "str.h"
41 #include "stralloc.h"
42 #include "buffer.h"
43 #include "ip4.h"
44 #include "byte.h"
45 #include "strerr.h"
46 #include "error.h"
47 #include "sgetopt.h"
48 #include "fmt.h"
49 #include "open.h"
50 #include "scan.h"
51 #include "timestamp.h"
52 #include "cdb.h"
53 
54 static char seed[128];
55 struct rbl_entry {
56   char *server;
57   int txt;
58   int rev;
59   int otc;
60 } rbl[32];
61 
62 char skipip[64];
63 int iptoskip = 0;
64 unsigned long ignore = 0;
65 int rblnum = 0;
66 int scan_all = 0;
67 int cond_redir = 0;
68 int verbose = 0;
69 int quiet = 0;
70 int tagspam = 0;
71 char *delivery = NULL;
72 char *cdbrules = NULL;
73 stralloc why = {0};
74 buffer *buffer_log;
75 
76 char fntmptph[80 + FMT_ULONG * 2];
77 char fnnewtph[80 + FMT_ULONG * 2];
tryunlinktmp()78 void tryunlinktmp() { unlink (fntmptph); }
sigalrm()79 void sigalrm() { tryunlinktmp (); _exit(3); }
80 
81 #define TEMP_FAIL       111
82 #define EXIT_OK         (cond_redir ? 1 : 0)
83 #define EXIT_SPAM       (cond_redir ? 0 : (delivery ? 99 : 100))
84 #define FATAL		"qmail-rblchk: fatal: "
85 #define BUF_LEN		1024
86 #define VERSION		"qmail-rblchk 2.4.1"
87 #define AUTHOR		"Luca Morettoni <luca@morettoni.net>"
88 
do_log(void)89 void do_log (void)
90 {
91   char ts[TIMESTAMP];
92   char s[FMT_ULONG];
93   static pid_t pid = 0;
94 
95   if (!pid) pid = getpid();
96 
97   timestamp (ts);
98   buffer_put (buffer_log, ts, TIMESTAMP);
99   buffer_puts (buffer_log, " ");
100   buffer_put (buffer_log, s, fmt_ulong (s,pid));
101   buffer_puts (buffer_log, " ");
102 }
103 
nomem(void)104 void nomem (void)
105 {
106   strerr_die2x(111,FATAL,"out of memory");
107 }
108 
nocdb(void)109 void nocdb (void)
110 {
111   strerr_die4sys(111,FATAL,"unable to read ",cdbrules,": ");
112 }
113 
usage(void)114 void usage (void)
115 {
116   buffer_puts (buffer_2, "qmail-rblchk: usage: qmail-rblchk [opz] [dir]\n\n");
117   buffer_puts (buffer_2, " options may be:\n");
118   buffer_puts (buffer_2, "  -h       this screen\n");
119   buffer_puts (buffer_2, "  -s       add X-Spam header into the incoming mail (work only with delivery ``dir'')\n");
120   buffer_puts (buffer_2, "  -c       turn on condredirect compatibility mode\n");
121   buffer_puts (buffer_2, "  -i NUM   ignore first ``NUM'' IPs found in the header\n");
122   buffer_puts (buffer_2, "  -x IP    do not check ``IP'', try to find other address in header\n");
123   buffer_puts (buffer_2, "  -v       verbose (debug) mode\n");
124   buffer_puts (buffer_2, "  -V       show program version\n");
125   buffer_puts (buffer_2, "  -q       quiet mode (suppress any output)\n");
126   buffer_puts (buffer_2, "  -p       don't check private IP class:\n");
127   buffer_puts (buffer_2, "             127.0.0.0   - 127.255.255.255\n");
128   buffer_puts (buffer_2, "             10.0.0.0    - 10.255.255.255\n");
129   buffer_puts (buffer_2, "             172.16.0.0  - 172.31.255.255\n");
130   buffer_puts (buffer_2, "             192.168.0.0 - 192.168.255.255\n");
131   buffer_puts (buffer_2, "  -m       check all IPs in email (default: check only first IP)\n");
132   buffer_puts (buffer_2, "  -l log   write program action to ``log'' file\n");
133   buffer_puts (buffer_2, "  -L data  write blocked IP to ``data'' file\n");
134   buffer_puts (buffer_2, "  -r addr  use ``addr'' for RBL checking (if TXT record exist block mail)\n");
135   buffer_puts (buffer_2, "  -R addr  use ``addr'' for RBL reverted checking (if TXT record NOT exist block mail)\n");
136   buffer_puts (buffer_2, "  -C addr  use ``addr'' for one time RBL checking (if TXT or A record exist don't block mail)\n");
137   buffer_puts (buffer_2, "  -a addr  use ``addr'' for anti-RBL checking (if A record NOT exist block mail)\n");
138   buffer_puts (buffer_2, "  -A addr  use ``addr'' for anti-RBL reverted checking (if A record exist block mail)\n");
139   buffer_puts (buffer_2, "  -X cdb   check IP from tcpserver-style ``cdb'' file (IP:deny block mail from that IP)\n");
140 
141   buffer_puts (buffer_2, "\nYou must specify one or more (max 32) RBL address, example:\n");
142   buffer_puts (buffer_2, " qmail-rblchk -r dnsbl.sorbs.net -r sbl-xbl.spamhaus.org -r relays.ordb.org\n\n");
143   buffer_puts (buffer_2, "You can ignore (-x option) no more than 16 IP address\n\n");
144   buffer_puts (buffer_2, "If ``dir'' is given in command line and it exist all blocked mails are delivered\n");
145   buffer_puts (buffer_2, "into Maildir ``dir'' (dir must start with a / or a . and end with a /);\n");
146   buffer_puts (buffer_2, "the program run in ``delivery mode''.\n");
147 
148   buffer_puts (buffer_2, "\nThe program exit status may be (normal mode):\n");
149   buffer_puts (buffer_2, " 0    when the message is not blocked\n");
150   buffer_puts (buffer_2, " 100  when the message is blocked\n");
151   buffer_puts (buffer_2, " 111  when fails or no checking options was given\n");
152   buffer_puts (buffer_2, "In ``delivery mode'':\n");
153   buffer_puts (buffer_2, " 0    the message is not blocked (continue .qmail checking)\n");
154   buffer_puts (buffer_2, " 99   the blocked message has been wrote to ``dir'' Maildir\n");
155   buffer_puts (buffer_2, " 111  same as above\n");
156   buffer_puts (buffer_2, "In ``condredirect compatibility mode'':\n");
157   buffer_puts (buffer_2, " 0    when the message is blocked\n");
158   buffer_puts (buffer_2, " 1    when the message is not blocked\n");
159   buffer_puts (buffer_2, " 111  same as above\n");
160 
161   buffer_flush (buffer_2);
162 
163   _exit (TEMP_FAIL);
164 }
165 
docheck(struct cdb * c,stralloc * ip)166 int docheck (struct cdb *c, stralloc *ip)
167 {
168   char *data;
169   unsigned int datalen;
170   int ret = -1; /* not found, try again! */
171 
172   switch (cdb_find (c, ip->s, ip->len)) {
173     case -1: nocdb ();
174     case 0: return -1;
175   }
176 
177   datalen = cdb_datalen(c);
178   data = alloc(datalen);
179   if (!data) nomem ();
180   if (cdb_read (c,data,datalen,cdb_datapos (c)) == -1) {
181     alloc_free(data);
182     nocdb ();
183   }
184 
185   switch (data[0]) {
186     case 'D': ret = 1; break; /* deny */
187     default:  ret = 0; break; /* allow */
188   }
189 
190   alloc_free(data);
191 
192   return ret;
193 }
194 
check_cdb(char ip[4])195 int check_cdb (char ip[4])
196 {
197   int fd;
198   struct cdb c;
199   char ip_fmt[IP4_FMT];
200   stralloc ipcdb = {0};
201   int ret = -1; /* default: NOT found in CDB file */
202 
203   ip4_fmt (ip_fmt, ip);
204 
205   fd = open_read (cdbrules);
206   if (fd == -1)
207     nocdb ();
208   else {
209     cdb_init(&c,fd);
210     if (!stralloc_copyb (&ipcdb, ip_fmt, ip4_fmt (ip_fmt, ip))) nomem ();
211 
212     if (verbose) {
213       do_log ();
214       buffer_puts (buffer_log, "checking: ");
215       buffer_put (buffer_log, ipcdb.s, ipcdb.len);
216       buffer_puts (buffer_log, " into ");
217       buffer_puts (buffer_log, cdbrules);
218       buffer_puts (buffer_log, "\n");
219       buffer_flush (buffer_log);
220     }
221 
222     /* check all octets */
223     ret = docheck (&c, &ipcdb);
224     while (ipcdb.len > 0 && ret == -1) {
225       /* delete right octect and try again */
226       if (ip_fmt[ipcdb.len - 1] == '.')
227         ret = docheck (&c, &ipcdb);
228       --ipcdb.len;
229     }
230     /* if no match, we see the defaul rule in the file */
231     if (ret == -1) ret = docheck (&c, &ipcdb);
232 
233     cdb_free(&c);
234     close (fd);
235 
236     if (verbose) {
237       do_log ();
238       stralloc_copyb (&ipcdb, ip_fmt, ip4_fmt (ip_fmt, ip));
239       buffer_put (buffer_log, ipcdb.s, ipcdb.len);
240       switch (ret) {
241         case -1: buffer_puts (buffer_log, " not listed"); break;
242         case 0: buffer_puts (buffer_log, " allowed"); break;
243         case 1: buffer_puts (buffer_log, " blocked"); break;
244       }
245       buffer_puts (buffer_log, "\n");
246       buffer_flush (buffer_log);
247     }
248 
249     if (ret == 1) {
250       stralloc_copyb (&why, ip_fmt, ip4_fmt (ip_fmt, ip));
251       stralloc_cats (&why, " is blocked (deny) in ");
252       stralloc_cats (&why, cdbrules);
253     }
254   }
255 
256   return ret;
257 }
258 
check_ip(char ip[4])259 int check_ip (char ip[4])
260 {
261   stralloc check = {0};
262   stralloc out = {0};
263   char ip_fmt[IP4_FMT];
264   char rev[4];
265   int i;
266   char s[FMT_ULONG];
267 
268   for (i = 0; i < 4; i++) rev[i] = ip[3-i];
269 
270   for (i = 0; i < rblnum; i++) {
271     stralloc_copyb (&check,ip_fmt, ip4_fmt (ip_fmt, rev));
272     stralloc_cats (&check, ".");
273     stralloc_cats (&check, rbl[i].server);
274 
275     if (verbose) {
276       do_log ();
277       if (rbl[i].otc) buffer_puts (buffer_log, "one time ");
278       buffer_puts (buffer_log, "checking: ");
279       buffer_put (buffer_log, check.s, check.len);
280       buffer_puts (buffer_log, "\n");
281       buffer_flush (buffer_log);
282     }
283 
284     if ((!rbl[i].txt || rbl[i].otc) && dns_ip4 (&out, &check) != -1) {
285       if (verbose) {
286         do_log ();
287         if (out.len) {
288           buffer_puts (buffer_log, "list #");
289           buffer_put (buffer_log, s, fmt_ulong (s,i+1));
290           buffer_puts (buffer_log, ", A record: ");
291           buffer_put (buffer_log, ip_fmt, ip4_fmt (ip_fmt, out.s));
292           buffer_puts (buffer_log, "\n");
293         } else {
294           buffer_put (buffer_log, ip_fmt, ip4_fmt (ip_fmt, ip));
295           buffer_puts (buffer_log, " no A record\n");
296         }
297         buffer_flush (buffer_log);
298       }
299 
300       if (out.len && !rbl[i].rev) {
301         stralloc_copyb (&why, ip_fmt, ip4_fmt (ip_fmt, ip));
302         stralloc_cats (&why, " is listed in ");
303         stralloc_cats (&why, rbl[i].server);
304         return (rbl[i].otc ? 0 : 1);
305       }
306 
307       if (!out.len && rbl[i].rev && !rbl[i].otc) {
308         stralloc_copyb (&why, ip_fmt, ip4_fmt (ip_fmt, ip));
309         stralloc_cats (&why, " is NOT listed in ");
310         stralloc_cats (&why, rbl[i].server);
311         return 1;
312       }
313     }
314 
315     if ((rbl[i].txt || rbl[i].otc) && dns_txt (&out, &check) != -1) {
316       if (verbose) {
317         do_log ();
318         if (out.len) {
319           buffer_puts (buffer_log, "list #");
320           buffer_put (buffer_log, s, fmt_ulong (s,i+1));
321           buffer_puts (buffer_log, ", TXT record: ");
322           buffer_put (buffer_log, out.s, out.len);
323           buffer_puts (buffer_log, "\n");
324         } else {
325           buffer_put (buffer_log, ip_fmt, ip4_fmt (ip_fmt, ip));
326           buffer_puts (buffer_log, " no TXT record\n");
327         }
328         buffer_flush (buffer_log);
329       }
330 
331       if (out.len && !rbl[i].rev) {
332         stralloc_copy (&why, &out);
333         return (rbl[i].otc ? 0 : 1);
334       }
335 
336       if (!out.len && rbl[i].rev) {
337         stralloc_copys (&why, "No TXTs for ");
338         stralloc_catb (&why, ip_fmt, ip4_fmt (ip_fmt, ip));
339         return 1;
340       }
341     }
342   }
343 
344   return 0;
345 }
346 
347 /* child process */
buffer_copy(ssout,ssin)348 int buffer_copy(ssout,ssin)
349 register buffer *ssout;
350 register buffer *ssin;
351 {
352   register int n;
353   register char *x;
354   stralloc tag = {0};
355 
356   if (tagspam) {
357      stralloc_copys (&tag, "X-Spam: yes\n");
358      if (buffer_put(ssout,tag.s,tag.len) == -1) return -3;
359 
360      stralloc_copys (&tag, "X-Spam-Status: ");
361      stralloc_cat (&tag, &why);
362      stralloc_cats (&tag, "\n");
363      if (buffer_put(ssout,tag.s,tag.len) == -1) return -3;
364 
365      stralloc_copys (&tag, "X-Spam-Version: ");
366      stralloc_cats (&tag, VERSION);
367      stralloc_cats (&tag, "\n");
368      if (buffer_put(ssout,tag.s,tag.len) == -1) return -3;
369   }
370 
371   for (;;) {
372     n = buffer_feed(ssin);
373     if (n < 0) return -2;
374     if (!n) return 0;
375     x = buffer_PEEK(ssin);
376     if (buffer_put(ssout,x,n) == -1) return -3;
377     buffer_SEEK(ssin,n);
378   }
379 }
380 
maildir_child(dir)381 void maildir_child(dir)
382 char *dir;
383 {
384  unsigned long pid;
385  unsigned long now;
386  char host[64];
387  char *s;
388  int loop;
389  struct stat st;
390  int fd;
391  char buf[BUF_LEN];
392  char outbuf[BUF_LEN];
393  buffer ss;
394  buffer ssout;
395 
396  signal (SIGALRM,sigalrm);
397  if (chdir(dir) == -1) { if (error_temp(errno)) _exit(1); _exit(2); }
398  pid = getpid();
399  host[0] = 0;
400  gethostname(host,sizeof(host));
401  for (loop = 0;;++loop)
402   {
403    now = time(0);
404    s = fntmptph;
405    s += fmt_str(s,"tmp/");
406    s += fmt_ulong(s,now); *s++ = '.';
407    s += fmt_ulong(s,pid); *s++ = '.';
408    s += fmt_strn(s,host,sizeof(host)); *s++ = 0;
409    if (stat(fntmptph,&st) == -1) if (errno == error_noent) break;
410    /* really should never get to this point */
411    if (loop == 2) _exit(1);
412    sleep(2);
413   }
414  str_copy(fnnewtph,fntmptph);
415  byte_copy(fnnewtph,3,"new");
416 
417  alarm(86400);
418  fd = open_excl(fntmptph);
419  if (fd == -1) _exit(1);
420 
421  buffer_init (&ss,read,0,buf,sizeof(buf));
422  buffer_init (&ssout,write,fd,outbuf,sizeof(outbuf));
423 
424  switch(buffer_copy(&ssout,&ss))
425   {
426    case -2: tryunlinktmp(); _exit(4);
427    case -3: goto fail;
428   }
429 
430  if (buffer_flush(&ssout) == -1) goto fail;
431  if (fsync(fd) == -1) goto fail;
432  if (close(fd) == -1) goto fail; /* NFS dorks */
433 
434  if (link(fntmptph,fnnewtph) == -1) goto fail;
435    /* if it was error_exist, almost certainly successful; i hate NFS */
436  tryunlinktmp(); _exit(0);
437 
438  fail: tryunlinktmp(); _exit(1);
439 }
440 
441 /* end child process */
wait_pid(wstat,pid)442 int wait_pid(wstat,pid) int *wstat; int pid;
443 {
444   int r;
445 
446   do
447     r = waitpid(pid,wstat,0);
448   while ((r == -1) && (errno == error_intr));
449   return r;
450 }
451 
ip4_equal(const char * ip1,const char * ip2)452 int ip4_equal (const char *ip1, const char *ip2)
453 {
454   register char i;
455 
456   for (i = 0; i < 4; i++)
457     if (*(ip1+i) != *(ip2+i)) return 0;
458 
459   return 1;
460 }
461 
ip4_isprivate(const unsigned char * ip)462 int ip4_isprivate (const unsigned char *ip)
463 {
464   /* 127.0.0.0 - 127.255.255.255 */
465   if (*ip == 127) return 1;
466 
467   /* 10.0.0.0 - 10.255.255.255 */
468   if (*ip == 10) return 1;
469 
470   /* 172.16.0.0 - 172.31.255.255 */
471   if (*ip == 172 && (*(ip+1) >> 4 == 1)) return 1;
472 
473   /* 192.168.0.0 - 192.168.255.255 */
474   if (*ip == 192 && *(ip+1) == 168) return 1;
475 
476   return 0;
477 }
478 
main(int argc,char * argv[])479 int main (int argc, char* argv[])
480 {
481   char ip[4];
482   stralloc partial = {0};
483   stralloc out = {0};
484   char ip_fmt[IP4_FMT];
485   char line[BUF_LEN];
486   char s[FMT_ULONG];
487   int r, i, j;
488   int inbuflen = 0;
489   int flag0 = 1;
490   int opt;
491   int block = 0;
492   int skip_priv = 0;
493   int list = 0;
494   int child;
495   int wstat;
496   buffer ssout;
497   buffer sslist;
498   int fd = 0;
499   int fdlist = 0;
500   char outbuf[BUF_LEN];
501   char outlist[BUF_LEN];
502 
503   buffer_log = buffer_2;
504 
505   while ((opt = getopt (argc, argv, "a:A:cC:hi:l:L:mpqr:R:svVx:X:")) != opteof)
506     switch(opt) {
507       case 'a':
508       case 'A':
509       case 'C':
510         rbl[rblnum].server = optarg;
511         rbl[rblnum].txt = 0;
512         rbl[rblnum].rev = (opt == 'a');
513         rbl[rblnum].otc = (opt == 'C');
514 
515         if (rblnum < 32-1) rblnum++;
516         break;
517       case 'c':
518         cond_redir = 1;
519         break;
520       case 'i':
521         scan_ulong (optarg, &ignore);
522         if (ignore < 1) ignore = 1;
523         if (ignore > 10) ignore = 10;
524         scan_all = 1;
525         break;
526       case 'l':
527         verbose = 1;
528         fd = open_append(optarg);
529         if (fd == -1)
530           strerr_die4sys (111,FATAL,"unable to write ",optarg,": ");
531         buffer_init (&ssout,write,fd,outbuf,sizeof(outbuf));
532         buffer_log = &ssout;
533         break;
534       case 'L':
535         list = 1;
536         fdlist = open_append(optarg);
537         if (fdlist == -1)
538           strerr_die4sys (111,FATAL,"unable to write ",optarg,": ");
539         buffer_init (&sslist,write,fdlist,outlist,sizeof(outlist));
540         break;
541       case 'm':
542         scan_all = 1;
543         break;
544       case 'p':
545         skip_priv = 1;
546         break;
547       case 'q':
548         quiet = 1;
549         verbose = 0;
550         break;
551       case 'r':
552       case 'R':
553         rbl[rblnum].server = optarg;
554         rbl[rblnum].txt = 1;
555         rbl[rblnum].rev = (opt == 'R');
556 	rbl[rblnum].otc = 0;
557 
558         if (rblnum < 32-1) rblnum++;
559 	break;
560       case 's':
561         tagspam = 1;
562         break;
563       case 'v':
564         verbose = 1;
565         break;
566       case 'V':
567         buffer_puts (buffer_2, VERSION); buffer_puts (buffer_2, " - ");
568         buffer_puts (buffer_2, AUTHOR); buffer_puts (buffer_2, "\n");
569         buffer_flush (buffer_2);
570         _exit (TEMP_FAIL);
571         break;
572       case 'x':
573         if (iptoskip < 16)
574           if (ip4_scan (optarg, skipip+(iptoskip*4)))
575             iptoskip++;
576           else
577             strerr_die3x (111, FATAL, "unable to parse IP ", optarg);
578         break;
579       case 'X':
580 	cdbrules = optarg;
581 	break;
582       case 'h':
583       default:
584         usage ();
585     }
586 
587   argc -= optind;
588   argv += optind;
589 
590   /* set verbose mode if we want to log */
591   if (fd) verbose = 1;
592 
593   if (!rblnum && !cdbrules)
594     strerr_die2x (111, FATAL, "you must supply one or more RLB list address or a CDB rule file");
595 
596   /* check if a delivery dir was given */
597   if (argc > 0) {
598     cond_redir = 0;
599     delivery = argv[0];
600 
601     if ((*delivery != '.' && *delivery != '/') || *(delivery+str_len (delivery)-1) != '/')
602       strerr_die2x (111, FATAL, "spam delivery path must start with . or / and end with /.");
603   }
604 
605   dns_random_init(seed);
606 
607   if (!stralloc_copys (&partial, "")) nomem ();
608 
609   while (flag0 || inbuflen || partial.len) {
610     if (flag0)
611       if (inbuflen < sizeof line) {
612         r = read (0, line+inbuflen, sizeof line-inbuflen);
613 
614         if (r <= 0)
615           flag0 = 0;
616         else
617           inbuflen += r;
618       }
619 
620     while (flag0) {
621       i = byte_chr (line, inbuflen, '\n');
622       if (inbuflen && (i == inbuflen)) {
623         if (!stralloc_catb (&partial, line, inbuflen)) nomem ();
624         inbuflen = 0;
625         continue;
626       }
627 
628       if ((i < inbuflen) || (!flag0 && partial.len)) {
629         if (i < inbuflen) ++i;
630         if (!stralloc_catb (&partial, line, i)) nomem ();
631 
632         inbuflen -= i;
633         for (j = 0; j < inbuflen; ++j) line[j] = line[j + i];
634 
635         /* end of header */
636         if (partial.len == 1) {
637           inbuflen = partial.len = flag0 = 0;
638           break;
639         }
640 
641         if (partial.len && flag0) {
642           if (verbose) {
643             do_log ();
644             buffer_puts (buffer_log, "header: ");
645             buffer_put (buffer_log, partial.s, partial.len);
646             buffer_flush (buffer_log);
647           }
648 
649           if (str_start (partial.s, "Received: from ")) {
650 /* OLD SCAN MODE
651             for (j = 15; flag0 && j < partial.len-8; j++) {
652    OLD SCAN MODE */
653             for (j = str_rchr (partial.s, '(')+1; flag0 && j; j--) {
654               i = ip4_scan (partial.s+j, ip);
655               if (i) {
656                 /* skip listed IP */
657                 for (r = 0; r < iptoskip; r++)
658                   if (ip4_equal(ip, skipip+r*4)) {
659                     j += i-1;
660                     i = 0;
661 
662                     if (verbose) {
663                       do_log ();
664                       buffer_puts (buffer_log, "skip IP: ");
665                       stralloc_copyb (&out,ip_fmt, ip4_fmt (ip_fmt, ip));
666                       buffer_put (buffer_log, out.s, out.len);
667                       buffer_puts (buffer_log, "\n");
668                     }
669                   }
670 
671                 /* skip private IP */
672                 if (skip_priv && ip4_isprivate (ip)) {
673                   j += i-1;
674                   i = 0;
675                   if (verbose) {
676                     do_log ();
677                     buffer_puts (buffer_log, "skip private IP: ");
678                     stralloc_copyb (&out,ip_fmt, ip4_fmt (ip_fmt, ip));
679                     buffer_put (buffer_log, out.s, out.len);
680                     buffer_puts (buffer_log, "\n");
681                   }
682                 }
683 
684                 /* now we can check... */
685                 if (i) {
686                   if (!ignore) {
687 		    if (cdbrules) {
688 		      block = check_cdb (ip);
689 		      if (block == -1) block = check_ip (ip);
690 		    } else
691                       block = check_ip (ip);
692 		  } else
693                     ignore--;
694 
695                   j += i-1;
696 
697                   if (!scan_all)
698                     flag0 = 0;
699                   else if (block)
700                     flag0 = 0;
701 
702                   if (!flag0) inbuflen = 0;
703                 }
704               }
705             }
706           }
707         }
708 
709         partial.len = 0;
710         continue;
711       }
712 
713       break;
714     }
715   }
716 
717   if (block) {
718     /* append blocked IP to the list */
719     if (list) {
720       stralloc_copyb (&out,ip_fmt, ip4_fmt (ip_fmt, ip));
721       buffer_put (&sslist, out.s, out.len);
722       buffer_puts (&sslist, "\n");
723       buffer_flush (&sslist);
724     }
725 
726     /* report why we block into qmail-send logs */
727     if (!quiet) {
728       if (delivery) {
729         buffer_puts (buffer_2, "qmail-rblchk[spam]: writing to ");
730         buffer_puts (buffer_2, delivery);
731         buffer_puts (buffer_2, "\n");
732       } else {
733         buffer_puts (buffer_2, "qmail-rblchk[spam]: ");
734         buffer_put (buffer_2, why.s, why.len);
735         buffer_puts (buffer_2, "\n");
736       }
737       buffer_flush (buffer_2);
738     }
739 
740     if (delivery) {
741       if (verbose) {
742         do_log ();
743         buffer_puts (buffer_log, "spam, writing to ");
744         buffer_puts (buffer_log, delivery);
745         buffer_puts (buffer_log, "\n");
746         buffer_flush (buffer_log);
747       }
748 
749       /* write spam to spam maildir */
750       if (lseek (0, 0, SEEK_SET) == -1)
751         strerr_die1x (111, "Unable to rewind message. (#4.3.0)");
752 
753       switch (child = fork ()) {
754         case -1:
755           break;
756         case 0:
757           maildir_child (delivery);
758           _exit(111);
759       }
760 
761       wait_pid (&wstat, child);
762       if (wstat & 127)
763         strerr_die1x(111,"Aack, child crashed. (#4.3.0)");
764 
765       switch ((wstat >> 8)) {
766         case 0: break;
767         case 2: strerr_die1x(111,"Unable to chdir to maildir. (#4.2.1)");
768         case 3: strerr_die1x(111,"Timeout on maildir delivery. (#4.3.0)");
769         case 4: strerr_die1x(111,"Unable to read message. (#4.3.0)");
770         default: strerr_die1x(111,"Temporary error on maildir delivery. (#4.3.0)");
771       }
772     } else {
773       if (verbose) {
774         do_log ();
775         buffer_puts (buffer_log, "spam, exit code: ");
776         buffer_put (buffer_log, s, fmt_ulong (s,(block ? EXIT_SPAM : EXIT_OK)));
777         buffer_puts (buffer_log, "\n");
778         buffer_flush (buffer_log);
779       }
780     }
781   } else {
782     if (verbose) {
783       do_log ();
784       buffer_puts (buffer_log, "ok, default delivery\n");
785     }
786   }
787 
788   buffer_flush (buffer_log);
789   if (fd) {
790     fsync(fd);
791     close(fd);
792   }
793 
794   if (fdlist) {
795     fsync(fdlist);
796     close(fdlist);
797   }
798 
799   _exit (block ? EXIT_SPAM : EXIT_OK);
800 }
801