/*

Part of the TCP Testing Tool (ttt).

Welcome to the alphabet soup!

$Id: options.c,v 1.4 2003/05/02 22:09:48 peloy Exp $

*/

#include "includes.h"

static void usage(void)
{
	fprintf(stderr, "\
TCP Test Tool (ttt) Version %s\n\
Eloy Paris <elparis@cisco.com>\n\
From ideas by Sean Convery <sean@cisco.com> and the NEMESIS Project\n\
\n\
Usage: %s [-h] [options]\n\
\n\
General options:\n\
  -h, --help                     display this help and exit\n\
  -c, --count NUM                number of segments to send (default is 1)\n\
  -d, --delay NUM                delay in milliseconds (default is 0)\n\
      --flood NUM                flood the network by sending NUM packets\n\
\n\
TCP options:\n\
  -x, --sport NUM                TCP source port\n\
  -y, --dport NUM                TCP destination port\n\
  -f, --tcpflags                 TCP flags\n\
     -fS SYN, -fA ACK, -fR RST, -fP PSH, -fF FIN, -fU URG\n\
     (can also use --syn, --ack, --rst, --psh, --fin, and --urg)\n\
  -w, --window NUM               window size\n\
  -s, --sequence NUM             sequence number\n\
  -a, --acknowledgement NUM      acknowledgement number\n\
  -u, --urgent NUM               urgent pointer\n\
  -P, --payload FILE             payload file (use stdin if FILE is '-')\n\
  -5, --md5 SECRET               use TCP MD5 signatures (TCP option 19)\n\
      --mss NUM                  TCP maximum segment size\n\
      --wscale NUM               window scale option\n\
      --nocksum                  don't compute TCP checksums\n\
\n\
IP options:\n\
  -S, --src ADDRESS              source IP address\n\
  -D, --dst ADDRESS              destination IP address\n\
  -I, --id NUM                   IP ID\n\
  -T, --ttl NUM                  IP time to live\n\
  -t, --tos NUM                  IP type of service\n\
\n", VERSION, program_name);
	exit(1);
}

/* For command-line options */
struct options options = {
	/* General options */
	1, /* count */
	0, /* delay */
	1, /* flood_count */

	/* IP-related options */
	{0}, /* source_ip */
	{0}, /* destination_ip */
	NULL, /* IP ID */
	NULL, /* IP TTL */
	NULL, /* IP TOS */

	/* TCP-related options */
	NULL, /* src_port */
	NULL, /* dst_port */
	NULL, /* sequence_number */
	NULL, /* ack */
	NULL, /* TCP window size */
	NULL, /* TCP urgent pointer */
	0, /* TCP URG */
	0, /* TCP ACK */
	0, /* TCP PSH */
	0, /* TCP RST */
	0, /* TCP SYN */
	0,  /* TCP FIN */
	{NULL, 0}, /* Payload */
	NULL, /* MD5 signature secret */
	NULL, /* Maximum segment size */
	NULL, /* Window scale */
	0 /* no-cksum */
};

/* For options that do not have a short option equivalent */
enum {
	OPT_URG,
	OPT_ACK,
	OPT_PSH,
	OPT_RST,
	OPT_SYN,
	OPT_FIN,
	OPT_MSS,
	OPT_WSCALE,
	OPT_NOCKSUM,
	OPT_FLOOD
};

static const struct option long_options[] = {
	{"urg", no_argument, NULL, OPT_URG},
	{"ack", no_argument, NULL, OPT_ACK},
	{"psh", no_argument, NULL, OPT_PSH},
	{"rst", no_argument, NULL, OPT_RST},
	{"syn", no_argument, NULL, OPT_SYN},
	{"fin", no_argument, NULL, OPT_FIN},
	{"mss", required_argument, NULL, OPT_MSS},
	{"wscale", required_argument, NULL, OPT_WSCALE},
	{"nocksum", no_argument, NULL, OPT_NOCKSUM},
	{"flood", required_argument, NULL, OPT_FLOOD},
	{"help", no_argument, NULL, 'h'},
	{"src", required_argument, NULL, 'S'},
	{"dst", required_argument, NULL, 'D'},
	{"sequence", required_argument, NULL, 's'},
	{"acknowledgement", required_argument, NULL, 'a'},
	{"sport", required_argument, NULL, 'x'},
	{"dport", required_argument, NULL, 'y'},
	{"count", required_argument, NULL, 'c'},
	{"delay", required_argument, NULL, 'd'},
	{"ttl", required_argument, NULL, 'T'},
	{"window", required_argument, NULL, 'w'},
	{"urgent", required_argument, NULL, 'u'},
	{"tos", required_argument, NULL, 't'},
	{"id", required_argument, NULL, 'I'},
	{"tcpflags", required_argument, NULL, 'f'},
	{"payload", required_argument, NULL, 'P'},
	{"md5", required_argument, NULL, '5'},
	{0, 0, NULL, 0}
};

void process_options(int argc, char **argv)
{
	int c;
	struct hostent *host;
	char *s;

	while (1) {
		c = getopt_long(argc, argv,
				"h?S:D:a:s:x:y:c:d:T:w:u:t:I:f:P:5:",
				long_options, NULL);

		if (c == -1)
			break;

		switch (c) {
			case OPT_URG:
				options.urg = 1;
				break;
			case OPT_ACK:
				options.ack = 1;
				break;
			case OPT_PSH:
				options.psh = 1;
				break;
			case OPT_RST:
				options.rst = 1;
				break;
			case OPT_SYN:
				options.syn = 1;
				break;
			case OPT_FIN:
				options.fin = 1;
				break;
			case OPT_MSS:
				options.mss = u_int16_init(optarg);
				if (!options.mss)
					error(1, "Invalid numeric format");
				break;
			case OPT_WSCALE:
				options.wscale = u_int8_init(optarg);
				if (!options.wscale)
					error(1, "Invalid numeric format");
				break;
			case OPT_NOCKSUM:
				options.no_cksum = 1;
				break;
			case OPT_FLOOD:
				/* Should make sure number is valid */
				options.flood_count = atoi(optarg);
				break;
			case 'h':
			case '?':
				usage();
				exit(1);
			case 'S': /* Source IP address */
				if ( (host = gethostbyname(optarg) ) == NULL)
					error(1, "Error resolving hostname.");

				options.source_ip.s_addr = *(unsigned *)
							host->h_addr_list[0];
				break;
			case 'D': /* Destination IP address */
				if ( (host = gethostbyname(optarg) ) == NULL)
					error(1, "Error resolving hostname.");

				options.destination_ip.s_addr = *(unsigned *)
							host->h_addr_list[0];
				break;
			case 's': /* TCP seq. number */
				options.sequence_number = u_int32_init(optarg);
				if (!options.sequence_number)
					error(1, "Invalid numeric format");
				break;
			case 'a': /* TCP ack. seq. number */
				options.ack_seq = u_int32_init(optarg);
				if (!options.ack_seq)
					error(1, "Invalid numeric format");
				break;
			case 'x': /* TCP src. port number */
				options.src_port = u_int16_init(optarg);
				if (!options.src_port)
					error(1, "Invalid numeric format");
				break;
			case 'y': /* TCP dest. port number */
				options.dst_port = u_int16_init(optarg);
				if (!options.dst_port)
					error(1, "Invalid numeric format");
				break;
			case 'c':
				/* Should make sure number is valid */
				options.count = atoi(optarg);
				break;
			case 'd':
				/* Should make sure number is valid */
				options.delay = atoi(optarg);
				break;
			case 'T': /* IP TTL */
				options.ip_ttl = u_int8_init(optarg);
				if (!options.ip_ttl)
					error(1, "Invalid numeric format");
				break;
			case 'w': /* TCP window size */
				options.window = u_int16_init(optarg);
				if (!options.window)
					error(1, "Invalid numeric format");
				break;
			case 'u': /* TCP urgent pointer */
				options.urg_ptr = u_int16_init(optarg);
				if (!options.urg_ptr)
					error(1, "Invalid numeric format");
				break;
			case 't': /* IP TOS */
				options.ip_tos = u_int8_init(optarg);
				if (!options.ip_tos)
					error(1, "Invalid numeric format");
				break;
			case 'I': /* IP ID */
				options.ip_id = u_int16_init(optarg);
				if (!options.ip_id)
					error(1, "Invalid numeric format");
				break;
			case 'f':
				for (s = optarg; *s; s++)
					switch (*s) {
						case 'U':
							options.urg = 1;
							break;
						case 'A':
							options.ack = 1;
							break;
						case 'P':
							options.psh = 1;
							break;
						case 'R':
							options.rst = 1;
							break;
						case 'S':
							options.syn = 1;
							break;
						case 'F':
							options.fin = 1;
							break;
						default:
							fprintf(stderr, "Unknown TCP flag '%c'\n", *s);
					}
				break;
			case 'P':
				if (strcmp("-", optarg) )
					read_payload(optarg, &options.payload);
				else
					read_payload_from_stdin(&options.payload);
				break;
			case '5':
				options.md5_secret = optarg;
				break;
			default:
				usage();
		}
	}

	/*
	 * Set some default options if nothing was specified on the
	 * command line.
	 */
	if (!options.ip_ttl)
		options.ip_ttl = u_int8_init("64");

	/*
	 * The command line has been processed, now we need to make
	 * sure we have the minimum options to be able to run.
	 */
	if (!options.destination_ip.s_addr || !options.dst_port)
		usage();
}

