/*
 * Copyright (c) 1996-2007, OpenFWTK Development Group
 * All rights reserved. See LICENSE.
 */

/*
	soio -- usefull routines to simplify sockets handling in manner of 
	regular filedescriptors.

	void sosetline(int) -- set line termination style

	time_t sotimeout(sec) -- set timeout in seconds for socket operations, 
		returns old timeout set by sotimeout().

	int soread(fdes, buf, siz) -- read socket using select(), returns number
		of bytes read.

	int sogets(fdes,  buf, siz) -- read line \n terminated. returns number
		of bytes read.

	int sosayn(fdes, str, siz) -- put line to socket, terminate with \r\n; 
		returns 0 when ok, other when write fails.

	int sosay(fdes, str) -- simplified version of sosayn(), do not uses 
		string length.

 $Id: soio.c,v 1.15 2007/09/10 02:49:13 arkenoi Exp $

 */


#include <stdio.h>
#include <ctype.h>
#include <string.h>
#include <strings.h>
#include <syslog.h>
#include <fcntl.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <errno.h>
#include <unistd.h>


#include "firewall.h"

/* this is to be moved into specific .h */

#define	SO_LNONE	0
#define	SO_LCRLF	1
#define	SO_LCR		2
#define	SO_LLF		3

static char* moduleId ATTR_UNUSED = "$Id: soio.c,v 1.15 2007/09/10 02:49:13 arkenoi Exp $";

extern 	int proxy_timeout;
extern  size_t strlcat(char*,const char*,size_t);
extern  size_t strlcpy(char*,const char*,size_t);

static struct timeval sotimo = { 0L, 0L };
static char crlf[3] = "\r\n";
int    proxy_linestyle = SO_LCRLF;

void
sosetline(t)
int t;
{
	bzero(crlf, sizeof(crlf));
	switch(t) {
	case SO_LNONE:	break;
	case SO_LCRLF:	(void)strlcpy(crlf, "\r\n", 3); break;
	case SO_LCR:	(void)strlcpy(crlf, "\r", 3); break;
	case SO_LLF:	(void)strlcpy(crlf, "\n", 3); break;
	}
	proxy_linestyle = t;
	return;
}


time_t
sotimeout(sec)
int sec;
{
	time_t oldtim = sotimo.tv_sec;
	if(sec) {
		sotimo.tv_sec = (sec == -1) ? 0L : (time_t)sec;
		sotimo.tv_usec = 0L;
	}
	return oldtim;
}

int
soread(fd, buf, siz)
int	fd, siz;
char *buf;
{
	fd_set	r;
	struct	timeval timo;
	int	x;

	if (!sotimo.tv_sec)
		sotimeout(proxy_timeout);

	timo = sotimo;

	while (1) {

		FD_ZERO(&r);
		FD_SET(fd, &r);
		x = select(fd + 1, &r, (fd_set *)0, (fd_set *)0, &timo);
		if (x == 0) {
			syslog(LLEV, "Network timeout signal after %lu seconds",
				sotimo.tv_sec);
			return(-1);
		}
		if (x < 0) {
			if (errno == EINTR) continue;
			return -1;
		}
		break;
	}
	x = read(fd, buf, siz);
	return((x == 0) ? -1 : x);
}

int
sowrite(fd,s,n)
int	fd, n;
char *s;
{
	char	*buffer;
	int	x, nleft;
	fd_set	w;
	struct	timeval timo;

	buffer = s;
	nleft  = n;

	if (!sotimo.tv_sec)
		sotimeout(proxy_timeout);

	timo = sotimo;

        while ( nleft > 0 ) {
		if ((x = write(fd, buffer, nleft)) <= 0 ) {
		    if ( errno == EINTR )
		  	x = 0;
	    	    else if (errno == EAGAIN) {
			FD_ZERO(&w);
			FD_SET(fd, &w);
			switch(select(fd+1,NULL,&w,NULL,&timo)) {
				case	-1:	return(-1);
				case	 0:	syslog(LLEV, 
					"Network timeout signal after %lu seconds",
					sotimo.tv_sec);
						return(-1);
			}
		    } else
			return(-1);
	    	}
		nleft  -= x;
		buffer += x;
	}
    	return n;
}

int
sogets(fd, buf, siz)
int fd, siz;	
char *buf;
{
	int		x = 0;

	while(1) {
		if(soread(fd, (char *)&buf[x], 1) != 1)
			return(-1);

		/* some telnets send NULL-terminated lines in NOECHO :( */
		if(buf[x] == '\0')
			continue;

		if(buf[x] == '\n') {
			buf[++x] = '\0';
			return(x);
		}

		if(++x >= siz) {
			syslog(LLEV, "fwtksyserr: sogets(): buffer overrun");
			return(-1);
		}
	}
}

int
sosayn(fd, s, n)
int	fd, n;
char *s;
{
	int tl = strlen(crlf);
	if(sowrite(fd, s, n) != n)
		return(1);
	
	return(tl ? sowrite(fd, crlf, tl) != tl : 0);
}

int
sosay(fd, s)
int	fd;
char *s;
{
	return(sosayn(fd, s, strlen(s)));
}

