IPV6_V6ONLY setsockopt added for the udp server.
#include <unistd.h> // Per les crides a fitxer
#include <stdio.h> // Per l'I/O de stdin/stdout
#include <errno.h> // Pels errors de les crides a fitxer
#include <string.h> // Per strerror()
#include <stdlib.h> // Per abort()
#include <fcntl.h> // Per fcntl()
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdbool.h>
#include <signal.h>
#include <assert.h> // Per assert()
#include <sys/select.h> // Pel select()
#include "syslog.h"
/* Prototypes */
static void listen_tcp(const int port, const bool ipv6enabled);
static int listen_tcp_ipv6(int port);
static int listen_tcp_ipv4(int port);
static int accept_connection(int socket);
static int sendlog(const int socket);
/* Globals */
static int tcp_server_pid = 0;
/* Code */
/* Returns 0 on success. Returns -1 on error. */
int init_tcp_server()
{
int pid;
int port;
char service[MAX_STRING];
enum {
SUCCESS=0,
ERROR=-1,
DISABLED=-2
} ret;
/* Get the configuration */
get_config(TCP_MANAGER, service, MAX_STRING);
if (strncmp(service, "disabled", MAX_STRING) == 0)
return DISABLED;
/* Check the configuration */
port = atoi(service);
if (port < -65535 || port > 65535)
return ERROR;
/* Create the server process */
pid = child_fork();
if (pid == 0)
{
program_child_handler(SIG_DFL);
/* Child */
close(0);
close(1);
//close(2);
if(port < 0)
{
listen_tcp(-port, true);
} else
{
listen_tcp(port, false);
}
/* Should be Not reachable */
exit(0);
} else if (pid > 0)
{
/* Parent */
tcp_server_pid = pid;
fprintf(stderr, "Child forked (tcp server on port %i): %i\n",
port, pid);
ret = SUCCESS;
} else
{
ret = ERROR;
}
return ret;
}
static void listen_tcp(const int port, const bool ipv6enabled)
{
int socket_ipv6, socket_ipv4;
fd_set listen_sockets;
int result;
int high_socket;
socket_ipv4 = listen_tcp_ipv4(port);
if (ipv6enabled)
socket_ipv6 = listen_tcp_ipv6(port);
/* Mirem quin és el socket més alt pel select() */
high_socket = socket_ipv4;
if (ipv6enabled && (high_socket < socket_ipv6) )
high_socket = socket_ipv6;
high_socket += 1;
result = 0;
while (result >= 0)
{
/* Establim els FDs que volem esperar al select() */
FD_ZERO(&listen_sockets);
if (ipv6enabled)
FD_SET(socket_ipv6, &listen_sockets);
FD_SET(socket_ipv4, &listen_sockets);
result = select(high_socket, &listen_sockets, NULL, NULL,
NULL);
if (result == 0)
{
/* Això no hauria de passar, no tenim timeout fixat. */
continue;
}
else if (result == -1 && errno != EINTR)
{
fprintf(stderr, "Error in select(): %s\n",
strerror(errno));
abort();
}
/* Algun FD té dades... */
if (FD_ISSET(socket_ipv4, &listen_sockets))
{
accept_connection(socket_ipv4);
}
if (ipv6enabled && FD_ISSET(socket_ipv6, &listen_sockets))
{
accept_connection(socket_ipv6);
}
}
}
static int listen_tcp_ipv6(int port)
{
int socket_ipv6;
struct sockaddr_in6 source_ipv6;
int result;
int on=1;
socket_ipv6 = socket(PF_INET6, SOCK_STREAM, 0);
if (socket_ipv6 == -1)
{
fprintf(stderr, "IPv6 socket failed.\n");
abort();
}
/* Fem que poguem reutilitzar l'adreça encara que no s'hagi acabat
el timeout després de tancar-se la connexió */
if (setsockopt(socket_ipv6, SOL_SOCKET, SO_REUSEADDR,
(char *)&on,sizeof(on)) < 0)
{
fprintf(stderr, "IPv6 setsockopt() failed for SO_REUSEADDR: %s.\n",
strerror(errno));
abort();
}
/* Es necessita per a que linux no fagi Bind del port IPv4 alhora */
if (setsockopt(socket_ipv6, IPPROTO_IPV6, IPV6_V6ONLY,
(char *)&on,sizeof(on)) < 0)
{
fprintf(stderr, "IPv6 setsockopt() failed for IPV6_V6ONLY: %s.\n",
strerror(errno));
abort();
}
/* No volem que aquest socket bloquegi. */
if (fcntl(socket_ipv6, F_SETFL, O_NONBLOCK) == -1)
{
fprintf(stderr, "IPv6 fcntl() failed: %s.\n",
strerror(errno));
abort();
}
/* IPv6 listen address */
memset(&source_ipv6, 0, sizeof(source_ipv6));
source_ipv6.sin6_family = AF_INET6;
source_ipv6.sin6_flowinfo = 0;
source_ipv6.sin6_port = htons(port);
source_ipv6.sin6_addr = in6addr_any;
result = bind(socket_ipv6, (struct sockaddr *) &source_ipv6,
sizeof(source_ipv6));
assert(result == 0);
result = listen(socket_ipv6, TCP_LISTEN_QUEUE);
if (result != 0)
{
fprintf(stderr,"Error in listen() ipv6: %s\n", strerror(errno));
abort();
}
return socket_ipv6;
}
static int listen_tcp_ipv4(int port)
{
int socket_ipv4;
struct sockaddr_in source_ipv4;
int result;
int on=1;
socket_ipv4 = socket(PF_INET, SOCK_STREAM, 0);
if (socket_ipv4 == -1)
{
fprintf(stderr, "IPv4 socket not supported.\n");
abort();
}
/* Fem que poguem reutilitzar l'adreça encara que no s'hagi acabat
el timeout després de tancar-se la connexió */
if (setsockopt(socket_ipv4, SOL_SOCKET, SO_REUSEADDR,
(char *)&on,sizeof(on)) < 0)
{
fprintf(stderr, "IPv4 setsockopt() failed: %s.\n",
strerror(errno));
abort();
}
/* No volem que aquest socket bloquegi. */
if (fcntl(socket_ipv4, F_SETFL, O_NONBLOCK) == -1)
{
fprintf(stderr, "IPv6 fcntl() failed: %s.\n",
strerror(errno));
abort();
}
/* IPv4 listen address */
memset(&source_ipv4, 0, sizeof(source_ipv4));
source_ipv4.sin_family = AF_INET;
source_ipv4.sin_port = htons(port);
source_ipv4.sin_addr.s_addr = htonl(INADDR_ANY);
result = bind(socket_ipv4, (struct sockaddr *) &source_ipv4,
sizeof(source_ipv4));
assert(result == 0);
result = listen(socket_ipv4, TCP_LISTEN_QUEUE);
if (result != 0)
{
fprintf(stderr,"Error in listen() ipv4: %s\n", strerror(errno));
abort();
}
return socket_ipv4;
}
static int accept_connection(int socket)
{
int newsock, pid;
newsock = accept(socket, NULL, NULL);
if (newsock == -1)
return 0;
/* We don't use child_fork(), as it's only for the _main parent_ */
pid = fork();
if (pid == 0)
{
/* Child */
close(socket);
sendlog(newsock);
close(newsock);
exit(0);
/* Unreachable */
} else if (pid > 0)
{
/* Parent */
close(newsock);
}
else
return -1;
return 0;
}
/* Return -2 when the socket is not enabled. Otherwise, the output of close().*/
void close_out_tcp()
{
if (tcp_server_pid != 0)
{
fprintf(stderr,"close_out_tcp: Sending SIGTERM to %i\n", tcp_server_pid);
kill(tcp_server_pid, SIGTERM);
tcp_server_pid = 0;
}
return;
}
static int sendlog(const int socket)
{
int fh;
int bytes, res;
enum config
{
BUFFER_SIZE=500
};
char buffer[BUFFER_SIZE];
char *buffer2;
char name[MAX_STRING];
if (get_config(LOG_FILE, name, MAX_STRING) < 0)
{
fprintf(stderr, "The tcp_server received a connection, but"
" there isn't any log file.");
return -1;
}
if (strncmp(name, "disabled", MAX_STRING) == 0)
{
fprintf(stderr, "The tcp_server received a connection, but"
" there isn't any log file.");
return -1;
}
fh = open(name, O_RDONLY);
if (fh == -1)
{
fprintf(stderr, "Error opening the log file for read: %s\n",
strerror(errno));
return -1;
}
while((bytes = read(fh, buffer, BUFFER_SIZE)) > 0)
{
buffer2 = buffer;
while(bytes > 0)
{
res = send(socket, buffer2, bytes, 0);
if (res == 0)
sleep(1);
else if (res < 0)
{
close(fh);
return -1;
}
bytes = bytes - res;
buffer2 = buffer2 + res;
}
}
close(fh);
return bytes;
}