Add IPv6 support for listening sockets

This commit is contained in:
Grégory Soutadé 2016-02-17 18:15:04 +01:00
parent 3370e83f55
commit 2456039df1
2 changed files with 132 additions and 48 deletions

View File

@ -1,9 +1,9 @@
BIN_DIR=../bin
SRCS = ip_to_geo.c test.c cmdline.c server.c
TARGET = $(BIN_DIR)/ip_to_geo
CFLAGS = -Wall
LDFLAGS= -lpthread
BIN_DIR =../bin
SRCS = ip_to_geo.c test.c cmdline.c server.c
TARGET = $(BIN_DIR)/ip_to_geo
CFLAGS = -Wall
LDFLAGS = -lpthread
ifneq ($(DEBUG),)
CFLAGS += -ggdb -O0

View File

@ -25,6 +25,7 @@
#include <sys/stat.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <netdb.h>
#include <time.h>
#include <syslog.h>
#include <signal.h>
@ -46,6 +47,8 @@
#define WAIT_TIME 100
#define MAX_WAIT_TIME 500
#define MAX_LISTENING_SOCKETS 16
typedef struct {
int socket;
time_t timeout; // in µs
@ -69,14 +72,19 @@ typedef struct thread_ctx_s{
static pthread_mutex_t s_fastmutex = PTHREAD_MUTEX_INITIALIZER;
static thread_ctx_t* s_last_thread = NULL;
static int s_server_socket = -1;
static int s_server_sockets[MAX_LISTENING_SOCKETS];
static int s_nb_server_sockets = 0;
static int s_stop = 0;
void sigint(int sig)
{
int i;
syslog(LOG_WARNING, "signal received, stopping threads");
s_stop = 1;
shutdown(s_server_socket, SHUT_RDWR);
for (i=0; i<s_nb_server_sockets; i++)
shutdown(s_server_sockets[i], SHUT_RDWR);
}
static int check_request(request_t* req)
@ -400,53 +408,90 @@ static void fill_new_socket(struct gengetopt_args_info* params, int socket)
int daemonize(struct gengetopt_args_info* params)
{
int ret;
struct sockaddr_in sockaddr;
int ret, i;
struct sockaddr_in6 sockaddr;
socklen_t sockaddr_len;
int new_socket;
void* thread_ret;
// Should have both ipv4 & ipv6
s_server_socket = socket(AF_INET, SOCK_STREAM, 0); // Should have both TCP & UDP
struct addrinfo hints;
struct addrinfo *result, *rp;
char buffer[64];
int on;
struct pollfd pollfds[MAX_LISTENING_SOCKETS];
if (!s_server_socket)
memset(&hints, 0, sizeof(hints));
hints.ai_family = AF_UNSPEC; /* Allow IPv4 or IPv6 */
hints.ai_socktype = SOCK_STREAM; /* Datagram socket */
hints.ai_flags = (params->bind_ip_given)?0:AI_PASSIVE; /* For wildcard IP address */
hints.ai_protocol = 0; /* Any protocol */
snprintf(buffer, sizeof(buffer), "%d", params->port_arg);
ret = getaddrinfo(params->bind_ip_arg, buffer, &hints, &result);
if (ret)
{
if (!params->quiet_flag)
fprintf(stderr, "Unable to create socket (%m)\n");
fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(ret));
return -1;
}
memset(&sockaddr, 0, sizeof(sockaddr));
sockaddr.sin_family = AF_INET; // Should detect interface type (v4 or v6)
sockaddr.sin_port = htons(params->port_arg);
if (params->bind_ip_given)
for (rp=result; rp && s_nb_server_sockets < MAX_LISTENING_SOCKETS;
rp=rp->ai_next, s_nb_server_sockets++)
{
ret = inet_aton(params->bind_ip_arg, &sockaddr.sin_addr);
new_socket = socket(rp->ai_family,
rp->ai_socktype,
rp->ai_protocol); // Should have both TCP & UDP
if (!new_socket)
{
if (!params->quiet_flag)
fprintf(stderr, "Unable to create socket (%m)\n");
return -1;
}
s_server_sockets[s_nb_server_sockets] = new_socket;
pollfds[s_nb_server_sockets].fd = new_socket;
pollfds[s_nb_server_sockets].events = POLLIN|POLL_ERR_MASK;
on=1; setsockopt(new_socket, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
if (rp->ai_family == AF_INET6)
{
on=1; setsockopt(new_socket, IPPROTO_IPV6, IPV6_V6ONLY, &on, sizeof(on));
}
memset(&sockaddr, 0, sizeof(sockaddr));
sockaddr.sin6_family = rp->ai_family; // Should detect interface type (v4 or v6)
sockaddr.sin6_port = htons(params->port_arg);
if (params->bind_ip_given)
{
ret = inet_pton(rp->ai_family, params->bind_ip_arg, &sockaddr.sin6_addr);
if (ret)
{
if (!params->quiet_flag)
fprintf(stderr, "Error with bind address %s (%m)\n", params->bind_ip_arg);
return -1;
}
}
else
sockaddr.sin6_addr = in6addr_any;
ret = bind(new_socket, (struct sockaddr *)&sockaddr, sizeof(sockaddr));
if (ret)
{
if (!params->quiet_flag)
fprintf(stderr, "Error with bind address %s (%m)\n", params->bind_ip_arg);
return -1;
fprintf(stderr, "Unable to bind (%m)\n");
return -2;
}
ret = listen(new_socket, 0);
if (ret)
{
if (!params->quiet_flag)
fprintf(stderr, "Unable to listen (%m)\n");
return -3;
}
}
else
sockaddr.sin_addr.s_addr = INADDR_ANY;
ret = bind(s_server_socket, (struct sockaddr *)&sockaddr, sizeof(sockaddr));
if (ret)
{
if (!params->quiet_flag)
fprintf(stderr, "Unable to bind (%m)\n");
return -2;
}
ret = listen(s_server_socket, 0);
if (ret)
{
if (!params->quiet_flag)
fprintf(stderr, "Unable to listen (%m)\n");
return -3;
}
if (rp)
fprintf(stderr, "Warning, max listening sockets reached !!\n");
if (!params->no_background_flag)
{
@ -497,6 +542,7 @@ int daemonize(struct gengetopt_args_info* params)
seccomp_rule_add(seccomp_ctx, SCMP_ACT_ALLOW, SCMP_SYS(fstat), 0);
seccomp_rule_add(seccomp_ctx, SCMP_ACT_ALLOW, SCMP_SYS(access), 0);
seccomp_rule_add(seccomp_ctx, SCMP_ACT_ALLOW, SCMP_SYS(exit), 0);
seccomp_rule_add(seccomp_ctx, SCMP_ACT_ALLOW, SCMP_SYS(exit_group), 0);
seccomp_rule_add(seccomp_ctx, SCMP_ACT_ALLOW, SCMP_SYS(set_robust_list), 0);
seccomp_rule_add(seccomp_ctx, SCMP_ACT_ALLOW, SCMP_SYS(madvise), 0);
@ -513,21 +559,59 @@ int daemonize(struct gengetopt_args_info* params)
while (!s_stop)
{
sockaddr_len = sizeof(sockaddr);
new_socket = accept(s_server_socket, (struct sockaddr *) &sockaddr, &sockaddr_len);
if (new_socket < 0)
ret = poll(pollfds, s_nb_server_sockets, 1000);
if (ret < 0)
{
if (!s_stop)
syslog(LOG_ERR, "accept error (%m), exiting");
syslog(LOG_ERR, "main poll err %d", ret);
break;
}
if (!params->quiet_flag)
syslog(LOG_INFO, "new connection from %s, socket %d",
inet_ntoa(sockaddr.sin_addr), new_socket);
fill_new_socket(params, new_socket);
if (ret == 0) continue; // timeout
for (i=0; i<s_nb_server_sockets; i++)
{
if (pollfds[i].revents & POLL_ERR_MASK)
{
if (!s_stop)
syslog(LOG_ERR, "Error with main socket %d (%m)", s_server_sockets[i]);
close (s_server_sockets[i]);
continue;
}
if (!(pollfds[i].revents & POLLIN)) continue;
sockaddr_len = sizeof(sockaddr);
new_socket = accept(s_server_sockets[i], (struct sockaddr *) &sockaddr, &sockaddr_len);
if (new_socket < 0)
{
if (!s_stop)
syslog(LOG_ERR, "accept error (%m), exiting");
goto end;
}
if (!params->quiet_flag)
{
switch(sockaddr.sin6_family)
{
case AF_INET:
inet_ntop(AF_INET, &((struct sockaddr_in *)&sockaddr)->sin_addr, buffer, sizeof(buffer));
break;
case AF_INET6:
inet_ntop(AF_INET6, &sockaddr.sin6_addr, buffer, sizeof(buffer));
break;
}
/* inet_ntop(sockaddr.sin6_family, &sockaddr.sin6_addr, buffer, sockaddr_len); */
syslog(LOG_INFO, "new connection from %s, socket %d", buffer, new_socket);
}
fill_new_socket(params, new_socket);
}
}
close(s_server_socket);
end:
for (i=0; i<s_nb_server_sockets; i++)
close(s_server_sockets[i]);
while (s_last_thread)
{