dnsrbl.c

Nikola Vladov <[email protected]> Thu, 19 Aug 2010 12:55:29 +0300
Newsgroups gmane.network.djbdns
Message-ID <[email protected]>
I wrote next for me.  I use ir to test if IP(s) is in RBL list.
Maybe it is useful for some people

Enjoy, Nikola


-------------------------------------------------------
/* dnsrbl.c
   save me under dnstxt.c in djbdns-1.05 and recompile.
   then: mv dnstxt dnsrbl
   or
   diet -Os gcc -Wall -W -DUSE_LIBOWFAT -s -o dnsrbl dnsrbl.c -lowfat

   author: Nikola Vladov
   http://riemann.fmi.uni-sofia.bg/programs/
   See some examples at the end of this file!
*/

#define MAX_RBL_CONNECTIONS	10
#define RBLSERVERS \
	"sbl.spamhaus.org\n" \
	"xbl.spamhaus.org\n" \
	"zen.spamhaus.org\n" \
	"dnsbl.njabl.org\n" \
	"bl.spamcop.net\n" \
	"cbl.abuseat.org\n" \
	"spam.dnsbl.sorbs.net"

#include "dns.h"
#include "fmt.h"
#include "scan.h"
#include "byte.h"
#include "openreadclose.h"
#include "buffer.h"
#include <alloca.h>

extern int close(int);
extern void _exit(int);

#ifndef USE_LIBOWFAT
#include "strerr.h"
#include "env.h"
char buf_1sp[128];
buffer buf_rbl = BUFFER_INIT(buffer_unixwrite,1,buf_1sp,sizeof(buf_1sp));
#define buffer_rbl_1 &buf_rbl

#else
#include "errmsg.h"
#define strerr_die3x(n,a,b,c)	die(n,a,b,c)
#define strerr_die2x(n,a,b)	die(n,a,b)
#define strerr_die1x(n,a)	die(n,a)
#define strerr_die2sys(n,a,b)	diesys(n,a)
#define strerr_die4sys(n,a,b,c,d)	diesys(n,a,b,c)
#define strerr_warn6(a,b,c,d,e,f,E)	carpsys(a,b,c,d,e);
#define env_get getenv
#define buffer_rbl_1 buffer_1small
#endif

#define B_p(s,l)	buffer_put(buffer_rbl_1, s, l)
#define B_ps(st)	buffer_puts(buffer_rbl_1, st)
#define B_f		buffer_flush(buffer_rbl_1)

#define FATAL "dnsrbl: fatal: "
#define WARNING "dnsrbl: warning: "
#define MSG_HELP \
  "usage: dnsrbl [options] ip.ad.dr.ess a[-b].c[-d].e[-f].g[-h] ...\n"\
  "options: [-lv] [-fRBLfile] [-cNumber]\n"\
  "environ: DNSCACHEIP, DNSRBLSERVERS"

#define Xfor(x,from) for (x=ip_buf[from]; x<=ip_buf[from+4]; x++)
#define set_t(i) struct line *t = xxx + i
#define set_ip(z, t, srv) { char *z = t->ip;\
    z[0]=A; z[1]=B; z[2]=C; z[3]=D; }\
    t->rbl = *srv

struct line {
  char *rbl;	/* if (rbl) -> active */
  iopause_fd *io;
  struct dns_transmit d;
  char ip[4];
} *xxx;

static iopause_fd *io;
static stralloc fqdn, sa, out;
static char txt_ip[20];

static int exit_status, flagdebug, resolv_conf, io_flush;
static unsigned int numactive, left;
unsigned int maxactive = MAX_RBL_CONNECTIONS;

unsigned int splitmem(char **v, char *s, char c) /*EXTRACT_INCL*/ {
  if (v) {
    char **w=v;
    *w++=s;
    for (;;) {
      while (*s && *s!=c) s++;
      if (*s==0) break;
      *s=0;
      *w++ = ++s;
    }
    *w=0;
    return (w-v);
  } else {
    unsigned int n=1;
    for (; *s; s++) if (*s==c) n++;
    return n;
  }
}

unsigned int scan_rbl(char *s, unsigned int b[8]) {
  unsigned int k, n;
  unsigned long u;
  char *x = s;
  for (n=0;; n++) {
    k = scan_ulong(x, &u); x += k; u %= 256; b[0] = u; if (k==0) return 0;
    if (*x == '-') { k = scan_ulong(++x, &u); x += k; if (k==0) u = 255; }
    b[4] = u % 256;
    if (b[0] > b[4]) return 0;
    ++b;

    if (n == 3) break;
    if (*x != '.') return 0;
    ++x;
  }
  return x-s;
}

unsigned int fmt_rbl(char *s, char *b, int reverse) {
  char *t=s;
  int n;
  for (n=0; n<4; n++) {
    unsigned char c = (reverse) ? b[3-n] : b[n];
    t += fmt_ulong(t, c);
    if (n == 3) break;
    *t++ = '.';
  }
  *t = 0;
  return t-s;
}

void got_err(struct line *t) {
  exit_status |= 2;
  fmt_rbl(txt_ip, t->ip, 0);
  strerr_warn6(WARNING,"unable to find TXT records for ",
	       txt_ip, "-",t->rbl,": ",&strerr_sys);
}

void got_rbl(struct line *t) {
  fmt_rbl(txt_ip, t->ip, 0);
  B_ps(txt_ip);
  if (flagdebug) {
    B_ps("-");
    B_ps(t->rbl);
  }
  B_ps(":\t");
  B_p(out.s, out.len);
  B_ps("\n");
  B_f;
  exit_status |= 1;
}

void io_loop() {
  while ((io_flush && numactive) || numactive >= maxactive) {
    struct taia stamp, deadline;
    unsigned int kactive, i, k;

    taia_now(&stamp);
    taia_uint(&deadline,120);
    taia_add(&deadline,&deadline,&stamp);

    for (i=0, k=0; k<numactive; ++i) {
      set_t(i);
      if (t->rbl) {
	t->io = io + k++;
	dns_transmit_io(&t->d, t->io, &deadline);
      }
    }
    iopause(io,k,&deadline,&stamp);

    for (i=0, k=0, kactive=numactive; k < kactive; ++i) {
      set_t(i);
      if (t->rbl) {
	int r = dns_transmit_get(&t->d, t->io, &stamp);
	++k;
	if (r == 0) continue;

	if (r == -1) got_err(t);
	else {
	  if (dns_txt_packet(&out,t->d.packet,t->d.packetlen) ==-1) got_err(t);
	  if (flagdebug > 1 || out.len) got_rbl(t);
	}

	--numactive;
	t->rbl = 0;
	if (i < left) left = i;
      }
    }
  }
}

void nomem() { strerr_die2x(111,FATAL,"out of memory"); }

int main(int argc,char **argv) {
  int flaglist=0;
  char *buf=0, *rbl=0, *Q=0;
  char **rblservers, **p, **q;
  char split_char = '\n', base[] = RBLSERVERS;
  unsigned int len, A,B,C,D, ip_buf[8];

  char servers[16 * sizeof(xxx->d.localip)];
  static char localip[sizeof(xxx->d.localip)];

  close(0); ++argv; (void)argc;

  while (*argv && **argv == '-') {
    char *opt, *z=argv[0]+1;
    for (; *z; z++) {
      switch (*z) {
      case 'l': flaglist++; break;
      case 'v': flagdebug++; break;
      case 'f':
      case 'c':
	opt = z++;
	if (*z == 0) {
	  z = *++argv;
	  if (z == 0)
	    strerr_die3x(100, "Option -",opt," requires an argument");
	}

	if (*opt == 'f') rbl = z;
	else {
	  unsigned long u;
	  scan_ulong(z,&u);
	  if (u < 1) u = 1;
	  if (u > 1000) u = 1000;
	  maxactive = u;
	}
	goto next;
      default:
	strerr_die1x(100, MSG_HELP);
      }
    }
  next:
    argv++;
  }

  if (rbl == 0) {
    buf = env_get("DNSRBLSERVERS");
    if (buf) split_char = ':';
  } else {
    if (1 != openreadclose(rbl, &sa, 128))
      strerr_die4sys(100,FATAL,"error reading ",rbl,": ");
    if (!stralloc_0(&sa)) nomem();
    buf = sa.s;
  }
  if (buf==0) buf = base;

  len = splitmem(0, buf, split_char);
  rblservers = alloca((len+2) * sizeof(char *));
  splitmem(rblservers, buf, split_char);

  for (q=rblservers, p=rblservers; *p; p++)
    if ((unsigned char)((**p | 32) - 'a') < 26) *q++ = *p;
  *q=0;

  if (flaglist) {
    for (p=rblservers; *p; B_ps("\n"), p++)
      B_ps(*p);
    B_f;
    _exit(0);
  }

  if (!stralloc_ready(&fqdn,40)) nomem();
  io = alloca(maxactive * sizeof(iopause_fd));

  len = maxactive * sizeof(struct line);
  xxx = alloca(len);
  byte_zero(xxx, len);

  while (*argv) {
    if (!scan_rbl(*argv, ip_buf)) {
      ++io_flush;
      io_loop();
      strerr_die3x(111,FATAL,"unable to parse IP address ",*argv);
    }

    Xfor(A,0) Xfor(B,1) Xfor(C,2) Xfor(D,3) for (p=rblservers; *p; p++) {
      for (;; left++) {
	set_t(left);
	if (t->rbl == 0) {
	  set_ip(zzz, t, p);

	  fqdn.len = fmt_rbl(fqdn.s, t->ip, 1);
	  fqdn.s[fqdn.len++] = '.';
	  if (!stralloc_cats(&fqdn, t->rbl)) nomem();

	  if (resolv_conf==0) {
	    resolv_conf = 1024;
	    if (dns_resolvconfip(servers) == -1)
	      strerr_die2sys(111,FATAL,"unable to read /etc/resolv.conf: ");
	  } else resolv_conf--;

	  if (dns_domain_fromdot(&Q, fqdn.s, fqdn.len) == 0 ||
	      dns_transmit_start(&t->d,servers,1,Q,DNS_T_TXT,localip) == -1) {
	    got_err(t);
	    t->rbl = 0;
	  } else {
	    ++numactive;
	    io_loop();
	  }

	  break;
	}
      }
    }
    ++argv;
    if (*argv == 0) { ++io_flush; io_loop(); }
  }
  _exit(exit_status);
}


#if 0
dnsrbl -h
dnsrbl 127.0.0.2
dnsrbl -v 127.0.0.2-30
dnsrbl -vv -c30 127.0.0.2-30
dnsrbl -c100 208.66.77.60-80
DNSCACHEIP=8.8.8.8 dnsrbl -c1 208.66.70-80.60-70
dnsrbl 20-.20.40.50
dnsrbl -l
dnsrbl -fSome_RBL_File 127.0.0.2
DNSRBLSERVERS=zen.spamhaus.org dnsrbl -c100 127.0.0.0-255
DNSCACHEIP=8.8.8.8 DNSRBLSERVERS=zen.spamhaus.org dnsrbl -c20 127.0.0.0-255
DNSRBLSERVERS=sbl.spamhaus.org:xbl.spamhaus.org dnsrbl -c20 127.0.0.1-12

Format of RBL_File (option -f)
Text lines.  If the first letter of a line is different from [a-zA-Z]
this line is comment.  Test it with:
dnsrbl -l -fRBL_File

The manual page is missing currently...
Version: Sat Aug 19 07:40:02 UTC 2010
#endif