# 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); }