# define INCLUDE_FILE_IO # include "dgd.h" # define FD_SETSIZE 1024 # include <winsock.h> # include "str.h" # include "array.h" # include "object.h" # include "comm.net.h" struct _connection_ { SOCKET fd; /* file descriptor */ struct sockaddr_in addr; /* internet address of connection */ 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 rfds; /* read file descriptor bitmap */ static fd_set wfds; /* write file descriptor bitmap */ static fd_set readfds; /* fds selected for reading */ static fd_set writefds; /* fds selected for writing */ static fd_set xxxfds; static fd_set ufds; /* identical to rfds, minus those that are blocked. */ /* * NAME: conn->init() * DESCRIPTION: initialize connections */ void conn_init(int maxusers) { int n; connection *conn; WSADATA wsadata; /* initialize winsock */ if (WSAStartup(MAKEWORD(1, 1), &wsadata) != 0) { P_message("WSAStartup failed (no winsock?)\n"); exit(2); } if (LOBYTE(wsadata.wVersion) != 1 || HIBYTE(wsadata.wVersion) != 1) { WSACleanup(); P_message("Winsock 1.1 not supported\n"); exit(2); } connections = ALLOC(connection, nusers = maxusers); for (n = nusers, conn = connections; n > 0; --n, conn++) { conn->fd = INVALID_SOCKET; conn->next = flist; flist = conn; } FD_ZERO(&rfds); FD_ZERO(&wfds); FD_ZERO(&ufds); } /* * NAME: conn->finish() * DESCRIPTION: terminate connections */ void conn_finish(void) { WSACleanup(); } /* * NAME: conn->new() * DESCRIPTION: initialize a new connection struct */ static connection *conn_new(SOCKET fd, struct sockaddr_in *sin) { connection *conn; conn = flist; flist = conn->next; conn->fd = fd; memcpy(&conn->addr, sin, sizeof(conn->addr)); strcpy(conn->host, inet_ntoa(sin->sin_addr)); conn->hostlen = strlen(conn->host); if (fd != INVALID_SOCKET) { FD_SET(fd, &xxxfds); } return conn; } /* * NAME: conn->listen() * DESCRIPTION: bind a connection to a local port */ connection *conn_listen(int port, int protocol) { SOCKET fd; static struct sockaddr_in sin; int size; int on; unsigned long nonblock; if (port < 0) { /* Request automatic port assignment */ port = 0; } /* No free connections: see the comment about nusers in comm_receive. */ if (flist == (connection *) NULL) { return (connection *) NULL; } if (protocol == PRC_UDP) { fd = socket(PF_INET, SOCK_DGRAM, 0); } else { fd = socket(PF_INET, SOCK_STREAM, 0); } if (fd == INVALID_SOCKET) { return (connection *)NULL; } on = 1; if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char *)&on, sizeof(on)) != 0) { closesocket(fd); return (connection *)NULL; } sin.sin_family = AF_INET; sin.sin_port = htons((u_short) port); sin.sin_addr.s_addr = INADDR_ANY; /* htonl? */ if (bind(fd, (struct sockaddr *)&sin, sizeof(sin)) != 0) { closesocket(fd); return (connection *)NULL; } size = sizeof(sin); if (getsockname(fd, (struct sockaddr *)&sin, &size) != 0) { closesocket(fd); return (connection *)NULL; } if (protocol != PRC_UDP && listen(fd, 64) != 0) { closesocket(fd); return (connection *)NULL; } nonblock = TRUE; if (ioctlsocket(fd, FIONBIO, &nonblock) != 0) { closesocket(fd); return (connection *)NULL; } FD_SET(fd, &rfds); FD_SET(fd, &ufds); return conn_new(fd, &sin); } /* * NAME: conn->accept() * DESCRIPTION: accept a connection */ connection *conn_accept(connection *conn) { SOCKET fd; struct sockaddr_in sin; int len, on; if (!FD_ISSET(conn->fd, &readfds)) { return (connection *) NULL; } len = sizeof(sin); fd = accept(conn->fd, (struct sockaddr *) &sin, &len); if (fd == INVALID_SOCKET) { return (connection *) NULL; } on = 1; setsockopt(conn->fd, SOL_SOCKET, SO_OOBINLINE, (char *)&on, sizeof(on)); FD_SET(fd, &rfds); FD_SET(fd, &ufds); return conn_new(fd, &sin); } /* * NAME: conn->connect() * DESCRIPTION: initiate a tcp connection */ connection *conn_connect(char *host, int port, int protocol) { SOCKET fd; static struct sockaddr_in sin; unsigned long nonblock; int err; if (protocol != PRC_TCP) { return (connection *) NULL; } /* No free connections: see the comment about nusers in comm_receive(). */ if (flist == (connection *) NULL) { return (connection *) NULL; } sin.sin_family = AF_INET; sin.sin_port = htons((u_short) port); sin.sin_addr.s_addr = inet_addr(host); fd = socket(PF_INET, SOCK_STREAM, 0); if (fd == INVALID_SOCKET ) { return conn_new(INVALID_SOCKET, &sin); } nonblock = TRUE; if (ioctlsocket(fd, FIONBIO, &nonblock) != 0) { closesocket(fd); return conn_new(INVALID_SOCKET, &sin); } if (connect(fd, (struct sockaddr *)&sin, sizeof(sin)) == SOCKET_ERROR) { err = WSAGetLastError(); if (err != WSAEWOULDBLOCK) { closesocket(fd); return conn_new(INVALID_SOCKET, &sin); } } FD_SET(fd, &wfds); return conn_new(fd, &sin); } /* * NAME: conn->connected() * DESCRIPTION: mark a connecting socket as active */ int conn_connected(connection *conn) { struct sockaddr sa; int salen = sizeof(sa); if (conn->fd == INVALID_SOCKET) return -1; if (!FD_ISSET(conn->fd, &writefds)) return 0; /* Use getpeername() solely to check for errors. */ if (getpeername(conn->fd, &sa, &salen) != 0) { FD_CLR(conn->fd, &wfds); return -1; } FD_CLR(conn->fd, &wfds); FD_SET(conn->fd, &rfds); FD_SET(conn->fd, &ufds); return 1; } /* * NAME: conn->writable() * DESCRIPTION: check if a connection has been selected for writing */ int conn_writable(connection *conn) { return conn->fd != INVALID_SOCKET && FD_ISSET(conn->fd, &writefds); } /* * NAME: conn->del() * DESCRIPTION: delete a connection */ void conn_del(connection *conn) { if (conn->fd != INVALID_SOCKET) { closesocket(conn->fd); FD_CLR(conn->fd, &rfds); FD_CLR(conn->fd, &ufds); FD_CLR(conn->fd, &wfds); FD_CLR(conn->fd, &readfds); FD_CLR(conn->fd, &writefds); conn->fd = INVALID_SOCKET; } conn->next = flist; flist = conn; } /* * NAME: conn->wait() * DESCRIPTION: mark a connection as waiting for writability */ void conn_wait(connection *conn, int wait) { if (conn->fd == INVALID_SOCKET) return; if (wait) { FD_SET(conn->fd, &wfds); } else { FD_CLR(conn->fd, &wfds); } } /* * NAME: conn->block() * DESCRIPTION: temporarily unselect (block) a connection with respect to * incoming data */ void conn_block(conn, flag) register connection *conn; register int flag; { if (conn->fd < 0) { return; } if (flag) { FD_CLR(conn->fd, &ufds); FD_CLR(conn->fd, &readfds); } else { FD_SET(conn->fd, &ufds); } } /* * NAME: conn->select() * DESCRIPTION: wait for input from connections */ int conn_select(int wait) { struct timeval timeout; int retval; /* * First, check readability and writability for binary sockets with pending * data only. */ memcpy(&readfds, &ufds, sizeof(fd_set)); memcpy(&writefds, &wfds, sizeof(fd_set)); timeout.tv_sec = (int) wait; timeout.tv_usec = 0; retval = select(0, &readfds, &writefds, (fd_set *) NULL, &timeout); /* * Now check writability for all sockets in a polling call. */ memcpy(&xxxfds, &rfds, sizeof(fd_set)); timeout.tv_sec = 0; timeout.tv_usec = 0; select(0, (fd_set *) NULL, &xxxfds, (fd_set *) NULL, &timeout); return retval; } /* * NAME: conn->recvfrom() * DESCRIPTION: read a datagram */ int conn_recvfrom(connection *conn, char *buf, int size) { struct sockaddr_in sin; int len = sizeof(sin); if (conn->fd == INVALID_SOCKET) { return -1; } if (!FD_ISSET(conn->fd, &readfds)) { return 0; } size = recvfrom(conn->fd, buf, size, 0, (struct sockaddr *)&sin, &len); memcpy(&(conn->addr), &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(connection *conn, char *buf, int size) { if (conn->fd == INVALID_SOCKET) { return -1; } if (!FD_ISSET(conn->fd, &readfds) || size == 0) { return 0; } size = recv(conn->fd, buf, size, 0); return (size == 0) ? -1 : size; } /* * NAME: conn->sendto() * DESCRIPTION: send a datagram */ void conn_sendto(connection *conn, char *data, int size, char *host, int port) { static struct sockaddr_in sin; if (conn->fd != INVALID_SOCKET) { sin.sin_family = AF_INET; sin.sin_addr.s_addr = inet_addr(host); sin.sin_port = htons((unsigned short) port); sendto(conn->fd, data, size, 0, (struct sockaddr *)&sin, sizeof(sin)); } } /* * NAME: conn->write() * DESCRIPTION: write to a connection; return the number of bytes written */ int conn_write(connection *conn, char *buf, int size) { int len; int err; if (conn->fd == INVALID_SOCKET) { return -1; } else if (!FD_ISSET(conn->fd, &xxxfds)) { return 0; } else { /* I'm assuming Win32 writes are not interruptible. */ len = send(conn->fd, buf, size, 0); if (len == SOCKET_ERROR) { FD_CLR(conn->fd, &xxxfds); err = WSAGetLastError(); if (err == WSAEWOULDBLOCK) { return 0; } else { closesocket(conn->fd); FD_CLR(conn->fd, &rfds); FD_CLR(conn->fd, &ufds); FD_CLR(conn->fd, &wfds); FD_CLR(conn->fd, &readfds); FD_CLR(conn->fd, &writefds); conn->fd = INVALID_SOCKET; return -1; } } return len; } } /* * NAME: conn->ipnum() * DESCRIPTION: return the ip number of a connection */ string *conn_ipnum(connection *conn) { return str_new(conn->host, conn->hostlen); } /* * NAME: conn->port() * DESCRIPTION: return the port number of a connection */ Int conn_port(connection *conn) { return ntohs(conn->addr.sin_port); }