/* qotdserver Copyright (C) 2019 Richard Knight This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include #include #include #include #include #include #include #include #include #include #include #include "qotdserver.h" #include "logger.h" const char *port = "17"; struct workerArgs { int socket; }; void *accept_clients(void *args); void *service_single_client(void *args); int serve() { pthread_t server_thread; sigset_t new; sigemptyset (&new); sigaddset(&new, SIGPIPE); if (pthread_sigmask(SIG_BLOCK, &new, NULL) != 0) { write_log("Unable to mask SIGPIPE"); raise(SIGTERM); } if (pthread_create(&server_thread, NULL, accept_clients, NULL) < 0) { write_log("Could not create server thread"); raise(SIGTERM); } pthread_join(server_thread, NULL); pthread_exit(NULL); } void *accept_clients(void *args) { int serverSocket; int clientSocket; pthread_t worker_thread; struct addrinfo hints, *res, *p; struct sockaddr_storage *clientAddr; socklen_t sinSize = sizeof(struct sockaddr_storage); struct workerArgs *wa; int yes = 1; memset(&hints, 0, sizeof hints); hints.ai_family = AF_UNSPEC; hints.ai_socktype = SOCK_STREAM; hints.ai_flags = AI_PASSIVE; if (getaddrinfo(NULL, port, &hints, &res) != 0) { write_log("getaddrinfo() failed"); pthread_exit(NULL); } for(p = res;p != NULL; p = p->ai_next) { if ((serverSocket = socket(p->ai_family, p->ai_socktype, p->ai_protocol)) == -1) { write_log("Could not open socket"); continue; } if (setsockopt(serverSocket, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int)) == -1) { write_log("Socket setsockopt() failed"); close(serverSocket); continue; } if (bind(serverSocket, p->ai_addr, p->ai_addrlen) == -1) { write_log("Socket bind() failed"); close(serverSocket); continue; } if (listen(serverSocket, 5) == -1) { write_log("Socket listen() failed"); close(serverSocket); continue; } break; } freeaddrinfo(res); if (p == NULL) { write_log("Could not find a socket to bind to"); pthread_exit(NULL); } /* Loop and wait for connections */ while (1) { clientAddr = malloc(sinSize); if ((clientSocket = accept(serverSocket, (struct sockaddr *) clientAddr, &sinSize)) == -1) { free(clientAddr); write_log("Could not accept() connection"); continue; } wa = malloc(sizeof(struct workerArgs)); wa->socket = clientSocket; if (pthread_create(&worker_thread, NULL, service_single_client, wa) != 0) { write_log("Could not create a worker thread"); free(clientAddr); free(wa); close(clientSocket); close(serverSocket); pthread_exit(NULL); } } pthread_exit(NULL); } void *service_single_client(void *args) { struct workerArgs *wa; int socket, nbytes; wa = (struct workerArgs*) args; socket = wa->socket; pthread_detach(pthread_self()); // log connection struct sockaddr_in addr; socklen_t addr_size = sizeof(struct sockaddr_in); int res = getpeername(socket, (struct sockaddr *)&addr, &addr_size); char log_string[20 + INET_ADDRSTRLEN] = "Connection from "; strcat(log_string, inet_ntoa(addr.sin_addr)); // send char *tosend = get_quote(&config); nbytes = send(socket, tosend, strlen(tosend), 0); close(socket); free(wa); // write log write_log(log_string); pthread_exit(NULL); }