

#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <errno.h>
#include <string.h>
#include <ctype.h>
#include <sys/types.h>
#include <signal.h>

#ifdef _WIN32

#include <io.h>
#include <winsock2.h>

#define closePLATFORMSPECIFIC(x)         closesocket((x))
#define ioctlPLATFORMSPECIFIC(x, y, z)   ioctlsocket((x), (y), (z))

#else

#include <sys/times.h>
#include <sys/socket.h>
#include <netdb.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/ioctl.h>
#include <unistd.h>

#define closePLATFORMSPECIFIC(x)         close((x))
#define ioctlPLATFORMSPECIFIC(x, y, z)   ioctl((x), (y), (z))

#endif

#include "netlib.h"
#include "debug.h"
#include "func_private.h"

#define EMPTYSLOT	-1
#define MAX(x, y)	(x) > (y) ? (x) : (y)

NETSOCKET          *sockhandles;	/* malloced array */
int                 sockhandles_size = 1;
int                *listeners;	/* malloced array */
int                 listeners_size = 1;
int                 highest_listener_fd;
int                 rebuild_fdsets;

fd_set              listeners_fdset;

static char const   rcsid[] = "$Id: net.c,v 1.29.1.1 1998/03/30 18:16:39 abakun Exp $";

func_private void   netprivate_setup_signal_handlers();


func_private error_t netprivate_create_socket(int domain, int type, int protocol)
{
    int                 orig_sock;

    orig_sock = socket(domain, type, protocol);

#ifdef _WIN32
    if (orig_sock == INVALID_SOCKET) {
	DB(104, (stderr, "socket(%d, %d, %d) = %d (INVALID_SOCKET = %d)\n", domain, type, protocol, orig_sock, INVALID_SOCKET));
	return NET_ESOCKET;
    }
#endif

    return orig_sock;

}

func_private void   netprivate_sigpipe_handler(int s)
{
    DB(-1, (stderr, "SIGPIPE received, arg is %d\n", s));
    /* signal was caught, and may be reset, so ignore it again */
    netprivate_setup_signal_handlers();
}

func_private void   netprivate_setup_signal_handlers()
{
#ifndef _WIN32
    signal(SIGPIPE, netprivate_sigpipe_handler);
#endif
}

func_private void   netprivate_clear_sockhandle(NETSOCKET * h)
{
    DB(111, (stderr, "clearing handle at %p\n", h));
    memset(h, 0, sizeof(NETSOCKET));
}


func_private void   netprivate_expand_sockhandles_array()
{

    NETSOCKET          *new;
    int                 old_num_sockhandles = sockhandles_size;
    int                 clearall, t;

    clearall = sockhandles == NULL;
    new = (NETSOCKET *) realloc((void *) sockhandles, sizeof(NETSOCKET) * sockhandles_size * 2);
    if (new == NULL) {
	/* FIXME: out of mem */
	DB(-1, (stderr, "out of memory, realloc failed (netprivate_expand_sockhandle_array)\n"));
	exit(1);
    }
    sockhandles = new;
    sockhandles_size *= 2;

    if (clearall)
	old_num_sockhandles = 0;

    for (t = old_num_sockhandles; t < sockhandles_size; t++) {
	netprivate_clear_sockhandle(&sockhandles[t]);
    }


}


func_private void   netprivate_expand_listeners_array()
{

    int                *new;
    int                 clearall, t, old_num_listeners = listeners_size;

    clearall = listeners == NULL;
    new = (int *) realloc((void *) listeners, sizeof(int) * listeners_size * 2);
    if (new == NULL) {
	/* FIXME: out of mem */
	DB(-1, (stderr, "out of memory, realloc failed (netprivate_expand_listeners_array)\n"));
	exit(1);
    }
    listeners = new;
    listeners_size *= 2;

    if (clearall)
	old_num_listeners = 0;

    for (t = old_num_listeners; t < sockhandles_size; t++)
	listeners[t] = EMPTYSLOT;

}


func_private void   netprivate_close_sockhandle(netsocket i)
{
    NETSOCKET *handle;

    handle = &sockhandles[i];
    DB(105, (stderr, "netprivate_close_sockhandle(%p)\n", handle));
    if (handle != NULL)
	if (handle->type == HANDLETYPE_NETSTREAM || handle->type == HANDLETYPE_FILE) {
	    DB(105, (stderr, "closing NETSTREAM sockhandle %p\n", handle));
	    if (handle->on_close != NULL) {
		(handle->on_close) (i);
	    } else {
		DB(105, (stderr, "no on_close to call\n"));
	    }
	    closePLATFORMSPECIFIC(handle->fd);
	    netprivate_clear_sockhandle(handle);
	    rebuild_fdsets = 1;
	}
}



func_public void    net_end(void)
{
    int                 i;

    DB(105, (stderr, "closing all open connections (net_end)\n"));
    for (i = 0; i < sockhandles_size; i++) {
	netprivate_close_sockhandle(i);
    }

#ifdef _WIN32
    WSACleanup();
#endif
}


func_public void    net_start()
{
    static int          net_started = 0;

    if (net_started)
	return;

#ifdef _WIN32

    int                 winWinsockErr;
    WSADATA             wsadata;

    if (WSAStartup(MAKEWORD(1, 1), &wsadata) != 0) {
	winWinsockErr = WSAGetLastError();
	fprintf(stderr, "WSAStartup Error : %d\n", &winWinsockErr);
	exit(1);
    }
#endif

    sockhandles = NULL;
    netprivate_expand_sockhandles_array();
    listeners = NULL;
    netprivate_expand_listeners_array();

    FD_ZERO(&listeners_fdset);
    rebuild_fdsets = 1;

    netprivate_setup_signal_handlers();
    atexit(net_end);

    net_started = 1;
}


func_private void   netprivate_add_listener(int h)
{

    int                 i, tries;

    highest_listener_fd = MAX(highest_listener_fd, sockhandles[h].fd);
    tries = 0;
    do {
	for (i = 0; i < listeners_size; i++) {
	    DB(110, (stderr, "looking at listeners[%d] == %d\n", i, listeners[i]));
	    if (listeners[i] == EMPTYSLOT) {
		listeners[i] = h;
		DB(105, (stderr, "listener slot %d pointing to sockhandle %d (port %d)\n", i, h, sockhandles[h].listen_port));
		return;
	    }
	}
	netprivate_expand_listeners_array();
	tries++;
    } while (tries < 2);
    DB(101, (stderr, "did not find a free sockhandle in netprivate_add_listener, this should not happen\n"));
    exit(15);
}


func_private int    netprivate_find_free_sockhandle()
{

    int                 i, tries;

    tries = 0;
    do {
	DB(105, (stderr, "try #%d to find a free sockhandle\n", tries));
	/* this is totally wasteful, since if we extend, we search the entire
	   array again, I could fix this by including a new starting value, but
	   whatever... FIXME
	 */
	DB(105, (stderr, "sockhandles_size == %d\n", sockhandles_size));
	for (i = 0; i < sockhandles_size; i++) {
	    DB(110, (stderr, "looking at sockhandles[%d] == %d\n", i, sockhandles[i].type));
	    if (sockhandles[i].type == HANDLETYPE_NONE)
		return i;
	}
	netprivate_expand_sockhandles_array();
	tries++;
    } while (tries < 2);

    DB(101, (stderr, "didn't find a free sockhandle in netprivate_find_free_sockhandle\n"));
    return NET_ENOTFOUND;

}


func_public error_t net_listen(int port, net_func_ptr accept_func)
{

    struct sockaddr_in  sa;
    int                 orig_sock, h;
    int                 ioctlflag = 1;

    if (accept_func == NULL)
	return NET_EINVARG;

    if ((orig_sock = netprivate_create_socket(AF_INET, SOCK_STREAM, 0)) < 0)
	return NET_ESOCKET;

    if (ioctlPLATFORMSPECIFIC(orig_sock, FIONBIO, &ioctlflag) < 0) {
	closePLATFORMSPECIFIC(orig_sock);
	return NET_EBADIOCTL;
    }
    memset(&sa, 0, sizeof(sa));
    sa.sin_family = AF_INET;
    sa.sin_addr.s_addr = htonl(INADDR_ANY);
    sa.sin_port = htons((short) (port & 65535));

    if (bind(orig_sock, (struct sockaddr *) &sa, sizeof(struct sockaddr_in)) < 0) {
	closePLATFORMSPECIFIC(orig_sock);
	return NET_EINVARG;
    }
    if (listen(orig_sock, 5) < 0) {
	closePLATFORMSPECIFIC(orig_sock);
	return NET_EINVARG;
    }
    h = netprivate_find_free_sockhandle();
    if (h == NET_ENOTFOUND) {
	closePLATFORMSPECIFIC(orig_sock);
	return NET_EINVARG;
    }
    DB(105, (stderr, "listener using fd %d in sockhandle %d\n", orig_sock, h));

    netprivate_clear_sockhandle(&sockhandles[h]);
    sockhandles[h].fd = orig_sock;
    sockhandles[h].type = HANDLETYPE_LISTENING;
    sockhandles[h].listen_port = port;
    sockhandles[h].on_accept = accept_func;

    netprivate_add_listener(h);
    FD_SET((unsigned int) orig_sock, &listeners_fdset);

    return NET_SUCCESS;

}


func_public error_t net_unlisten(int port)
{

    int                 listenh, i, j;

    listenh = -1;
    for (i = 0; i < listeners_size; i++) {
	j = listeners[i];
	if (sockhandles[j].listen_port == port)
	    listenh = j;
	listeners[i] = EMPTYSLOT;
	break;
    }

    if (listenh != -1) {
	closePLATFORMSPECIFIC(sockhandles[listenh].fd);
	FD_CLR((unsigned int) sockhandles[listenh].fd, &listeners_fdset);
	netprivate_clear_sockhandle(&sockhandles[listenh]);
    }
    return NET_SUCCESS;

}


func_public int     net_poll_listeners(int sec, int usec)
{

    struct sockaddr_in  csa;
    struct timeval      tv;
    fd_set              tfds;
    int                 num, i, h, new_sock, cln, numactive, ioctlflag = 1;

    tv.tv_sec = sec;
    tv.tv_usec = usec;

    /* FIXME: should be able to copy fd_sets using assignment, may be non-portable tho */
    tfds = listeners_fdset;

    num = select(highest_listener_fd + 1, &tfds, NULL, NULL, &tv);
    if (num == -1) {
	if (errno == EBADF) {
	    DB(105, (stderr, "bad file descriptor in listeners_fdset\n"));
	    exit(2);
	}
    }
    if (!num) {			/* no pending connections */
	return 0;
    }
    numactive = 0;
    for (i = 0; i < listeners_size; i++) {
	int                 fd;

	if (listeners[i] == EMPTYSLOT)
	    continue;

	if (sockhandles[listeners[i]].on_accept != NULL) {
	    fd = sockhandles[listeners[i]].fd;
	    if (FD_ISSET((unsigned int) fd, &tfds)) {
		/* accept the connection */

		cln = sizeof(csa);
		new_sock = accept(fd, (struct sockaddr *) &csa, &cln);
		if (new_sock == -1) {	/* FIXME: why are we setting non-blocking if accept returned -1? check this */
		    if (ioctlPLATFORMSPECIFIC(new_sock, FIONBIO, &ioctlflag) < 0) {
			DB(104, (stderr, "unable to set socket to non-blocking\n"));
			closePLATFORMSPECIFIC(new_sock);
			continue;
		    }
		}
		h = netprivate_find_free_sockhandle();
		netprivate_clear_sockhandle(&sockhandles[h]);

		DB(105, (stderr, "new connection accepted on fd %d, stored in sockhandle %d\n", new_sock, h));

		sockhandles[h].fd = new_sock;
		sockhandles[h].type = HANDLETYPE_NETSTREAM;
		sockhandles[h].on_accept = NULL;
		sockhandles[h].on_read = NULL;
		sockhandles[h].on_close = NULL;
		sockhandles[h].on_fullbuffer = NULL;

		rebuild_fdsets = 1;
		(sockhandles[listeners[i]].on_accept) (h);
		numactive++;
	    }
	}
    }
    return numactive;

}


func_public error_t net_on_read(netsocket i, net_func_ptr on_read_func)
{
    NETSOCKET          *handle;

    handle = &sockhandles[i];
    if (handle != NULL)
	if (handle->type == HANDLETYPE_NETSTREAM || handle->type == HANDLETYPE_FILE) {
	    handle->on_fullbuffer = NULL;
	    handle->on_read = on_read_func;
	    rebuild_fdsets = 1;
	    DB(105, (stderr, "%p->on_read = %p\n", handle, on_read_func));
	    return NET_SUCCESS;
	}
    return NET_EINVARG;

}


func_public int     net_poll_readers(int sec, int usec)
{
    int                 i, num, numactive;
    static int          highest_reader_fd = 0;
    static fd_set       rfds;
    fd_set              tfds;
    struct timeval      tv;

    tv.tv_sec = sec;
    tv.tv_usec = usec;

    if (rebuild_fdsets) {
	DB(107, (stderr, "rebuilding fdsets\n"));
	FD_ZERO(&rfds);
	highest_reader_fd = 0;
	for (i = 0; i < sockhandles_size; i++) {
	    if (sockhandles[i].type == HANDLETYPE_NETSTREAM || sockhandles[i].type == HANDLETYPE_FILE)
		if (sockhandles[i].on_read != NULL || sockhandles[i].on_fullbuffer != NULL) {
		    DB(107, (stderr, "building for fd %d\n", sockhandles[i].fd));
		    FD_SET((unsigned int) sockhandles[i].fd, &rfds);
		    highest_reader_fd = MAX(highest_reader_fd, sockhandles[i].fd);

		}
	}
	rebuild_fdsets = 0;
	DB(105, (stderr, "highest reader %d\n", highest_reader_fd));
    }
    /* FIXME: should be able to copy fd_sets using assignment, may be non-portable tho */
    tfds = rfds;

    num = select(highest_reader_fd + 1, &tfds, NULL, NULL, &tv);
    if (num == -1) {
	if (errno == EBADF) {
	    DB(105, (stderr, "bad file descriptor in readers fd_set\n"));
	    exit(2);
	}
    }
    numactive = 0;
    if (!num)			/* no pending input */
	return numactive;

    for (i = 0; i < sockhandles_size; i++) {
	NETSOCKET          *h = &(sockhandles[i]);

	if (h->type == HANDLETYPE_NETSTREAM || h->type == HANDLETYPE_FILE) {

	    if (FD_ISSET((unsigned int) h->fd, &tfds)) {
		DB(105, (stderr, "input on fd %d (handle %d)\n", h->fd, i));

		if (h->buffer_size && h->on_fullbuffer != NULL && h->on_read == NULL) {
		    size_t              s;
		    char                type = 'c';	/* c = recv, r = read */

		    if (h->type == HANDLETYPE_NETSTREAM)
			s = recv(h->fd, h->buffer, h->buffer_size, 0);
		    else if (h->type == HANDLETYPE_FILE)
			type = 'r', s = read(h->fd, h->buffer, h->buffer_size);
		    else
			type = 'c', s = 0;	/* ie, do nothing */

		    if (s == -1) {
			/* FIXME: we should abstract out checking the value of errno for remote end close states */
			char               *e = strerror(errno);
			DB(105, (stderr, "bytes recv'd = %d, fd = %d, errno = %d %s\n", s, h->fd, errno, e));
			DB(105, (stderr, "error detected, closing fd %d\n", h->fd));
			netprivate_close_sockhandle(i);
		    } else if (s == 0 && type == 'r') {		/* read(2) returned 0 bytes => EOF */
			DB(105, (stderr, "read() => 0, assuming EOF\n"));
			netprivate_close_sockhandle(i);
		    } else {
			DB(105, (stderr, "buffer read of %d bytes\n", s));
			/* increment the buffer pointer, decrement the size of the buffer
			 * as buffer_size approaches zero, less room is left in buffer
			 */
			h->buffer_size -= s;
			h->buffer += s;
			DB(105, (stderr, "%d bytes of buffer left to fill for NETSOCKET %p\n", h->buffer_size, h));
			if (h->buffer_size <= 0) {	/* buffer is full */
			    DB(105, (stderr, "buffer full, calling on_fullbuffer func\n"));
			    (h->on_fullbuffer) (i);
			}
		    }

		} else if (h->on_read != NULL) {
		    (h->on_read) (i);
		}
		numactive++;
	    }
	}
    }
    return numactive;

}


func_public error_t net_on_close(netsocket i, net_func_ptr on_close_func)
{
    NETSOCKET          *handle;

    handle = &sockhandles[i];
    if (handle != NULL)
	if (handle->type == HANDLETYPE_NETSTREAM || handle->type == HANDLETYPE_FILE) {
	    handle->on_close = on_close_func;
	    rebuild_fdsets = 1;
	    return NET_SUCCESS;
	}
    return NET_EINVARG;

}



func_public error_t net_close(netsocket i)
{
    NETSOCKET          *h;

    h = &sockhandles[i];
    if (h != NULL)
	if (h->type == HANDLETYPE_FILE || h->type == HANDLETYPE_NETSTREAM) {
	    netprivate_close_sockhandle(i);
	    return NET_SUCCESS;
	}
    return NET_EINVARG;
}



/* this will block (if we don't set FIONBIO) if no input is avaiable... should be called from the .on_read func */
func_public error_t net_read(netsocket i, char *buf, size_t bufsiz)
{
    int                 bytes;
    NETSOCKET          *h;

    h = &sockhandles[i];
    DB(105, (stderr, "in net_read, netsocket %d\n", i));

    if (h == NULL)
	return NET_EINVARG;

    if (h->type == HANDLETYPE_NETSTREAM)
	bytes = recv(h->fd, buf, bufsiz, 0);
    else if (h->type == HANDLETYPE_FILE)
	bytes = read(h->fd, buf, bufsiz);
    else
	return NET_EINVARG;

    if (bytes == -1) {
	if (errno == EWOULDBLOCK) {	/* shouldn't get here */
	    DB(105, (stderr, "EWOULDBLOCK received from non-blocking socket\n"));
	    return 0;
	} else {
	    /* FIXME: we should abstract out checking the value of errno for remote end close states */
	    char               *e = strerror(errno);
	    DB(105, (stderr, "bytes recv'd = %d, fd = %d, errno = %d %s\n", bytes, h->fd, errno, e));
	    netprivate_close_sockhandle(i);
	}
    } else if (bytes == 0) {
	/* as best as I can make out, this means EOF when the socket is non-blocking */
	netprivate_close_sockhandle(i);
    } else {
	DB(105, (stderr, "recv/read(%d, %p, %d, 0) = %d\n", h->fd, buf, bufsiz, bytes));
    }

    return bytes;

}


func_public error_t net_write(netsocket i, char *buf, size_t bufsiz)
{
    int                 bytes;
    NETSOCKET          *h;

    h = &sockhandles[i];
    DB(105, (stderr, "net_write(%d (fd %d), %p, %d)\n", i, h->fd, buf, bufsiz));

    if (h == NULL)
	return NET_EINVARG;

    if (h->type == HANDLETYPE_NETSTREAM)
	bytes = send(h->fd, buf, bufsiz, 0);
    else if (h->type == HANDLETYPE_FILE)
	bytes = write(h->fd, buf, bufsiz);
    else
	return NET_EINVARG;

    if (bytes == -1) {
	char               *e = strerror(errno);
	DB(105, (stderr, "bytes sent = %d, fd = %d, errno = %d %s\n", bytes, h->fd, errno, e));
	if (errno == EWOULDBLOCK) {
	    DB(105, (stderr, "EWOULDBLOCK received from non-blocking socket\n"));
	    return bytes;
	} else {
	    /* FIXME: we should abstract out checking the value of errno for remote end close states */
	    netprivate_close_sockhandle(i);
	}
    }
    return bytes;
}


/* will return, in handle, a NETSOCKET *, which should be used for net_read and net_write */
func_public error_t net_open_connection(char *host, int port, netsocket * handle)
{

    struct sockaddr_in  sa;
    struct in_addr      address;
    int                 h, orig_sock, ioctlflag = 1;

#ifdef _WIN32
    /* Must use inet_addr because Windows is braindead.  Windows
       defines inet_ntoa, but stupidly, it does not have inet_aton
       Yet another reason to not program under windows.
     */
    if ((address.s_addr = inet_addr(host)) == -1)
	return NET_ENOTHOST;
#else
    if (inet_aton(host, &address) == 0)
	return NET_ENOTHOST;
#endif

    if ((orig_sock = netprivate_create_socket(AF_INET, SOCK_STREAM, 0)) < 0)
	return NET_ESOCKET;

    memset(&sa, 0, sizeof(sa));
    sa.sin_family = AF_INET;
    sa.sin_addr = address;
    sa.sin_port = htons((short) (port & 65535));

    if (connect(orig_sock, (struct sockaddr *) &sa, sizeof(sa)) < 0) {
	closePLATFORMSPECIFIC(orig_sock);
	return NET_ECONNECT;
    }
    if (ioctlPLATFORMSPECIFIC(orig_sock, FIONBIO, &ioctlflag) < 0) {
	closePLATFORMSPECIFIC(orig_sock);
	return NET_EBADIOCTL;
    }
    h = netprivate_find_free_sockhandle();
    netprivate_clear_sockhandle(&sockhandles[h]);

    sockhandles[h].fd = orig_sock;
    sockhandles[h].type = HANDLETYPE_NETSTREAM;

    *handle = h;
    return NET_SUCCESS;

}


func_public error_t net_local_connection_info(netsocket i, char *host, int *port)
{
    struct sockaddr_in  con;
    int                 conl;
    NETSOCKET          *handle;

    handle = &sockhandles[i];
    if (handle == NULL)
	return NET_EINVARG;

    if (handle->type != HANDLETYPE_NETSTREAM)
	return NET_EUNSUPPORT;

    conl = sizeof(con);
    if (getsockname(handle->fd, (struct sockaddr *) &con, &conl) < 0)
	return NET_EINVARG;

    if (host != NULL)
	sprintf(host, "%s", inet_ntoa(con.sin_addr));

    if (port != NULL)
	*port = ntohs(con.sin_port);

    return NET_SUCCESS;
}


func_public error_t net_remote_connection_info(netsocket i, char *host, int *port)
{
    struct sockaddr_in  con;
    int                 conl;
    NETSOCKET          *handle;

    handle = &sockhandles[i];
    if (handle == NULL)
	return NET_EINVARG;

    if (handle->type != HANDLETYPE_NETSTREAM)
	return NET_EUNSUPPORT;

    conl = sizeof(con);
    if (getpeername(handle->fd, (struct sockaddr *) &con, &conl) < 0)
	return NET_EINVARG;

    if (host != NULL)
	sprintf(host, "%s", inet_ntoa(con.sin_addr));

    if (port != NULL)
	*port = ntohs(con.sin_port);

    return NET_SUCCESS;
}


/* returns in *host the dotted decimal notation of the machine in hostname */
func_public error_t net_host_lookup(char *hostname, char *host)
{

    struct hostent     *h;
    struct in_addr      in;

    h = gethostbyname(hostname);
    if (h == NULL)
	return NET_ENOHOST;

    memcpy(&in.s_addr, *h->h_addr_list, sizeof(in.s_addr));

    if (host != NULL)
	sprintf(host, "%s", inet_ntoa(in));

    return NET_SUCCESS;

}


/* returns in port the port that service specifies */
func_public error_t net_service_lookup(char *service, int *port)
{
    struct servent     *s;

    s = getservbyname(service, "tcp");
    if (s == NULL)
	return NET_ENOSERVICE;

    *port = ntohs(s->s_port);
    return NET_SUCCESS;

}


func_public error_t net_read_into_buffer(netsocket i, net_func_ptr bufferfunc, char *buf, size_t bytes)
{
    NETSOCKET          *h;

    h = &sockhandles[i];
    if (h == NULL)
	return NET_EINVARG;

    if (h->type == HANDLETYPE_NETSTREAM || h->type == HANDLETYPE_FILE) {
	h->buffer = buf;
	h->buffer_size = bytes;
	h->on_read = NULL;
	h->on_fullbuffer = bufferfunc;
	rebuild_fdsets = 1;
	return NET_SUCCESS;
    }
    return NET_EINVARG;

}


func_public int     net_bytes_left_in_buffer(netsocket i)
{
    NETSOCKET          *h;

    h = &sockhandles[i];
    if (h == NULL)
	return NET_EINVARG;

    if (h->type == HANDLETYPE_NETSTREAM || h->type == HANDLETYPE_FILE)
	return h->buffer_size;

    return NET_EINVARG;
}


func_public error_t net_put_assoc_data(netsocket i, long data)
{
    NETSOCKET          *h;

    h = &sockhandles[i];
    if (h == NULL)
	return NET_EINVARG;

    h->user_storage = data;
    return NET_SUCCESS;

}


func_public error_t net_get_assoc_data(netsocket i, long *data)
{
    NETSOCKET          *h;

    h = &sockhandles[i];
    if (h == NULL)
	return NET_EINVARG;

    *data = h->user_storage;
    return NET_SUCCESS;

}

func_public FILE   *net_tofile(netsocket i)
{
    NETSOCKET          *h;

    h = &sockhandles[i];
    if (h == NULL)
	return NULL;

    if (h->fileptr == NULL)
	h->fileptr = fdopen(h->fd, "rb+");

    return h->fileptr;

}


func_public error_t net_fdtonetsocket(int fd, netsocket * rethandle)
{
    int                 h;
    int                 flag = 1;

    if (fd < 1)
	return NET_EINVARG;

    if (ioctl(fd, FIONBIO, &flag) < 0) {
	DB(105, (stderr, "unable to set regular fd (%d) to non-blocking\n", fd));
	return NET_EINVARG;
    }
    h = netprivate_find_free_sockhandle();
    if (h == NET_ENOTFOUND) {
	return NET_EINVARG;
    }
    netprivate_clear_sockhandle(&sockhandles[h]);
    sockhandles[h].fd = fd;
    sockhandles[h].type = HANDLETYPE_FILE;

    *rethandle = h;

    return NET_SUCCESS;

}


/*
 * $Log: net.c,v $
 * Revision 1.29.1.1  1998/03/30 18:16:39  abakun
 * was returning pointers into an area of memory that was being realloc()'d
 * after realloc, there was little chance that the pointers would actually still be valid
 * changed all the externally visible code to use indexes into the sockhandles array
 * we still use NETSOCKET* internall in some places, but now we operate on
 * netsocket types.  Every occurance of NETSOCKET* in old code should be changed
 * to netsocket
 *
 * Revision 1.29  1998/03/25 17:29:38  abakun
 * when compiling -O2, listenh in net_unlisten may have been used uninitialized
 * fixed to avoid warning
 *
 * Revision 1.28  1998/03/23 22:24:46  abakun
 * fixed a typo net_bytes_left_in_buffer
 *
 * Revision 1.27  1998/03/23 21:39:44  abakun
 * added net_bytes_left_in_buffer
 *
 * Revision 1.26  1998/03/20 21:37:55  abakun
 * net_started, if called more than once, will just return now
 * (just like the rest of the modules *_start routines do)
 *
 * Revision 1.25  1998/03/19 23:04:53  abakun
 * added "func_private.h" to the include list
 *
 * Revision 1.24  1998/03/18 20:54:37  abakun
 * net_close was not returning NET_SUCCESS when it should be
 *
 * Revision 1.23  1998/03/16 22:05:58  abakun
 * changed references to sys_errlist[errno] to strerror(errno)
 *
 * Revision 1.22  1998/03/10 19:52:23  abakun
 * improved EOF handling when using read(2), which returns 0 on EOF when select(2) says there is activity
 *
 * Revision 1.21  1998/03/10 18:12:57  abakun
 * fixed a whole shit load of problems with HANDLETYPE_FILE being ignored when it shouldn't be
 * I'm kicking myself for not recognizing this problem sooner.  I am a loser.
 *
 * Revision 1.20  1998/03/09 19:57:10  abakun
 * added error code NET_EUNSUPPORT, which net_*_connection_info will return if the passed handle is not HANDLETYPE_NETSTREAM
 *
 * Revision 1.19  1998/03/09 19:26:36  abakun
 * added net_tofile (returns a FILE* for use in stdio functions)
 * added net_fdtonetsocket (converts a file descriptor into a NETSOCKET*, for use with net_poll_readers, etc)
 *
 * Revision 1.18  1998/03/09 02:21:00  abakun
 * removed definition of DEBUG_ITERATION; it was moved to debug.c
 *
 * Revision 1.17  1998/03/07 00:00:43  abakun
 * handles return values from recv/send better now
 * (previous) fixed off-by-one error in expand_*_array when clearing the new elements
 *
 * Revision 1.16  1998/03/06 22:51:59  abakun
 * forgot trailing newline on debug messages
 *
 * Revision 1.15  1998/03/06 22:49:56  abakun
 * changed HANDLETYPE_CONNECTED to HANDLETYPE_NETSTREAM
 * more logical method of detecting remote close on writing
 * more logical method of detecting remote close on reading
 * more logical method of detecting remote close on reading into buffer
 *
 * Revision 1.14  1998/03/05 21:00:39  abakun
 * added net_get_assoc_data and net_put_assoc_data
 *
 * Revision 1.13  1998/03/05 19:41:14  abakun
 * net_end now takes no arguments specified via (void) argument list, rather than an empty arg list
 * this is how atexit() functions should be declared
 *
 * Revision 1.12  1998/03/05 19:35:48  abakun
 * added a debugging line when socket returns INVALID_SOCKET under win32
 *
 * Revision 1.11  1998/03/05 19:25:09  abakun
 * added explict casts to avoid compiler warnings for things I know I want to do
 * the FD_* manlipulators and htons were the big players in this change
 *
 * Revision 1.10  1998/03/03 20:16:19  abakun
 * added net_read_into_buffer, changed net_poll_readers to support it
 *
 * Revision 1.9  1998/02/27 21:59:29  abakun
 * moved things around in netprivate_create_socket so that the return value of socket(2) always gets returned.
 * there was a problem with the _WIN32 conditional
 *
 * Revision 1.8  1998/02/27 21:18:38  abakun
 * added the functions net_host_lookup and net_service_lookup
 *
 * Revision 1.7  1998/02/27 18:34:09  abakun
 * changed net_read to not add a null byte, making it easier to read in structures
 *
 * Revision 1.6  1998/02/27 17:54:22  abakun
 * fixed every warning that gcc -Wall output, including some stupid mistakes concerning no-return value
 * for non-void functions
 *
 * Revision 1.5  1998/02/27 17:41:19  abakun
 * abstracted calls to socket(2) to netprivate_create_socket, which contains code to make up for differences between
 * winsock and UNIX socket(2) calls
 *
 * Revision 1.4  1998/02/27 17:10:41  abakun
 * added win32 winsock startup calls, and added a call to atexit
 * so that net_end will be called if we exit... this is only really
 * important for win32, since you need to call WSACleanup or a (OS?)
 * memory leak will result
 *
 * Revision 1.3  1998/02/27 16:37:47  abakun
 * conditional compliation of converting string addresses to binary for winblows because it does not define
 * inet_aton and I had to use inet_addr instead.  See the man page for inet_addr to see why you should
 * not use it.
 *
 * Revision 1.2  1998/02/27 16:15:31  abakun
 * added ifndef for _win32 when setting up the signal handler
 *
 * Revision 1.1  1998/02/26 23:12:03  abakun
 * Initial revision
 *
 */
