/* netio.c
 * This file provides the network connectivity and utility functions used by
 * the remainder of the TSL program */

/* This file is part of TSL.
 *
 * TSL 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; either version 2 of the License, or
 * (at your option) any later version.
 *
 * TSL is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with TSL; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

#include "tsl.h"

#define BUFFERSIZE 1048576
#define MAXPATTERNSIZE 1024
#define MINGETSIZE 8192
static char *iobuffer;					/* Buffer */
static char *pos;								/* Position of next byte to read */
static char *bufend;						/* First non-data character */

/* Return 1 on buffer match, 0 on no match */
int netmatch( char *match )
{
	if ( strncmp( match, pos-strlen(match), strlen(match) ) == 0 )
		return 1;
	return 0;
}

int tsl_connect( char *host, int port )
{
	int sockfd;											/* I/O handle for TCP communications */
	struct sockaddr_in dest_addr;		/* Structure for remote host */
        struct hostent *h;

	/* Create a TCP/IP socket for connecting to the remote host */
	sockfd = socket( AF_INET, SOCK_STREAM, 0 );
	if ( sockfd == -1 )
	{
		perror( "Socket: " );
		exit(1);
	}

	/* Fill in the structure describing the remote host */
	memset( &(dest_addr), 0, sizeof(dest_addr) );	/* Unused fields must be 0'd */
	dest_addr.sin_family = AF_INET;							/* Using TCP/IP */
	dest_addr.sin_port = htons(port);						/* This TCP port */

	if ((dest_addr.sin_addr.s_addr=inet_addr(host)) == -1 )		/* This address */
	{
        h = gethostbyname(host);
        if (h == NULL) {
		    fprintf(stderr, "Cannot resolve host: %s\n", host);
		    exit(1);
        }
        bcopy(h->h_addr, &dest_addr.sin_addr.s_addr, h->h_length);
	}

	/* Connect to the specified host */
	if ( connect( sockfd, (struct sockaddr *)&dest_addr,
				sizeof(struct sockaddr)) == -1 )
	{
		perror( "Connect" );
		exit(1);
	}

	iobuffer = calloc( 1, BUFFERSIZE );
	assert( iobuffer );
	bufend=iobuffer;
	pos=iobuffer;

	return sockfd;
}	

/* Send a string to the remote host */
void send_string( int sockfd, char *to_send )
{
	char *pos;
	int send_length;
	int result;

	pos=to_send;
	send_length=strlen(to_send);
	while( send_length > 0 )
	{
		 result = send( sockfd, to_send, strlen(to_send), 0 );
		 if ( result == -1 )
		 {
			 perror( "send" );
			 exit(1);
		 } else {
			 send_length -= result;
			 pos += result;
		 }
	}
}

/* Reads a character from the remote host.  Returns -1 on EOF. */
int get_char( int sockfd )
{
	int result;
	char *dummy;

	while( 1 )
	{
		/* If we already have characters in the buffer, return one */
		if ( pos < bufend )
		{
			if ( *pos == -1 && *(pos+1) == -3 )  /* Process Telnet handshakes */
				/* (Send a "won't do" for anything requested)  */
			{
				dummy = calloc( 1, 4 );
				strncpy( dummy, pos, 3 );
				dummy[1] = -4;
				send_string(sockfd,dummy);
				free(dummy);
				pos += 3;
				continue;
			}
			if ( *pos == -1 )              /* Telnet non-requests */
			{
				pos += 3;										 /*    ignore... */
				continue;
			}
			pos++;
			return *(pos-1);
		}

		/* If we can't read MINGETSIZE characters, shift the buffer left some */
		if ( iobuffer+BUFFERSIZE-bufend < MINGETSIZE )
		{
			memmove( iobuffer, pos-MAXPATTERNSIZE-1, MAXPATTERNSIZE-1 );
			bufend=iobuffer+MAXPATTERNSIZE;  /* New non-data character */
		  pos=bufend;                      /* Still at the end 	of the buffer */
		}

		/* There are no characters in the read buffer, so we attempt to read
			 MINGETSIZE bytes from the remote end */
		result = read(sockfd, pos, MINGETSIZE );
		/* Unknown error */
		if( result == -1 )
		{
			if ( errno == EINTR )  /* Alarm went off */
				return -1;
			perror( "read" );
			exit(1);
		}
		/* EOF */
 		else if ( result == 0 )
		{
			return -1;
		}
		/* Otherwise info has been read */
		else
		{
			bufend += result;					/* Adjust the end-of-buffer mark */
			continue;
		}
	}
}	

