/* @@@HEAD@@@
// Network routines.
//
// This stuff is not POSIX, and thus must be ported separately to each
// network interface. This code is for a BSD interface.
//
// RFC references: inverse name resolution--1293, 903
// 1035 - domain name system
*/
#define _BSD 44 /* For RS6000s. */
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#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>
#include <errno.h>
#include <fcntl.h>
#include "config.h"
#include "defs.h"
#include "memory.h"
#include "net.h"
#include "io.h"
#include "log.h"
#include "util.h"
#include "ident.h"
extern int socket(), bind(), listen(), getdtablesize(void), select(), accept();
extern int connect(), getpeername(), getsockopt(), setsockopt();
#ifndef sys_linux
extern void bzero(char *, int);
#endif
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;
int get_server_socket(int port)
{
int fd, one;
/* Create a socket. */
fd = socket(AF_INET, SOCK_STREAM, 0);
if (fd < 0) {
server_failure_reason = socket_id;
return -1;
}
/* Set SO_REUSEADDR option to avoid restart problems. */
one = 1;
setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char *) &one, sizeof(int));
/* Bind the socket to port. */
memset(&sockin, 0, sizeof(sockin));
sockin.sin_family = AF_INET;
sockin.sin_port = htons((unsigned short) port);
if (bind(fd, (struct sockaddr *) &sockin, sizeof(sockin)) < 0) {
server_failure_reason = bind_id;
return -1;
}
/* Start listening on port. This shouldn't return an error under any
* circumstances. */
listen(fd, 8);
return fd;
}
/* 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(long sec, Connection *connections, Server *servers,
Pending *pendings)
{
struct timeval tv, *tvp;
Connection *conn;
Server *serv;
Pending *pend;
fd_set read_fds, write_fds;
int flags, nfds, count, result, error, 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 = 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 EINTR, we'll return 0. */
if (count == -1 && errno != EINTR)
panic("select() failed");
/* Stop and return zero if no I/O events occurred. */
if (count <= 0)
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 < 0)
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 == 0) {
pend->error = NOT_AN_IDENT;
} else {
getsockopt(pend->fd, SOL_SOCKET, SO_ERROR, (char *) &error,
&dummy);
pend->error = translate_connect_error(error);
}
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)
{
int fd, 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 == -1)
return socket_id;
/* Set the socket non-blocking. */
flags = fcntl(fd, F_GETFL);
#ifdef FNDELAY
flags |= FNDELAY;
#else
#ifdef O_NDELAY
flags |= O_NDELAY;
#endif
#endif
fcntl(fd, F_SETFL, flags);
/* 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 == -1 && errno == EINTR);
*socket_return = fd;
if (result != -1 || errno == EINPROGRESS)
return NOT_AN_IDENT;
else
return translate_connect_error(errno);
}
static long translate_connect_error(int error)
{
switch (error) {
case ECONNREFUSED:
return refused_id;
case ENETUNREACH:
return net_id;
case ETIMEDOUT:
return timeout_id;
default:
return other_id;
}
}
string_t *hostname(char *chaddr)
{
unsigned addr;
register struct hostent *hp;
addr = inet_addr(chaddr);
if (addr == -1)
return string_from_chars(chaddr, strlen(chaddr));
hp = gethostbyaddr((char *) &addr, 4, AF_INET);
if (hp)
return string_from_chars(hp->h_name, strlen(hp->h_name));
else
return string_from_chars(chaddr, strlen(chaddr));
}
string_t *ip(char *chaddr)
{
unsigned addr;
register struct hostent *hp;
addr = inet_addr(chaddr);
if (addr == -1) {
hp = gethostbyname(chaddr);
if (hp)
return string_from_chars(inet_ntoa(*(struct in_addr *)hp->h_addr), strlen(inet_ntoa(*(struct in_addr *)hp->h_addr)));
else
return string_from_chars("-1", 2);
} else
return string_from_chars(chaddr, strlen(chaddr));
}