dnsrbl.c

Nikola Vladov <[email protected]> 31 Jul 2010 10:38:58 -0000
Newsgroups gmane.linux.lib.dietlibc
Message-ID <[email protected]>
This is a command line utility to test if an IP(s) is in RBL-srever list.
See the examples at the end of this message.  Maybe I will post it
in djbdns mailing list.  I expect first some comment here :)

It work asynchronous like dnsfilter.  I use it te test if a network
send spam (tcpserver: allow, deny).

Enjoy, Nikola



/* dnsrbl.c 
   safe 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 "buffer.h"

#ifndef USE_LIBOWFAT
#include "exit.h"
#include "strerr.h"
#include "env.h"

#else
#include <unistd.h>
#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
#endif

#include "dns.h"
#if defined(DNS_NAME6_DOMAIN) || defined(USE_LIBOWFAT)
static char servers[256], localip[16];		/* libowfat */
#else
static char servers[64],  localip[4];
#endif

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

#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 B_p(s,l)	buffer_put(buffer_1, s, l)
#define B_ps(st)	buffer_puts(buffer_1, st)
#define B_f		buffer_flush(buffer_1)

#define do_it(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) char *z = t->ip;\
  z[0]=A; z[1]=B; z[2]=C; z[3]=D;\
  t->rbl = *p

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

static stralloc fqdn, sa;
static char *Q, txt_ip[20];

static int exit_status, flagdebug;
static unsigned int numactive, left, A,B,C,D;
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 char *b) {
  unsigned int k, n;
  unsigned long u;
  char *x = s;
  for (n=0;; n++) {
    k = scan_ulong(x, &u); x += k; b[0] = b[8] = u; if (k==0) return 0;

    u = *b;
    if (*x == '-') { k = scan_ulong(++x, &u); x += k; if (k==0) u = 255; }
    if (u < *b) return 0;
    b[4] = u;
    ++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(t->out.s, t->out.len);
  B_ps("\n");
  B_f;
  exit_status |= 1;
}

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

int main(int argc,char **argv) {
  int flaglist=0;
  char *buf = 0, *rbl = 0;
  char **rblservers, **p, **q;
  char split_char = '\n';
  char base[] = RBLSERVERS;
  struct taia stamp;
  struct taia deadline;
  unsigned int i,k;
  int r,got_last=0;
  unsigned char ip_buf[8];
  iopause_fd *io;

  (void)argc;

  while (*++argv && **argv == '-') {
    char *z=argv[0]+1;
    for (; *z; z++) {
      unsigned long u;
      switch (*z) {
      case 'l': flaglist++; break;
      case 'v': flagdebug++; break;
      case 'f': rbl = z+1; goto next;
      case 'c':
	scan_ulong(z+1,&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;

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

  for (q=rblservers, p=rblservers; *p; p++) {
    unsigned char c = **p;
    if (c >= 'a') c -= 32;
    c -= 'A';
    if (c <= 26) *q++ = *p;
  }
  *q=0;

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

  if (!stralloc_ready(&fqdn,40)) nomem();

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

  io = alloca(maxactive * sizeof(iopause_fd));

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

    do_it(A,0) do_it(B,1) do_it(C,2) do_it(D,3) for (p=rblservers; *p; p++) {

      if (!got_last && numactive < maxactive) {
	for (;;) {
	  set_t(left++);
	  if (t->io == 0) {
	    set_ip(zzz, t);

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

	    if (dns_resolvconfip(servers) == -1)
	      strerr_die2sys(111,FATAL,"unable to read /etc/resolv.conf: ");

	    if (!dns_domain_fromdot(&Q, fqdn.s, fqdn.len) ||
		dns_transmit_start(&t->dt,servers,1,Q,DNS_T_TXT,localip) ==-1)
	      got_err(t);
	    else {
	      t->io += 1;
	      ++numactive;
	    }
	    break;
	  }
	}
      }

    again:
      while ((got_last && numactive) || numactive >= maxactive) {
	unsigned int kactive;
	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->io) {
	    t->io = io + k++;
	    dns_transmit_io(&t->dt, t->io, &deadline);
	  }
	}
	iopause(io,k,&deadline,&stamp);

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

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

	    if (r == -1) got_err(t);
	    else {
	      if (dns_txt_packet(&t->out, t->dt.packet,
				 t->dt.packetlen) == -1) got_err(t);
	      if (flagdebug > 1 || t->out.len) got_rbl(t);
	    }
	  }
	}
      }
    }
    if (numactive) { got_last=1; goto again; }
    got_last=0;
    argv++;
  }
  _exit(exit_status);
}


#if 0
Do not put spaces after options with arguments!

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.  Do not put spaces or tabs at the end of line.
Test it with:
dnsrbl -l -fRBL_File

The manual page is missing currently...
Version: Sat Jul 31 09:31:02 UTC 2010
#endif