/* __ _ * ____ / /_ ____ ___ __ ___________ _(_) * / __ \/ __ \/ __ `__ \/ / / / ___/ __ `/ / * / /_/ / / / / / / / / / /_/ / /__/ /_/ / / * \____/_/ /_/_/ /_/ /_/\__, /\___/\__, /_/ * /____/ /____/ * * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (c) 2021, Max Christian Pohle * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * {{{ DISCLAIMER * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * }}} */ #include "main.h" static int read_everything(FILE * f_r, FILE * output) { const int read_buffer_length = READ_BUFFER_LENGTH; char read_buffer[read_buffer_length]; for(size_t size = -2 ; ; size = fread(read_buffer, 1, read_buffer_length, f_r)) { if(-2 == size || (-1 == size && EWOULDBLOCK == errno)) { usleep(EWOULDBLOCK_DELAY); // try again a little later continue; } fwrite(read_buffer, 1, size, output); if (read_buffer_length > POST_DATA_MAX_LENGTH) return EXIT_FAILURE; if (size < read_buffer_length) { // I expect one nmemb of data break; } } fflush(output); return EXIT_SUCCESS; } static void * answer_request(size_t new_socket) { FILE * f_r = fdopen((size_t) new_socket, "r"); char * output_buffer = NULL; size_t output_buffer_length = 0; FILE * output = open_memstream(&output_buffer, &output_buffer_length); read_everything(f_r, output); // TODO: catch return value and error handling fflush(output); // shutdown(new_socket, SHUT_RD); // shutdown the reading half of the connection DEBUG("output buffer has a length of %ld bytes\n", output_buffer_length); // TODO: make parsing function abstract (e.g. parse(...) function point with dlsym) parse_http(new_socket, output_buffer, output_buffer_length); fclose(f_r); fclose(output); free(output_buffer); return NULL; } int become_daemon(const char * name) { # ifndef DEBUG // debugging is simpler in foreground mode if(fork()) { exit(EXIT_SUCCESS); } else { setsid(); if(fork()) { exit(EXIT_SUCCESS); } else { syslog(0, "daemon '%s' is running with pid %d", name, getpid()); } } # endif return EXIT_SUCCESS; } static int serve(int server_fd) { struct sockaddr_in address; socklen_t address_len = sizeof(address); DEBUG("> Waiting for connections on server file descriptor %d\n", server_fd); size_t new_socket = -1; while(-1 != (new_socket = accept(server_fd, (struct sockaddr*) &address, &address_len))) { DEBUG("> Client %ld is connected via port %d\n", new_socket, address.sin_port); // set non blocking mode... fcntl(new_socket, F_SETFL, fcntl(new_socket, F_GETFL) | O_NONBLOCK ); answer_request(new_socket); #ifdef VALGRIND break; // only run once, so that valgrind can test allocations&frees #endif } err(errno, "error serving"); close(server_fd); } int main(const int argc, char const * argv[]) { int server_fd = -1; int port = atoi(argc > 1 ? argv[1] : "8080"); 0 == port ? port = 8080 : port; struct sockaddr_in address = { .sin_family = AF_INET, .sin_addr.s_addr = INADDR_ANY, .sin_port = htons(port) }; // I <3 C 0 == (server_fd = socket(AF_INET, SOCK_STREAM, 0)) ? err(errno, NULL) : bind(server_fd, (struct sockaddr*) &address, sizeof(address)) ? err(errno, NULL) : listen(server_fd, SOMAXCONN) ? err(errno, NULL) : become_daemon(argv[0]) ? err(errno, NULL) : serve(server_fd) ? err(errno, NULL) : exit(EXIT_SUCCESS) ; return EXIT_FAILURE; } // modeline for vim: shiftwidth=2 tabstop=2 number foldmethod=marker