/*
// Full copyright information is available in the file ../doc/CREDITS
//
// RFC references: inverse name resolution--1293, 903 1035 - domain name system
*/
#define _BSD 44 /* For RS6000s. */
#include "defs.h"
#include <sys/types.h>
#ifdef __UNIX__
#include <sys/param.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <sys/resource.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#endif
#include <fcntl.h>
#include "net.h"
#include "util.h"
INTERNAL SOCKET grab_port(Int port, char * addr, int socktype);
static Long translate_connect_error(Int error);
static struct sockaddr_in sockin; /* An internet address. */
static int addr_size = sizeof(sockin); /* Size of sockin. */
Long server_failure_reason;
void init_net(void) {
#ifdef __Win32__
WSADATA wsa;
WSAStartup(0x0101, &wsa);
#endif
}
void uninit_net(void) {
#ifdef __Win32__
WSACleanup();
#endif
}
/*
// -------------------------------------------------------------------
// inet_aton() courtesy of Luc Girardin <girardin@hei.unige.ch>, I dont
// know where he got it 8)
*/
#ifndef HAVE_INET_ATON
int inet_aton (const char * cp, struct in_addr * addr) {
unsigned long parts[4];
register unsigned long val;
register unsigned long part0;
register unsigned long part1;
register unsigned long part2;
register unsigned long part3;
int part;
char *next;
part = 0;
forever {
if (!isdigit (*cp)) /* not decimal digit or leading 0, 0x */
return 0;
errno = 0;
parts[part++] = strtoul (cp, &next, 0); /* leading 0=octal, 0x=hex */
if (errno == ERANGE)
return 0;
if (*next == '.') {
if (part >= 4)
return 0;
cp = next + 1;
} else
break; /* from for loop */
}
/* Check for trailing non-whitespace characters */
if (strlen (next) != strspn (next, " \t\n\v\f\r"))
return 0;
/* Concoct the address according to the number of parts specified. */
val = 0;
part0 = parts[0];
part1 = parts[1];
part2 = parts[2];
part3 = parts[3];
switch (part) {
case 4: /* a.b.c.d -- 8.8.8.8 bits */
if (part3 > 0xff || part2 > 0xff)
return 0;
val = part3;
part2 <<= 8;
/* FALLTHROUGH */
case 3: /* a.b.c -- 8.8.16 bits */
if (part2 > 0xffff || part1 > 0xff)
return 0;
val |= part2;
part1 <<= 16;
/* FALLTHROUGH */
case 2: /* a.b -- 8.24 bits */
if (part1 > 0xffffff || part0 > 0xff)
return 0;
val |= part1;
part0 <<= 24;
/* FALLTHROUGH */
case 1: /* a -- 32 bits */
val |= part0;
}
addr->s_addr = htonl (val);
return 1;
}
#endif
/*
// -----------------------------------------------------------------------
// prebind things--basically call socket() and bind() but nothing else,
// later we can call other things ..
*/
typedef struct Prebind Prebind;
struct Prebind {
SOCKET sock;
uShort port;
Bool tcp;
char addr[BUF];
Prebind * next;
};
Prebind * prebound = NULL;
#define DIE(_reason_) { \
fputs(_reason_, stderr); \
exit(1); \
}
Bool prebind_port(int port, char * addr, int tcp) {
SOCKET sock;
Prebind * pb;
/* address too long? */
if (addr && (strlen(addr) > BUF))
return NO;
sock = grab_port(port, addr, tcp ? SOCK_STREAM : SOCK_DGRAM);
if (sock != SOCKET_ERROR) {
pb = (Prebind *) malloc(sizeof(Prebind));
pb->sock = sock;
pb->port = port;
pb->tcp = tcp;
if (addr)
strcpy(pb->addr, addr);
else
pb->addr[0] = (char) NULL;
pb->next = prebound;
prebound = pb;
} else if (server_failure_reason == address_id) {
fprintf(stderr, "** Invalid internet address: '%s'\n", addr);
exit(1);
} else if (server_failure_reason == socket_id) {
fprintf(stderr, "** Unable to open socket: %s\n", strerror(errno));
exit(1);
} else if (server_failure_reason == bind_id) {
fprintf(stderr, "** Unable to bind port: %s\n", strerror(errno));
exit(1);
}
return YES;
}
INTERNAL int use_prebound(SOCKET * sock, int port, char * addr, int socktype) {
Prebind * pb,
** pbp = &prebound;
while (*pbp) {
pb = *pbp;
if (pb->port == port) {
if (addr) {
if (!pb->addr[0] || strccmp(pb->addr, addr)) {
server_failure_reason = preaddr_id;
return F_FAILURE;
}
} else if (pb->addr[0]) {
server_failure_reason = preaddr_id;
return F_FAILURE;
}
if ((pb->tcp && socktype == SOCK_DGRAM) ||
(!pb->tcp && socktype == SOCK_STREAM))
{
server_failure_reason = pretype_id;
return F_FAILURE;
}
*sock = pb->sock;
*pbp = pb->next;
free(pb);
return TRUE;
} else {
pbp = &pb->next;
}
}
return FALSE;
}
INTERNAL SOCKET grab_port(Int port, char * addr, int socktype) {
int one = 1;
SOCKET sock;
/* see if its pre-bound? */
switch (use_prebound(&sock, port, addr, socktype)) {
case F_FAILURE:
return SOCKET_ERROR;
case TRUE:
return sock;
}
/* verify the address first */
memset(&sockin, 0, sizeof(sockin)); /* zero it */
sockin.sin_family = AF_INET; /* set inet */
sockin.sin_port = htons((unsigned short) port); /* set port */
if (addr && !inet_aton(addr, &sockin.sin_addr)) {
server_failure_reason = address_id;
return SOCKET_ERROR;
}
/* Create a socket. */
sock = socket(AF_INET, socktype, 0);
if (sock == SOCKET_ERROR) {
server_failure_reason = socket_id;
return SOCKET_ERROR;
}
/* Set SO_REUSEADDR option to avoid restart problems. */
setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (char *) &one, sizeof(Int));
/* Bind the socket to port. */
if (bind(sock, (struct sockaddr *) &sockin, sizeof(sockin)) == F_FAILURE) {
server_failure_reason = bind_id;
return SOCKET_ERROR;
}
return sock;
}
SOCKET get_tcp_socket(Int port, char * addr) {
SOCKET sock;
sock = grab_port(port, addr, SOCK_STREAM);
if (sock == SOCKET_ERROR)
return SOCKET_ERROR;
listen(sock, 8);
return sock;
}
SOCKET get_udp_socket(Int port, char * addr) {
SOCKET sock;
sock = grab_port(port, addr, SOCK_DGRAM);
if (sock == SOCKET_ERROR)
return SOCKET_ERROR;
return sock;
}
/* Wait for I/O events. sec is the number of seconds we can wait before
* returning, or -1 if we can wait forever. Returns nonzero if an I/O event
* happened. */
Int io_event_wait(Int sec, Conn *connections, server_t *servers,
pending_t *pendings)
{
struct timeval tv, *tvp;
Conn *conn;
server_t *serv;
pending_t *pend;
fd_set read_fds, write_fds;
Int flags, nfds, count, result, error;
int dummy = sizeof(int);
/* Set time structure according to sec. */
if (sec == -1) {
tvp = NULL;
/* this is a rather odd thing to happen for me */
write_err("select: forever wait");
} else {
tv.tv_sec = (long) sec;
tv.tv_usec = 0;
tvp = &tv;
}
/* Begin with blank file descriptor masks and an nfds of 0. */
FD_ZERO(&read_fds);
FD_ZERO(&write_fds);
nfds = 0;
/* Listen for new data on connections, and also check for ability to write
* to them if we have data to write. */
for (conn = connections; conn; conn = conn->next) {
if (!conn->flags.dead)
FD_SET(conn->fd, &read_fds);
if (conn->write_buf->len)
FD_SET(conn->fd, &write_fds);
if (conn->fd >= nfds)
nfds = conn->fd + 1;
}
/* Listen for connections on the server sockets. */
for (serv = servers; serv; serv = serv->next) {
FD_SET(serv->server_socket, &read_fds);
if (serv->server_socket >= nfds)
nfds = serv->server_socket + 1;
}
/* Check pending connections for ability to write. */
for (pend = pendings; pend; pend = pend->next) {
if (pend->error != NOT_AN_IDENT) {
/* The connect has already failed; just set the finished bit. */
pend->finished = 1;
} else {
FD_SET(pend->fd, &write_fds);
if (pend->fd >= nfds)
nfds = pend->fd + 1;
}
}
/* Call select(). */
count = select(nfds, &read_fds, &write_fds, NULL, tvp);
/* Lose horribly if select() fails on anything but an interrupted system
* call. On ERR_INTR, we'll return 0. */
if (count == SOCKET_ERROR) {
if (GETERR() != ERR_INTR)
panic("select() failed");
/* Stop and return zero if no I/O events occurred. */
return 0;
}
/* Check if any connections are readable or writable. */
for (conn = connections; conn; conn = conn->next) {
if (FD_ISSET(conn->fd, &read_fds))
conn->flags.readable = 1;
if (FD_ISSET(conn->fd, &write_fds))
conn->flags.writable = 1;
}
/* Check if any server sockets have new connections. */
for (serv = servers; serv; serv = serv->next) {
if (FD_ISSET(serv->server_socket, &read_fds)) {
serv->client_socket = accept(serv->server_socket,
(struct sockaddr *) &sockin, &addr_size);
if (serv->client_socket == SOCKET_ERROR)
continue;
/* Get address and local port of client. */
strcpy(serv->client_addr, inet_ntoa(sockin.sin_addr));
serv->client_port = ntohs(sockin.sin_port);
/* Set the CLOEXEC flag on socket so that it will be closed for a
* execute() operation. */
#ifdef FD_CLOEXEC
flags = fcntl(serv->client_socket, F_GETFD);
flags |= FD_CLOEXEC;
fcntl(serv->client_socket, F_SETFD, flags);
#endif
}
}
/* Check if any pending connections have succeeded or failed. */
for (pend = pendings; pend; pend = pend->next) {
if (FD_ISSET(pend->fd, &write_fds)) {
result = getpeername(pend->fd, (struct sockaddr *) &sockin,
&addr_size);
if (result == SOCKET_ERROR) {
getsockopt(pend->fd, SOL_SOCKET, SO_ERROR, (char *) &error,
&dummy);
pend->error = translate_connect_error(error);
} else {
pend->error = NOT_AN_IDENT;
}
pend->finished = 1;
}
}
/* Return nonzero, indicating that at least one I/O event occurred. */
return 1;
}
Long non_blocking_connect(char *addr, Int port, Int *socket_return)
{
SOCKET fd;
Int result, flags;
struct in_addr inaddr;
struct sockaddr_in saddr;
/* Convert address to struct in_addr. */
inaddr.s_addr = inet_addr(addr);
if (inaddr.s_addr == -1)
return address_id;
/* Get a socket for the connection. */
fd = socket(AF_INET, SOCK_STREAM, 0);
if (fd == SOCKET_ERROR)
return socket_id;
/* Set the socket non-blocking. */
#ifdef __Win32__
result = 1;
ioctlsocket(fd, FIONBIO, &result);
#else
flags = fcntl(fd, F_GETFL);
#ifdef FNDELAY
flags |= FNDELAY;
#else
#ifdef O_NDELAY
flags |= O_NDELAY;
#endif
#endif
fcntl(fd, F_SETFL, flags);
#endif
/* Make the connection. */
memset(&saddr, 0, sizeof(saddr));
saddr.sin_family = AF_INET;
saddr.sin_port = htons((unsigned short) port);
saddr.sin_addr = inaddr;
do {
result = connect(fd, (struct sockaddr *) &saddr, sizeof(saddr));
} while (result == SOCKET_ERROR && GETERR() == ERR_INTR);
*socket_return = fd;
if (result != SOCKET_ERROR || GETERR() == ERR_INPROGRESS)
return NOT_AN_IDENT;
else
return translate_connect_error(GETERR());
}
Long udp_connect(char *addr, Int port, Int *socket_return)
{
SOCKET fd;
Int result, flags;
struct in_addr inaddr;
struct sockaddr_in saddr;
/* Convert address to struct in_addr. */
inaddr.s_addr = inet_addr(addr);
if (inaddr.s_addr == -1)
return address_id;
/* Get a socket for the connection. */
fd = socket(AF_INET, SOCK_DGRAM, 0);
if (fd == SOCKET_ERROR)
return socket_id;
/* Set the socket non-blocking. */
#ifdef __Win32__
result = 1;
ioctlsocket(fd, FIONBIO, &result);
#else
flags = fcntl(fd, F_GETFL);
#ifdef FNDELAY
flags |= FNDELAY;
#else
#ifdef O_NDELAY
flags |= O_NDELAY;
#endif
#endif
fcntl(fd, F_SETFL, flags);
#endif
/* Make the connection. */
memset(&saddr, 0, sizeof(saddr));
saddr.sin_family = AF_INET;
saddr.sin_port = htons((unsigned short) port);
saddr.sin_addr = inaddr;
do {
result = connect(fd, (struct sockaddr *) &saddr, sizeof(saddr));
} while (result == SOCKET_ERROR && GETERR() == ERR_INTR);
*socket_return = fd;
if (result != SOCKET_ERROR || GETERR() == ERR_INPROGRESS)
return NOT_AN_IDENT;
else
return translate_connect_error(GETERR());
}
static Long translate_connect_error(Int error)
{
switch (error) {
case ERR_CONNREFUSED:
return refused_id;
case ERR_NETUNREACH:
return net_id;
case ERR_TIMEDOUT:
return timeout_id;
default:
return other_id;
}
}