
/*
net.c	-network functions..
   distributed as part of _ s u l a _

Tano Fotang, 1997

*/

/* 

    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; either version 2 of the License , or
    (at your option) any later version.
      see the file COPYING for more info.
    ----
    This file also contains modified versions of functions taken from
    W. Richard Steven's books on unix programming. The func names have been
    retained:
    	writen(), readn()
	Unix network programming, Hanser/Prentice-Hall


*/

#include <sys/param.h>
#include "aux.h"
#include <netdb.h>

#include <arpa/inet.h>
#include <stdarg.h>


extern void     err_ret (const char *,...);
extern void     err_msg (const char *,...);
extern void     err_sys (const char *,...);

/*extern jmp_buf	env;*/
/* if longjmp isnt siglongjmp then we're in trouble..bloody masks*/


static char *err_str(int flag, const char *fmt,...){
        static char buf[512];
	int err=errno;
        va_list ap;
        va_start(ap, fmt);
        vsprintf(buf, fmt, ap);
        va_end(ap);
        if(flag) sprintf(buf+strlen(buf),": %s", strerror(err));
	return buf;
}
int			/* return socket descriptor if ok, -1 if error*/
tcp_open(char *host,	/* system to connect to*/
	unsigned long inetaddr,	/* used if host=NULL*/	
 	char *service,	/* requested service; can be NULL if port>0*/
 	int port,	/* ==0:use port # of service */
			/* <0: bind a reserved local port*/
			/* >0: port # of server*/
	u_int timeout)/*timeout in seconds. 0 -> no timeout*/
{

/*
this is based on a procedure found in Richard Stevens' book on Unix network 
programming, published by Hanser / Prentice-Hall Intl.
*/
volatile int		fd;
int resvport;
struct sockaddr_in	tcp_rem_addr;	/*remote internet socket addr*/
struct servent	*sp;	/*service entry*/
/*struct hostent	*hp;*/
int result;

	
	
	tcp_err_msg=NULL;/* no error yet */
	bzero( (char *) &tcp_rem_addr, sizeof(tcp_rem_addr));
	tcp_rem_addr.sin_family=AF_INET;
	if(service){
		if( (sp=getservbyname(service, "tcp"))==NULL){
			tcp_err_msg=
			err_str(1, "tcp_open: unknown service: %s/tcp", service);
			return -1;
		}
		if(port>0)
			tcp_rem_addr.sin_port=htons(port);
		else
			tcp_rem_addr.sin_port=sp->s_port;
	}
	else{
		if(port<=0){
			tcp_err_msg=err_str
			(0, "tcp_open: must specify either service or port");
			return -1;
		}
		tcp_rem_addr.sin_port=htons(port);
	}
	if( host==NULL ){
		if( (tcp_rem_addr.sin_addr.s_addr=htonl(inetaddr))<=0){
		       tcp_err_msg=err_str
		       (0, "tcp_open: must specify either host or inet_addr");
			return -1;
		}
	}
   	else
  {
	tcp_rem_addr.sin_addr.s_addr=inet_addr(host);
	if(tcp_rem_addr.sin_addr.s_addr == INADDR_NONE){	/* -1*/
		struct hostent	*hp=NULL;
		#if DEBUG
		fprintf(stderr,"calling gethostbyname(%s)\n", host);
		#endif
		hp=gethostbyname(host);
		if(hp==NULL){
			switch(h_errno){
			case HOST_NOT_FOUND:
			tcp_err_msg= 
			"tcp_open: gethostbyname error (HOST_NOT_FOUND):"
			" Unknown host"; break;
			case TRY_AGAIN:
			tcp_err_msg=err_str(1,
			"tcp_open: gethostbyname error (TRY_AGAIN): "
			"Host name lookup failure.");
			break;
			case NO_RECOVERY:
			tcp_err_msg=err_str(1,
			"tcp_open: gethostbyname error (NO_RECOVERY):"
			" Unexpected server failure.");
			break;
			case NO_DATA:
			tcp_err_msg=err_str(1,
			"tcp_open: gethostbyname error (NO_DATA)");
			break;
			case NETDB_INTERNAL:
			tcp_err_msg=err_str(1,
			"tcp_open: gethostbyname error (NETDB_INTERNAL)");
			break;
			default:
			tcp_err_msg=err_str(1,
			"tcp_open: gethostbyname error");
			break;
			}/* switch*/ 
			return -1;
		}
		#if DEBUG
		fprintf(stderr, " ..ok\n");
		#endif
		bcopy(hp->h_addr, (char *)&tcp_rem_addr.sin_addr, hp->h_length);
	}
 }/* host==NULL*/
	if(port>=0){
		if( (fd=socket(AF_INET, SOCK_STREAM, 0)) <0){
			tcp_err_msg=err_str(1, "tcp_open: unable to create TCP socket");
			return -1;
		}
	}else if(port<0){
		resvport=IPPORT_RESERVED -1;
		if( (fd=rresvport (&resvport)) <0){
			tcp_err_msg=err_str(1, "tcp_open: unable to get reserved TCP port");
			return -1;
		}
	}
	
#ifndef SA_RESTART
retry:
#endif
	result=connect(fd, (struct sockaddr *)&tcp_rem_addr, sizeof(tcp_rem_addr));
	if(result<0)
	{
	       #ifndef SA_RESTART
	       if(errno==EINTR) {
	       		#if DEBUG
			fprintf(stderr,strerror(errno));
			#endif
			goto retry;
		 }
		else
		#endif
		{
	       tcp_err_msg=err_str(1, "Unable to connect to server");
	       close(fd);
	       return -1;
	       }
	}
	return fd;
}

extern int tcp_create_server(void){
/* create a server on local host and rreturn fd */

	struct sockaddr_in server_addr;
	int fd;

	if( (fd=socket(AF_INET, SOCK_STREAM, 0)) <0){
			err_ret("tcp_create_server: cant create tcp socket");
			return -1;
	}
	bzero(&server_addr, sizeof(server_addr));
	server_addr.sin_family		=AF_INET;
	server_addr.sin_addr.s_addr	=INADDR_ANY;
	server_addr.sin_port		=0;
	if (bind(fd, (struct sockaddr *) &server_addr, sizeof(server_addr))<0){
		err_ret("tcp_create_server:cant bind local addr");
		close(fd);
		return -1;
	}
	if(listen(fd, 1)<0){
		err_ret("tcp_create_server:listen error");
		close(fd);
		return -1;
	}
	else{
		int ll=sizeof(server_addr);
		/* get sin_family, sin_port, sin_addr :*/
		if(getsockname(fd, (struct sockaddr *) &server_addr, &ll)<0){
			err_ret("tcp_create_server:getsockname error");
			close(fd);
			return -1;
		}
		
	}
	return fd;
}

/*

 the next 4 functionss are taken from
Richard Stevens' book on Unix network 
programming, published by Hanser / Prentice-Hall Intl.

*/
ssize_t
writen(int fd, const void *vptr, size_t n)
{
	size_t	nleft	;
	ssize_t	nwritten;
	const char *ptr;

	ptr=vptr;
	nleft=n;
	while(nleft>0){
		if( (nwritten=write(fd, ptr, nleft)) <=0)
			return(nwritten);
		nleft-=nwritten;
		ptr+=nwritten;
	}
	return (n);
}
ssize_t readn(int fd, void *vptr, size_t n)
{
	size_t	nleft	;
	ssize_t	nread;
	char *ptr;

	ptr=vptr;
	nleft=n;
	while(nleft>0){
		if( (nread=read(fd, ptr, nleft)) <0)
			return(nread);
		else if(nread==0)	/*EOF*/
			break;
		nleft-=nread;
		ptr+=nread;
	}
	return(n-nleft);
}
static volatile sig_atomic_t sigflag;
static sigset_t newmask, oldmask, zeromask;
static void sig_usr(int signo){
	sigflag=1;
	return;
}
extern void TELL_WAIT(void){

if(Signal(SIGUSR1, sig_usr)==SIG_ERR)
	err_sys("signal SIGUSR1");
sigemptyset(&zeromask);
sigemptyset(&newmask);
sigaddset(&newmask, SIGUSR1);
if(sigprocmask(SIG_BLOCK,&newmask,&oldmask)  <0)
	err_sys("signal SIG_BLOCK");
}
extern void TELL_CHILD(pid_t pid){
	kill(pid, SIGUSR1);
}
extern void WAIT_PARENT(void){
	while(sigflag==0)
		sigsuspend(&zeromask);
	sigflag=0;
	if(sigprocmask(SIG_SETMASK,&oldmask,NULL)  <0)
	err_sys("signal SIG_SETMASK");
}			

