#include "net.h"
#include "log.h"
#include <netdb.h>
#include <signal.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <unistd.h>
#define gai_print_errno(ec) print_error("getaddrinfo: %s\n", gai_strerror(ec))
static int ignore_signal(int signum)
{
int ret = 0;
struct sigaction act;
memset(&act, 0, sizeof(act));
act.sa_handler = SIG_IGN;
ret = sigaction(signum, &act, NULL);
if (ret < 0) {
print_errno("sigaction");
return ret;
}
return ret;
}
static int ignore_sigchld()
{
return ignore_signal(SIGCHLD);
}
int bind_to_port(const char *port)
{
struct addrinfo *result, *it = NULL;
struct addrinfo hints;
int socket_fd, ret = 0;
memset(&hints, 0, sizeof(hints));
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
hints.ai_flags = AI_PASSIVE;
ret = getaddrinfo(NULL, port, &hints, &result);
if (ret) {
gai_print_errno(ret);
return -1;
}
for (it = result; it; it = it->ai_next) {
socket_fd = socket(it->ai_family, it->ai_socktype | SOCK_CLOEXEC, it->ai_protocol);
if (socket_fd < 0) {
print_errno("socket");
continue;
}
static const int yes = 1;
if (setsockopt(socket_fd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes)) == -1) {
print_errno("setsockopt");
goto close_socket;
}
if (bind(socket_fd, it->ai_addr, it->ai_addrlen) < 0) {
print_errno("bind");
goto close_socket;
}
break;
close_socket:
close(socket_fd);
}
freeaddrinfo(result);
if (!it) {
print_error("Couldn't bind to port %s\n", port);
return -1;
}
ret = listen(socket_fd, 4096);
if (ret < 0) {
print_errno("listen");
goto fail;
}
/* Don't make zombies. The alternative is wait()ing for the child in
* the signal handler. */
ret = ignore_sigchld();
if (ret < 0)
goto fail;
return socket_fd;
fail:
close(socket_fd);
return ret;
}
int accept_connection(int fd, connection_handler handler, void *arg)
{
int conn_fd, ret = 0;
ret = accept4(fd, NULL, NULL, SOCK_CLOEXEC);
if (ret < 0) {
print_errno("accept");
return ret;
}
conn_fd = ret;
pid_t child_pid = fork();
if (child_pid < 0) {
print_errno("fork");
ret = -1;
goto close_conn;
}
if (!child_pid) {
close(fd);
ret = handler(conn_fd, arg);
close(conn_fd);
exit(ret);
}
close_conn:
close(conn_fd);
return ret;
}
int connect_to_host(const char *host, const char *port)
{
struct addrinfo *result, *it = NULL;
struct addrinfo hints;
int socket_fd, ret = 0;
memset(&hints, 0, sizeof(hints));
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
ret = getaddrinfo(host, port, &hints, &result);
if (ret) {
gai_print_errno(ret);
return -1;
}
for (it = result; it; it = it->ai_next) {
socket_fd = socket(it->ai_family, it->ai_socktype | SOCK_CLOEXEC, it->ai_protocol);
if (socket_fd < 0) {
print_errno("socket");
continue;
}
if (connect(socket_fd, it->ai_addr, it->ai_addrlen) < 0) {
print_errno("connect");
goto close_socket;
}
break;
close_socket:
close(socket_fd);
}
freeaddrinfo(result);
if (!it) {
print_error("Couldn't connect to host %s, port %s\n", host, port);
return -1;
}
return socket_fd;
}
int send_all(int fd, const void *buf, size_t len)
{
size_t sent_total = 0;
while (sent_total < len) {
ssize_t sent_now = write(fd, (const char *)buf + sent_total, len - sent_total);
if (sent_now < 0) {
print_errno("write");
return -1;
}
sent_total += sent_now;
}
return 0;
}
ssize_t recv_all(int fd, void *buf, size_t len)
{
ssize_t read_total = 0;
while ((size_t)read_total < len) {
ssize_t read_now = read(fd, buf, len);
if (!read_now)
break;
if (read_now < 0) {
print_errno("read");
return -1;
}
read_total += read_now;
}
return read_total;
}
int send_buf(int fd, const void *buf, size_t len)
{
int ret = 0;
ret = send_all(fd, &len, sizeof(len));
if (ret < 0)
return ret;
ret = send_all(fd, buf, len);
if (ret < 0)
return ret;
return ret;
}
int recv_buf(int fd, void **buf, size_t *len)
{
ssize_t nb = 0;
nb = recv_all(fd, len, sizeof(*len));
if (nb < 0)
goto fail;
if (nb != sizeof(*len)) {
print_error("Couldn't read buffer length\n");
goto fail;
}
*buf = malloc(*len);
if (!*buf) {
print_errno("malloc");
goto fail;
}
nb = recv_all(fd, *buf, *len);
if (nb < 0)
goto free_buf;
if ((size_t)nb != *len) {
print_error("Couldn't read the entire buffer\n");
goto free_buf;
}
return 0;
free_buf:
free(*buf);
fail:
return -1;
}
int recv_static(int fd, void *buf, size_t len)
{
void *actual_buf;
size_t actual_len;
int ret = 0;
ret = recv_buf(fd, &actual_buf, &actual_len);
if (ret < 0)
return ret;
if (actual_len != len) {
print_error("Expected message length: %lu, actual: %lu\n", len, actual_len);
ret = -1;
goto free_buf;
}
memcpy(buf, actual_buf, len);
free_buf:
free(actual_buf);
return ret;
}