/*
 * author: Darren Bounds <dbounds@intrusense.com>
 * copyright: Copyright (C) 2002 by Darren Bounds
 * license: This software is under GPL version 2 of license
 *
 * 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
 *
 * packit official page at http://packit.sourceforge.net
 */

#include "../include/packit.h"
#include "../include/capture.h"
#include "../include/error.h"

pcap_t *p;
static unsigned int p_count;
static unsigned int r_count;

void
capture_stats()
{
    struct pcap_stat p_stats;

#ifdef __DEBUG
    fprintf(stderr, "DEBUG: capture_stats()\n");
#endif

    pcap_stats(p, &p_stats);

    fprintf(stdout, "-| Packet Capture Statistics |-"
		    "----------------------"
                    "----------------------"
		    "\n");
    fprintf(stdout,
        "Packet(s) received: %d  "
        "Packet(s) dropped: %d  ",
        p_stats.ps_recv, p_stats.ps_drop);

    if(traffic_reset)
	fprintf(stdout, "Packet(s) reset: %d ", r_count);
    else
	fprintf(stdout, "Packet(s) processed: %d  ", p_count);
 
    fprintf(stdout, "\n");

    return;
}

void
process_packets(unsigned char *user, struct pcap_pkthdr *pkthdr, unsigned char *packet)
{
    struct libnet_ethernet_hdr *ehdr;
    struct libnet_ipv4_hdr *iphdr;
    
#ifdef __DEBUG
    fprintf(stderr, "DEBUG: process_packets()\n");
#endif
    
    if(display)
    {
#ifdef __DEBUG
        fprintf(stderr, "DEBUG: Display mode enabled\n");
#endif

        ehdr = (struct libnet_ethernet_hdr *)(packet);

        if(ehdr->ether_type == PACKIT_ETHERTYPE_IP)
        {
#ifdef __DEBUG
            fprintf(stderr, "DEBUG: Ethernet type: IP\n", ehdr->ether_type);
#endif

            if(!traffic_reset)
	    {
                print_separator(p_count);
	        print_timestamp(pkthdr);
	    }
	 
            iphdr = (struct libnet_ipv4_hdr *)(packet + header_len);

            switch(iphdr->ip_p)
            {
                case IPPROTO_TCP: 
		    if(!traffic_reset)
                        print_tcp_hdr(packet);
		    else if(traffic_reset == 1)
			if(reset_tcp_stream(packet, iphdr))
			    r_count++;

                    break;

                case IPPROTO_UDP: 
                    if(!traffic_reset)
                        print_udp_hdr(packet);
		    else if(traffic_reset > 1)
		        if(reset_udp_stream(packet, iphdr))
			    r_count++;
              
                    break;

                case IPPROTO_ICMP: 
                    if(!traffic_reset)
                        print_icmpv4_hdr(packet);

                    break;
            }

	    if(!traffic_reset)
	    {
    	        print_ipv4_hdr(iphdr);

	        if(link_layer)
	            print_ethernet_hdr(ehdr);
		
  	        if(dump_packet)
                    if(pkthdr->caplen > header_len)
                        print_packet_hexdump(packet + header_len, pkthdr->caplen - header_len);    

		fprintf(stdout, "\n");
	    }
        }
        else if(ehdr->ether_type == ETHERTYPE_ARP && !traffic_reset)
        {
#ifdef __DEBUG
            fprintf(stderr, "DEBUG: Ethernet type: ARP\n");
#endif

            print_separator(p_count);
	    print_timestamp(pkthdr);
            print_arp_hdr(packet);
	    print_ethernet_hdr(ehdr);

            if(dump_packet)
                if(pkthdr->caplen > header_len)
                    print_packet_hexdump(packet + header_len, pkthdr->caplen - header_len);

	    fprintf(stdout, "\n");
	}
    }

    p_count++;
    
    return;
}

int
start_packet_capture(char *p_filter, unsigned int count)
{
    int dlink;

    struct bpf_program bpf;
    bpf_u_int32 localnet, netmask;
    pcap_dumper_t *pd;

#ifdef __DEBUG
    fprintf(stderr, "DEBUG: start_packet_capture()\n");
#endif

    signal(SIGTERM, capture_clean_exit);
    signal(SIGINT, capture_clean_exit);
    signal(SIGQUIT, capture_clean_exit);
    signal(SIGHUP, capture_clean_exit);

    if(strlen(read_filename) > 0)
    {
#ifdef __DEBUG
        fprintf(stderr, "DEBUG: Reading from capture file\n", read_filename);
#endif

        if((p = pcap_open_offline(read_filename, errbuf)) == NULL)
        {
            fprintf(stderr, "\nError: Unable to open file: %s\n", errbuf); 
            exit(FAILURE);
        }
    }
    else
    {
#ifdef __DEBUG
        fprintf(stderr, "DEBUG: Opening capture device\n");
#endif
    
        if (device == NULL)
           if((device = pcap_lookupdev(errbuf)) == NULL)
           {
               fprintf(stderr, "\nError: Unable to find appropriate device: %s\n", errbuf);
               exit(FAILURE);
           }

        if((p = pcap_open_live(device, snap_len, 1, READ_TIMEOUT, errbuf)) == NULL)
        {
            fprintf(stderr, "\nError: Unable to open device: %s\n", errbuf);
            exit(FAILURE);
        }
    }

    display = 1;
    pd = NULL;

    if(strlen(write_filename) > 0)
    {
#ifdef __DEBUG
        fprintf(stderr, "DEBUG: Writing to capture file: %s\n", write_filename);
#endif

        if((pd = pcap_dump_open(p, write_filename)) == NULL)
        {
            fprintf(stderr, "\nError: Unable to initialize packet capture: %s\n", pcap_geterr(p));
            exit(FAILURE);
        }

        display--;
    }

    if(pcap_lookupnet(device, &localnet, &netmask, errbuf) < 0)
    {
	fprintf(stderr, "\nError: Unable to lookup network: %s", errbuf);
	exit(FAILURE);
    }

    if(pcap_compile(p, &bpf, p_filter, 0, netmask) < 0)
    {
        fprintf(stderr, "\nError: Unable to compile packet filters: %s", pcap_geterr(p));
        exit(FAILURE);
    }

    if(pcap_setfilter(p, &bpf) < 0)
    {
        fprintf(stderr, "\nError: Unable to set packet filters: %s", pcap_geterr(p));
        exit(FAILURE);
    }

    if((dlink = pcap_datalink(p)) < 0)
    {
        fprintf(stderr, "\nError: Unable to determine datalink type: %s", pcap_geterr(p));
        exit(FAILURE);
    }

    switch(dlink)
    {
        case DLT_NULL:
            header_len = 4;
            break;

        case DLT_EN10MB:
            header_len = PACKIT_ETH_H; 
            break;

        default:
            header_len = PACKIT_ETH_H; 
            break;
    }

    fprintf(stdout, "Mode:  Packet Capture ");

    if(traffic_reset == 1)
	fprintf(stdout, "[TCP Reset] ");
    else if(traffic_reset > 1)
	fprintf(stdout, "[UDP Reset] ");

    if(strlen(read_filename) > 0)
        fprintf(stdout, "using file: %s ", read_filename);
    else
        fprintf(stdout, "using device: %s ", device);

    if(p_filter)
        fprintf(stdout, "[%s]", p_filter);

    fprintf(stdout, "\n\n");

    p_count = 0;

    if(display)
    {
        if(pcap_loop(p, count, (pcap_handler)process_packets, NULL) < 0)
        {
            fprintf(stderr, "\nError: Unable to initialize pcap_loop: %s", pcap_geterr(p));
            exit(FAILURE);
        }
    }
    else
    {
        if(pcap_loop(p, count, (pcap_handler)pcap_dump, (unsigned char *)pd) < 0)
        {
            fprintf(stderr, "\nError: Unable to initialize pcap_loop: %s", pcap_geterr(p));
            exit(FAILURE);
        }
    }

    capture_clean_exit(1);

    return 1;
}


void
print_separator(unsigned int p_count)
{
    int c = 65;
    int sep_on = 1;
    float i = 1;

#ifdef __DEBUG
    fprintf(stderr, "DEBUG: print_separater()\n");
#endif

    while(sep_on)
    {
	if(((p_count + 1) / i) == (1 | 0))
	{
	    c--;
	    sep_on--;
	}
	else if(((p_count + 1)/ i) < (1 | 0)) 
	{
            sep_on--;
	}
	else
	{
	    c--;
	}

	i = i * 10;
    }

    fprintf(stdout, "-| PID %d |-", p_count + 1);
	    
    for(i = 0; i < c; i++)
        fprintf(stdout, "-");

    fprintf(stdout, "\n\n");
    
    return;
}


void
capture_clean_exit(int sig)
{
    fprintf(stdout, "\n");

#ifdef __DEBUG
    fprintf(stderr, "DEBUG: capture_clean_exit() SIG: %d\n", sig);
#endif

    capture_stats();
    pcap_close(p);

    fprintf(stdout, "\n");

    exit(FAILURE);

    return;
}
