summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--CMakeLists.txt9
-rw-r--r--src/config.h3
-rw-r--r--src/server/job.c14
-rw-r--r--src/server/net.c42
-rw-r--r--src/server/net.h2
-rw-r--r--src/server/rpc.c150
-rw-r--r--src/server/saveload.c2
-rw-r--r--src/server/server.c69
-rw-r--r--src/server/sockhelper.c159
-rw-r--r--src/server/sockhelper.h50
10 files changed, 341 insertions, 159 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 3a68bce..75044c8 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -6,13 +6,14 @@ PROJECT(dnbd3)
CMAKE_MINIMUM_REQUIRED(VERSION 2.8.0)
SET(CMAKE_BUILD_TYPE Debug)
-SET(CMAKE_C_FLAGS_DEBUG "-O0 -g -Wall -Wno-unused-result -D_GNU_SOURCE -D_FILE_OFFSET_BITS=64 -D_DEBUG")
-SET(CMAKE_C_FLAGS_RELEASE "-O2 -Wno-unused-result -D_GNU_SOURCE -D_FILE_OFFSET_BITS=64")
-SET(CMAKE_CXX_FLAGS_DEBUG "-O0 -g -Wall -Wno-unused-result -D_GNU_SOURCE -D_FILE_OFFSET_BITS=64 -D_DEBUG")
-SET(CMAKE_CXX_FLAGS_RELEASE "-O2 -Wno-unused-result -D_GNU_SOURCE -D_FILE_OFFSET_BITS=64" )
+SET(CMAKE_C_FLAGS_DEBUG "-std=c99 -O0 -g -Wall -Wno-unused-result -D_GNU_SOURCE -D_DEBUG")
+SET(CMAKE_C_FLAGS_RELEASE "-std=c99 -O2 -Wno-unused-result -D_GNU_SOURCE")
+SET(CMAKE_CXX_FLAGS_DEBUG "-std=c99 -O0 -g -Wall -Wno-unused-result -D_GNU_SOURCE -D_DEBUG")
+SET(CMAKE_CXX_FLAGS_RELEASE "-std=c99 -O2 -Wno-unused-result -D_GNU_SOURCE" )
ADD_DEFINITIONS(-DIPC_TCP)
ADD_DEFINITIONS(-D_FILE_OFFSET_BITS=64)
+ADD_DEFINITIONS(-DWITH_IPV6)
FIND_PACKAGE(Threads REQUIRED)
FIND_PACKAGE(PkgConfig REQUIRED)
diff --git a/src/config.h b/src/config.h
index 35e5647..2668410 100644
--- a/src/config.h
+++ b/src/config.h
@@ -37,7 +37,8 @@
// No payload allowed exceeding this many bytes (actual data from client->server is not affected by this limit!)
#define MAX_PAYLOAD 1000
-#define SOCKET_TIMEOUT_SERVER 30
+// in seconds if not stated otherwise (MS = milliseconds)
+#define SOCKET_TIMEOUT_SERVER_MS 30000
#define SOCKET_TIMEOUT_CLIENT_DATA 2
#define SOCKET_TIMEOUT_CLIENT_DISCOVERY 1
diff --git a/src/server/job.c b/src/server/job.c
index b9e17f6..31f577b 100644
--- a/src/server/job.c
+++ b/src/server/job.c
@@ -399,13 +399,18 @@ static void query_servers()
pthread_spin_unlock(&_spinlock);
break; // Done
}
- host = server->host; // Copy host, in case server gets deleted by another thread
+ host = server->host; // Copy host, in case server gets deleted by another thread while processing continues here
pthread_spin_unlock(&_spinlock);
// Connect
host.port = htons(ntohs(host.port) + 1); // RPC port is client port + 1
client_sock = sock_connect(&host, 800, 600);
if (client_sock == -1)
- continue;
+ {
+ char buf[100];
+ if (host_to_string(&host, buf, 100))
+ printf("[DEBUG] Could not connect to trusted server %s\n", buf);
+ goto communication_error;
+ }
//
// Send and receive info from server
// Send message
@@ -415,7 +420,7 @@ static void query_servers()
send(client_sock, (char *)&header, sizeof(header), 0);
if (!recv_data(client_sock, &header, sizeof(header)))
{
- printf("[DEBUG] Could not get status from other server...\n");
+ printf("[DEBUG] Could not receive IMG_LIST header from a trusted server...\n");
goto communication_error;
}
header.cmd = ntohl(header.cmd);
@@ -567,7 +572,8 @@ free_current_image:
//
continue;
communication_error:
- close(client_sock);
+ if (client_sock != -1)
+ close(client_sock);
pthread_spin_lock(&_spinlock);
if (g_slist_find(_trusted_servers, server))
{
diff --git a/src/server/net.c b/src/server/net.c
index bb395fb..f28fc25 100644
--- a/src/server/net.c
+++ b/src/server/net.c
@@ -25,13 +25,10 @@
#include <unistd.h>
#include <pthread.h>
#include <fcntl.h>
-#include <sys/socket.h>
#include <sys/sendfile.h>
#include <sys/types.h>
-#include <netinet/in.h>
-#include <arpa/inet.h>
-#include <netinet/tcp.h>
+#include "sockhelper.h"
#include "helper.h"
#include "server.h"
#include "saveload.h"
@@ -435,40 +432,3 @@ void *dnbd3_handle_query(void *dnbd3_client)
dnbd3_free_client(client);
pthread_exit((void *) 0);
}
-
-int dnbd3_setup_socket()
-{
- int sock;
- struct sockaddr_in server;
-
- // Create socket
- sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
- if (sock < 0)
- {
- memlogf("ERROR: Socket setup failure\n");
- return -1;
- }
- const int opt = 1;
- setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
-
- memset(&server, 0, sizeof(server));
- server.sin_family = AF_INET; // IPv4
- server.sin_addr.s_addr = htonl(INADDR_ANY); // Take all IPs
- server.sin_port = htons(PORT); // set port number
-
- // Bind to socket
- if (bind(sock, (struct sockaddr *) &server, sizeof(server)) < 0)
- {
- memlogf("ERROR: Bind failure\n");
- return -1;
- }
-
- // Listen on socket
- if (listen(sock, 100) == -1)
- {
- memlogf("ERROR: Listen failure\n");
- return -1;
- }
-
- return sock;
-}
diff --git a/src/server/net.h b/src/server/net.h
index 3e34c99..9b163de 100644
--- a/src/server/net.h
+++ b/src/server/net.h
@@ -23,6 +23,4 @@
void *dnbd3_handle_query(void *client_socket);
-int dnbd3_setup_socket();
-
#endif /* NET_H_ */
diff --git a/src/server/rpc.c b/src/server/rpc.c
index ffd386c..e253ace 100644
--- a/src/server/rpc.c
+++ b/src/server/rpc.c
@@ -34,7 +34,6 @@
#include <unistd.h>
#include <pthread.h>
#include <errno.h>
-#include <fcntl.h>
#include "sockhelper.h"
#include <libxml/parser.h>
@@ -43,7 +42,8 @@
#define RPC_PORT (PORT+1)
-static int server_sock = -1;
+#define MAX_SERVER_SOCKETS 50 // Assume there will be no more than 50 sockets the server will listen on
+static int server_socks[MAX_SERVER_SOCKETS], server_count = 0;
static volatile int keep_running = 1;
static char *payload = NULL;
@@ -61,10 +61,15 @@ static int rpc_send_reply(int sock, dnbd3_rpc_t* header, int result_code, xmlDoc
static int get_highest_fd(GSList *sockets)
{
- GSList *iterator;
- int max = server_sock;
+ int max = 0;
- for (iterator = sockets; iterator; iterator = iterator->next)
+ for (int i = 0; i < server_count; ++i)
+ {
+ if (server_socks[i] > max)
+ max = server_socks[i];
+ }
+
+ for (GSList *iterator = sockets; iterator; iterator = iterator->next)
{
const int fd = (int)(size_t)iterator->data;
if (fd > max)
@@ -88,35 +93,17 @@ void *dnbd3_rpc_mainloop()
return NULL;
}
- struct sockaddr_in server, client;
- socklen_t len = sizeof(client);
+ struct sockaddr_storage client;
- // Create socket
- if ((server_sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0)
- {
- perror("ERROR: RPC socket");
- exit(EXIT_FAILURE);
- }
-
- memset(&server, 0, sizeof(server));
- server.sin_family = AF_INET; // IPv4
- server.sin_addr.s_addr = INADDR_ANY;
- server.sin_port = htons(RPC_PORT); // set port number
-
- const int optval = 1;
- setsockopt(server_sock, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval));
+ sock_add_array(sock_listen_any(PF_INET, RPC_PORT), server_socks, &server_count, MAX_SERVER_SOCKETS);
+#ifdef WITH_IPV6
+ sock_add_array(sock_listen_any(PF_INET6, RPC_PORT), server_socks, &server_count, MAX_SERVER_SOCKETS);
+#endif
// Bind to socket
- if (bind(server_sock, (struct sockaddr *)&server, sizeof(server)) < 0)
- {
- perror("ERROR: RPC bind");
- exit(EXIT_FAILURE);
- }
-
- // Listen on socket
- if (listen(server_sock, 5) < 0)
+ if (server_count == 0)
{
- perror("ERROR: RPC listen");
+ perror("ERROR: RPC bind/listen unsuccessful");
exit(EXIT_FAILURE);
}
@@ -126,22 +113,18 @@ void *dnbd3_rpc_mainloop()
GSList *sockets = NULL, *iterator;
- int client_sock, ret, flags;
- int maxfd = server_sock + 1;
+ int client_sock, ret;
+ int maxfd = get_highest_fd(sockets);
int error_count = 0;
- struct timeval client_timeout, select_timeout;
- client_timeout.tv_sec = 0;
- client_timeout.tv_usec = 500 * 1000;
+ struct timeval select_timeout;
FD_ZERO(&all_sockets);
- FD_SET(server_sock, &all_sockets);
-
- // Make listening socket non-blocking
- flags = fcntl(server_sock, F_GETFL, 0);
- if (flags == -1)
- flags = 0;
- fcntl(server_sock, F_SETFL, flags | O_NONBLOCK);
+ for (int i = 0; i < server_count; ++i)
+ {
+ FD_SET(server_socks[i], &all_sockets);
+ sock_set_nonblock(server_socks[i]);
+ }
while (keep_running)
{
@@ -151,42 +134,43 @@ void *dnbd3_rpc_mainloop()
ret = select(maxfd, &readset, NULL, &exceptset, &select_timeout);
while (ret > 0)
{
- --ret;
- if (FD_ISSET(server_sock, &readset))
+ for (int i = 0; i < server_count; ++i)
{
- // Accept connection
- if ((client_sock = accept(server_sock, &client, &len)) < 0)
+ if (FD_ISSET(server_socks[i], &readset))
{
- if (errno != EAGAIN)
+ --ret;
+ // Accept connection
+ socklen_t len = sizeof(client);
+ if ((client_sock = accept(server_socks[i], (struct sockaddr *)&client, &len)) < 0)
{
- memlogf("[ERROR] Error accepting an RPC connection");
- if (++error_count > 10)
- goto end_loop;
+ if (errno != EAGAIN)
+ {
+ memlogf("[ERROR] Error accepting an RPC connection");
+ if (++error_count > 10)
+ goto end_loop;
+ }
+ continue;
}
- continue;
+ error_count = 0;
+ // Apply read/write timeout
+ sock_set_timeout(client_sock, 500);
+ // Make new connection blocking
+ sock_set_block(client_sock);
+ sockets = g_slist_prepend(sockets, (void *)(size_t)client_sock);
+ if (client_sock >= maxfd)
+ maxfd = client_sock + 1;
+ //printf("Max fd: %d\n", (maxfd-1));
+ FD_SET(client_sock, &all_sockets);
+ }
+ if (FD_ISSET(server_socks[i], &exceptset))
+ {
+ --ret;
+ memlogf("[ERROR] An exception occurred on the RPC listening socket.");
+ if (++error_count > 10)
+ goto end_loop;
}
- error_count = 0;
- // Apply read/write timeout
- setsockopt(client_sock, SOL_SOCKET, SO_RCVTIMEO, &client_timeout, sizeof(client_timeout));
- setsockopt(client_sock, SOL_SOCKET, SO_SNDTIMEO, &client_timeout, sizeof(client_timeout));
- // Make new connection blocking
- flags = fcntl(client_sock, F_GETFL, 0);
- if (flags == -1)
- flags = 0;
- fcntl(client_sock, F_SETFL, flags & ~(int)O_NONBLOCK);
- sockets = g_slist_prepend(sockets, (void *)(size_t)client_sock);
- if (client_sock >= maxfd)
- maxfd = client_sock + 1;
- //printf("Max fd: %d\n", (maxfd-1));
- FD_SET(client_sock, &all_sockets);
- }
- else if (FD_ISSET(server_sock, &exceptset))
- {
- memlogf("[ERROR] An exception occurred on the RPC listening socket.");
- if (++error_count > 10)
- goto end_loop;
}
- else
+ if (ret > 0)
{
// Must be an active RPC connection
int del = -1;
@@ -202,6 +186,7 @@ void *dnbd3_rpc_mainloop()
client_sock = (int)(size_t)iterator->data;
if (FD_ISSET(client_sock, &readset))
{
+ --ret;
// Client sending data
if (!rpc_receive(client_sock))
{
@@ -211,8 +196,9 @@ void *dnbd3_rpc_mainloop()
FD_CLR(client_sock, &all_sockets);
}
}
- else if (FD_ISSET(client_sock, &exceptset))
+ if (FD_ISSET(client_sock, &exceptset))
{
+ --ret;
// Something unexpected happened, just close connection
close(client_sock);
del = client_sock;
@@ -231,11 +217,7 @@ void *dnbd3_rpc_mainloop()
end_loop:
memlogf("[INFO] Shutting down RPC interface.");
- if (server_sock != -1)
- {
- close(server_sock);
- server_sock = -1;
- }
+ dnbd3_rpc_shutdown();
free(payload);
xmlCleanupParser();
@@ -246,10 +228,14 @@ end_loop:
void dnbd3_rpc_shutdown()
{
keep_running = 0;
- if (server_sock == -1)
- return;
- close(server_sock);
- server_sock = -1;
+ for (int i = 0; i < server_count; ++i)
+ {
+ if (server_socks[i] == -1)
+ continue;
+ close(server_socks[i]);
+ server_socks[i] = -1;
+ }
+ server_count = 0;
}
/**
diff --git a/src/server/saveload.c b/src/server/saveload.c
index 51869b1..315287e 100644
--- a/src/server/saveload.c
+++ b/src/server/saveload.c
@@ -582,11 +582,13 @@ dnbd3_trusted_server_t *dnbd3_get_trusted_server(char *address, char create_if_n
memlogf("[WARNING] Could not parse address '%s' of trusted server", address);
return NULL;
}
+#ifndef WITH_IPV6
if (server.host.type == AF_INET6)
{
printf("[DEBUG] Ignoring IPv6 trusted server.\n");
return NULL;
}
+#endif
GSList *iterator;
for (iterator = _trusted_servers; iterator; iterator = iterator->next)
{
diff --git a/src/server/server.c b/src/server/server.c
index a020b40..03e8564 100644
--- a/src/server/server.c
+++ b/src/server/server.c
@@ -20,17 +20,18 @@
#include <stdio.h>
#include <stdlib.h>
-#include <arpa/inet.h>
#include <signal.h>
#include <getopt.h>
#include <pthread.h>
#include <string.h>
#include <fcntl.h>
#include <sys/ioctl.h>
+#include <stdint.h>
#include "../types.h"
#include "../version.h"
+#include "sockhelper.h"
#include "server.h"
#include "saveload.h"
#include "job.h"
@@ -38,7 +39,8 @@
#include "rpc.h"
#include "memlog.h"
-static int sock;
+#define MAX_SERVER_SOCKETS 50 // Assume there will be no more than 50 sockets the server will listen on
+static int sockets[MAX_SERVER_SOCKETS], socket_count = 0;
#ifdef _DEBUG
int _fake_delay = 0;
#endif
@@ -79,8 +81,14 @@ void dnbd3_cleanup()
int fd;
memlogf("INFO: Cleanup...\n");
- close(sock);
- sock = -1;
+ for (int i = 0; i < socket_count; ++i)
+ {
+ if (sockets[i] == -1)
+ continue;
+ close(sockets[i]);
+ sockets[i] = -1;
+ }
+ socket_count = 0;
dnbd3_rpc_shutdown();
dnbd3_job_shutdown();
@@ -217,20 +225,25 @@ int main(int argc, char *argv[])
signal(SIGINT, dnbd3_handle_sigterm);
// setup network
- sock = dnbd3_setup_socket();
- if (sock < 0)
+ sockets[socket_count] = sock_listen_any(PF_INET, PORT);
+ if (sockets[socket_count] != -1)
+ ++socket_count;
+#ifdef WITH_IPV6
+ sockets[socket_count] = sock_listen_any(PF_INET6, PORT);
+ if (sockets[socket_count] != -1)
+ ++socket_count;
+#endif
+ if (socket_count == 0)
exit(EXIT_FAILURE);
- struct sockaddr_in client;
- unsigned int len = sizeof(client);
+ struct sockaddr_storage client;
+ socklen_t len;
int fd;
- struct timeval timeout;
- timeout.tv_sec = SOCKET_TIMEOUT_SERVER;
- timeout.tv_usec = 0;
// setup rpc
pthread_t thread_rpc;
pthread_create(&(thread_rpc), NULL, &dnbd3_rpc_mainloop, NULL);
+ // setup the job thread (query other servers, delete old images etc.)
pthread_t thread_job;
pthread_create(&(thread_job), NULL, &dnbd3_job_thread, NULL);
@@ -239,16 +252,16 @@ int main(int argc, char *argv[])
// main loop
while (1)
{
- fd = accept(sock, (struct sockaddr *) &client, &len);
+ len = sizeof(client);
+ fd = accept_any(sockets, socket_count, &client, &len);
if (fd < 0)
{
- memlogf("[ERROR] Accept failure");
+ memlogf("[ERROR] Client accept failure");
continue;
}
//memlogf("INFO: Client %s connected\n", inet_ntoa(client.sin_addr));
- setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, (char *) &timeout, sizeof(timeout));
- setsockopt(fd, SOL_SOCKET, SO_SNDTIMEO, (char *) &timeout, sizeof(timeout));
+ sock_set_timeout(fd, SOCKET_TIMEOUT_SERVER_MS);
dnbd3_client_t *dnbd3_client = g_new0(dnbd3_client_t, 1);
if (dnbd3_client == NULL)
@@ -257,12 +270,26 @@ int main(int argc, char *argv[])
close(fd);
continue;
}
- // TODO: Extend this if you ever want to add IPv6 (something like:)
- // dnbd3_client->host.type = AF_INET6;
- // memcpy(dnbd3_client->host.addr, &(client.sin6_addr), 16);
- dnbd3_client->host.type = AF_INET;
- memcpy(dnbd3_client->host.addr, &(client.sin_addr), 4);
- dnbd3_client->host.port = client.sin_port;
+ if (client.ss_family == AF_INET) {
+ struct sockaddr_in *v4 = (struct sockaddr_in *)&client;
+ dnbd3_client->host.type = AF_INET;
+ memcpy(dnbd3_client->host.addr, &(v4->sin_addr), 4);
+ dnbd3_client->host.port = v4->sin_port;
+ }
+ else if (client.ss_family == AF_INET6)
+ {
+ struct sockaddr_in6 *v6 = (struct sockaddr_in6 *)&client;
+ dnbd3_client->host.type = AF_INET6;
+ memcpy(dnbd3_client->host.addr, &(v6->sin6_addr), 16);
+ dnbd3_client->host.port = v6->sin6_port;
+ }
+ else
+ {
+ memlogf("[ERROR] New client has unknown address family %d, disconnecting...", (int)client.ss_family);
+ close(fd);
+ g_free(dnbd3_client);
+ continue;
+ }
dnbd3_client->sock = fd;
dnbd3_client->image = NULL;
diff --git a/src/server/sockhelper.c b/src/server/sockhelper.c
index c72c74f..4c7c45b 100644
--- a/src/server/sockhelper.c
+++ b/src/server/sockhelper.c
@@ -1,6 +1,11 @@
#include "sockhelper.h"
+#include "memlog.h"
#include <string.h>
#include <stdio.h>
+#include <glib/gmacros.h>
+#include <unistd.h>
+#include <errno.h>
+#include <fcntl.h>
static inline int connect_shared(const int client_sock, void* addr, const int addrlen, int connect_ms, int rw_ms)
{
@@ -10,8 +15,10 @@ static inline int connect_shared(const int client_sock, void* addr, const int ad
tv.tv_usec = connect_ms * 1000;
setsockopt(client_sock, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv));
setsockopt(client_sock, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv));
- if (connect(client_sock, (struct sockaddr *)&addr, addrlen) == -1)
+ if (connect(client_sock, (struct sockaddr *)addr, addrlen) == -1)
{
+ //int e = errno;
+ //printf("connect -1 (%d)\n", e);
return -1;
}
// Apply read/write timeout
@@ -31,9 +38,14 @@ int sock_connect4(struct sockaddr_in *addr, const int connect_ms, const int rw_m
int sock_connect6(struct sockaddr_in6 *addr, const int connect_ms, const int rw_ms)
{
+#ifdef WITH_IPV6
int client_sock = socket(PF_INET6, SOCK_STREAM, IPPROTO_TCP);
if (client_sock == -1) return -1;
return connect_shared(client_sock, addr, sizeof(struct sockaddr_in6), connect_ms, rw_ms);
+#else
+ printf("[DEBUG] Not compiled with IPv6 support.\n");
+ return -1;
+#endif
}
int sock_connect(const dnbd3_host_t * const addr, const int connect_ms, const int rw_ms)
@@ -44,21 +56,162 @@ int sock_connect(const dnbd3_host_t * const addr, const int connect_ms, const in
struct sockaddr_in addr4;
memset(&addr4, 0, sizeof(addr4));
addr4.sin_family = AF_INET;
- memcpy(&addr4.sin_addr.s_addr, addr->addr, 4);
+ memcpy(&addr4.sin_addr, addr->addr, 4);
addr4.sin_port = addr->port;
return sock_connect4(&addr4, connect_ms, rw_ms);
}
+#ifdef WITH_IPV6
else if (addr->type == AF_INET6)
{
// Set host (IPv6)
struct sockaddr_in6 addr6;
memset(&addr6, 0, sizeof(addr6));
addr6.sin6_family = AF_INET6;
- memcpy(&addr6.sin6_addr.s6_addr, addr->addr, 16);
+ memcpy(&addr6.sin6_addr, addr->addr, 16);
addr6.sin6_port = addr->port;
return sock_connect6(&addr6, connect_ms, rw_ms);
}
+#endif
printf("[DEBUG] Unsupported address type: %d\n", (int)addr->type);
return -1;
}
+void sock_set_timeout(const int sockfd, const int milliseconds)
+{
+ struct timeval tv;
+ tv.tv_sec = milliseconds / 1000;
+ tv.tv_usec = milliseconds * 1000;
+ setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv));
+ setsockopt(sockfd, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv));
+}
+
+int sock_listen_any(int protocol_family, uint16_t port)
+{
+ struct sockaddr_storage addr;
+ memset(&addr, 0, sizeof(addr));
+ if (protocol_family == PF_INET)
+ {
+ struct sockaddr_in *v4 = (struct sockaddr_in *)&addr;
+ v4->sin_addr.s_addr = INADDR_ANY;
+ v4->sin_port = htons(port);
+ v4->sin_family = AF_INET;
+ }
+#ifdef WITH_IPV6
+ else if (protocol_family == PF_INET6)
+ {
+ struct sockaddr_in6 *v6 = (struct sockaddr_in6 *)&addr;
+ v6->sin6_addr = in6addr_any;
+ v6->sin6_port = htons(port);
+ v6->sin6_family = AF_INET6;
+ }
+#endif
+ else
+ {
+ printf("[DEBUG] sock_listen: Unsupported protocol: %d\n", protocol_family);
+ return -1;
+ }
+ return sock_listen(&addr, sizeof(addr));
+}
+
+int sock_listen(struct sockaddr_storage *addr, int addrlen)
+{
+ int pf; // On Linux AF_* == PF_*, but this is not guaranteed on all platforms, so let's be safe here:
+ if (addr->ss_family == AF_INET)
+ pf = PF_INET;
+#ifdef WITH_IPV6
+ else if (addr->ss_family == AF_INET6)
+ pf = PF_INET6;
+#endif
+ else
+ {
+ printf("[DEBUG] sock_listen: unsupported address type: %d\n", (int)addr->ss_family);
+ return -1;
+ }
+ int sock;
+
+ // Create socket
+ sock = socket(pf, SOCK_STREAM, IPPROTO_TCP);
+ if (sock < 0)
+ {
+ memlogf("[ERROR] sock_listen: Socket setup failure"); // TODO: print port number to help troubleshooting
+ return -1;
+ }
+ const int on = 1;
+ setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
+ if (pf == PF_INET6)
+ setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, &on, sizeof(on));
+
+ // Bind to socket
+ if (bind(sock, (struct sockaddr *) addr, addrlen) < 0)
+ {
+ int e = errno;
+ close(sock);
+ memlogf("[ERROR] Bind failure (%d)", e); // TODO: print port number to help troubleshooting
+ return -1;
+ }
+
+ // Listen on socket
+ if (listen(sock, 20) == -1)
+ {
+ close(sock);
+ memlogf("[ERROR] Listen failure"); // TODO ...
+ return -1;
+ }
+
+ return sock;
+}
+
+int accept_any(const int * const sockets, const int socket_count, struct sockaddr_storage *addr, socklen_t *length_ptr)
+{
+ fd_set set;
+ FD_ZERO(&set);
+ int max = 0;
+ for (int i = 0; i < socket_count; ++i)
+ {
+ FD_SET(sockets[i], &set);
+ if (sockets[i] > max)
+ max = sockets[i];
+ }
+ if (select(max + 1, &set, NULL, NULL, NULL) <= 0) return -1;
+ for (int i = 0; i < socket_count; ++i)
+ {
+ if (FD_ISSET(sockets[i], &set))
+ return accept(sockets[i], (struct sockaddr *)addr, length_ptr);
+ }
+ return -1;
+}
+
+void sock_set_nonblock(int sock)
+{
+ int flags = fcntl(sock, F_GETFL, 0);
+ if (flags == -1)
+ flags = 0;
+ fcntl(sock, F_SETFL, flags | O_NONBLOCK);
+}
+
+void sock_set_block(int sock)
+{
+ int flags = fcntl(sock, F_GETFL, 0);
+ if (flags == -1)
+ flags = 0;
+ fcntl(sock, F_SETFL, flags & ~(int)O_NONBLOCK);
+}
+
+int sock_add_array(const int sock, int *array, int *array_fill, const int array_length)
+{
+ if (sock == -1)
+ return TRUE;
+ for (int i = 0; i < *array_fill; ++i)
+ {
+ if (array[i] == -1)
+ {
+ array[i] = sock;
+ return TRUE;
+ }
+ }
+ if (*array_fill >= array_length)
+ return FALSE;
+ array[*array_fill] = sock;
+ (*array_fill) += 1;
+ return TRUE;
+}
diff --git a/src/server/sockhelper.h b/src/server/sockhelper.h
index 28525c4..2cbcda1 100644
--- a/src/server/sockhelper.h
+++ b/src/server/sockhelper.h
@@ -4,8 +4,8 @@
#include <stdint.h>
#include "../types.h"
#include <sys/socket.h>
-#include <netinet/in.h>
#include <arpa/inet.h>
+#include <netinet/in.h>
int sock_connect4(struct sockaddr_in *addr, const int connect_ms, const int rw_ms);
@@ -20,4 +20,52 @@ int sock_connect6(struct sockaddr_in6 *addr, const int connect_ms, const int rw_
*/
int sock_connect(const dnbd3_host_t * const addr, const int connect_ms, const int rw_ms);
+void sock_set_timeout(const int sockfd, const int milliseconds);
+
+/**
+ * Listen on all interfaces/available IP addresses, using the given protocol.
+ * IPv4 and IPv6 are supported.
+ * @param protocol_family PF_INET or PF_INET6
+ * @param port port to listen on
+ * @return the socket descriptor if successful, -1 otherwise.
+ */
+int sock_listen_any(int protocol_family, uint16_t port);
+
+/**
+ * Listen on a specific address and port.
+ * @param addr pointer to a properly filled sockaddr_in or sockaddr_in6
+ * @param addrlen length of the passed struct
+ */
+int sock_listen(struct sockaddr_storage *addr, int addrlen);
+
+/**
+ * This is a multi-socket version of accept. Pass in an array of listening sockets.
+ * If any of the sockets has an incoming connection, accept it and return the new socket's fd.
+ * On error, return -1, just like accept().
+ * @param sockets array of listening socket fds
+ * @param socket_count number of sockets in that array
+ * @return fd of new client socket, -1 on error
+ */
+int accept_any(const int * const sockets, const int socket_count, struct sockaddr_storage *addr, socklen_t *length_ptr);
+
+void sock_set_nonblock(int sock);
+
+void sock_set_block(int sock);
+
+/**
+ * Add given socket to array. Take an existing empty slot ( == -1) if available,
+ * append to end otherwise. Updates socket count variable passed by reference.
+ * The passed socket fd is only added if it is != -1 for convenience, so you can
+ * directly pass the return value of sock_listen or sock_create, without checking the
+ * return value first.
+ * @param sock socket fd to add
+ * @param array the array of socket fds to add the socket to
+ * @param array_fill pointer to int telling how many sockets there are in the array. Empty slots
+ * in between are counted too. In other words: represents the index of the last valid socket fd in the
+ * array plus one, or 0 if there are none.
+ * @param array_length the capacity of the array
+ * @return TRUE on success or if the passed fd was -1, FALSE iff the array is already full
+ */
+int sock_add_array(const int sock, int *array, int *array_fill, const int array_length);
+
#endif /* SOCKHELPER_H_ */