/*
 * 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/inject.h"
#include "../include/capture.h"
#include "../include/error.h"
#include "../include/version.h"


int opt;
char *optarg;

static unsigned int count;

void
parse_capture_options(int argc, char *argv[])
{
    count = 0;
    snap_len = SNAP_LEN_DEFAULT; 
    traffic_reset = 0;
    resolve = 3;

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

    while((opt = getopt(argc, argv, "c:eGi:nNr:Rs:w:Xx")) != -1)
    {
        switch(opt)
        {
            case 'c':
                count = (unsigned int)atoi(optarg);
                break;

            case 'e':
                link_layer = 1;
                break;

	    case 'G':
		time_gmt = 1;
		break;

            case 'i':
                device = optarg;
                break;

            case 'w':
                strncpy(write_filename, optarg, MAX_CMDOPT_LEN);
                break;

            case 'r':
                strncpy(read_filename, optarg, MAX_CMDOPT_LEN);
                break;

            case 'R':
	        traffic_reset++;
                break;

             case 's':
		snap_len = (unsigned short)atoi(optarg);
		break;

            case 'n':
                resolve--;
	        break;

	    case 'x': case 'X':
		dump_packet = 1;
		break;
        }
    }

    start_packet_capture(argv[optind], count);

    return;
}

void
parse_inject_options(int argc, char *argv[], unsigned short ip_version)
{
    unsigned short init_type = 1;

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

    memset(&ip4hdr_o, 0, sizeof(struct ip4hdr_opts));
    memset(&ehdr_o, 0, sizeof(struct enethdr_opts));

    count = 1;
    interval = 1;
    payload = NULL;
    payload_len = 0; 

    ip4hdr_o.ip_ttl = 128;
    ip4hdr_o.ip_frag = 0x00;
    ip4hdr_o.ip_tos = 0x00;
    ip4hdr_o.ip_version = ip_version;
    ip4hdr_o.ip_sum = 0;
    ip4hdr_o.ip_rand_id = 1;

    s_dport = "0";

    ehdr_o.d_addr = NULL;
    ehdr_o.s_addr = NULL; 

    init_type = 1;
    opterr = 0;

    while((opt = getopt(argc, argv, "t:")) != -1)
    {
        switch(opt)
        {
            case 't':
		if(!strncasecmp(optarg, "tcp", 3))
                {
                    goto parse_tcp;
                }
                else if(!strncasecmp(optarg, "udp", 3))
                {
                    goto parse_udp;
                }
                else if(!strncasecmp(optarg, "icmp", 4))
                {
                    goto parse_icmp;
                }
		else if(!strncasecmp(optarg, "arp", 3))
		{
		    goto parse_arp;
		}
                else
                {
                    fprintf(stderr, "\nError: Invalid injection protocol\n");
                    print_usage();
                }
              
                break;

            default:
                optind--;
                goto parse_tcp;

                break;
        }
    }

parse_tcp:
    memset(&thdr_o, 0, sizeof(thdr_o));

    injection_type = ETHERTYPE_IP;
    ip4hdr_o.ip_proto = IPPROTO_TCP;
    thdr_o.th_rand_seqn = 1;
    thdr_o.th_win = 1500;
    rand_sport = 1;

#ifdef __DEBUG
    fprintf(stderr, "DEBUG: Start TCP injection options\n");
#endif

    while((opt = getopt(argc, argv, "a:b:c:d:D:e:E:fF:i:n:p:q:s:S:T:O:u:w:W:")) != -1)
    {
        switch(opt)
        {
            case 'a':
                thdr_o.th_ackn = (unsigned int)atoi(optarg);
                break;

            case 'b':
                burst = (int)atoi(optarg);

		if(burst > BURST_MAX || burst < 1)
		{
		    fprintf(stderr, "\nError: Invalid burst value: %d\n", burst);
	            exit(FAILURE);
	        }
		
                break;

            case 'c':
                count = (unsigned int)atoi(optarg);
                break;

            case 'd':
		if(strlen(optarg) == 1 && !strncmp(optarg, "R", 1))
                    ip4hdr_o.ip_rand_daddr = 1;
                
		ip4hdr_o.ip_daddr = optarg;
                break;

            case 'D':
                s_dport = optarg;
                break;

            case 'e':
#ifdef __DARWIN
            fprintf(stderr, "\nError: You cannot specify an ethernet address on this OS platform.\n");
            exit(FAILURE);
#endif
		if(strlen(optarg) == 1 && !strncmp(optarg, "R", 1))
                    ehdr_o.eh_rand_saddr = 1;

      		ehdr_o.s_addr = optarg;
                init_type = 0;
                break;

            case 'E':
#ifdef __DARWIN
                fprintf(stderr, "\nError: You cannot specify an ethernet address on this OS platform.\n");
                exit(FAILURE);
#endif
                if(strlen(optarg) == 1 && !strncmp(optarg, "R", 1))
                    ehdr_o.eh_rand_daddr = 1;

                ehdr_o.d_addr = optarg;
                init_type = 0;
                break;

	    case 'f':
		ip4hdr_o.ip_frag = 0x4000;
		break;

            case 'F':
                if(strrchr(optarg, 'U'))
                    thdr_o.th_urg = 1;

                if(strrchr(optarg, 'A'))
                    thdr_o.th_ack = 1;

                if(strrchr(optarg, 'P'))
                    thdr_o.th_psh = 1;

                if(strrchr(optarg, 'R'))
                    thdr_o.th_rst = 1;

                if(strrchr(optarg, 'S'))
                    thdr_o.th_syn = 1;

                if(strrchr(optarg, 'F'))
                    thdr_o.th_fin = 1;

                break;

            case 'i':
                device = optarg;
                break;

            case 'n':
                ip4hdr_o.ip_id = (int)atoi(optarg);
		ip4hdr_o.ip_rand_id = 0;
                break;

            case 'O':
                ip4hdr_o.ip_tos = (int)atoi(optarg);
                break;

            case 'p':
                payload = optarg; 
                break;

            case 'q':
                thdr_o.th_seqn = (unsigned int)atoi(optarg);
		thdr_o.th_rand_seqn = 0;
                break;

            case 's':
                if(strlen(optarg) == 1 && !strncmp(optarg, "R", 1))
                    ip4hdr_o.ip_rand_saddr = 1;

                ip4hdr_o.ip_saddr = optarg;
                break;

            case 'S':
                sport = (int)atoi(optarg);
		rand_sport = 0;
                break;

            case 'T':
                ip4hdr_o.ip_ttl = (int)atoi(optarg);
                break;

            case 'u':
                thdr_o.th_urp = (int)atoi(optarg);
                break;

            case 'w':
                interval = (int)atoi(optarg);
                break;

            case 'W':
                thdr_o.th_win = (int)atoi(optarg);
                break;
        }
    }

#ifdef __DEBUG
    fprintf(stderr, "DEBUG: End TCP injection options\n");
#endif
 
    goto enter_foundry;

parse_icmp:
    memset(&i4hdr_o, 0, sizeof(struct icmp4hdr_opts));

    i4hdr_o.ih_type = 8; 
    i4hdr_o.ih_code = 0;
    i4hdr_o.ih_tos = 0x00;
    i4hdr_o.ih_ttl = 128;
    i4hdr_o.ih_proto = 17;
    i4hdr_o.ih_sum = 0x01;
    i4hdr_o.ih_id = libnet_get_prand(LIBNET_PRu16);
    i4hdr_o.ih_seq = libnet_get_prand(LIBNET_PRu16);
    i4hdr_o.ih_mask = MASK_DEFAULT;

    ip4hdr_o.ip_proto = IPPROTO_ICMP;
    injection_type = ETHERTYPE_IP;

#ifdef __DEBUG
    fprintf(stderr, "DEBUG: Start ICMP injection options\n");
#endif

    while((opt = getopt(argc, argv, "a:b:c:C:d:e:E:fg:G:i:j:J:K:l:L:m:M:n:N:o:O:p:P:s:Q:T:u:v:w:")) != -1)
    {

        switch(opt)
        {
	    case 'a':
                i4hdr_o.ih_otime = (unsigned long)atoi(optarg);
                break;

            case 'b':
                burst = (int)atoi(optarg);
	
                if(burst > BURST_MAX || burst < 1)
	        {
	            fprintf(stderr, "\nError: Invalid burst value: %d\n", burst);
	            exit(FAILURE);
	        }

               break;

            case 'c':
                count = (unsigned int)atoi(optarg);
                break;

            case 'C':
                i4hdr_o.ih_code = (unsigned short)atoi(optarg);
                break;

            case 'd':
                if(strlen(optarg) == 1 && !strncmp(optarg, "R", 1))
                    ip4hdr_o.ip_rand_daddr = 1;

	        ip4hdr_o.ip_daddr = optarg;
	        break;

            case 'e':
#ifdef __DARWIN
                fprintf(stderr, "\nError: You cannot specify an ethernet address on this OS platform.\n");
                exit(FAILURE);
#endif
                if(strlen(optarg) == 1 && !strncmp(optarg, "R", 1))
                    ehdr_o.eh_rand_saddr = 1;

                ehdr_o.s_addr = optarg;
                init_type = 0;
                break;

            case 'E':
#ifdef __DARWIN
                fprintf(stderr, "\nError: You cannot specify an ethernet address on this OS platform.\n");
                exit(FAILURE);
#endif
                if(strlen(optarg) == 1 && !strncmp(optarg, "R", 1))
	            ehdr_o.eh_rand_daddr = 1;
	        
	        ehdr_o.d_addr = optarg;
                init_type = 0;
                break;

	    case 'f':
		ip4hdr_o.ip_frag = 0x4000;
		break;

            case 'g':
		if(strlen(optarg) == 1 && !strncmp(optarg, "R", 1))
		    i4hdr_o.ih_rand_gw = 1;

		i4hdr_o.ih_gw = optarg;
		break;

	    case 'G':
                i4hdr_o.ih_mask = optarg;
                break;

            case 'i':
                device = optarg;
                break;

            case 'j':
                if(strlen(optarg) == 1 && !strncmp(optarg, "R", 1))
	            i4hdr_o.ih_rand_saddr = 1;

		i4hdr_o.ih_saddr = optarg;
                break;

	    case 'J':
		i4hdr_o.ih_sport = (int)atoi(optarg);
		break;

            case 'K':
                i4hdr_o.ih_type = (unsigned short)atoi(optarg);
                break;

            case 'l':
                if(strlen(optarg) == 1 && !strncmp(optarg, "R", 1))
	            i4hdr_o.ih_rand_daddr = 1;
		
                i4hdr_o.ih_daddr = optarg;
                break;

            case 'L':
                i4hdr_o.ih_dport = (int)atoi(optarg);
                break;

	    case 'm':
		i4hdr_o.ih_ttl = (int)atoi(optarg);
		break;

	    case 'M':
		i4hdr_o.ih_id = (int)atoi(optarg);
		break;

            case 'n':
                ip4hdr_o.ip_id = (int)atoi(optarg);
		ip4hdr_o.ip_rand_id = 0;
                break;

            case 'N':
                i4hdr_o.ih_id = (int)atoi(optarg);
                break;

            case 'o':
	        i4hdr_o.ih_tos = (int)atoi(optarg);
	        break;
		
            case 'O':
                ip4hdr_o.ip_tos = (int)atoi(optarg);
                break;

            case 'p':
                payload = optarg;
                break;

            case 'P':
	        if(!strncasecmp(optarg, "UDP", 3))
		{
		    i4hdr_o.ih_proto = 17;
		}
		else if(!strncasecmp(optarg, "TCP", 3))
		{
		    i4hdr_o.ih_proto = 6;
		}
		else if(!strncasecmp(optarg, "ICMP", 4))
		{
		    i4hdr_o.ih_proto = 1;
		}
		else
		{
		    fprintf(stderr, "\nError: Unknown ICMP original protocol: %s\n", optarg);
		    exit(FAILURE);
		}
				
		break;

            case 'Q':
                i4hdr_o.ih_seq = (int)atoi(optarg);
                break;

            case 'r':
                ip4hdr_o.ip_rand_saddr = 1;
                break;

            case 'R':
                ip4hdr_o.ip_rand_daddr = 1;
                break;

            case 's':
		if(strlen(optarg) == 1 && !strncmp(optarg, "R", 1))
		    ip4hdr_o.ip_rand_saddr = 1;

                ip4hdr_o.ip_saddr = optarg;
                break;

            case 'T':
                ip4hdr_o.ip_ttl = (int)atoi(optarg);
                break;

	    case 'u':
                i4hdr_o.ih_rtime = (unsigned long)atoi(optarg);
                break;

            case 'v':
		i4hdr_o.ih_ttime = (unsigned long)atoi(optarg);
		break;

	    case 'w':
	        interval = (int)atoi(optarg);
	        break;
        }
    }

#ifdef __DEBUG
    fprintf(stderr, "DEBUG: End ICMP injection options\n");
#endif

    goto enter_foundry;

parse_udp:
    memset(&uhdr_o, 0, sizeof(struct udphdr_opts));

    ip4hdr_o.ip_proto = IPPROTO_UDP;
    rand_sport = 1;
    injection_type = ETHERTYPE_IP;

#ifdef __DEBUG
    fprintf(stderr, "DEBUG: Start UDP injection options\n");
#endif

    while((opt = getopt(argc, argv, "b:c:d:D:e:E:fi:n:O:p:s:S:T:w:")) != -1)
    {
        switch(opt)
        {
            case 'b':
                burst = (int)atoi(optarg);
		
		if(burst > BURST_MAX || count < 1)
		{
	            fprintf(stderr, "\nError: Invalid burst value: %d\n", burst);
		    exit(FAILURE);
		}

                break;

            case 'c':
                count = (int)atoi(optarg);
	
                if(count > 65535 || count < 1)
                {
                    fprintf(stderr, "\nError: Invalid count value: %d\n", count);
                    exit(FAILURE);
                }

                break;

            case 'd':
                if(strlen(optarg) == 1 && !strncmp(optarg, "R", 1))
	            ip4hdr_o.ip_rand_daddr = 1;

                ip4hdr_o.ip_daddr = optarg;
                break;

            case 'D':
                s_dport = optarg;
                break;

            case 'e':
#ifdef __DARWIN
                fprintf(stderr, "Error: You cannot specify an ethernet address on this OS platform.\n");
                exit(FAILURE);
#endif
                if(strlen(optarg) == 1 && !strncmp(optarg, "R", 1))
                    ehdr_o.eh_rand_saddr = 1;
                
                ehdr_o.s_addr = optarg;
                init_type = 0;
                break;

            case 'E':
#ifdef __DARWIN
                fprintf(stderr, "Error: You cannot specify an ethernet address on this OS platform.\n");
                exit(FAILURE);
#endif
	        if(strlen(optarg) == 1 && !strncmp(optarg, "R", 1))
                    ehdr_o.eh_rand_saddr = 1;
                
                ehdr_o.s_addr = optarg;
                init_type = 0;
                break;

            case 'f':
	        ip4hdr_o.ip_frag = 0x4000;
	        break;

            case 'i':
                device = optarg;
                break;

            case 'n':
                ip4hdr_o.ip_id = (int)atoi(optarg);
		ip4hdr_o.ip_rand_id = 0;
                break;

            case 'O':
                ip4hdr_o.ip_tos = (int)atoi(optarg);
                break;

            case 'p':
                payload = optarg;
                break;

            case 's':
                if(strlen(optarg) == 1 && !strncmp(optarg, "R", 1))
	            ip4hdr_o.ip_rand_saddr = 1;

                ip4hdr_o.ip_saddr = optarg;
                break;

            case 'S':
                sport = (int)atoi(optarg);
                rand_sport = 0;
                break;

            case 'T':
                ip4hdr_o.ip_ttl = (int)atoi(optarg);
                break;

            case 'w':
                interval = (int)atoi(optarg);
                break;
        }
    }

#ifdef __DEBUG
    fprintf(stderr, "DEBUG: End ICMP injection options\n");
#endif

    goto enter_foundry;

parse_arp:
#ifdef __DARWIN
    fprintf(stderr, "Error: ARP injection is not supported on this OS platform.\n");
    exit(FAILURE);
#endif

    memset(&ahdr_o, 0, sizeof(struct arphdr_opts));

    ahdr_o.s_paddr = IPV4_DEFAULT; 
    ahdr_o.s_eaddr = ETH_DEFAULT;
    ahdr_o.r_paddr = IPV4_DEFAULT; 
    ahdr_o.r_eaddr = ETH_DEFAULT;
    ahdr_o.optype = 1;
    init_type = 0;
    injection_type = ETHERTYPE_ARP;

#ifdef __DEBUG
    fprintf(stderr, "DEBUG: Start ARP injection options\n");
#endif

    while((opt = getopt(argc, argv, "b:c:r:R:e:E:o:p:s:S:")) != -1)
    {
        switch(opt)
	{
	    case 'b':
                burst = (int)atoi(optarg);

                if(burst > BURST_MAX || count < 1)
	        {
	            fprintf(stderr, "\nError: Invalid burst value: %d\n", burst);
	            exit(FAILURE);
	        }

		break;
	
	    case 'c':
                count = (unsigned int)atoi(optarg);
	        break;

            case 'e':
#ifdef __DARWIN
                fprintf(stderr, "Error: You cannot specify an ethernet address on this OS platform.\n");
                exit(FAILURE);
#endif
	        if(strlen(optarg) == 1 && !strncmp(optarg, "R", 1))
	            ehdr_o.eh_rand_saddr = 1;
		
                ehdr_o.s_addr = optarg;
                break;

            case 'E':
#ifdef __DARWIN
                fprintf(stderr, "Error: You cannot specify an ethernet address on this OS platform.\n");
                exit(FAILURE);
#endif
	        if(strlen(optarg) == 1 && !strncmp(optarg, "R", 1))
	            ehdr_o.eh_rand_daddr = 1;

	  	ehdr_o.d_addr = optarg;
		break;

            case 'o':
                ahdr_o.optype = (int)atoi(optarg);
                break;
	
	    case 'p':
                payload = optarg;
		break;

	    case 'r':
		if(strlen(optarg) == 1 && !strncmp(optarg, "R", 1))
		    ahdr_o.ah_rand_r_paddr = 1;

	        ahdr_o.r_paddr = optarg;
	        break;

            case 'R':
		if(strlen(optarg) == 1 && !strncmp(optarg, "R", 1))
		    ahdr_o.ah_rand_r_eaddr = 1;

	        ahdr_o.r_eaddr = optarg;
	        break;
	
	    case 's':
	        if(strlen(optarg) == 1 && !strncmp(optarg, "R", 1))
		    ahdr_o.ah_rand_s_paddr = 1;

	        ahdr_o.s_paddr = optarg;
	        break;

            case 'S':
		if(strlen(optarg) == 1 && !strncmp(optarg, "R", 1))
		    ahdr_o.ah_rand_s_eaddr = 1;

                ahdr_o.s_eaddr = optarg;
                break;
        }
    }

#ifdef __DEBUG
    fprintf(stderr, "DEBUG: End ARP injection options\n");
#endif

    goto enter_foundry;
    
#ifdef __DEBUG
    fprintf(stderr, "DEBUG: Entering the packet foundry: INIT_TYPE: %d  COUNT:  %d\n", init_type, count);
#endif
 
enter_foundry:
    enter_packet_foundry(init_type, count);

    return;
}

int
main(int argc, char *argv[])
{
    if(argc < 2)
    {
        print_usage(); 
    }
    else if(getuid() != 0)
    {
        fprintf(stderr, "\nError: You're not root\n");
        exit(FAILURE);
    }
    
    opterr = 0;

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

    while((opt = getopt(argc, argv, "m:")) != -1)
    {
        opterr = 1;

        switch(opt)
        {
            case 'm':
                if(!strncasecmp(optarg, "capture", 7) || !strncasecmp(optarg, "c", 1))
                {
                    parse_capture_options(argc, argv);
                }
                else if(!strncasecmp(optarg, "inject", 6) || !strncasecmp(optarg, "i", 1))
                {
                    parse_inject_options(argc, argv, 4);
                }
                else
                {
                    fprintf(stderr, "\nError: Invalid runtime mode\n");
                    print_usage();
                }
 
                break;

            default: 
                optind--;
                parse_inject_options(argc, argv, 4);
                break;
        }
    }

    exit(SUCCESS);
}        
