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