/* 
 * $smu-mark$ 
 * $name: parseoptions.c$ 
 * $author: Salvatore Sanfilippo <antirez@invece.org>$ 
 * $copyright: Copyright (C) 1999 by Salvatore Sanfilippo$ 
 * $license: This software is under GPL version 2 of license$ 
 * $date: Fri Nov  5 11:55:49 MET 1999$ 
 * $rev: 8$ 
 */ 

#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/time.h>
#include "hgetopt.h"

#include "hping2.h"
#include "globals.h"

#define ONLYGNUCASE(x) if (!strcmp(opt, x)) {
#define GNUCASE(x,y) if(!strcmp(opt,x) || !strcmp(opt,y)) {
#define ESAC continue; }
#define CHECKARGERR printf("hping2: option requires an argument -%s\nTry `hping2 --help' for more information.\n", opt)
#define CHECKARG if (hoptarg == NULL) {CHECKARGERR; exit(1);}
#define SUIDLIMITERR printf("hping2: sorry, Operation not permitted\n")

#ifdef LIMITWHENSUID
#define SUIDLIMIT if (getuid() != geteuid()) {SUIDLIMITERR; exit(1);}
#else
#define SUIDLIMIT do { } while(0);
#endif /* LIMITWHENSUID */

int parse_options(int argc, char **argv)
{
	int src_ttl_set = 0;
	char *opt;
	int targethost_stat = 0;
	char *optype[]= /* preceeded by : require an argument */
	{
		":-count",	":c",
		":-interval",	":i",
		"=-numeric",	"=n",
		"=-quiet",	"=q",
		":-interface",	":I",
		"=-help",	"=h",
		"=-version",	"=v",
		":-destport",	":p",
		":-baseport",	":s",
		":-ttl",	":t",
		":-id",		":N",
		":-win",	":w",
		":-spoof",	":a",
		"=-fin",	"=F",
		"=-syn",	"=S",
		"=-rst",	"=R",
		"=-push",	"=P",
		"=-ack",	"=A",
		"=-urg",	"=U",
		"=-xmas",	"=X",
		"=-ymas",	"=Y",
		"=-frag",	"=f",
		"=-morefrag",	"=x",
		"=-dontfrag",	"=y",
		":-fragoff",	":g",
		":-tcpoff",	":O",
		"=-rel",	"=r",
		":-data",	":d",
		"=-rawip",	"=0",
		"=-icmp",	"=1",
		"=-udp",	"=2",
		"=-bind",	"=z",
		"=-unbind",	"=Z",
		"=-debug",	"=D",
		"=-verbose",	"=V",
		"=-winid",	"=W",
		"=-keep",	"=k",
		":-file",	":E",
		"=-dump",	"=j",
		"=-print",	"=J",
		":-sign",	":e",
		":-listen",	":9",
		"=-safe",	"=B",
		"=-traceroute", "=T",
		":-tos",	":o",
		":-mtu",	":m",
		"=-seqnum",	"=Q",
		"=-badcksum",	"-b",
		":-setseq",	":M",
		":-setack",	":L",
		":-icmptype",	":C",
		":-icmpcode",	":K",
		"=-end",	"=u",
		"=-rroute",	"=G",
		":-protoip",	":H",
		/* GNU style only options */
		":-icmp-ipver",
		":-icmp-iphlen",
		":-icmp-iplen",
		":-icmp-ipid",
		":-icmp-ipproto",
		":-icmp-cksum",
		"=-icmp-ts",
		"=-icmp-addr",
		"=-tcpexitcode",
		"=-fast",
		"=-tr-keep-ttl",
		"=-tcp-timestamp",
		"=-tr-stop",
		"=-tr-no-rtt",
		NULL
	};

	if (argc < 2) return -1;

	while ( (opt = hgetopt(argv, optype)) != NULL )
	{
		if (opt_debug)
			printf("--- %s --- (%s)\n", opt,
				hoptarg ? hoptarg : "none");

		if (targethost == 1)
		{
			if (targethost_stat == 0)
			{
				strncpy(targetname, opt, 1024);
				targethost_stat = 1;
				continue;
			}

			printf("sorry, you must specify only"
				" one host at a time\n");
			exit(1);
		}

		GNUCASE("c", "-count")
			CHECKARG;
			count = atoi(hoptarg);
		ESAC
		GNUCASE("i", "-interval")
			SUIDLIMIT;
			CHECKARG;
			if (*hoptarg=='u') {
				opt_waitinusec = TRUE;
				usec_delay.it_value.tv_sec =
				usec_delay.it_interval.tv_sec = 0;
				usec_delay.it_value.tv_usec = 
				usec_delay.it_interval.tv_usec = atol(hoptarg+1);
			}
			else
				sending_wait = atoi(hoptarg);
		ESAC
		GNUCASE("n", "-numeric")
			opt_numeric = TRUE;
		ESAC
		GNUCASE("q", "-quiet")
			opt_quiet = TRUE;
		ESAC
		GNUCASE("I", "-interface")
			CHECKARG;
			strncpy (ifname, hoptarg, 1024);
		ESAC
		GNUCASE("h", "-help")
			show_usage();
		ESAC
		GNUCASE("v", "-version")
			show_version();
		ESAC
		GNUCASE("p", "-destport")
			CHECKARG;
			if (*hoptarg == '+')
			{
				opt_incdport = TRUE;
				hoptarg++;
			}
			if (*hoptarg == '+')
			{
				opt_force_incdport = TRUE;
				hoptarg++;
			}
			dst_port = atoi(hoptarg);
		ESAC
		GNUCASE("s", "-baseport")
			SUIDLIMIT;
			CHECKARG;
			initsport = atoi(hoptarg);
		ESAC
		GNUCASE("t", "-ttl")
			CHECKARG;
			src_ttl = atoi(hoptarg);
			src_ttl_set = 1;
		ESAC
		GNUCASE("N", "-id")
			CHECKARG;
			src_id = atoi(hoptarg);
		ESAC
		GNUCASE("w", "-win")
			CHECKARG;
			src_winsize = atoi(hoptarg);
		ESAC
		GNUCASE("a", "-spoof")
			SUIDLIMIT;
			CHECKARG;
			strncpy (spoofaddr, hoptarg, 1024);
		ESAC
		GNUCASE("F", "-fin")
			tcp_th_flags |= TH_FIN;
		ESAC
		GNUCASE("S", "-syn")
			tcp_th_flags |= TH_SYN;
		ESAC
		GNUCASE("R", "-rst")
			tcp_th_flags |= TH_RST;
		ESAC
		GNUCASE("P", "-push")
			tcp_th_flags |= TH_PUSH;
		ESAC
		GNUCASE("A", "-ack")
			tcp_th_flags |= TH_ACK;
		ESAC
		GNUCASE("U", "-urg")
			tcp_th_flags |= TH_URG;
		ESAC
		GNUCASE("X", "-xmas")
			tcp_th_flags |= TH_X;
		ESAC
		GNUCASE("Y", "-ymax")
			tcp_th_flags |= TH_Y;
		ESAC
		GNUCASE("f", "-frag")
			SUIDLIMIT;
			opt_fragment = TRUE;
		ESAC
		GNUCASE("x", "-morefrag")
			SUIDLIMIT;
			opt_mf = TRUE;
		ESAC
		GNUCASE("y", "-dontfrag")
			opt_df = TRUE;
		ESAC
		GNUCASE("g", "-fragoff")
			SUIDLIMIT;
			CHECKARG;
			ip_frag_offset = atoi(hoptarg);
		ESAC
		GNUCASE("O", "-tcpoff")
			SUIDLIMIT;
			CHECKARG;
			src_thoff = atoi(hoptarg);
		ESAC
		GNUCASE("r", "-rel")
			opt_relid = TRUE;
		ESAC
		GNUCASE("d", "-data")
			SUIDLIMIT;
			CHECKARG;
			data_size = atoi(hoptarg);
		ESAC
		GNUCASE("0", "-rawip")
			opt_rawipmode = TRUE;
		ESAC
		GNUCASE("1", "-icmp")
			opt_icmpmode = TRUE;
		ESAC
		ONLYGNUCASE("-icmp-ts")
			opt_icmpmode = TRUE;
			opt_icmptype = 13;
		ESAC
		ONLYGNUCASE("-icmp-addr")
			opt_icmpmode = TRUE;
			opt_icmptype = 17;
		ESAC
		GNUCASE("2", "-udp")
			opt_udpmode = TRUE;
		ESAC
		GNUCASE("9", "-listen")
			SUIDLIMIT;
			CHECKARG;
			opt_listenmode = TRUE;
			strncpy(sign, hoptarg, 1024);
			signlen = strlen(hoptarg);
		ESAC
		GNUCASE("H", "-protoip")
			SUIDLIMIT;
			CHECKARG;
			raw_ip_protocol = atoi(hoptarg);
		ESAC
		GNUCASE("C", "-icmptype")
			SUIDLIMIT;
			CHECKARG;
			opt_icmpmode= TRUE;
			opt_icmptype = atoi(hoptarg);
		ESAC
		GNUCASE("K", "-icmpcode")
			SUIDLIMIT;
			CHECKARG;
			opt_icmpmode= TRUE;
			opt_icmpcode = atoi(hoptarg);
		ESAC
		GNUCASE("z", "-bind")
			ctrlzbind = BIND_TTL;
		ESAC
		GNUCASE("Z", "-unbind")
			ctrlzbind = BIND_NONE;
		ESAC
		GNUCASE("D", "-debug")
			opt_debug = TRUE;
		ESAC
		GNUCASE("V", "-verbose")
			opt_verbose = TRUE;
		ESAC
		GNUCASE("W", "-winid")
			opt_winid_order = TRUE;
		ESAC
		GNUCASE("k", "-keep")
			SUIDLIMIT;
			opt_keepstill = TRUE;
		ESAC
		GNUCASE("E", "-file")
			SUIDLIMIT;
			CHECKARG;
			opt_datafromfile = TRUE;
			strncpy(datafilename, hoptarg, 1024);
		ESAC
		GNUCASE("j", "-dump")
			SUIDLIMIT;
			opt_hexdump = TRUE;
		ESAC
		GNUCASE("J", "-print")
			SUIDLIMIT;
			opt_contdump = TRUE;
		ESAC
		GNUCASE("e", "-sign")
			SUIDLIMIT;
			CHECKARG;
			opt_sign = TRUE;
			strncpy(sign, hoptarg, 1024);
			signlen = strlen(hoptarg);
		ESAC
		GNUCASE("B", "-safe")
			opt_safe = TRUE;
		ESAC
		GNUCASE("u", "-end")
			opt_end = TRUE;
		ESAC
		GNUCASE("T", "-traceroute")
			opt_traceroute = TRUE;
		ESAC
		GNUCASE("o", "-tos")
			CHECKARG;
			if (!strcmp(hoptarg, "help"))
				tos_help();
			else
			{
				static unsigned int tos_tmp = 0;

				sscanf(hoptarg, "%2x", &tos_tmp);
				ip_tos |= tos_tmp; /* OR tos */
			}
		ESAC
		GNUCASE("m", "-mtu")
			SUIDLIMIT;
			CHECKARG;
			virtual_mtu = atoi(hoptarg);
			opt_fragment = TRUE;
			/* FIXME: lacks mtu range verification */
		ESAC
		GNUCASE("Q", "-seqnum")
			opt_seqnum = TRUE;
		ESAC
		GNUCASE("b", "-badcksum")
			SUIDLIMIT;
			opt_badcksum = TRUE;
		ESAC
		GNUCASE("M", "-setseq")
			SUIDLIMIT;
			CHECKARG;
			set_seqnum = TRUE;
			tcp_seqnum = atoi(hoptarg);
		ESAC
		GNUCASE("L", "-setack")
			SUIDLIMIT;
			CHECKARG;
			set_ack = TRUE;
			tcp_ack = atoi(hoptarg);
		ESAC
		GNUCASE("G", "-rroute")
			SUIDLIMIT;
			opt_rroute = TRUE;
		ESAC
		ONLYGNUCASE("-icmp-help")
			icmp_help();	/* ICMP options help */
		ESAC
		ONLYGNUCASE("-icmp-ipver")
			SUIDLIMIT;
			CHECKARG;
			icmp_ip_version = atoi(hoptarg);
		ESAC
		ONLYGNUCASE("-icmp-iphlen")
			SUIDLIMIT;
			CHECKARG;
			icmp_ip_ihl = atoi(hoptarg);
		ESAC
		ONLYGNUCASE("-icmp-iplen")
			SUIDLIMIT;
			CHECKARG;
			icmp_ip_tot_len = atoi(hoptarg);
		ESAC
		ONLYGNUCASE("-icmp-ipid")
			SUIDLIMIT;
			CHECKARG;
			icmp_ip_id = atoi(hoptarg);
		ESAC
		ONLYGNUCASE("-icmp-ipproto")
			SUIDLIMIT;
			CHECKARG;
			icmp_ip_protocol = atoi(hoptarg);
		ESAC
		ONLYGNUCASE("-icmp-cksum")
			SUIDLIMIT;
			CHECKARG;
			icmp_cksum = atoi(hoptarg);
		ESAC
		ONLYGNUCASE("-tcpexitcode")
			opt_tcpexitcode = TRUE;
		ESAC
		ONLYGNUCASE("-fast")
			SUIDLIMIT;
			opt_waitinusec = TRUE;
			usec_delay.it_value.tv_sec =
			usec_delay.it_interval.tv_sec = 0;
			usec_delay.it_value.tv_usec = 
			usec_delay.it_interval.tv_usec = 100000;
		ESAC
		ONLYGNUCASE("-tr-keep-ttl")
			opt_tr_keep_ttl = TRUE;
		ESAC
		ONLYGNUCASE("-tcp-timestamp")
			opt_tcp_timestamp = TRUE;
		ESAC
		ONLYGNUCASE("-tr-stop")
			opt_tr_stop = TRUE;
		ESAC
		ONLYGNUCASE("-tr-no-rtt")
			opt_tr_no_rtt = TRUE;
		ESAC

		/* DEFAULT: unrecognized option */
		printf("hping2: unrecognized option -%s\n"
			"Try `hping2 --help' for more information.\n", opt);
		exit(1);
	}

	/* missing target host? */
	if (targethost_stat == 0 && opt_listenmode && opt_safe)
	{
		printf(
		"you must specify a target host if you require safe protocol\n"
		"because hping needs a target for HCMP packets\n");
		exit(1);
	}

	if (targethost_stat == 0 && !opt_listenmode) return -1;

	if (opt_numeric == TRUE) opt_gethost = FALSE;

	/* some error condition */

	if (data_size+IPHDR_SIZE+TCPHDR_SIZE > 65535) {
		printf("sorry, data size must be <= %lu\n",
			(unsigned long)(65535-IPHDR_SIZE+TCPHDR_SIZE));
		exit(1);
	}
	else if (count <= 0 && count != -1) {
		printf("[parse_options] count must > 0\n");
		exit(1);
	}
	else if (sending_wait <= 0) {
		printf("[prase_options] bad timing interval\n");
		exit(1);
	}
	else if (opt_waitinusec == TRUE && usec_delay.it_value.tv_usec == 0)
	{
		printf("[parse_options] bad timing interval\n");
		exit(1);
	}
	else if (opt_datafromfile == TRUE && data_size == 0)
	{
		printf("[parse_options] -E option useless without -d\n");
		exit(1);
	}
	else if (opt_sign && signlen > data_size)
	{
		printf("[parse_options] signature (%d bytes) is larger than data size, check -d option\n", signlen);
		exit(1);
	}
	else if ((opt_sign || opt_listenmode) && signlen > 1024)
	{
		printf("[parse_options] signature too big\n");
		exit(1);
	}
	else if (opt_safe == TRUE && src_id != -1)
	{
		printf("[parse_options] sorry, you can't set id and use safe protocol at some time\n");
		exit(1);
	}
	else if (opt_safe == TRUE && opt_datafromfile == FALSE &&
			opt_listenmode == FALSE)
	{
		printf("[parse_options] sorry, safe protocol is useless without 'data from file' option\n");
		exit(1);
	}
	else if (opt_safe == TRUE && opt_sign == FALSE &&
			opt_listenmode == FALSE)
	{
		printf("[parse_options] sorry, safe protocol require you sign your packets, see --sign | -e option\n");
		exit(1);
	}

	/* dependences */
	if (opt_safe == TRUE)
		src_id = 1;

	if (opt_traceroute == TRUE && ctrlzbind == BIND_DPORT)
		ctrlzbind = BIND_TTL;

	if (opt_traceroute == TRUE && src_ttl_set == 0)
		src_ttl = DEFAULT_TRACEROUTE_TTL;

	return 1;
}
