/*
** @(#) Revision 1.4 - 07/01/98
** uici.c
**
** =======================================================================
** Andrea Whitlock, Mobius Software Services
** whitlock@mobius-soft.com
**
** Bug reports, questions, and suggestions should be emailed to:
** mobius@mobius-soft.com
**
** This software is provided under the terms of the GNU copyleft
** (ftp://prep.ai.mit.edu/pub/gnu/COPYING-2.0), without a warranty
** of any kind.  Use at your own risk.
** =======================================================================
**
** Description:
** Internet socket utility functions.
**
** Notes:
** Set tabstops to 4.
** This module can be used with Microsoft C.  Just compile without UNIX_CLIENT
**   defined.  It has been tested with MS Visual C++ 5.0.
** This code is based on an Operating Systems course I took (from Dr. Kay
**   Robbins) at UTSA.
**
*/

static char uiciSccsId[] = "@(#)uici.c	1.4\t07/01/98";
static char uiciVerId[] = "VER 1.4";

#include <stdio.h>
#ifdef UNIX_CLIENT
#include <netdb.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <strings.h>
#else
#include <winsock2.h>
#include <winsock.h>
#include <stdlib.h>
#include <io.h>
#include <string.h>
#endif
#include <sys/types.h>
#include <errno.h>
#include <fcntl.h>
#include "uici.h"

static char *err_msg = NULL;
#ifdef UNIX_CLIENT
extern int h_errno;
#else
#define errno WSAGetLastError()
#endif

void
u_sync(int fd)
{
/* Not needed for BSD sockets, retained for compatibility */
}

void
u_close(int fd)
{
	close(fd);
}

int
u_read(int fd, char *buf, int size)
{
	int retval;

	#ifdef UNIX_CLIENT
	while (retval = read(fd, buf, size), retval == -1 && errno == EINTR);
	#else
	while (retval = recv(fd, buf, size, 0), retval == -1 && errno == WSAEINTR);
	#endif
	if (retval == -1)
		err_msg = strerror(errno);
	return(retval);
}

int
u_write(int fd, char *buf, int size)
{
	int retval;

	#ifdef UNIX_CLIENT
	while (retval = write(fd, buf, size), retval == -1 && errno == EINTR);
	#else
	while (retval = send(fd, buf, size, 0), retval == -1 && errno == WSAEINTR);
	#endif
	if (retval == -1)
		err_msg = strerror(errno);
	
	return(retval);
}

void
u_error(char *user_msg)
{
	if (err_msg)
		fprintf(stderr, "%s: %s\n", user_msg, err_msg);
	else
		fprintf(stderr, "%s: No UICI Error\n", user_msg);
	err_msg = NULL;
}

int
u_connect(u_short port, char *the_host)
{
	struct sockaddr_in server;
	struct hostent *hp;
	int sock;
	int yes = 1;

	if (!(hp = gethostbyname(the_host)))
	{
		#ifdef UNIX_CLIENT
		switch (h_errno)
		{
			case HOST_NOT_FOUND :
				err_msg = "authoritative answer host not found";
				break;
			case TRY_AGAIN :
				err_msg = "non authoritative host not found, or SERVERFAIL";
				break;
			case NO_RECOVERY :
				err_msg = "non recoverable errors, FORMERR, REFUSED, NOTIMP";
				break;
			case NO_DATA :
				err_msg = "valid name, no data record of requested type";
				break;
			/*
			case NO_ADDRESS :
				err_msg = "no address, look for MX record";
				break;
			*/
			default :
				err_msg = "unknown host";
		}
		#else
		switch (WSAGetLastError())
		{
			case WSANOTINITIALISED :
				err_msg = "a successful WSAStartup must occur before using this function";
				break;
			case WSAENETDOWN :
				err_msg = "the network subsystem has failed";
				break;
			case WSAHOST_NOT_FOUND :
				err_msg = "authoritative answer host not found";
				break;
			case WSATRY_AGAIN :
				err_msg = "non authoritative host not found or server failure";
				break;
			case WSANO_RECOVERY :
				err_msg = "non recoverable error occurred";
				break;
			case WSANO_DATA :
				err_msg = "valid name, no data record of requested type";
				break;
			case WSAEINPROGRESS :
				err_msg = "a blocking windows sockets 1.1 call is in progress";
				break;
			case WSAEFAULT :
				err_msg = "the name parameter is not a valid part of the user address space";
				break;
			case WSAEINTR :
				err_msg = "the (blocking) call was canceled through WSACancelBlockingCall";
				break;
			default :
				err_msg = "unknown host";
		}
		#endif
		return(-1);
	}

	if ((sock = socket(hp->h_addrtype, SOCK_STREAM, 0)) < 0)
	{
		err_msg = strerror(errno);
		return(-1);
	}

	memcpy((char *)&server.sin_addr, hp->h_addr, hp->h_length);
	server.sin_port = htons(port);
	server.sin_family = hp->h_addrtype;

	if (connect(sock, (struct sockaddr *) & server, sizeof(server)) < 0) 
	{
		err_msg = strerror(errno);
		close(sock);
		return(-1);
	}

	if (setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, (char *)&yes, sizeof(yes)) 
		< 0)
	{
		err_msg = strerror(errno);
		close(sock);
		return(-1);
	}

	return(sock);
}

int
u_open(u_short port)
{
	int sock;
	int yes = 1;
	struct sockaddr_in server;
	struct hostent *hp;
	char host_name[100];


	gethostname( host_name, sizeof host_name );
	fprintf( stderr, "host_name is %s\n", host_name ); 
	if (!(hp = gethostbyname(host_name)))
	{
		switch (h_errno) 
		{
			case HOST_NOT_FOUND :
				err_msg = "authoritative answer host not found";
				break;
			case TRY_AGAIN :
				err_msg = "non authoritative host not found, or SERVERFAIL";
				break;
			case NO_RECOVERY :
				err_msg = "non recoverable errors, FORMERR, REFUSED, NOTIMP";
				break;
			case NO_DATA :
				err_msg = "valid name, no data record of requested type";
				break;
			/*
			case NO_ADDRESS :
				err_msg = "no address, look for MX record";
				break;
			*/
			default :
				err_msg = "unknown host";
		}
		return(-1);
	}

	if ((sock = socket(hp->h_addrtype, SOCK_STREAM, 0)) < 0)
	{
		err_msg = strerror(errno);
		return(-1);
	}
	server.sin_family = hp->h_addrtype;
	server.sin_addr.s_addr = INADDR_ANY;
	server.sin_port = htons(port);

	#if 1
	if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (char *)&yes, sizeof(yes)) 
		< 0)
	{
		err_msg = strerror(errno);
		return(-1);
	}
	#endif

	if (bind(sock, (struct sockaddr *) & server, sizeof(server)) < 0)
	{
		err_msg = strerror(errno);
		return(-1);
	}

	if (listen(sock, 5) < 0)
	{
		err_msg = strerror(errno);
		return(-1);
	}

	#if 0
	/* make socket non-blocking */
	if (fcntl(sock, F_SETFL, FNDELAY, &yes) == -1)
	{
		err_msg = strerror(errno);
		return(-1);
	}
	#endif

	return (sock);
}

int
u_listen(int fd)
{
	struct sockaddr_in net_client;
	int len = sizeof(net_client);
	int sock;
	int yes = 1;

	#ifdef UNIX_CLIENT
	if ((sock = accept(fd, (struct sockaddr *) & net_client, &len)) < 0 &&
		errno != EWOULDBLOCK && errno != EINTR)
	#else
	if ((sock = accept(fd, (struct sockaddr *) & net_client, &len)) < 0 &&
		errno != WSAEWOULDBLOCK && errno != WSAEINTR)
	#endif
	{
		err_msg = strerror(errno);
		close(sock);
		return(-1);
	}

	#if 0
	if (setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, (char *)&yes, sizeof(yes)) 
		< 0)
	{
		err_msg = strerror(errno);
		close(sock);
		return(-1);
	}
	#endif

	return(sock);
}

