/*****************************************************************************/
/* "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.   */
/*                                                                           */
/* TCP6 extension Copyright 2004 George V. Neville-Neil and Neville-Neil     */
/* Consulting                                                                */
/*                                                                           */
/*     * tcp6.c         ---- TCP over IPv6 calls source                      */
/*     * tcp.h          ---- Include file for TCP6 calls and data structs    */
/*****************************************************************************/
#include    "netpipe.h"

#if defined (MPLITE)
#include "mplite.h"
#endif


int doing_reset = 0;

void Init(ArgStruct *p, int* pargc, char*** pargv)
{
    p->reset_conn = 0; /* Default to not resetting connection */
    p->prot.sndbufsz = p->prot.rcvbufsz = 0;
    /* The transmitter will be set using the -h host flag. */
    p->tr = 0;
    p->rcv = 1;
}

void Setup(ArgStruct *p)
{
    int one = 1;
    int sockfd = -1;
    /* ptr to sockaddr_in in ArgStruct */
    struct sockaddr_in6 *lsin1, *lsin2;      
    
    char *host;
    struct hostent *hp;
    struct protoent *proto;
    int send_size, recv_size, sizeofint = sizeof(int);
	
    host = p->host;                           /* copy ptr to hostname */ 
	
    lsin1 = &(p->prot.sin1);
    lsin2 = &(p->prot.sin2);
	
    bzero((char *) lsin1, sizeof(*lsin1));
    bzero((char *) lsin2, sizeof(*lsin2));

    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)
    {
	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);
	    printf("You may have asked for a buffer larger than the system can handle\n");
	    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);
	    printf("You may have asked for a buffer larger than the system can handle\n");
	    exit(556);
	}
    }
    getsockopt(sockfd, SOL_SOCKET, SO_SNDBUF,
	       (char *) &send_size, (void *) &sizeofint);
    getsockopt(sockfd, SOL_SOCKET, SO_RCVBUF,
	       (char *) &recv_size, (void *) &sizeofint);
 
    if(!doing_reset) {
	fprintf(stderr,"Send and receive buffers are %d and %d bytes\n",
		send_size, recv_size);
	fprintf(stderr, "(A bug in Linux doubles the requested buffer sizes)\n");
    }

    if( p->tr ) {                             /* Primary transmitter */

	lsin1->sin6_family = AF_INET6;

	/* First attempt to convert the string to an IPv6 */
	/* address. */
	/* If the user supplied a real host name this will fail and */
  	/* we'll then do a name lookup. */

	if (inet_pton(AF_INET6, host, &lsin1->sin6_addr) == 0)
	{
	    if ((hp = gethostbyname2(host, AF_INET6)) == NULL)
	    {
		printf("NetPIPE: invalid hostname '%s'\n", host);
		exit(-5);
	    }

	    if (hp->h_addrtype != AF_INET6) 
	    {
		printf("NetPIPE: invalid hostname '%s'\n", host);
		exit(-5);
	    }
	    bcopy(hp->h_addr, (char*) &(lsin1->sin6_addr), 
		  hp->h_length);
	}

	lsin1->sin6_port = htons(p->port);
	
	p->commfd = sockfd;
	
    } else if( p->rcv ) {                     /* we are the receiver */
	bzero((char *) lsin1, sizeof(*lsin1));
	lsin1->sin6_family  = AF_INET6;
	lsin1->sin6_len     = sizeof(*lsin1);
	lsin1->sin6_port    = htons(p->port);
	/* Setting this to all 0 is the "ANY" address. */
	bzero(&lsin1->sin6_addr, sizeof(lsin1->sin6_addr));
   
	if (bind(sockfd, (struct sockaddr *) lsin1, sizeof(*lsin1)) < 0){
	    printf("NetPIPE: server: bind on local address failed! errno=%d", errno);
	    exit(-6);
	}

	p->servicefd = sockfd;
    }
    p->upper = send_size + recv_size;

    establish(p);                               /* Establish connections */

}   

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", response[] = "      ";

    if (write(p->commfd, s, strlen(s)) < 0 ||           /* Write to nbor */
	readFully(p->commfd, response, strlen(s)) < 0)  /* Read from nbor */
    {
	perror("NetPIPE: error writing or reading synchronization string");
	exit(3);
    }
    if (strncmp(s, response, strlen(s)))
    {
	fprintf(stderr, "NetPIPE: Synchronization string incorrect! |%s|\n", response);
	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->s_ptr;
    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->r_ptr;
    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);
    }
}

/* uint32_t is used to insure that the integer size is the same even in tests 
 * between 64-bit and 32-bit architectures. */

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

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

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

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

    bytesRead = readFully(p->commfd, (void *)&ntime, sizeof(uint32_t));
    if (bytesRead < 0)
    {
	printf("NetPIPE: read failed in RecvTime: errno=%d\n", errno);
	exit(302);
    }
    else if (bytesRead != sizeof(uint32_t))
    {
	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.0e8 to get seconds */

    *t = (double)ltime / 1.0e8;
}

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

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

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

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

    *rpt = lrpt;
}

void establish(ArgStruct *p)
{
    int one = 1;
    socklen_t clen;
    struct protoent *proto;

    clen = (socklen_t) sizeof(p->prot.sin2);

    if( p->tr ){

	while( connect(p->commfd, (struct sockaddr *) &(p->prot.sin1),
		       sizeof(p->prot.sin1)) < 0 ) {

	    /* If we are doing a reset and we get a connection refused from
	     * the connect() call, assume that the other node has not yet
	     * gotten to its corresponding accept() call and keep trying until
	     * we have success.
	     */
	    if(!doing_reset || errno != ECONNREFUSED) {
		printf("Client: Cannot Connect! errno=%d\n",errno);
		exit(-10);
	    } 
        
	}

    } else if( p->rcv ) {

	/* 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);
	    }
	}
    }
}

void CleanUp(ArgStruct *p)
{
    char *quit="QUIT";

    if (p->tr) {

	write(p->commfd,quit, 5);
	read(p->commfd, quit, 5);
	close(p->commfd);

    } else if( p->rcv ) {

	read(p->commfd,quit, 5);
	write(p->commfd,quit,5);
	close(p->commfd);
	close(p->servicefd);

    }
}


void Reset(ArgStruct *p)
{
  
    /* Reset sockets */

    if(p->reset_conn) {

	doing_reset = 1;

	/* Close the sockets */

	CleanUp(p);

	/* Now open and connect new sockets */

	Setup(p);

    }

}

void AfterAlignmentInit(ArgStruct *p)
{

}



syntax highlighted by Code2HTML, v. 0.9.1