/
dgd-net/
dgd-net/doc/
dgd-net/doc/kfun/
dgd-net/src/host/unix/
dgd-net/src/kfun/
# define INCLUDE_FILE_IO
# include "dgd.h"
# include <sys/time.h>
# include <sys/socket.h>
# include <netinet/in.h>
# include <arpa/inet.h>
# include <netdb.h>
# include <errno.h>
# include "str.h"
# include "array.h"
# include "object.h"
# include "comm.h"

struct _connection_ {
    int fd;				/* file descriptor */
    struct sockaddr_in sin;             /* peer's address */
    struct _connection_ *next;		/* next in list */
    char host[16];                      /* ascii ip number of peer */
    char hostlen;                       /* length of host string */
};

static int nusers;			/* # of users */
static connection *connections;		/* connections array */
static connection *flist;		/* list of free connections */
static fd_set fds;			/* file descriptor bitmap */
static fd_set connectfds;               /* file descriptor bitmap */
static fd_set readfds;			/* file descriptor read bitmap */
static fd_set writefds;                 /* file descriptor write bitmap */
static fd_set failfds;                  /* file descriptor exception bitmap */
static int maxfd;			/* largest fd opened yet */

/*
 * NAME:	conn->init()
 * DESCRIPTION:	initialize connections
 */
void conn_init(maxusers)
int maxusers;
{
    register int n;
    register connection *conn;

    connections = ALLOC(connection, nusers = maxusers);
    for (n = nusers, conn = connections; n > 0; --n, conn++) {
	conn->fd = -1;
	conn->next = flist;
	flist = conn;
    }

    FD_ZERO(&fds);
    FD_ZERO(&connectfds);
}

/*
 * NAME:	conn->finish()
 * DESCRIPTION:	terminate connections
 */
void conn_finish()
{
}

/*
 * NAME:	conn->new()
 * DESCRIPTION:	initialize a new connection struct
 */
static connection *conn_new(fd, sin)
int fd;
struct sockaddr_in *sin;
{
    register connection *conn;
    conn = flist;
    flist = conn->next;
    conn->fd = fd;
    memcpy(&conn->sin, sin, sizeof(conn->sin));
    strcpy(conn->host, inet_ntoa(sin->sin_addr));
    conn->hostlen = strlen(conn->host);
    if (fd > maxfd) {
	maxfd = fd;
    }

    return conn;
}

/*
 * NAME:	conn->listen()
 * DESCRIPTION:	bind a connection to a local port
 */
connection *conn_listen(port, protocol)
int port, protocol;
{
    int fd;
    static struct sockaddr_in sin;
    int on = 1;

    fd = socket(PF_INET, (protocol == PRC_UDP) ? SOCK_DGRAM : SOCK_STREAM, 0);
    if (fd < 0) {
	return (connection *) NULL;
    }
    if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on) < 0)) {
        close(fd);
        return (connection *) NULL;
    }

    sin.sin_family = AF_INET;
    sin.sin_port = htons(port);
    sin.sin_addr.s_addr = INADDR_ANY;
    if (bind(fd, (struct sockaddr *)&sin, sizeof(sin)) < 0) {
	close(fd);
	return (connection *) NULL;
    }

    if (protocol != PRC_UDP && listen(fd, 5) < 0) {
	close(fd);
	return (connection *) NULL;
    }

    if (fcntl(fd, F_SETFL, FNDELAY) < 0) {
	close(fd);
	return (connection *) NULL;
    }

    FD_SET(fd, &fds);
    return conn_new(fd, &sin);
}

/*
 * NAME:	conn->accept()
 * DESCRIPTION:	accept a connection
 */
connection *conn_accept(conn)
connection *conn;
{
    int fd;
    struct sockaddr_in sin;
    int len = sizeof(sin);

    fd = accept(conn->fd, (struct sockaddr *) &sin, &len);
    if (fd < 0) {
	return (connection *) NULL;
    }

    FD_SET(fd, &fds);
    return conn_new(fd, &sin);
}

/*
 * NAME:	conn->connect()
 * DESCRIPTION:	initiate a tcp connection
 */
connection *conn_connect(host, port, protocol)
char *host;
int port, protocol;
{
    int fd;
    static struct sockaddr_in sin;

    if (protocol != PRC_TCP) {
        return (connection *) NULL;
    }

    fd = socket(PF_INET, SOCK_STREAM, 0);
    if (fd < 0) {
	return (connection *) NULL;
    }

    if (fcntl(fd, F_SETFL, FNDELAY) < 0) {
	close(fd);
	return (connection *) NULL;
    }

    sin.sin_family = AF_INET;
    sin.sin_port = htons(port);
    sin.sin_addr.s_addr = inet_addr(host);
    if (connect(fd, (struct sockaddr *)&sin, sizeof(sin)) < 0 
	&& errno != EINPROGRESS) {
	close(fd);
	return (connection *) NULL;
    }

    FD_SET(fd, &connectfds);
    return conn_new(fd, &sin);
}

/*
 * NAME:	conn->connected()
 * DESCRIPTION:	mark a connecting socket as active
 */
int conn_connected(conn)
register connection *conn;
{
    /*
     * Den h{r mening {r i svensk f|r att irritera Dworkin.
     */
    if (FD_ISSET(conn->fd, &writefds)) {
	FD_CLR(conn->fd, &connectfds);
	FD_SET(conn->fd, &fds);
	return 1;
    }

    if (FD_ISSET(conn->fd, &failfds)) {
	FD_CLR(conn->fd, &connectfds);
	return -1;
    }
    return 0;
}
    

/*
 * NAME:	conn->del()
 * DESCRIPTION:	delete a connection
 */
void conn_del(conn)
register connection *conn;
{
    if (conn->fd >= 0) {
	close(conn->fd);
	FD_CLR(conn->fd, &fds);
	FD_CLR(conn->fd, &connectfds);
	conn->fd = -1;
    }
    conn->next = flist;
    flist = conn;
}

/*
 * NAME:	conn->select()
 * DESCRIPTION:	wait for input from connections
 */
int conn_select(wait)
bool wait;
{
    struct timeval timeout;

    memcpy(&readfds, &fds, sizeof(fd_set));
    memcpy(&writefds, &connectfds, sizeof(fd_set));
    memcpy(&failfds, &connectfds, sizeof(fd_set));
    timeout.tv_sec = (int) wait;
    timeout.tv_usec = 0;
    return select(maxfd + 1, &readfds, &writefds, &failfds, &timeout);
}

/*
 * NAME:	conn->recvfrom()
 * DESCRIPTION: read a datagram
 */
int conn_recvfrom(conn, buf, size)
connection *conn;
char *buf;
int size;
{
    struct sockaddr_in sin;
    int len = sizeof(sin);
    
    if (conn->fd < 0) {
	return -1;
    }
    if (!FD_ISSET(conn->fd, &readfds)) {
	return 0;
    }
    size = recvfrom(conn->fd, buf, size, 0, (struct sockaddr *)&sin, &len);
    memcpy(&(conn->sin), &sin, len);
    strcpy(conn->host, inet_ntoa(sin.sin_addr));
    conn->hostlen = strlen(conn->host);
    return size;  /* Size may legitimately be 0 */
}

/*
 * NAME:	conn->read()
 * DESCRIPTION:	read from a connection
 */
int conn_read(conn, buf, size)
connection *conn;
char *buf;
int size;
{
    if (conn->fd < 0) {
	return -1;
    }
    if (!FD_ISSET(conn->fd, &readfds) || size == 0) {
	return 0;
    }
    size = read(conn->fd, buf, size);
    return (size == 0) ? -1 : size;
}

/*
 * NAME:	conn->sendto()
 * DESCRIPTION: send a datagram
 */
void conn_sendto(conn, data, size, host, port)
connection *conn;
char *data, *host;
int   size,  port;
{
    static struct sockaddr_in sin;
    if (conn->fd >= 0) {
	sin.sin_family = AF_INET;
	sin.sin_addr.s_addr = inet_addr(host);
	sin.sin_port = htons(port);
	sendto(conn->fd, data, size, 0, (struct sockaddr *)&sin, sizeof(sin));
    }
}

/*
 * NAME:	conn->write()
 * DESCRIPTION:	write to a connection
 */
void conn_write(conn, buf, size)
connection *conn;
char *buf;
register int size;
{
    if (conn->fd >= 0) {
	if (write(conn->fd, buf, size) < 0 && errno != EWOULDBLOCK) {
	    close(conn->fd);
	    FD_CLR(conn->fd, &fds);
	    FD_CLR(conn->fd, &connectfds);
	    conn->fd = -1;
	}
    }
}

/*
 * NAME:	conn->ipnum()
 * DESCRIPTION:	return the ip number of a connection
 */
string *conn_ipnum(conn)
connection *conn;
{
    return str_new(conn->host, conn->hostlen);
}

/*
 * NAME:	conn->port()
 * DESCRIPTION:	return the port number of a connection
 */
int conn_port(conn)
connection *conn;
{
    return ntohs(conn->sin.sin_port);
}