/*****************************************************************************/
/* "NetPIPE" -- Network Protocol Independent Performance Evaluator.          */
/* Copyright 1997, 1998 Iowa State University Research Foundation, Inc.      */
/*                                                                           */
/* 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.  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., 675 Mass Ave, Cambridge, MA 02139, USA.   */
/*                                                                           */
/*     * TCP.c              ---- TCP calls source                            */
/*     * TCP.h              ---- Include file for TCP calls and data structs */
/* 2002/03/18 --- Modified for IPv6 - Robbie Williamson (robbiew@us.ibm.com) */
/*****************************************************************************/
#include    "netpipe.h"


int Setup(ArgStruct *p)
{

 int tr, one = 1;                 /* tr==1 if process is a transmitter */
 int sr = 0;                
 int sockfd;
 struct sockaddr *lsin1;
 char *host;
 char *server_host;
 struct addrinfo *addr;
 struct addrinfo *server_addr;
 struct addrinfo hints;
 struct protoent *proto;

 memset(&hints, 0, sizeof(hints));
 hints.ai_family = AF_INET6;


 host = p->host;                           /* copy ptr to hostname */ 
 server_host = p->server_host;             /* copy ptr to server */ 
 
 tr = p->tr;                               /* copy tr indicator */
 sr = p->sr;


 memset(&p->prot.sin1, 0, sizeof(p->prot.sin1));
 memset(&p->prot.sin2, 0, sizeof(p->prot.sin2));

 if ( (sockfd = socket(AF_INET6, SOCK_STREAM, 0)) < 0){
   printf("NetPIPE: can't open stream socket! errno=%d\n", errno);
   exit(-4);
 }

 if(!(proto = getprotobyname("tcp"))){
   printf("NetPIPE: protocol 'tcp' unknown!\n");
   exit(555);
 }

 /* Attempt to set TCP_NODELAY */
 if(setsockopt(sockfd, proto->p_proto, TCP_NODELAY, &one, sizeof(one)) < 0)
 {
   printf("NetPIPE: setsockopt: TCP_NODELAY failed! errno=%d\n", errno);
   exit(556);
 }

 /* If requested, set the send and receive buffer sizes */
 if(p->prot.sndbufsz > 0)
 {
      printf("Send and Receive Buffers set to %d bytes\n", p->prot.sndbufsz);
     if(setsockopt(sockfd, SOL_SOCKET, SO_SNDBUF, &(p->prot.sndbufsz), 
                                       sizeof(p->prot.sndbufsz)) < 0)
     {
          printf("NetPIPE: setsockopt: SO_SNDBUF failed! errno=%d\n", errno);
          exit(556);
     }
     if(setsockopt(sockfd, SOL_SOCKET, SO_RCVBUF, &(p->prot.rcvbufsz), 
                                       sizeof(p->prot.rcvbufsz)) < 0)
     {
          printf("NetPIPE: setsockopt: SO_RCVBUF failed! errno=%d\n", errno);
          exit(556);
     }
 }

 if (tr){                                  /* if client i.e., Sender */

   if (host) {                   
     getaddrinfo(host, NULL, &hints, &addr);
     memcpy(&p->prot.sin1, addr->ai_addr, addr->ai_addrlen);
   } else {
     if ((getaddrinfo(host,NULL,&hints,&addr)) != 0){
       printf("NetPIPE: invalid hostname '%s'\n", host);
       exit(-5);
   }
 }
   p->prot.sin1.sin6_port = htons(p->port);
 
} else {                                 /* we are the receiver (server) */

   memset(&p->prot.sin1, 0, sizeof(p->prot.sin1));
   if (sr==0){
     p->prot.sin1.sin6_addr = in6addr_any;
     p->prot.sin1.sin6_port = htons(p->port);
     lsin1=(struct sockaddr*)&p->prot.sin1; 
     if (bind(sockfd, (struct sockaddr*)lsin1, sizeof(p->prot.sin1)) < 0){
       printf("NetPIPE: server: bind on local address failed! errno=%d", errno);
       exit(-6);
     }
   }
   else{
       getaddrinfo(server_host, NULL, NULL, &server_addr);
       memcpy(&p->prot.sin1, server_addr->ai_addr, server_addr->ai_addrlen);
       if ((getaddrinfo(server_host, NULL, NULL, &server_addr)) != 0){
  	  printf("NetPIPE: invalid hostname '%s'\n", host);
	  exit(-5);
       }
       memcpy(&p->prot.sin1, server_addr->ai_addr, server_addr->ai_addrlen);
       p->prot.sin1.sin6_port = htons(p->port);
       lsin1=(struct sockaddr*)&p->prot.sin1; 
       if (bind(sockfd, (struct sockaddr*)lsin1, sizeof(p->prot.sin1)) < 0){
         printf("NetPIPE: server: bind on %s failed! errno=%d", server_host, errno);
         exit(-6);
       }
   }     
 }

 if(tr)
   p->commfd = sockfd;
 else
   p->servicefd = sockfd;

 return(0);
 
}   

static int
readFully(int fd, void *obuf, int len)
{
  int bytesLeft = len;
  char *buf = (char *) obuf;
  int bytesRead = 0;

  while (bytesLeft > 0 &&
	 (bytesRead = read(fd, (void *) buf, bytesLeft)) > 0)
    {
      bytesLeft -= bytesRead;
      buf += bytesRead;
    }
  if (bytesRead <= 0)
    return bytesRead;
  return len;
}

void Sync(ArgStruct *p)
{
    char s[] = "SyncMe";
    char response[7];

    if (write(p->commfd, s, strlen(s)) < 0 ||
	readFully(p->commfd, response, strlen(s)) < 0)
      {
	perror("NetPIPE: error writing or reading synchronization string");
	exit(3);
      }
    if (strncmp(s, response, strlen(s)))
      {
	fprintf(stderr, "NetPIPE: Synchronization string incorrect!\n");
	exit(3);
      }
}

void PrepareToReceive(ArgStruct *p)
{
	/*
	  The Berkeley sockets interface doesn't have a method to pre-post
	  a buffer for reception of data.
	*/
}

void SendData(ArgStruct *p)
{
    int bytesWritten, bytesLeft;
    char *q;

    bytesLeft = p->bufflen;
    bytesWritten = 0;
    q = p->buff;
    while (bytesLeft > 0 &&
	   (bytesWritten = write(p->commfd, q, bytesLeft)) > 0)
      {
	bytesLeft -= bytesWritten;
	q += bytesWritten;
      }
    if (bytesWritten == -1)
      {
	printf("NetPIPE: write: error encountered, errno=%d\n", errno);
	exit(401);
      }
}

void RecvData(ArgStruct *p)
{
    int bytesLeft;
    int bytesRead;
    char *q;

    bytesLeft = p->bufflen;
    bytesRead = 0;
    q = p->buff1;
    while (bytesLeft > 0 &&
	   (bytesRead = read(p->commfd, q, bytesLeft)) > 0)
      {
	bytesLeft -= bytesRead;
	q += bytesRead;
      }
    if (bytesLeft > 0 && bytesRead == 0)
      {
	printf("NetPIPE: \"end of file\" encountered on reading from socket\n");
      }
    else if (bytesRead == -1)
      {
	printf("NetPIPE: read: error encountered, errno=%d\n", errno);
	exit(401);
      }
}

void SendTime(ArgStruct *p, double *t)
{
    unsigned int ltime, ntime;

    /*
      Multiply the number of seconds by 1e6 to get time in microseconds
      and convert value to an unsigned 32-bit integer.
      */
    ltime = (unsigned int)(*t * 1.e6);

    /* Send time in network order */
    ntime = htonl(ltime);
    if (write(p->commfd, (char *)&ntime, sizeof(unsigned int)) < 0)
      {
	printf("NetPIPE: write failed in SendTime: errno=%d\n", errno);
	exit(301);
      }
}

void RecvTime(ArgStruct *p, double *t)
{
    unsigned int ltime, ntime;
    int bytesRead;

    bytesRead = readFully(p->commfd, (void *)&ntime, sizeof(unsigned int));
    if (bytesRead < 0)
      {
	printf("NetPIPE: read failed in RecvTime: errno=%d\n", errno);
	exit(302);
      }
    else if (bytesRead != sizeof(unsigned int))
      {
	fprintf(stderr, "NetPIPE: partial read in RecvTime of %d bytes\n",
		bytesRead);
	exit(303);
      }
    ltime = ntohl(ntime);

    /* Result is ltime (in microseconds) divided by 1.0e6 to get seconds */
    *t = (double)ltime / 1.0e6;
}

void SendRepeat(ArgStruct *p, int rpt)
{
  unsigned int lrpt, nrpt;

  lrpt = rpt;
  /* Send repeat count as an unsigned 32 bit integer in network order */
  nrpt = htonl(lrpt);
  if (write(p->commfd, (void *) &nrpt, sizeof(unsigned int)) < 0)
    {
      printf("NetPIPE: write failed in SendRepeat: errno=%d\n", errno);
      exit(304);
    }
}

void RecvRepeat(ArgStruct *p, int *rpt)
{
  unsigned int lrpt, nrpt;
  int bytesRead;

  bytesRead = readFully(p->commfd, (void *)&nrpt, sizeof(unsigned int));
  if (bytesRead < 0)
    {
      printf("NetPIPE: read failed in RecvRepeat: errno=%d\n", errno);
      exit(305);
    }
  else if (bytesRead != sizeof(unsigned int))
    {
      fprintf(stderr, "NetPIPE: partial read in RecvRepeat of %d bytes\n",
	      bytesRead);
      exit(306);
    }
  lrpt = ntohl(nrpt);

  *rpt = lrpt;
}

int Establish(ArgStruct *p)
{
 int clen;
 int one = 1;
 struct protoent *proto;

 clen = sizeof(p->prot.sin2);
 if(p->tr){
   if(connect(p->commfd, (struct sockaddr *) &(p->prot.sin1),
	      sizeof(p->prot.sin1)) < 0){
     printf("Client: Cannot Connect! errno=%d\n",errno);
     exit(-10);
   }
  }
  else {
    /* SERVER */
    listen(p->servicefd, 5);
    p->commfd = accept(p->servicefd, (struct sockaddr *) &(p->prot.sin2),
		       &clen);

    if(p->commfd < 0){
      printf("Server: Accept Failed! errno=%d\n",errno);
      exit(-12);
    }

    /*
      Attempt to set TCP_NODELAY. TCP_NODELAY may or may not be propagated
      to accepted sockets.
     */
    if(!(proto = getprotobyname("tcp"))){
      printf("unknown protocol!\n");
      exit(555);
    }

    if(setsockopt(p->commfd, proto->p_proto, TCP_NODELAY,
		  &one, sizeof(one)) < 0)
    {
      printf("setsockopt: TCP_NODELAY failed! errno=%d\n", errno);
      exit(556);
    }

    /* If requested, set the send and receive buffer sizes */
    if(p->prot.sndbufsz > 0)
    {
      printf("Send and Receive Buffers on accepted socket set to %d bytes\n",
	     p->prot.sndbufsz);
      if(setsockopt(p->commfd, SOL_SOCKET, SO_SNDBUF, &(p->prot.sndbufsz), 
                                       sizeof(p->prot.sndbufsz)) < 0)
      {
	printf("setsockopt: SO_SNDBUF failed! errno=%d\n", errno);
	exit(556);
      }
      if(setsockopt(p->commfd, SOL_SOCKET, SO_RCVBUF, &(p->prot.rcvbufsz), 
                                       sizeof(p->prot.rcvbufsz)) < 0)
      {
	printf("setsockopt: SO_RCVBUF failed! errno=%d\n", errno);
	exit(556);
      }
    }
  } 
  return(0);
}

int  CleanUp(ArgStruct *p)
{
 char *quit="QUIT";
 if (p->tr)
 {
     write(p->commfd,quit, 5);
     read(p->commfd, quit, 5);
     close(p->commfd);
 }
 else
 {
     read(p->commfd,quit, 5);
     write(p->commfd,quit,5);
     close(p->commfd);
     close(p->servicefd);
 }
 return(0);
}

