hypercube/net.c

299 lines
9 KiB
C
Raw Permalink Normal View History

2016-09-12 22:08:46 -07:00
#include "hypercube.h"
net_fd_entry net_fds[ASIO_MAX_FDS];
extern hypercube_config cfg;
time_t period_time;
void net_init(void)
{
unsigned int i;
period_time = time(NULL);
for (i = 0; i < ASIO_MAX_FDS; i ++) net_fds[i].type = NET_FD_UNUSED;
asio_init();
DEBUGF("network initialized");
}
void net_set_fd(int fd, net_fd_type type, void (*data_callback)(), void (*sent_callback)(), unsigned int alloc_buf)
{
AKassert(FD_VALID(fd));
net_fds[fd].type = type;
net_fds[fd].data_callback = data_callback;
net_fds[fd].sent_callback = sent_callback;
net_fds[fd].active_time = time(NULL);
net_fds[fd].ctx = akbuf_new_ctx();
if (alloc_buf == 1)
{
net_fds[fd].readbuf = akbuf_init(net_fds[fd].ctx, 0);
net_fds[fd].linebuf = akbuf_init(net_fds[fd].ctx, 0);
net_fds[fd].sendbuf = akbuf_init(net_fds[fd].ctx, 0);
net_fds[fd].peerbuf = akbuf_init(net_fds[fd].ctx, 0);
net_fds[fd].sockbuf = akbuf_init(net_fds[fd].ctx, 0);
} else
{
net_fds[fd].readbuf = net_fds[fd].linebuf = net_fds[fd].sendbuf = net_fds[fd].peerbuf = net_fds[fd].sockbuf = NULL;
}
net_fds[fd].send_fd = -1;
switch (type)
{
case NET_FD_LISTEN: case NET_FD_READ: case NET_FD_READLINE: case NET_FD_RAW:
if (asio_add_fd(fd, ASIO_R) != 0) { akperror("asio_add_fd()"); exit(1); }
break;
case NET_FD_SEND:
if (asio_add_fd(fd, ASIO_W) != 0) { akperror("asio_add_fd()"); exit(1); }
break;
}
}
void net_set_callbacks(int fd, void (*data_callback)(), void (*sent_callback)())
{
AKassert(FD_VALID(fd));
net_fds[fd].data_callback = data_callback;
net_fds[fd].sent_callback = sent_callback;
}
void net_set_type(int fd, net_fd_type type)
{
AKassert(FD_VALID(fd));
net_fds[fd].type = type;
}
void net_send_buf(int fd, akbuf *buf)
{
AKassert(FD_VALID(fd));
akbuf_append_buf(net_fds[fd].sendbuf, buf);
}
void net_unset_fd(int fd)
{
AKassert(FD_VALID(fd));
DEBUGF("unsetting fd %d", fd);
net_unset_fd_callback(fd);
if (net_fds[fd].type != NET_FD_UNUSED)
{
akbuf_free_ctx(net_fds[fd].ctx);
net_fds[fd].ctx = (akbuf_ctxh)0;
if (net_fds[fd].send_fd != -1) close(net_fds[fd].send_fd);
net_fds[fd].type = NET_FD_UNUSED;
}
asio_del_fd(fd, ASIO_R | ASIO_W);
close(fd);
}
void net_start_listen(void)
{
int sock;
unsigned int i;
struct sockaddr_in sin;
if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0 || sock >= ASIO_MAX_FDS) { akperror("socket()"); exit(1); }
i = 1;
if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (char *)&i, sizeof(i)) != 0) { akperror("setsockopt()"); exit(1); }
memset(&sin, 0, sizeof(sin));
sin.sin_family = AF_INET;
sin.sin_addr.s_addr = cfg.listen_addr.s_addr;
sin.sin_port = htons(cfg.listen_port);
if (bind(sock, (struct sockaddr *)&sin, sizeof(sin)) != 0) { akperror("bind()"); exit(1); }
if (listen(sock, LISTEN_BACKLOG) != 0) { akperror("listen() (decrease LISTEN_BACKLOG?)"); exit(1); }
net_set_fd(sock, NET_FD_LISTEN, NULL, NULL, 0);
aklogf(LOG_INFO, "Listening on port %d.", cfg.listen_port);
}
void test_readline(int fd, net_fd_entry *net_ent, akbuf *line)
{
akbuf_asciiz(line);
printf("TEST: readline [%s]\n", akbuf_data(line));
}
void net_accept_connection(int fd)
{
struct sockaddr_in sin;
size_t sin_len;
int newfd;
sin_len = sizeof(sin);
memset(&sin, 0, sizeof(sin));
if ((newfd = accept(fd, (struct sockaddr *)&sin, &sin_len)) < 0) { akperror("accept()"); return; }
if (!FD_VALID(newfd))
{
aklogf(LOG_INFO, "Client limit reached. Increase ASIO_MAX_FDS?");
close(newfd);
return;
}
net_set_fd(newfd, NET_FD_INIT_TYPE, net_fd_init_data_callback, net_fd_init_sent_callback, 1);
akbuf_sprintf(net_fds[newfd].peerbuf, "%s:%u", inet_ntoa(sin.sin_addr), ntohs(sin.sin_port));
sin_len = sizeof(sin);
memset(&sin, 0, sizeof(sin));
if (getsockname(newfd, (struct sockaddr *)&sin, &sin_len) == 0)
{
akbuf_sprintf(net_fds[newfd].sockbuf, "%s:%u", inet_ntoa(sin.sin_addr), ntohs(sin.sin_port));
} else
{
akbuf_strcpy(net_fds[newfd].sockbuf, "");
}
aklogf(LOG_CONNECTION, "%d: Connection from %s", newfd, akbuf_data(net_fds[newfd].peerbuf));
}
void net_send(int fd)
{
DEBUGF("send on fd %d", fd);
asio_set_events(fd, ASIO_W);
net_fds[fd].type = NET_FD_SEND;
}
void net_sent(int fd)
{
DEBUGF("sent on fd %d", fd);
asio_set_events(fd, ASIO_R);
net_fds[fd].type = NET_FD_INIT_TYPE;
if (net_fds[fd].send_fd != -1) { close(net_fds[fd].send_fd); net_fds[fd].send_fd = -1; }
if (net_fds[fd].sent_callback != NULL) net_fds[fd].sent_callback(fd, &net_fds[fd]);
}
void net_handle_send(int fd)
{
AKssize_t n, m;
if (!akbuf_empty(net_fds[fd].sendbuf))
{
if ((n = write(fd, akbuf_data(net_fds[fd].sendbuf), akbuf_idx(net_fds[fd].sendbuf))) <= 0)
{
if (errno != EINTR && errno != EAGAIN) net_unset_fd(fd);
return;
}
akbuf_consume(net_fds[fd].sendbuf, n);
if (akbuf_empty(net_fds[fd].sendbuf) && net_fds[fd].send_fd == -1)
{
net_sent(fd);
return;
}
} else if (net_fds[fd].send_fd != -1)
{
#ifdef USE_LINUX_SENDFILE
if ((m = sendfile(fd, net_fds[fd].send_fd, &net_fds[fd].send_fd_off, (net_fds[fd].send_fd_len >= SEND_BUF_SIZE)? SEND_BUF_SIZE : net_fds[fd].send_fd_len)) < 0)
{
if (errno != EAGAIN && errno != EINTR) { akperror("sendfile()"); net_unset_fd(fd); }
return;
}
net_fds[fd].send_fd_len -= m;
if (net_fds[fd].send_fd_len == 0) net_sent(fd);
#else
unsigned char readbuf[SEND_BUF_SIZE];
if (net_fds[fd].send_fd_off != 0)
{
if (lseek(fd, net_fds[fd].send_fd_off, SEEK_SET) == (off_t)-1) akperror("lseek()");
net_fds[fd].send_fd_off = 0;
}
if ((n = read(net_fds[fd].send_fd, readbuf, sizeof(readbuf))) <= 0)
{
if (errno != EINTR && errno != EAGAIN) net_sent(fd);
return;
}
if ((m = write(fd, readbuf, n)) <= 0)
{
if (errno != EINTR && errno != EAGAIN) net_unset_fd(fd);
return;
}
if (m < n) akbuf_append_data(net_fds[fd].sendbuf, &readbuf[m], n - m);
#endif
} else
{
net_sent(fd);
}
}
void net_handle_readline(int fd)
{
AKssize_t n;
unsigned char readbuf[BUF_SIZE];
int i, j;
if ((n = read(fd, readbuf, sizeof(readbuf))) <= 0)
{
if (errno != EINTR && errno != EAGAIN) net_unset_fd(fd);
return;
}
DEBUGF("read %d bytes", n);
akbuf_append_data(net_fds[fd].readbuf, readbuf, n);
if (akbuf_idx(net_fds[fd].readbuf) > MAX_LINE_LEN)
{
net_unset_fd(fd);
return;
}
while ((i = akbuf_chr(net_fds[fd].readbuf, '\n')) >= 0 && net_fds[fd].type == NET_FD_READLINE)
{
akbuf_split(net_fds[fd].readbuf, net_fds[fd].linebuf, i);
if ((j = akbuf_chr(net_fds[fd].linebuf, '\r')) >= 0) akbuf_set_idx(net_fds[fd].linebuf, j);
if (net_fds[fd].data_callback != NULL) net_fds[fd].data_callback(fd, &net_fds[fd], net_fds[fd].linebuf);
}
if (net_fds[fd].type == NET_FD_READ && akbuf_idx(net_fds[fd].readbuf) > 0)
{
/* State changed with data remaining in buffer, so handle it. */
if (net_fds[fd].data_callback != NULL) net_fds[fd].data_callback(fd, &net_fds[fd], net_fds[fd].readbuf);
akbuf_set_idx(net_fds[fd].readbuf, 0);
}
}
void net_handle_read(int fd)
{
AKssize_t n;
unsigned char readbuf[BUF_SIZE];
if ((n = read(fd, readbuf, sizeof(readbuf))) <= 0)
{
if (errno != EINTR && errno != EAGAIN) net_unset_fd(fd);
return;
}
DEBUGF("read %d bytes", n);
akbuf_set_data(net_fds[fd].readbuf, readbuf, n);
if (net_fds[fd].data_callback != NULL) net_fds[fd].data_callback(fd, &net_fds[fd], net_fds[fd].readbuf);
}
void net_handle_raw(int fd)
{
DEBUGF("raw event on fd %d", fd);
if (net_fds[fd].data_callback != NULL) net_fds[fd].data_callback(fd, &net_fds[fd], NULL);
}
void net_wait_for_events(void)
{
asio_event_list *evs;
unsigned int i;
int fd;
evs = asio_wait_for_events();
AKassert(evs != NULL);
//DEBUGF("got %u events", evs->num_events);
for (i = 0; i < evs->num_events; i ++)
{
fd = evs->events[i].fd;
AKassert(FD_VALID(fd));
DEBUGF("event on fd %d", fd);
net_fds[fd].active_time = time(NULL);
if (evs->events[i].event & ASIO_R)
{
switch (net_fds[fd].type)
{
case NET_FD_LISTEN: net_accept_connection(fd); break;
case NET_FD_READLINE: net_handle_readline(fd); break;
case NET_FD_READ: net_handle_read(fd); break;
case NET_FD_RAW: net_handle_raw(fd); break;
default: DEBUGF("Unknown revent type %u on fd %d", net_fds[fd].type, fd);
}
}
if (evs->events[i].event & ASIO_W)
{
switch (net_fds[fd].type)
{
case NET_FD_SEND: net_handle_send(fd); break;
default: DEBUGF("Unknown wevent type %u on fd %d", net_fds[fd].type, fd);
}
}
}
}
void net_periodic(void)
{
#ifdef SOCKET_TIMEOUT
unsigned int i;
time_t t;
if (time(NULL) - period_time >= SOCKET_TIMEOUT / 2)
{
t = time(NULL);
for (i = 0; i < ASIO_MAX_FDS; i ++) if (net_fds[i].type != NET_FD_UNUSED && net_fds[i].type != NET_FD_LISTEN && net_fds[i].type != NET_FD_RAW && t - net_fds[i].active_time >= SOCKET_TIMEOUT)
{
net_unset_fd(i);
}
period_time = time(NULL);
}
#endif
}