Fix coding style in server code
This commit is contained in:
parent
5c790813df
commit
227a9f7b47
@ -1,3 +1,2 @@
|
|||||||
all:
|
all:
|
||||||
gcc -g -Wall -o server server.c
|
gcc -g -Wall -o server server.c
|
||||||
|
|
||||||
|
@ -1,8 +1,22 @@
|
|||||||
// TODO: Put these values into a config file
|
// TODO: Put these values into a config file
|
||||||
#define PORT "2884" // Port number to listen on. It must be a string!
|
|
||||||
#define DROP_AFTER 10 // Drop clients who don't send any packets in this many seconds
|
// Port number to listen on. It must be a string!
|
||||||
#define CURRENT_LOG_LEVEL LOG_LEVEL_DEBUG // Logging level. See LOG_LEVEL_* defines in server.h
|
#define PORT "2884"
|
||||||
#define LOGFILE "auth.log" // Place of the log file
|
|
||||||
#define CLIENT_CONNECT_SCRIPT "/usr/local/sbin/ip_allow" // Script to run when a client connects
|
// Drop clients who don't send any packets in this many seconds
|
||||||
#define CLIENT_DISCONNECT_SCRIPT "/usr/local/sbin/ip_block" // Script to run when a client disconnects
|
#define DROP_AFTER 10
|
||||||
|
|
||||||
|
// Logging level. See LOG_LEVEL_* defines in server.h
|
||||||
|
#define CURRENT_LOG_LEVEL LOG_LEVEL_DEBUG
|
||||||
|
|
||||||
|
// Place of the log file
|
||||||
|
#define LOGFILE "auth.log"
|
||||||
|
|
||||||
|
// Script to run when a client connects
|
||||||
|
#define CLIENT_CONNECT_SCRIPT "/usr/local/sbin/ip_allow"
|
||||||
|
|
||||||
|
// Script to run when a client disconnects
|
||||||
|
#define CLIENT_DISCONNECT_SCRIPT "/usr/local/sbin/ip_block"
|
||||||
|
|
||||||
|
// Number of concurrent incoming (non-accepted) connections
|
||||||
#define BACKLOG 10
|
#define BACKLOG 10
|
||||||
|
732
server/server.c
732
server/server.c
@ -29,471 +29,527 @@ fd_set master;
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* log_message(level, format, ...)
|
* log_message(level, format, ...)
|
||||||
* Puts a message in the log file. If level is less than CURRENT_LOG_LEVEL (less means more importance), the message gets logged.
|
*
|
||||||
|
* Puts a message in the log file. If level is less than
|
||||||
|
* CURRENT_LOG_LEVEL (less means more importance), the message gets
|
||||||
|
* logged.
|
||||||
|
*
|
||||||
* The log message should not end with a newline character.
|
* The log message should not end with a newline character.
|
||||||
*/
|
*/
|
||||||
static void
|
static void
|
||||||
log_message(int level, const char *format, ...)
|
log_message(int level, const char *format, ...)
|
||||||
{
|
{
|
||||||
if (CURRENT_LOG_LEVEL >= level)
|
if (CURRENT_LOG_LEVEL >= level) {
|
||||||
{
|
va_list ap;
|
||||||
va_list ap;
|
char *message;
|
||||||
char *message;
|
char date[100];
|
||||||
char date[100];
|
time_t t;
|
||||||
time_t t;
|
struct tm *tmp;
|
||||||
struct tm *tmp;
|
|
||||||
|
|
||||||
t = time(NULL);
|
t = time(NULL);
|
||||||
tmp = localtime(&t);
|
tmp = localtime(&t);
|
||||||
|
|
||||||
/* This should never happen */
|
/* This should never happen */
|
||||||
if (tmp == NULL)
|
if (tmp == NULL) {
|
||||||
{
|
// Cannot log the error here, as we caught error in the
|
||||||
// Cannot log the error here, as we caught error in the logger. Simply exit with an error status.
|
// logger. Simply exit with an error status.
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Start handling the variable-length parameters
|
// Start handling the variable-length parameters
|
||||||
va_start(ap, format);
|
va_start(ap, format);
|
||||||
/* XXX: This is a bit unportable, maybe it should be changed to something else. */
|
/* TODO: This is a bit unportable, maybe it should be changed
|
||||||
// Create the formatted message string
|
* to something else. */
|
||||||
vasprintf(&message, format, ap);
|
// Create the formatted message string
|
||||||
// End handling the variable-length parameters
|
vasprintf(&message, format, ap);
|
||||||
va_end(ap);
|
// End handling the variable-length parameters
|
||||||
|
va_end(ap);
|
||||||
|
|
||||||
// Zero-fill date's placeholder
|
// Zero-fill date's placeholder
|
||||||
memset(&date, 0, 100);
|
memset(&date, 0, 100);
|
||||||
// Put the current time stamp into date
|
// Put the current time stamp into date
|
||||||
strftime((char *)&date, 100, "%Y-%m-%d %H:%M:%S", tmp);
|
strftime((char *)&date, 100, "%Y-%m-%d %H:%M:%S", tmp);
|
||||||
|
|
||||||
// Put the timestamp and the message into the logfile
|
// Put the timestamp and the message into the logfile
|
||||||
fprintf(log_fd, "[%s] %s\n", (char *)&date, message);
|
fprintf(log_fd, "[%s] %s\n", (char *)&date, message);
|
||||||
fflush(log_fd);
|
fflush(log_fd);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* sigchld_handler(signal)
|
* sigchld_handler(signal)
|
||||||
* This is the signal handler for the SIGCHLD signal. It gets called every time a child finishes its work (even with failure)
|
*
|
||||||
|
* This is the signal handler for the SIGCHLD signal. It gets called
|
||||||
|
* every time a child finishes its work (even with failure)
|
||||||
*/
|
*/
|
||||||
void
|
void
|
||||||
sigchld_handler(int s)
|
sigchld_handler(int s)
|
||||||
{
|
{
|
||||||
// Log a debug message about the finished child
|
// Log a debug message about the finished child
|
||||||
log_message(LOG_LEVEL_DEBUG, "Found a hung child.");
|
log_message(LOG_LEVEL_DEBUG, "Found a hung child.");
|
||||||
// Clean up all the dead children (otherwise they turn into zombie processes)
|
// Clean up all the dead children (otherwise they turn into zombie
|
||||||
while (waitpid(-1, NULL, WNOHANG) > 0);
|
// processes)
|
||||||
|
while (waitpid(-1, NULL, WNOHANG) > 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* sigterm_handler(signal)
|
* sigterm_handler(signal)
|
||||||
* This is the signal handler for the SIGTERM signal. It gets called if someone kills the process with SIGTERM.
|
*
|
||||||
|
* This is the signal handler for the SIGTERM signal. It gets called
|
||||||
|
* if someone kills the process with SIGTERM.
|
||||||
*/
|
*/
|
||||||
void
|
void
|
||||||
sigterm_handler(int s)
|
sigterm_handler(int s)
|
||||||
{
|
{
|
||||||
// Log this event as an information (it's not a real error, and shouldn't be a debug-only message)
|
// Log this event as an information (it's not a real error, and
|
||||||
log_message(LOG_LEVEL_INFO, "Got SIGTERM, shutting down.");
|
// shouldn't be a debug-only message)
|
||||||
// TODO: clean shutdown! (Close client sockets, etc.)
|
log_message(LOG_LEVEL_INFO, "Got SIGTERM, shutting down.");
|
||||||
// Exit after the shutdown.
|
// TODO: clean shutdown! (Close client sockets, etc.)
|
||||||
exit(1);
|
// Exit after the shutdown.
|
||||||
|
exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* execute(command, parameter)
|
* execute(command, parameter)
|
||||||
|
*
|
||||||
* Fork and execute the given program with exactly one parameter
|
* Fork and execute the given program with exactly one parameter
|
||||||
*/
|
*/
|
||||||
void
|
void
|
||||||
execute(char *command, char *parameter)
|
execute(char *command, char *parameter)
|
||||||
{
|
{
|
||||||
pid_t pid;
|
pid_t pid;
|
||||||
|
|
||||||
/* Do the fork() */
|
/* Do the fork() */
|
||||||
pid = fork();
|
pid = fork();
|
||||||
|
|
||||||
if (pid == 0)
|
if (pid == 0) {
|
||||||
{
|
// If fork() returns zero, we are the child
|
||||||
// If fork() returns zero, we are the child
|
// Try to execute the script. This will give control to the
|
||||||
// Try to execute the script. This will give control to the executed script. exec() returns only when an error occurs (e.g the command cannot be found).
|
// executed script. exec() returns only when an error occurs
|
||||||
log_message(LOG_LEVEL_DEBUG, "Executing '%s \"%s\"'...", command, parameter);
|
// (e.g the command cannot be found).
|
||||||
execl(command, command, parameter, (char *)NULL);
|
log_message(LOG_LEVEL_DEBUG,
|
||||||
// If we get here, we got an error, which should be logged
|
"Executing '%s \"%s\"'...",
|
||||||
log_message(LOG_LEVEL_ERROR, "execl: %s", strerror(errno));
|
command, parameter);
|
||||||
// Close the log file
|
execl(command, command, parameter, (char *)NULL);
|
||||||
fclose(log_fd);
|
// If we get here, we got an error, which should be logged
|
||||||
// TODO: Do a clean shutdown
|
log_message(LOG_LEVEL_ERROR, "execl: %s", strerror(errno));
|
||||||
exit(1);
|
// Close the log file
|
||||||
}
|
fclose(log_fd);
|
||||||
|
// TODO: Do a clean shutdown
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* client_new(socket, remote_addr)
|
* client_new(socket, remote_addr)
|
||||||
|
*
|
||||||
* Create a new client structure with the given data, and fully reset timer
|
* Create a new client structure with the given data, and fully reset timer
|
||||||
*/
|
*/
|
||||||
void
|
void
|
||||||
client_new(int socket, struct sockaddr_in *remote_addr)
|
client_new(int socket, struct sockaddr_in *remote_addr)
|
||||||
{
|
{
|
||||||
client_t *client_data;
|
client_t *client_data;
|
||||||
client_t *temp;
|
client_t *temp;
|
||||||
char *tmp_addr;
|
char *tmp_addr;
|
||||||
|
|
||||||
// Allocate memory for the new client's data
|
// Allocate memory for the new client's data
|
||||||
client_data = malloc(sizeof(client_t));
|
client_data = malloc(sizeof(client_t));
|
||||||
if (client_data == NULL)
|
if (client_data == NULL) {
|
||||||
{
|
// Log an error message if allocation fails and exit with failure code
|
||||||
// Log an error message if allocation fails and exit with failure code
|
log_message(LOG_LEVEL_ERROR, "malloc: %s", strerror(errno));
|
||||||
log_message(LOG_LEVEL_ERROR, "malloc: %s", strerror(errno));
|
exit(1);
|
||||||
exit(1);
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// Allocate memory for the new client's IP address
|
// Allocate memory for the new client's IP address
|
||||||
tmp_addr = malloc(16);
|
tmp_addr = malloc(16);
|
||||||
if (tmp_addr == NULL)
|
if (tmp_addr == NULL) {
|
||||||
{
|
// Log an error message if allocation fails and exit with
|
||||||
// Log an error message if allocation fails and exit with failure code. Here we also free the previously allocated client_data
|
// failure code. Here we also free the previously allocated
|
||||||
log_message(LOG_LEVEL_ERROR, "malloc: %s", strerror(errno));
|
// client_data
|
||||||
free(client_data);
|
log_message(LOG_LEVEL_ERROR, "malloc: %s", strerror(errno));
|
||||||
exit(1);
|
free(client_data);
|
||||||
}
|
exit(1);
|
||||||
// Zero-fill the address location, so the string will be surely nul-terminated
|
}
|
||||||
memset(tmp_addr, 0, 16);
|
|
||||||
// Get the numeric hostname (IP address) of the remote side
|
|
||||||
getnameinfo((const struct sockaddr *)remote_addr, sizeof(struct sockaddr_in), (char *)tmp_addr, 15, NULL, 0, NI_NUMERICHOST);
|
|
||||||
|
|
||||||
// Log the connection
|
// Zero-fill the address location, so the string will be surely
|
||||||
log_message(LOG_LEVEL_INFO, "New connection: %d (IP: %s)", socket, tmp_addr);
|
// nul-terminated
|
||||||
|
memset(tmp_addr, 0, 16);
|
||||||
|
// Get the numeric hostname (IP address) of the remote side
|
||||||
|
getnameinfo((const struct sockaddr *)remote_addr,
|
||||||
|
sizeof(struct sockaddr_in),
|
||||||
|
(char *)tmp_addr,
|
||||||
|
15, NULL, 0, NI_NUMERICHOST);
|
||||||
|
|
||||||
// Fill the client_data struct
|
// Log the connection
|
||||||
client_data->socket = socket;
|
log_message(LOG_LEVEL_INFO,
|
||||||
client_data->ip = tmp_addr;
|
"New connection: %d (IP: %s)",
|
||||||
client_data->last_reset = time(NULL);
|
socket, tmp_addr);
|
||||||
client_data->previous = NULL;
|
|
||||||
client_data->next = NULL;
|
|
||||||
|
|
||||||
// If the client list is empty (this is the first client)
|
// Fill the client_data struct
|
||||||
if (!client_list)
|
client_data->socket = socket;
|
||||||
{
|
client_data->ip = tmp_addr;
|
||||||
// The client_list should point to the newly allocated struct
|
client_data->last_reset = time(NULL);
|
||||||
client_list = client_data;
|
client_data->previous = NULL;
|
||||||
}
|
client_data->next = NULL;
|
||||||
// Otherwise (this is not the first client
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// Set temp to the last element of the client list
|
|
||||||
for (temp = client_list; temp->next; temp = temp->next);
|
|
||||||
// Set the last element's next pointer to point to the newly allocated struct
|
|
||||||
temp->next = client_data;
|
|
||||||
// Set the newly allocated struct's previous pointer to the (till here) last client's struct
|
|
||||||
client_data->previous = temp;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Execute the connect script
|
// If the client list is empty (this is the first client)
|
||||||
execute(CLIENT_CONNECT_SCRIPT, client_data->ip);
|
if (!client_list) {
|
||||||
|
// The client_list should point to the newly allocated struct
|
||||||
|
client_list = client_data;
|
||||||
|
}
|
||||||
|
// Otherwise (this is not the first client
|
||||||
|
else {
|
||||||
|
// Set temp to the last element of the client list
|
||||||
|
for (temp = client_list; temp->next; temp = temp->next);
|
||||||
|
// Set the last element's next pointer to point to the newly
|
||||||
|
// allocated struct
|
||||||
|
temp->next = client_data;
|
||||||
|
// Set the newly allocated struct's previous pointer to the
|
||||||
|
// (till here) last client's struct
|
||||||
|
client_data->previous = temp;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Execute the connect script
|
||||||
|
execute(CLIENT_CONNECT_SCRIPT, client_data->ip);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* client_remove(socket)
|
* client_remove(socket)
|
||||||
|
*
|
||||||
* Remove a client identified by its local socket number
|
* Remove a client identified by its local socket number
|
||||||
*/
|
*/
|
||||||
void
|
void
|
||||||
client_remove(int socket)
|
client_remove(int socket)
|
||||||
{
|
{
|
||||||
client_t *temp;
|
client_t *temp;
|
||||||
|
|
||||||
// If we don't have an allocated client_list, we simply return, as we won't find the named client. However, this should never happen
|
// If we don't have an allocated client_list, we simply return, as
|
||||||
if (!client_list)
|
// we won't find the named client. However, this should never
|
||||||
return;
|
// happen
|
||||||
|
if (!client_list) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Walk through the client list
|
// Walk through the client list
|
||||||
for (temp = client_list; temp; temp = temp->next)
|
for (temp = client_list; temp; temp = temp->next) {
|
||||||
{
|
// If this element's socket number is the one we are looking for
|
||||||
// If this element's socket number is the one we are looking for
|
if (temp->socket == socket) {
|
||||||
if (temp->socket == socket)
|
// Logging a message about the disconnection
|
||||||
{
|
log_message(LOG_LEVEL_INFO,
|
||||||
// Logging a message about the disconnection
|
"Connection lost: %d (IP: %s)",
|
||||||
log_message(LOG_LEVEL_INFO, "Connection lost: %d (IP: %s)", temp->socket, temp->ip);
|
temp->socket, temp->ip);
|
||||||
// Remove this client from the linked list
|
// Remove this client from the linked list
|
||||||
if (temp->previous)
|
if (temp->previous) {
|
||||||
temp->previous->next = temp->next;
|
temp->previous->next = temp->next;
|
||||||
if (temp->next)
|
}
|
||||||
temp->next->previous = temp->previous;
|
|
||||||
|
|
||||||
// If this is the first client, set the client_list to point to the second item (or to NULL if this is also the last client)
|
if (temp->next) {
|
||||||
if (temp == client_list)
|
temp->next->previous = temp->previous;
|
||||||
client_list = temp->next;
|
}
|
||||||
|
|
||||||
// If we don't have a previous, nor a next client in the list, then this was the last client, so client_list should be NULL
|
// If this is the first client, set the client_list to
|
||||||
// This is only a paranoia check, this should already happen in the previous instruction
|
// point to the second item (or to NULL if this is also
|
||||||
if ((!temp->previous) && (!temp->next))
|
// the last client)
|
||||||
client_list = NULL;
|
if (temp == client_list) {
|
||||||
|
client_list = temp->next;
|
||||||
|
}
|
||||||
|
|
||||||
// Execute the disconnect script
|
// If we don't have a previous, nor a next client in the
|
||||||
execute(CLIENT_DISCONNECT_SCRIPT, temp->ip);
|
// list, then this was the last client, so client_list
|
||||||
|
// should be NULL This is only a paranoia check, this
|
||||||
|
// should already happen in the previous instruction
|
||||||
|
if ((!temp->previous) && (!temp->next)) {
|
||||||
|
client_list = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
// Free the IP address' memory
|
// Execute the disconnect script
|
||||||
free(temp->ip);
|
execute(CLIENT_DISCONNECT_SCRIPT, temp->ip);
|
||||||
// Free the whols struct's memory
|
|
||||||
free(temp);
|
|
||||||
|
|
||||||
// Close the socket itself
|
// Free the IP address' memory
|
||||||
close(socket);
|
free(temp->ip);
|
||||||
// Remove this socket from the watched sockets' list
|
// Free the whols struct's memory
|
||||||
FD_CLR(socket, &master);
|
free(temp);
|
||||||
}
|
|
||||||
}
|
// Close the socket itself
|
||||||
|
close(socket);
|
||||||
|
// Remove this socket from the watched sockets' list
|
||||||
|
FD_CLR(socket, &master);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* client_reset_timer(socket)
|
* client_reset_timer(socket)
|
||||||
|
*
|
||||||
* Reset a client's timer, identified by the local socket number
|
* Reset a client's timer, identified by the local socket number
|
||||||
*/
|
*/
|
||||||
void
|
void
|
||||||
client_reset_timer(int socket)
|
client_reset_timer(int socket)
|
||||||
{
|
{
|
||||||
client_t *temp;
|
client_t *temp;
|
||||||
|
|
||||||
// Walk through the client list
|
// Walk through the client list
|
||||||
for (temp = client_list; temp; temp = temp->next)
|
for (temp = client_list; temp; temp = temp->next) {
|
||||||
// If the current client is the one we are looking for
|
// If the current client is the one we are looking for
|
||||||
if (temp->socket == socket)
|
if (temp->socket == socket) {
|
||||||
// Set its last reset time to the current timestamp
|
// Set its last reset time to the current timestamp
|
||||||
temp->last_reset = time(NULL);
|
temp->last_reset = time(NULL);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* check_timers()
|
* check_timers()
|
||||||
* Check all clients if they have sent data in the near past, and disconnect (thus, deauthenticate) them if not
|
*
|
||||||
|
* Check all clients if they have sent data in the near past, and
|
||||||
|
* disconnect (thus, deauthenticate) them if not
|
||||||
*/
|
*/
|
||||||
void
|
void
|
||||||
check_timers(void)
|
check_timers(void)
|
||||||
{
|
{
|
||||||
client_t *temp;
|
client_t *temp;
|
||||||
|
|
||||||
// Walk through the client list
|
// Walk through the client list
|
||||||
for (temp = client_list; temp; temp = temp->next)
|
for (temp = client_list; temp; temp = temp->next) {
|
||||||
// If this client hasn't sent data in DROP_AFTER seconds
|
// If this client hasn't sent data in DROP_AFTER seconds
|
||||||
if (time(NULL) - temp->last_reset > DROP_AFTER)
|
if (time(NULL) - temp->last_reset > DROP_AFTER) {
|
||||||
{
|
// Log the timeout event
|
||||||
// Log the timeout event
|
log_message(LOG_LEVEL_INFO,
|
||||||
log_message(LOG_LEVEL_INFO, "Client timeout, dropping connection %d (IP: %s).", temp->socket, temp->ip);
|
"Client timeout, dropping connection %d (IP: %s).",
|
||||||
// And remove the client from the client list
|
temp->socket, temp->ip);
|
||||||
client_remove(temp->socket);
|
// And remove the client from the client list
|
||||||
}
|
client_remove(temp->socket);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* main()
|
* main()
|
||||||
|
*
|
||||||
* The main function of the server program
|
* The main function of the server program
|
||||||
*/
|
*/
|
||||||
int
|
int
|
||||||
main(void)
|
main(void)
|
||||||
{
|
{
|
||||||
int sock_listen;
|
int sock_listen;
|
||||||
struct addrinfo hints;
|
struct addrinfo hints;
|
||||||
struct addrinfo *servinfo;
|
struct addrinfo *servinfo;
|
||||||
struct addrinfo *p;
|
struct addrinfo *p;
|
||||||
int yes = 1;
|
int yes = 1;
|
||||||
int rv;
|
int rv;
|
||||||
fd_set read_fds;
|
fd_set read_fds;
|
||||||
int fdmax;
|
int fdmax;
|
||||||
struct sigaction sa;
|
struct sigaction sa;
|
||||||
|
|
||||||
// Initially set the client list to empty
|
// Initially set the client list to empty
|
||||||
client_list = NULL;
|
client_list = NULL;
|
||||||
|
|
||||||
// Set the SIGCHLD handler (which will purge zombie children)
|
// Set the SIGCHLD handler (which will purge zombie children)
|
||||||
sa.sa_handler = sigchld_handler;
|
sa.sa_handler = sigchld_handler;
|
||||||
sigemptyset(&sa.sa_mask);
|
sigemptyset(&sa.sa_mask);
|
||||||
sa.sa_flags = SA_RESTART;
|
sa.sa_flags = SA_RESTART;
|
||||||
if (sigaction(SIGCHLD, &sa, NULL) < 0)
|
if (sigaction(SIGCHLD, &sa, NULL) < 0) {
|
||||||
{
|
perror("sigaction");
|
||||||
perror("sigaction");
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set the SIGTERM handler
|
return 1;
|
||||||
sa.sa_handler = sigterm_handler;
|
}
|
||||||
sigemptyset(&sa.sa_mask);
|
|
||||||
sa.sa_flags = SA_RESTART;
|
|
||||||
if (sigaction(SIGTERM, &sa, NULL) < 0)
|
|
||||||
{
|
|
||||||
perror("sigaction");
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set the hints to "any"
|
// Set the SIGTERM handler
|
||||||
memset (&hints, 0, sizeof hints);
|
sa.sa_handler = sigterm_handler;
|
||||||
hints.ai_family = AF_UNSPEC;
|
sigemptyset(&sa.sa_mask);
|
||||||
hints.ai_socktype = SOCK_STREAM;
|
sa.sa_flags = SA_RESTART;
|
||||||
hints.ai_flags = AI_PASSIVE; // use my IP
|
if (sigaction(SIGTERM, &sa, NULL) < 0) {
|
||||||
|
perror("sigaction");
|
||||||
|
|
||||||
// Check if our port number is already in use
|
return 1;
|
||||||
if ((rv = getaddrinfo (NULL, PORT, &hints, &servinfo)) != 0)
|
}
|
||||||
{
|
|
||||||
fprintf(stderr, "getaddrinfo: %s", gai_strerror(rv));
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (p = servinfo; p != NULL; p = p->ai_next)
|
// Set the hints to "any"
|
||||||
{
|
memset (&hints, 0, sizeof hints);
|
||||||
if ((sock_listen = socket (p->ai_family, p->ai_socktype,
|
hints.ai_family = AF_UNSPEC;
|
||||||
p->ai_protocol)) == -1)
|
hints.ai_socktype = SOCK_STREAM;
|
||||||
{
|
hints.ai_flags = AI_PASSIVE; // use my IP
|
||||||
perror("socket");
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (setsockopt (sock_listen, SOL_SOCKET, SO_REUSEADDR, &yes,
|
// Check if our port number is already in use
|
||||||
sizeof (int)) == -1)
|
if ((rv = getaddrinfo (NULL, PORT, &hints, &servinfo)) != 0) {
|
||||||
{
|
fprintf(stderr, "getaddrinfo: %s", gai_strerror(rv));
|
||||||
perror("setsockopt");
|
|
||||||
exit (1);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (bind (sock_listen, p->ai_addr, p->ai_addrlen) == -1)
|
return 1;
|
||||||
{
|
}
|
||||||
close(sock_listen);
|
|
||||||
perror("bind");
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
for (p = servinfo; p != NULL; p = p->ai_next) {
|
||||||
}
|
if ((sock_listen = socket (p->ai_family, p->ai_socktype,
|
||||||
|
p->ai_protocol)) == -1) {
|
||||||
|
perror("socket");
|
||||||
|
|
||||||
if (p == NULL)
|
continue;
|
||||||
{
|
}
|
||||||
perror("bind");
|
|
||||||
return 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
freeaddrinfo(servinfo);
|
if (setsockopt (sock_listen, SOL_SOCKET, SO_REUSEADDR, &yes,
|
||||||
|
sizeof (int)) == -1) {
|
||||||
|
perror("setsockopt");
|
||||||
|
|
||||||
// Start listening on the listener socket
|
exit (1);
|
||||||
if (listen(sock_listen, BACKLOG) == -1)
|
}
|
||||||
{
|
|
||||||
perror("listen");
|
|
||||||
exit (1);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Clear the list of the watched sockets
|
if (bind (sock_listen, p->ai_addr, p->ai_addrlen) == -1) {
|
||||||
FD_ZERO(&master);
|
close(sock_listen);
|
||||||
FD_ZERO(&read_fds);
|
perror("bind");
|
||||||
|
|
||||||
// Add the listener to the watched sockets
|
continue;
|
||||||
FD_SET(sock_listen, &master);
|
}
|
||||||
fdmax = sock_listen;
|
|
||||||
|
|
||||||
// Try to open (or create) the log file
|
break;
|
||||||
if ((log_fd = fopen(LOGFILE, "a")) == NULL)
|
}
|
||||||
{
|
|
||||||
perror("fopen");
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Try to go into the background
|
if (p == NULL) {
|
||||||
if (daemon(0, 0) < 0)
|
perror("bind");
|
||||||
{
|
|
||||||
perror("daemon");
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
log_message(LOG_LEVEL_INFO, "Started.");
|
return 2;
|
||||||
|
}
|
||||||
|
|
||||||
while (1)
|
freeaddrinfo(servinfo);
|
||||||
{
|
|
||||||
struct timeval tv;
|
|
||||||
int t;
|
|
||||||
|
|
||||||
// Reset the read_fds with the full list of watched sockets
|
// Start listening on the listener socket
|
||||||
read_fds = master;
|
if (listen(sock_listen, BACKLOG) == -1) {
|
||||||
|
perror("listen");
|
||||||
|
|
||||||
// Set the timeout value
|
exit (1);
|
||||||
tv.tv_sec = 1;
|
}
|
||||||
tv.tv_usec = 0;
|
|
||||||
|
|
||||||
// Wait for incoming connection or incoming data. "Modified" sockets (ones with incoming connections or incoming data) will be put in read_fds
|
// Clear the list of the watched sockets
|
||||||
t = select(fdmax + 1, &read_fds, NULL, NULL, &tv);
|
FD_ZERO(&master);
|
||||||
|
FD_ZERO(&read_fds);
|
||||||
|
|
||||||
// If select() returns a negative, it means an error
|
// Add the listener to the watched sockets
|
||||||
if (t < 0)
|
FD_SET(sock_listen, &master);
|
||||||
{
|
fdmax = sock_listen;
|
||||||
// However, if select() was only interrupted by a signal, simply continue
|
|
||||||
if (errno == EINTR)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
// Otherwise log an error and exit
|
// Try to open (or create) the log file
|
||||||
log_message(LOG_LEVEL_ERROR, "select: %s", strerror(errno));
|
if ((log_fd = fopen(LOGFILE, "a")) == NULL) {
|
||||||
|
perror("fopen");
|
||||||
|
|
||||||
return 1;
|
exit(1);
|
||||||
}
|
}
|
||||||
// If select() returns a positive, it means that there are incoming connections or incoming data
|
|
||||||
else if (t != 0)
|
|
||||||
{
|
|
||||||
int sock;
|
|
||||||
|
|
||||||
// Walk through the watched sockets (the lazy way, later this can cause some microseconds of hang up, as not all sockets are really monitored in this set [0..fdmax])
|
// Try to go into the background
|
||||||
for (sock = 0; sock <= fdmax; sock++)
|
if (daemon(0, 0) < 0) {
|
||||||
{
|
perror("daemon");
|
||||||
// If the current socket exists in read_fds (it has data to read)
|
|
||||||
if (FD_ISSET(sock, &read_fds))
|
|
||||||
{
|
|
||||||
// If the socket we found is the listener
|
|
||||||
if (sock == sock_listen)
|
|
||||||
{
|
|
||||||
int new_socket;
|
|
||||||
struct sockaddr_in remote_addr;
|
|
||||||
socklen_t addrlen = sizeof(struct sockaddr_in);
|
|
||||||
|
|
||||||
// Accept the new connection
|
exit(1);
|
||||||
new_socket = accept(sock_listen, (struct sockaddr *)&remote_addr, &addrlen);
|
}
|
||||||
|
|
||||||
// Add the new connection to the watched sockets
|
log_message(LOG_LEVEL_INFO, "Started.");
|
||||||
FD_SET(new_socket, &master);
|
|
||||||
if (new_socket > fdmax)
|
|
||||||
fdmax = new_socket;
|
|
||||||
|
|
||||||
// Create a new client entry for the new connection
|
while (1) {
|
||||||
client_new(new_socket, &remote_addr);
|
struct timeval tv;
|
||||||
}
|
int t;
|
||||||
else
|
|
||||||
{
|
|
||||||
// Otherwise it's an already existing socket which has data to read
|
|
||||||
size_t read_len;
|
|
||||||
char buf[128];
|
|
||||||
|
|
||||||
// Read the data from the socket (in 128-bytes chunks)
|
// Reset the read_fds with the full list of watched sockets
|
||||||
read_len = recv(sock, (char *)&buf, 128, 0);
|
read_fds = master;
|
||||||
|
|
||||||
// If recv() returns a negative value, this means an error, so we should remove this client
|
// Set the timeout value
|
||||||
if (read_len < 0)
|
tv.tv_sec = 1;
|
||||||
{
|
tv.tv_usec = 0;
|
||||||
// In this case we also log an error
|
|
||||||
log_message(LOG_LEVEL_ERROR, "recv: %s", strerror(errno));
|
|
||||||
client_remove(sock);
|
|
||||||
}
|
|
||||||
// Otherwise if recv() returns 0, we just simply remove the client (0 means the client already closed the connection)
|
|
||||||
else if (read_len == 0)
|
|
||||||
{
|
|
||||||
client_remove(sock);
|
|
||||||
}
|
|
||||||
// If recv() returns a positive, the client sent some data, which will be discarded, but the timer of the client is reset
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// Log a debugging message about the reset timer
|
|
||||||
log_message(LOG_LEVEL_DEBUG, "Connection timer reset: %d", sock);
|
|
||||||
client_reset_timer(sock);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// After we checked all the sockets, or there was no sockets to check (select() returned 0), we go and check if any clients timed out
|
// Wait for incoming connection or incoming data. "Modified"
|
||||||
check_timers();
|
// sockets (ones with incoming connections or incoming data)
|
||||||
}
|
// will be put in read_fds
|
||||||
|
t = select(fdmax + 1, &read_fds, NULL, NULL, &tv);
|
||||||
|
|
||||||
fclose(log_fd);
|
// If select() returns a negative, it means an error
|
||||||
|
if (t < 0) {
|
||||||
|
// However, if select() was only interrupted by a signal,
|
||||||
|
// simply continue
|
||||||
|
if (errno == EINTR) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
return 0;
|
// Otherwise log an error and exit
|
||||||
|
log_message(LOG_LEVEL_ERROR, "select: %s", strerror(errno));
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If select() returns a positive, it means that there are
|
||||||
|
// incoming connections or incoming data
|
||||||
|
else if (t != 0) {
|
||||||
|
int sock;
|
||||||
|
|
||||||
|
// Walk through the watched sockets (the lazy way, later
|
||||||
|
// this can cause some microseconds of hang up, as not all
|
||||||
|
// sockets are really monitored in this set [0..fdmax])
|
||||||
|
for (sock = 0; sock <= fdmax; sock++) {
|
||||||
|
// If the current socket exists in read_fds (it has
|
||||||
|
// data to read)
|
||||||
|
if (FD_ISSET(sock, &read_fds)) {
|
||||||
|
// If the socket we found is the listener
|
||||||
|
if (sock == sock_listen) {
|
||||||
|
int new_socket;
|
||||||
|
struct sockaddr_in remote_addr;
|
||||||
|
socklen_t addrlen = sizeof(struct sockaddr_in);
|
||||||
|
|
||||||
|
// Accept the new connection
|
||||||
|
new_socket = accept(sock_listen,
|
||||||
|
(struct sockaddr *)&remote_addr,
|
||||||
|
&addrlen);
|
||||||
|
|
||||||
|
// Add the new connection to the watched sockets
|
||||||
|
FD_SET(new_socket, &master);
|
||||||
|
if (new_socket > fdmax) {
|
||||||
|
fdmax = new_socket;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a new client entry for the new connection
|
||||||
|
client_new(new_socket, &remote_addr);
|
||||||
|
} else {
|
||||||
|
// Otherwise it's an already existing socket
|
||||||
|
// which has data to read
|
||||||
|
size_t read_len;
|
||||||
|
char buf[128];
|
||||||
|
|
||||||
|
// Read the data from the socket (in 128-bytes chunks)
|
||||||
|
read_len = recv(sock, (char *)&buf, 128, 0);
|
||||||
|
|
||||||
|
// If recv() returns a negative value, this
|
||||||
|
// means an error, so we should remove this
|
||||||
|
// client
|
||||||
|
if (read_len < 0) {
|
||||||
|
// In this case we also log an error
|
||||||
|
log_message(LOG_LEVEL_ERROR,
|
||||||
|
"recv: %s",
|
||||||
|
strerror(errno));
|
||||||
|
client_remove(sock);
|
||||||
|
}
|
||||||
|
// Otherwise if recv() returns 0, we just
|
||||||
|
// simply remove the client (0 means the
|
||||||
|
// client already closed the connection)
|
||||||
|
else if (read_len == 0) {
|
||||||
|
client_remove(sock);
|
||||||
|
}
|
||||||
|
|
||||||
|
// If recv() returns a positive, the client
|
||||||
|
// sent some data, which will be discarded,
|
||||||
|
// but the timer of the client is reset
|
||||||
|
else {
|
||||||
|
// Log a debugging message about the reset timer
|
||||||
|
log_message(LOG_LEVEL_DEBUG,
|
||||||
|
"Connection timer reset: %d",
|
||||||
|
sock);
|
||||||
|
client_reset_timer(sock);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// After we checked all the sockets, or there was no sockets
|
||||||
|
// to check (select() returned 0), we go and check if any
|
||||||
|
// clients timed out
|
||||||
|
check_timers();
|
||||||
|
}
|
||||||
|
|
||||||
|
fclose(log_fd);
|
||||||
|
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -6,14 +6,14 @@
|
|||||||
#define LOG_LEVEL_INFO 1
|
#define LOG_LEVEL_INFO 1
|
||||||
#define LOG_LEVEL_DEBUG 2
|
#define LOG_LEVEL_DEBUG 2
|
||||||
|
|
||||||
/* The client_t struct. With this struct full client data can be stored in a doubly-linked list */
|
/* The client_t struct. With this struct full client data can be
|
||||||
|
* stored in a doubly-linked list */
|
||||||
typedef struct _client_t {
|
typedef struct _client_t {
|
||||||
int socket;
|
int socket;
|
||||||
char *ip;
|
char *ip;
|
||||||
time_t last_reset;
|
time_t last_reset;
|
||||||
struct _client_t *previous;
|
struct _client_t *previous;
|
||||||
struct _client_t *next;
|
struct _client_t *next;
|
||||||
} client_t;
|
} client_t;
|
||||||
|
|
||||||
#endif /* _AUTH_SERVER_H */
|
#endif /* _AUTH_SERVER_H */
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user