/***************************************************************************
 *   Copyright (C) 2010 by Velan.   *
 *   vlabs.c@gmail.com  *
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 *   This program is distributed in the hope that it will be useful,       *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
 *   GNU General Public License for more details.                          *
 *                                                                         *
 *   You should have received a copy of the GNU General Public License     *
 *   along with this program; if not, write to the                         *
 *   Free Software Foundation, Inc.,                                       *
 *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
 ***************************************************************************/
// 0.0.5 - included ethernet header, modified ARP header, changing APR processing by including ntohs; etc
// dependencies - libpcap -> tcpdump.org
// compilation - g++ arp_sniff.cc -o arp_sniff -lpcap -w 


#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

//#include <iostream>
//#include <cstdlib>

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

//#include <features.h>
#include <sys/types.h>
#include <sys/socket.h>

#include <netinet/in.h>
#include <arpa/inet.h>
#include <netinet/tcp.h>

#include <pcap.h>

using namespace std;

void List_Devices();
void Sniff_Device(char *Device);
void Got_ARP_Packet(u_char *args, const struct pcap_pkthdr *header, const u_char *packet);

	// Ethernet header
typedef u_int32_t tcp_seq;



main(int argc, char *argv[])
{
	//printf("Shellcode detector. \n");
	char *ArgDevice;
	ArgDevice = (char*)malloc(256);
	bzero(ArgDevice, 256);

	printf("Sniffer Lite. by Velan. vlabs.c@gmail.com\nThis module contains the ARPSniffer.\n");
	if(argc < 2)
	{
		printf("\nUsage: %s [ld] [device name]\narg\t\tfunction\n", argv[0]);
		printf("-l\t\tlisting available devices\n");
		printf("-d\t\tuse default device\n");
		printf("\n");
		exit(0);
	}
	else if(strncmp(argv[1], "-l", 2) == 0)
	{
		List_Devices();
	}
	else if(strncmp(argv[1], "-d", 2) == 0)
	{
		printf("Selecting default device. ");
		snprintf(ArgDevice, 3, "%s", "-d");
		Sniff_Device(ArgDevice);
	}
	else if(argc == 2 && strncmp(argv[1], "-l", 2) != 0 && strncmp(argv[1], "-d", 2) != 0)
	{
		snprintf(ArgDevice, 256, "%s", argv[1]);
		Sniff_Device(ArgDevice);
	}
}

void List_Devices()
{
	pcap_if_t *All_Devices_SP, *d;
	int ret = 0;
	char pcap_errbuf[PCAP_ERRBUF_SIZE];

	printf("Listing available devices.\n");
	ret = pcap_findalldevs(&All_Devices_SP, pcap_errbuf);
	if(ret == -1)
	{
		printf("Error in looking up all available devices. %s\n", pcap_errbuf);
		exit(-1);
	}
	
	printf("\n----------------------------\nDevice\t\tDescription\n----------------------------\n");
	for(d = All_Devices_SP; d; d = d->next)
	{
		printf("%s\t\t%s\n", d->name, d->description);

	}
	printf("\n");
	exit(0);
}


void Sniff_Device(char *Device)
{
	char *dev = NULL;
	char pcap_errbuf[PCAP_ERRBUF_SIZE];
	const u_char *packet = NULL;
	int ret = 0, i = 0;
	unsigned int ts = 0;

	pcap_t *pcap_handle;
	bpf_u_int32 pcap_device_mask;
	bpf_u_int32 pcap_device_ip;
	struct pcap_pkthdr header;
	struct bpf_program filter;
	
	dev = (char*) malloc(256);
	if(strncmp(Device, "-d", 2) == 0)
	{
		dev = pcap_lookupdev(pcap_errbuf);
		printf("Default device: %s, ", dev);
	}
	else
	{	
		snprintf(dev, 256, "%s", Device);
	}

	if(dev == NULL)
	{
		printf("Error in looking up default device. %s\n", pcap_errbuf);
		exit(0);
	}
	ret = pcap_lookupnet(dev, &pcap_device_ip, &pcap_device_mask, pcap_errbuf);
	if(ret == -1)
	{
		printf("Error in lookup device. %s\n", pcap_errbuf);
		exit(0);
	}	
	printf("opening the device %s for sniffing\n", dev);

	// print sniffing IP - start
	ts = pcap_device_ip;
	ts = (ts << 24) >> 24;
	printf("Sniffing device IP: %u.", ts);

	ts = pcap_device_ip;
	ts = (ts << 16) >> 24;
	printf("%u.", ts);

	ts = pcap_device_ip;
	ts = (ts << 8) >> 24;
	printf("%u.", ts);

	ts = pcap_device_ip;
	ts = (ts >> 24);
	printf("%u, ", ts);
	// print sniffing IP - end

	// print sniffing IP Mask - start
	ts = pcap_device_mask;
	ts = (ts << 24) >> 24;
	printf("Mask: %u.", ts);

	ts = pcap_device_mask;
	ts = (ts << 16) >> 24;
	printf("%u.", ts);
	
	ts = pcap_device_mask;
	ts = (ts << 8) >> 24;
	printf("%u.", ts);

	ts = pcap_device_mask;
	ts = (ts >> 24);
	printf("%u\n", ts);
	// print sniffing IP Mask - end
	printf("\n\n");

	pcap_handle = pcap_open_live(dev, 1000, 0, 10000, pcap_errbuf);
	if(pcap_handle == NULL){	printf("Error!\n");	}

	pcap_compile(pcap_handle, &filter, "arp", 1, pcap_device_mask);	// create filter to grab only ARP
	pcap_setfilter(pcap_handle, &filter);				// apply the filter to the handle
	printf("______________________________________________________\n\n");
	pcap_loop(pcap_handle, -1, Got_ARP_Packet, NULL);		// -1 means unlimited packet
	
}


void Got_ARP_Packet(u_char *args, const struct pcap_pkthdr *header, const u_char *packet)
{

typedef struct EthernetHeader_StructMain{
	u_char DestinationMACAddress[6];
	u_char SourceMACAddress[6];
	u_short Type;
}Struct_EthernetHeader;					// 14 bytes


typedef struct ARPHeader_StructMain{
	u_int16_t HardwareType;				// hardware type 
	u_int16_t ProtocolType;				// protocol type

	u_char HardwareAddressLength;			// harware address length
	u_char ProtocolAddressLength;			// protocol address length
	u_int16_t Opcode;				// opcode - request, reply, re request

	u_char SourceHardwareAddress[6];		// source MAC address
	u_char SourceProtocolAddress[4];		// source IP address
	u_char DestinationHardwareAddress[6];		// target MAC address
	u_char DestinationProtocolAddress[4];		// target IP address
}Struct_ARPHeader;					// 28 bytes

	unsigned int ts = 0, j = 0 ;
	u_int16_t HardwareType_Host = 0, ProtocolType_Host = 0, Opcode_Host = 0;
	u_char HardwareAddressLength_Host = 0, ProtocolAddressLength_Host = 0;

	Struct_EthernetHeader *EthernetHeader = NULL;
	Struct_ARPHeader *ARPHeader = NULL;
	bzero(&EthernetHeader, sizeof(Struct_EthernetHeader));
	bzero(&ARPHeader, sizeof(Struct_ARPHeader));
	
	EthernetHeader = (Struct_EthernetHeader *) packet;
	ARPHeader = (Struct_ARPHeader *) (packet+14);

	HardwareType_Host = ntohs(ARPHeader->HardwareType);
	ProtocolType_Host = ntohs(ARPHeader->ProtocolType);
	Opcode_Host = ntohs(ARPHeader->Opcode);

	HardwareAddressLength_Host = ARPHeader->HardwareAddressLength;
	ProtocolAddressLength_Host = ARPHeader->ProtocolAddressLength;

	// print Ethernet header - 14 bytes - start

	printf("Ethernet packet - Frame II: ");
	// print source MAC address
	for(j = 0; j < 6; j++)
	{	
		//printf("(%u, %02X)", ARPHeader->SourceHardwareAddress[j], ARPHeader->SourceHardwareAddress[j]);
		printf("%02X", EthernetHeader->SourceMACAddress[j]);
		if(j != 5){	printf(":");	}
	}
	printf(" > ");
	// print destination MAC address
	for(j = 0; j < 6; j++)
	{	
		//printf("(%u, %02X)", ARPHeader->SourceHardwareAddress[j], ARPHeader->SourceHardwareAddress[j]);
		printf("%02X", EthernetHeader->DestinationMACAddress[j]);
		if(j != 5){	printf(":");	}
	}
	// print Ethernet type
	printf(", Type: 0x%X", ntohs(EthernetHeader->Type));

	// print Ethernet header - 14 bytes - end

	printf("\n");

	// print ARP header - 28 bytes - start

	printf("ARP Header: ");
	
	// print source ip address
	for(j = 0; j < 4; j++)
	{	
		printf("%d", ARPHeader->SourceProtocolAddress[j]);
		if(j != 3){	printf(".");	}
	}	
	printf(" > ");
	// print target ip address
	for(j = 0; j < 4; j++)
	{	
		printf("%d", ARPHeader->DestinationProtocolAddress[j]);
		if(j != 3){	printf(".");	}
	}	
	
	printf("\t\t=\t");
	//printf("\n\t\t");
	printf("MAC: ");

	// print source hardware address
	for(j = 0; j < 6; j++)
	{	
		//printf("(%u, %02X)", ARPHeader->SourceHardwareAddress[j], ARPHeader->SourceHardwareAddress[j]);
		printf("%02X", ARPHeader->SourceHardwareAddress[j]);
		if(j != 5){	printf(":");	}
	}
	printf(" > ");
	// print target hardware address
	for(j = 0; j < 6; j++)
	{	
		printf("%02X", ARPHeader->DestinationHardwareAddress[j]);
		if(j != 5){	printf(":");	}
	}
	printf("\n\t\t");

	//print hardware type - start
	printf("Hardware type: ");
	
	if(HardwareType_Host == 0)
	{	printf("Reserved");	}
	else if(HardwareType_Host == 1)
	{	printf("Ethernet");	}
	else if(HardwareType_Host == 2)
	{	printf("Experimental Ethernet");	}
	else if(HardwareType_Host == 3)
	{	printf("Amateur Radio AX25");	}
	else if(HardwareType_Host == 4)
	{	printf("Proteon ProNET Token Ring");	}
	else if(HardwareType_Host == 5)
	{	printf("Chaos");	}
	else if(HardwareType_Host == 6)
	{	printf("IEEE 802");	}
	else if(HardwareType_Host == 7)
	{	printf("ARCNET");	}
	else if(HardwareType_Host == 8)
	{	printf("Hyperchannel");	}
	else if(HardwareType_Host == 9)
	{	printf("Lanstar");	}
	else if(HardwareType_Host == 10)
	{	printf("Atuonet Short Address");	}
	else if(HardwareType_Host == 11)
	{	printf("LocalTalk");	}
	else if(HardwareType_Host == 12)
	{	printf("LocalNet (IBM PCNet or SYTEK LocalNET)");	}
	else if(HardwareType_Host == 13)
	{	printf("Ultra link");	}
	else if(HardwareType_Host == 14)
	{	printf("SMDS");	}
	else if(HardwareType_Host == 15)
	{	printf("Frame Relay");	}
	else if(HardwareType_Host == 16)
	{	printf("ATM, Asynchronous Transmission Mode");	}
	else if(HardwareType_Host == 17)
	{	printf("HDLC");	}
	else if(HardwareType_Host == 18)
	{	printf("Fibre Channel");	}
	else if(HardwareType_Host == 19)
	{	printf("ATM, Asynchronous Transmission Mode");	}
	else if(HardwareType_Host == 20)
	{	printf("Serial Line");	}
	else if(HardwareType_Host == 21)
	{	printf("ATM, Asynchronous Transmission Mode");	}
	else if(HardwareType_Host == 22)
	{	printf("MIL-STD-188-220");	}
	else if(HardwareType_Host == 23)
	{	printf("Metricom");	}
	else if(HardwareType_Host == 24)
	{	printf("IEEE 1394.1995");	}
	else if(HardwareType_Host == 25)
	{	printf("MAPOS");	}
	else if(HardwareType_Host == 26)
	{	printf("Twinazial");	}
	else if(HardwareType_Host == 27)
	{	printf("EUI-64");	}
	else if(HardwareType_Host == 28)
	{	printf("HIPARP");	}
	else if(HardwareType_Host == 29)
	{	printf("IP and ARP over ISO 7816-3");	}
	else if(HardwareType_Host == 30)
	{	printf("ARPSec");	}
	else if(HardwareType_Host == 31)
	{	printf("IPsec tunnel");	}
	else if(HardwareType_Host == 32)
	{	printf("Infiniband");	}
	else if(HardwareType_Host == 33)
	{	printf("CAI, TIA-102 Project 25 Common Air Interface");	}
	else if(HardwareType_Host == 34)
	{	printf("Wiegand Interface");	}
	else if(HardwareType_Host == 35)
	{	printf("Pure IP");	}
	else if(HardwareType_Host == 36)
	{	printf("HW_EXP1");	}
	else if(HardwareType_Host > 36 && HardwareType_Host <256)
	{	printf("Unknown");	}
	else if(HardwareType_Host == 256)
	{	printf("HW_EXP2");	}
	else if(HardwareType_Host > 256 && HardwareType_Host <65535)
	{	printf("Unknown");	}
	else if(HardwareType_Host == 65535)
	{	printf("Reserved");	}
	printf(" (0x%X)", HardwareType_Host);
	// print hardware type - end

	// print protocol type - start
	printf(", ");
	printf("Protocol: ");
	if(ProtocolType_Host == 0x0800)		// 0x0800 - IP
	{	printf("IP ");	}
	printf(" (0x%X)", ProtocolType_Host);
	// print protocol type - end

	
	// print opcode type - start
	printf(", ");
	printf("Opcode: ");
	if(Opcode_Host == 0)
	{	printf("Reserved");	}
	else if(Opcode_Host == 1)
	{	printf("Request");	}
	else if(Opcode_Host == 2)
	{	printf("Reply");	}
	else if(Opcode_Host == 3)
	{	printf("Request Reverse");	}
	else if(Opcode_Host == 4)
	{	printf("Reply Reverse");	}
	else if(Opcode_Host == 5)
	{	printf("DRARP Request");	}
	else if(Opcode_Host == 6)
	{	printf("DRARP Reply");	}
	else if(Opcode_Host == 7)
	{	printf("DRARP Error");	}
	else if(Opcode_Host == 8)
	{	printf("InARP Request");	}
	else if(Opcode_Host == 9)
	{	printf("InARP Reply");	}
	else if(Opcode_Host == 10)
	{	printf("ARP NAK");	}
	else if(Opcode_Host == 11)
	{	printf("MARS Request");	}
	else if(Opcode_Host == 12)
	{	printf("MARS Multi");	}
	else if(Opcode_Host == 13)
	{	printf("MARS MServ");	}
	else if(Opcode_Host == 14)
	{	printf("MARS Join");	}
	else if(Opcode_Host == 15)
	{	printf("MARS Leave");	}
	else if(Opcode_Host == 16)
	{	printf("MARS NAK");	}
	else if(Opcode_Host == 17)
	{	printf("MARS Unserv");	}
	else if(Opcode_Host == 18)
	{	printf("MARS SJoin");	}
	else if(Opcode_Host == 19)
	{	printf("MARS SLeave");	}
	else if(Opcode_Host == 20)
	{	printf("MARS Grouplist Reply");	}
	else if(Opcode_Host == 21)
	{	printf("MARS Grouplist Reply");	}
	else if(Opcode_Host == 22)
	{	printf("MARS Redirect Map");	}
	else if(Opcode_Host == 23)
	{	printf("MAPOS UNARP");	}
	else if(Opcode_Host == 24)
	{	printf("OP_EXP1");	}
	else if(Opcode_Host == 25)
	{	printf("OP_EXP2");	}
	else if(Opcode_Host > 25 && Opcode_Host < 65535)
	{	printf("Unknown");	}
	else if(Opcode_Host == 65535)
	{	printf("Unknown");	}
	printf(" (0x%X)", Opcode_Host);
	// print opcode type - end

	printf("\n\t\t");

	// print hardware address length and protocol address length - start
	printf("Hardware address length: %u, Protocol address length: %u", HardwareAddressLength_Host, ProtocolAddressLength_Host);
	// print hardware address length and protocol address length - end

	printf("\n\t\t");

	// print hardware vendor name - start

	// 000000, 000001, 000002, 000003, 000004, 000005, 000006, 000007, 000008, 000009 - XEROX CORPORATION.
	// 00000C - CISCO SYSTEMS, INC.
	// 00001A - ADVANCED MICRO DEVICES
	// 0002B3 - Intel Corporation.
	// 0001E6 - Hewlett-Packard Company.
	// 000255 - IBM Corporation
	// 00065B - Dell Computer Corp.
	// 000102 - 3COM CORPORATION
	// 00178D - Checkpoint Systems, Inc.
	// 000585 - Juniper Networks, Inc.	
	//000569, 000C29, 001C14, 005056 - VMWare Inc.

	char *MAC_IDs[23] = {"000000", "000001", "000002", "000003", "000004", "000005", "000006", "000007", "000008", "000009", "00000C", "00001A", "0002B3", "0001E6", "000255", "00065B", "000102", "00178D", "000585", "000569", "000C29", "001C14", "005056"};

	char *MAC_Vendors[23] = {"XEROX CORPORATION", "XEROX CORPORATION", "XEROX CORPORATION", "XEROX CORPORATION", "XEROX CORPORATION", "XEROX CORPORATION", "XEROX CORPORATION", "XEROX CORPORATION", "XEROX CORPORATION", "XEROX CORPORATION", "CISCO SYSTEMS, INC.", "ADVANCED MICRO DEVICES", "Intel Corporation.", "Hewlett-Packard Company.", "IBM Corporation", "Dell Computer Corp.", "3COM CORPORATION", "Checkpoint Systems, Inc.", "Juniper Networks, Inc.", "VMWare Inc.", "VMWare Inc.", "VMWare Inc.", "VMWare Inc."};
	char *MACID;

	MACID = (char *)malloc(6);
	sprintf(MACID, "%02X%02X%02X", ARPHeader->SourceHardwareAddress[0], ARPHeader->SourceHardwareAddress[1], ARPHeader->SourceHardwareAddress[2]);

	printf("Source Ethernet Vendor: ");
	ts = 0;
	for(j = 0; j < 23; j++)
	{
		if(strncmp(MAC_IDs[j], MACID, 6) == 0)
		{
			printf("%s\n", MAC_Vendors[j]);
			ts = 1;
			break;
		}
	}
	if(ts == 0)
	{
		printf("Unknown. oui id: %s. Refer http://standards.ieee.org/regauth/oui/oui.txt\n", MACID);
	}
	// print hardware vendor name - end

	// print ARP header - 28 bytes - end

	printf("\n______________________________________________________\n\n");
}

