/* 
 *  Ths code in this file is part of tcptrack. For more information see
 *    http://www.rhythm.cx/~steve/devel/tcptrack
 *
 *     Copyright (C) Steve Benson - 2003
 *
 *  tcptrack 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, or (at your
 *  option) any later version.
 *   
 *  tcptrack 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 GNU Make; see the file COPYING.  If not, write to
 *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. 
 *  
 */
#include "../config.h"
#define _BSD_SOURCE 1
#define _REENTRANT
#include <stdio.h>
#include <pthread.h>
#include <assert.h>
#ifdef HAVE_PCAP_PCAP_H
#include <pcap/pcap.h>
#elif HAVE_PCAP_H
#include <pcap.h>
#endif
#include "headers.h"
#include "PacketBuffer.h"
#include "Sniffer.h"
#include "defs.h"

Sniffer::Sniffer( PacketBuffer *npb, char *iface, char *fexp )
{
	started=false;
	pb=npb;
	char errbuf[PCAP_ERRBUF_SIZE]; // error messages stored here
	
	//
	// open the network interface for sniffing
	//
	//                             snaplen                                ,promisc, to_ms    ,err
	handle = pcap_open_live(iface, ENET_HEADER_LEN+IP_HEADER_LEN+TCP_HEADER_LEN, 1, POL_TO_MS, errbuf);
	if( !handle )
	{
		fprintf(stderr,"pcap_open_live(): %s\n",errbuf);
		exit(1);
	}
	
	if( pcap_datalink(handle) != DLT_EN10MB ) 
	{
		printf("The specified interface is a non-ethernet interface.\n");
		printf("%s currently only works on ethernet interfaces.\n",PACKAGE);
		exit(1);
	}

	//
	// prepare the filter	
	//
	struct bpf_program filter; // the filter for the sniffer
	char *filter_app = fexp;  // The filter expression
	bpf_u_int32 mask;  // The netmask of our sniffing device
	bpf_u_int32 net;    // The IP of our sniffing device
	pcap_lookupnet(iface, &net, &mask, errbuf);
	pcap_compile(handle, &filter, filter_app, 0, net);
	pcap_setfilter(handle, &filter); // apply filter to sniffer
	
	// handle is now ready for pcap_loop()...
}

void Sniffer::start()
{
	if( started )
	{
		fprintf(stderr,"tried to start sniffer a second time\n");
		return;
	}
	
	int rc = pthread_create(&sniffer_tid,NULL,sniffer_thread_func,this);
	if( rc )
	{
		perror("pthread_create");
		exit(1);
	}
	started=true;
}

// this method gets run in a new thread
void Sniffer::run()
{
	u_char *other = (u_char *) this;
	pcap_loop(handle, -1, handle_packet, other);
}

void Sniffer::processPacket( const u_char *packet )
{
	if( pb )
		pb->pushPacket(packet);
}

//////////////////////


// callback for pthread_create, gets executed in a newly created thread.
void *sniffer_thread_func(void *arg)
{
	Sniffer *sniffer = (Sniffer *) arg;
	sniffer->run();
	return NULL;
}

// callback function called by pcap_loop every time it receives a packet
void handle_packet(u_char *other, const struct pcap_pkthdr *header, const u_char *packet)
{
	assert(header->caplen >= ENET_HEADER_LEN+IP_HEADER_LEN); // ensure packet is big enough to see ip hdr
	Sniffer *sniffer = (Sniffer *) other;
	sniffer->processPacket(packet);
}

