/* Name:  Chuck Winters
 * Corp:  Lockheed Martin ATL
 * Date:  24 April, 2002
 * File:  basic_s.c
 * Comp:  gcc -o basic_s basic_s.c
 * Desc:  Server for basic SCTP test
 *        This test doesn't even worry about multi-homing
 *
*/

#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <stdlib.h>
#include <basic_common.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>

#define PROG_NAME "basic_s"

int  list_sock = -1, conn_sock = -1;

void print_usage(FILE *f)
{
	fprintf(f, "Usage:  \n");
	fprintf(f, " %s [options] [host]\n", PROG_NAME);
	fprintf(f, "\nOptions:\n");
	fprintf(f, " -%c num -  Send num msgs instead of default %d messages\n", 
			       NUM_CHAR, DEFAULT_NUM_MSGS);
	fprintf(f, " -%c     -  Send user level acknowledgements\n", ACK_CHAR);
	fprintf(f, " -%c     -  Set socket to SO_LINGER\n", LINGER_CHAR);
	fprintf(f, " -%c     -  Use the Nagle Algorithm\n", DELAY_CHAR);
	fprintf(f, " -%c num -  Use num port instead of default %d port\n", 
			       REMOTE_PORT_CHAR, DEFAULT_REMOTE_PORT);
	fprintf(f, " -%c     -  Use recv(2) system call (cannot be used with -%c)\n", 
			       SEND_RECV_CHAR, SEND_RECV_MSG_CHAR);
	fprintf(f, " -%c     -  Use recvmsg(2) system call (cannot be used with -%c)\n", 
			       SEND_RECV_MSG_CHAR, SEND_RECV_CHAR);
	fprintf(f, " -%c     -  Print this message\n", HELP_CHAR);
}

void cleanup(void)
{
	if(conn_sock > 0)
	{
		if(close(conn_sock) < 0)
		{
			perror("Closing connected socket");
			while(errno == EINTR)
				close(conn_sock);
		}
	}
}

int main(int argc, char **argv)
{
	char **local_ips;
	int num_ips = 0;
	int valid_ips = 0;
	struct sockaddr_in *local_addrs, *tmp; 
	struct sockaddr_in remote_addrs[MAX_REMOTE_ADDRS];
	int remote_addrs_len = 0;
	int num_r_addrs = 0;
	unsigned bool_args = 0;
	char c_arg;
	int i, j;
	char ack_msg[ACK_MSG_LEN] = ACK_MSG;

	/** User Control Variables **/
		int num_msgs = DEFAULT_NUM_MSGS;
		unsigned local_port = DEFAULT_REMOTE_PORT;

	/** Register my cleanup function in case of exit **/
		atexit(&cleanup);

	/** Parse Command Line Args **/
		while( (c_arg = getopt(argc, argv, OPT_STRING)) != -1)
		{
			switch(c_arg)
			{
				case HELP_CHAR:
						print_usage(stderr);
						exit(1);
				case ACK_CHAR:
						fprintf(stdout, "## Turning on Acknowledgements\n");
						bool_args |= OPT_ACK;
						break;
				case NUM_CHAR:
						num_msgs = strtol(optarg, NULL, 0);
						fprintf(stdout, "## Setting number of messages to %d\n", num_msgs);
						if(((num_msgs == LONG_MIN) || (num_msgs == LONG_MAX)) 
							 && (errno == ERANGE))
						{
							fprintf(stderr, "The value %s given for the number of messages is too large\n", optarg);
							exit(1);
						}
						break;
				case REMOTE_PORT_CHAR:
						local_port = strtol(optarg, NULL, 0);
						fprintf(stdout, "## Setting port number to %d\n",  local_port);
						break;
				case LINGER_CHAR:
						fprintf(stdout, "## Turning on SO_LINGER\n");
						bool_args |= OPT_LINGER;
						break;
				case DELAY_CHAR:
						fprintf(stdout, "## Turning on Nagle Algorithm\n");
						bool_args |= OPT_DELAY;
						break;
				case SEND_RECV_CHAR:
						if(bool_args&OPT_SEND_RECV_MSG)
						{
							fprintf(stderr, "** Cannot use %c with %c\n", SEND_RECV_CHAR, SEND_RECV_MSG_CHAR);
							print_usage(stderr);
							exit(1);
						}
						fprintf(stdout, "## Using send(2)\n");
						bool_args |= OPT_SEND_RECV;
						break;
				case SEND_RECV_MSG_CHAR:
						if(bool_args&OPT_SEND_RECV)
						{
							fprintf(stderr, "** Cannot use %c with %c\n", SEND_RECV_CHAR, SEND_RECV_MSG_CHAR);
							print_usage(stderr);
							exit(1);
						}
						fprintf(stdout, "## Using sendmsg(2)\n");
						bool_args |= OPT_SEND_RECV_MSG;
						break;
				default:
						fprintf(stderr, "Unrecognized Option -%c\n", optopt);
						print_usage(stderr);
						exit(1);
			}
		}
			
	/** If there were no arguments then we are in error **/
		/** Add other args to ip **/
			num_ips = argc - optind;
			if(num_ips > 0)
			{
				local_addrs = (struct sockaddr_in* ) malloc(sizeof(struct sockaddr_in) * num_ips);
				local_ips = (char** ) malloc(sizeof(char*) * num_ips);
				fprintf(stdout, "-- You have entered %d ip addresses\n", num_ips);
				if(!local_ips || !local_addrs)
				{
					fprintf(stderr, "Out of memory\n");
					exit(2);
				}
			}
			else
			{
				fprintf(stdout, "-- Setting local address to INADDR_ANY\n");
				if(!(local_addrs = (struct sockaddr_in* ) malloc(sizeof(struct sockaddr_in))))
				{
					perror("Allocating memory");
					exit(2);
				}
				local_addrs->sin_family = AF_INET;
				local_addrs->sin_port = htons(local_port);
				local_addrs->sin_addr.s_addr = INADDR_ANY;
				valid_ips++;
			}


			for(i = 0, j = optind; i < num_ips, j < argc; i++, j++)
			{
				char *tmp = (char* ) malloc(MAX_ARGLEN * sizeof(char));

				if(!tmp)
				{
					fprintf(stderr, "Out of memory\n");
					exit(2);
				}

				local_ips[i] = tmp;
				memset(tmp, 0, MAX_ARGLEN);
				strncpy(tmp, argv[j], MAX_ARGLEN);
				fprintf(stdout, " %s\n", local_ips[i]);
			}

	/** Fill in sockaddr structure **/
		for(i = 0; i < num_ips; i++)
		{
			local_addrs[i].sin_family = AF_INET;
			local_addrs[i].sin_port = htons(local_port);
			if(!inet_aton(local_ips[i], &(local_addrs[i].sin_addr)))
			{
				fprintf(stderr, "The %d address %s is not a valid address\n", i, local_ips[i]);
				continue;
			}
			valid_ips++;
#if (PROTO_TYPE == IPPROTO_TCP)
			break;
#endif
		}

	/** Create the socket **/
		fprintf(stdout, "-- Creating socket\n");
		if( (list_sock = socket(PF_INET, SOCKET_TYPE, PROTO_TYPE)) < 0)
		{
			perror("Creating socket");
			exit(1);
		}
		fprintf(stdout, "-- Created socket with fd of %d\n", list_sock);


	/** Setup the options **/
		if(bool_args & OPT_LINGER)
		{
			struct linger lin = { on , 3 };

			fprintf(stdout, "-- Setting SO_LINGER\n");
			if( setsockopt(list_sock, SOL_SOCKET, SO_LINGER, &lin, sizeof(struct linger)) < 0)
			{
				perror("Setting SO_LINGER");
				exit(3);
			}
		}
		if(!(bool_args & OPT_DELAY))
		{
			fprintf(stdout, "-- Turning off Nagle Algorithm\n");
			if( setsockopt(list_sock, PROTO_LEVEL, PROTO_DELAY, &off, sizeof(int)) < 0)
			{
				perror("Turning off the Nagle Algorithm");
				exit(4);
			}
		}

	/** Bind to the local interface **/
		fprintf(stdout, "-- Binding to interface\n");
		tmp = &local_addrs[0];
		fprintf(stdout, "-- Binding with following address\n");
		for(i = 0; i < valid_ips; i++)
			print_sockaddr_in(stdout, tmp);
		if( bind(list_sock, (struct sockaddr* ) tmp, (valid_ips * sizeof(struct sockaddr_in))) < 0)
		{
			perror("Binding to socket");
			exit(2);
		}

	/** Listen for connections **/
		fprintf(stdout, "-- Listening for clients\n");
		if(listen(list_sock, 1) < 0)
		{
			perror("Listening on list_sock\n");
			exit(5);
		}

	/** Accept connections **/
		fprintf(stdout, "-- Accepting connection\n");
		memset(remote_addrs, 0, (sizeof(struct sockaddr_in) * MAX_REMOTE_ADDRS));
		remote_addrs_len = sizeof(struct sockaddr_in) * MAX_REMOTE_ADDRS;
		if( (conn_sock = accept(list_sock, (struct sockaddr* ) &remote_addrs, &remote_addrs_len)) <= 0)
		{
			perror("Accepting Connection");
			exit(6);
		}

	/** Printout who has connected with us **/
	/** Currently we only support IPv4 **/
		num_r_addrs = remote_addrs_len / sizeof(struct sockaddr_in);
		fprintf(stdout, "-- Connected to Client with %d Address%s:\n", num_r_addrs, (num_r_addrs > 1 ? "es": ""));
		for(i = 0; i < num_r_addrs; i++)
		{
			char tmp[20];

			fprintf(stdout, "   %d)  %s:%d\n", i, inet_ntop(AF_INET, &(remote_addrs[i].sin_addr), tmp, 20),
					    ntohs(remote_addrs[i].sin_port));
		}

	/** Now start reading data **/
		fprintf(stdout, "-- Receiving messages\n");
		for(i = 0; i < num_msgs; i++)
		{
			char msg[MSG_LEN];
			int ret_val;

			if(bool_args&OPT_SEND_RECV)
			{
				if( (ret_val = recv(conn_sock, msg, MSG_LEN, MSG_WAITALL)) < MSG_LEN)
				{
					if(ret_val < 0)
					{
						perror("Receiving data\n");
						exit(7);
					}
					else
						fprintf(stderr, "Received %d bytes of message of length %d\n", ret_val, MSG_LEN);
				}
			}
			else if(bool_args&OPT_SEND_RECV_MSG)
			{
				fprintf(stderr, "Not implemented yet\n");
				exit(8);
			}
			else
			{
				if( (ret_val = read(conn_sock, msg, MSG_LEN)) < MSG_LEN)
				{
					if(ret_val < 0)
					{
						perror("Receiving data\n");
						exit(7);
					}
					else
						fprintf(stderr, "Received %d bytes of message of length %d\n", ret_val, MSG_LEN);
				}
			}

			/** If we want user level acknowledgements **/
				if(bool_args&OPT_ACK)
				{
					fprintf(stdout, "--- Sending Acknowledgement\n");
					if(bool_args&OPT_SEND_RECV)
					{
						if(send(conn_sock, ack_msg, ACK_MSG_LEN, 0) != ACK_MSG_LEN)
						{
							fprintf(stderr, "Problem sending ACK\n");
							exit(8);
						}
					}
					else if(bool_args&OPT_SEND_RECV_MSG)
					{
						fprintf(stderr, "sendmsg not implemented yet\n");
						exit(8);
					}
					else
					{
						if(write(conn_sock, ack_msg, ACK_MSG_LEN) != ACK_MSG_LEN)
						{
							fprintf(stderr, "Problem sending ACK\n");
							exit(8);
						}
					}
				}

			/** Print out the message, then zero it out **/
				fprintf(stdout, "-- Received Message:  %s\n", msg);
				memset(msg, 0, MSG_LEN);
		}

	/** Later **/
		fprintf(stdout, "-- Exiting\n");
		exit(0);
}
