/* 
 * swrite - write stream message to daemon on specified host and 
 * print reply received.
 *
 * Programming Assignment #6, Part 3 of 3
 *
 * CSc 645  Spring 1995  Jim Warhol
 *
 * swrite creates a connection to a TCP daemon on a specifed host, sends an
 * Internet stream message consisting of "jwarhol", then waits for a reply
 * from that host. The response received is printed.
 *
 * Compile with:  cc -o swrite swrite.c
 *
 * Execute with:  swrite hostname port#
 *
 */

#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <stdio.h> 
#include <strings.h>
#include <sys/param.h>

/* define compile-time constants */

#define stdoutx	1		/* standard output file ordinal */
#define stderrx	2		/* standard error file ordinal */

#define RCVLEN	1024		/* maximum receive buffer size */

/* data definitions */

struct sockaddr_in local_addr;	/* local IP address */
struct sockaddr_in dest_addr;	/* destination IP address */
struct hostent *hp;		/* result of host name lookup */
int sock;			/* socket descriptor */
char rcvbuf[RCVLEN];		/* buffer to receive message from daemon */
int portnum;			/* destination port number */
int st;				/* return status */
int i;				/* scratch variable */

/* function prototypes */

void error(char errmsg[]);	/* print error message and abort */
void initialize(int argc, char *argv[]); /* initialize program */
void print(char msg[]);		/* write line to standard output */
void printn(char msg[], int n);	/* write line with integer to standard output */

/* main program */

void main(int argc, char *argv[])
{

   initialize(argc, argv);		/* process command parameters */

   /* set up local IP address */

   bzero(&local_addr, sizeof(local_addr));	/* clear all bytes */
   local_addr.sin_family = AF_INET;	/* protocol family */

   /* create Internet stream socket */

   sock = socket(PF_INET, SOCK_STREAM, 0);
   if (sock < 0) error("An error occurred while creating the socket.");

   /* bind socket to an arbitrary port */

   local_addr.sin_addr.s_addr = htonl(INADDR_ANY); /* asg. any local address */
   local_addr.sin_port = 0;		/* assign any local port */
   st = bind(sock, (struct sockaddr *) (&local_addr), sizeof(local_addr));
   if (st < 0) error("Unable to bind socket.");

   /* print out port number for local address */

   i = sizeof(local_addr);
   st = getsockname(sock, (struct sockaddr *) (&local_addr), &i);
   if (st < 0) error("Error calling getsockname().");
   printn("Socket has been created and is now connected to local port #%d.\n", 
      ntohs(local_addr.sin_port));

   /* set up destination address for S95_tcpd daemon */

   hp = gethostbyname(argv[1]);
   if (hp == NULL) error("An error occured when calling gethostbyname().");
   bzero(&dest_addr, sizeof(dest_addr));	/* clear all bytes */
   dest_addr.sin_family = AF_INET;	/* protocol family */
   bcopy(hp->h_addr_list[0], &dest_addr.sin_addr, hp->h_length);
   dest_addr.sin_port = htons(portnum);	/* local port # of S95_tcpd daemon */

   /* create a connection from the socket to the daemon */

   st = connect(sock, (struct sockaddr *) (&dest_addr), sizeof(dest_addr));
   if (st < 0) error("Unable to create connection to S95_tcpd daemon.");

   /* send a stream message to the daemon */

   printn("Sending stream data to S95_tcpd daemon at port #%d....\n", portnum);
   st = write(sock, "jwarhol", 8); 
   if (st < 0) error("Error sending stream message to S95_tcpd daemon.");
   print("Message sent successfully; now awaiting response.\n");

   /* receive a message from the daemon */

   for (i = 0; i < RCVLEN; i+= st) {
      st = read(sock, &rcvbuf[i], RCVLEN - i);
      if (st < 0) error("Error receiving data from S95_tcpd daemon.");
   }
   printn("\n%d bytes received from S95_tcpd daemon:\n", st);
   print(rcvbuf);

   /* close socket and terminate program */

   close(sock);			/* close socket */
   exit(0);

}

/*
 * error(char errmsg[]);
 *
 * error prints out an error message followed by a new line character and
 * then aborts program execution.
 *
 */

void error(char errmsg[])
{
   write(stderrx, errmsg, strlen(errmsg));
   write(stderrx, "\n", 2);
   exit(-1);
}

/*
 * void initialize(int argc, char *argv[]);
 *
 * initialize processes the swrite command parameters.
 *
 */

void initialize(int argc, char *argv[])
{
   /* make sure exactly two parameters specified */

   if (argc != 3)
      error("You must specify two parameters:\n   swrite hostname port#");

   /* get port number parameter */

   if (sscanf(argv[2], "%d", &portnum) != 1)
      error("Error in port number parameter.");
   return;
}

/*
 * void print(char msg[]);
 *
 * print writes a message to standard output. NOTE: stdoutx is used 
 * instead of stdout because of NeXT system header file anomalies.
 *
 */

void print(char msg[])
{
   write(stdoutx, msg, strlen(msg));
   return;
}

/*
 * void printn(char msg[], int n);
 *
 * printn writes a format string containing an integer to standard output.
 * NOTE: stdoutx is used instead of stdout because of NeXT system header 
 * file anomalies.
 *
 */

void printn(char msg[], int n)
{
   char printbuf[100];

   if (strlen(msg) > 100 - 10)	/* ensure room for msg and integer */
      error("printn: Message too long to fit in print buffer.");

   sprintf(printbuf, msg, n);
   write(stdoutx, printbuf, strlen(printbuf));
   return;
}

