From 2d9e630a430e079ec30f674de1abbd7f4a186657 Mon Sep 17 00:00:00 2001 From: sr Date: Tue, 15 Jan 2013 17:57:27 +0100 Subject: [SERVER] Add IPv6 support (clients and RPC connections) --- CMakeLists.txt | 9 +-- src/config.h | 3 +- src/server/job.c | 14 +++-- src/server/net.c | 42 +------------ src/server/net.h | 2 - src/server/rpc.c | 150 +++++++++++++++++++++------------------------ src/server/saveload.c | 2 + src/server/server.c | 69 ++++++++++++++------- src/server/sockhelper.c | 159 +++++++++++++++++++++++++++++++++++++++++++++++- src/server/sockhelper.h | 50 ++++++++++++++- 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 #include #include -#include #include #include -#include -#include -#include +#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 #include #include -#include #include "sockhelper.h" #include @@ -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 #include -#include #include #include #include #include #include #include +#include #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 #include +#include +#include +#include +#include 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 #include "../types.h" #include -#include #include +#include 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_ */ -- cgit v1.2.3-55-g7522