From 4730c7946f7b6c9b204676b6f314b4c22ba0e09c Mon Sep 17 00:00:00 2001 From: silvioprog Date: Tue, 13 Feb 2024 12:18:56 -0300 Subject: [PATCH] Implement custom host name support --- CMakeLists.txt | 33 ------- Dockerfile | 7 +- doxygen/Doxyfile.in | 9 +- examples/example_httpsrv.c | 14 ++- include/sagui.h | 54 ++++++++++- src/CMakeLists.txt | 5 +- src/inet.h | 38 -------- src/inet_ntop.c | 194 ------------------------------------- src/sg_httpsrv.c | 87 ++++++++++++++--- src/sg_utils.c | 18 +--- test/test_httpsrv.c | 94 +++++++++++++++++- 11 files changed, 242 insertions(+), 311 deletions(-) delete mode 100644 src/inet.h delete mode 100644 src/inet_ntop.c diff --git a/CMakeLists.txt b/CMakeLists.txt index 94105e4b..9a36f8c4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -166,39 +166,6 @@ if(UNIX AND ((NOT APPLE) AND (NOT ANDROID))) endif() endif() -if(MINGW) - message(STATUS "Looking for inet_ntop") - set(_chk_inet_ntop_src "${CMAKE_BINARY_DIR}/check_inet_ntop.c") - file( - WRITE ${_chk_inet_ntop_src} - "#include \n#include \nint main(void){inet_ntop(0,NULL,NULL,0);return 0;}" - ) - try_compile(HAVE_INET_NTOP ${CMAKE_BINARY_DIR} ${_chk_inet_ntop_src} - LINK_LIBRARIES ws2_32) - file(REMOVE ${_chk_inet_ntop_src}) - unset(_chk_inet_ntop_src) - if(${HAVE_INET_NTOP}) - message(STATUS "Looking for inet_ntop - found") - else() - message(STATUS "Looking for inet_ntop - not found") - endif() -else() - include(CheckIncludeFile) - check_include_file(arpa/inet.h HAVE_ARPA_INET_H) - if(HAVE_ARPA_INET_H) - add_definitions(-DHAVE_ARPA_INET_H=1) - endif() - include(CheckFunctionExists) - check_function_exists(inet_ntop HAVE_INET_NTOP) - if(NOT HAVE_INET_NTOP) - include(CheckSymbolExists) - check_symbol_exists(inet_ntop "arpa/inet.h" HAVE_INET_NTOP) - endif() -endif() -if(${HAVE_INET_NTOP}) - add_definitions(-DHAVE_INET_NTOP=1) -endif() - include_directories(${SG_INCLUDE_DIR}) include_directories(${MHD_INCLUDE_DIR}) if(SG_HTTP_COMPRESSION) diff --git a/Dockerfile b/Dockerfile index 6625a84f..4cd23bf2 100644 --- a/Dockerfile +++ b/Dockerfile @@ -4,8 +4,8 @@ # SPDX-License-Identifier: MIT ###################################################################### -# podman build --platform linux/amd64 -t hello_sagui . -# podman run --platform linux/amd64 --rm -it -p 8080:8080 hello_sagui +# podman build -t hello_sagui . +# podman run --rm -p 8080:8080 -it hello_sagui FROM alpine:3.19.1 AS builder @@ -15,11 +15,8 @@ RUN apk add --no-cache \ automake \ clang \ cmake - WORKDIR /app - COPY . /app/ - RUN mkdir build && \ cd build/ && \ cmake -DBUILD_SHARED_LIBS=OFF .. && \ diff --git a/doxygen/Doxyfile.in b/doxygen/Doxyfile.in index 70d26b23..f0a70305 100644 --- a/doxygen/Doxyfile.in +++ b/doxygen/Doxyfile.in @@ -7,7 +7,7 @@ # # Cross-platform library which helps to develop web servers or frameworks. # -# Copyright (C) 2016-2021 Silvio Clecio +# Copyright (C) 2016-2024 Silvio Clecio # # Sagui library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public @@ -1163,13 +1163,6 @@ CLANG_DATABASE_PATH = ALPHABETICAL_INDEX = YES -# The COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns in -# which the alphabetical index list will be split. -# Minimum value: 1, maximum value: 20, default value: 5. -# This tag requires that the tag ALPHABETICAL_INDEX is set to YES. - -COLS_IN_ALPHA_INDEX = 5 - # In case all classes in a project start with a common prefix, all classes will # be put under the same header in the alphabetical index. The IGNORE_PREFIX tag # can be used to specify a prefix (or a list of prefixes) that should be ignored diff --git a/examples/example_httpsrv.c b/examples/example_httpsrv.c index eec77f7e..3c75b112 100644 --- a/examples/example_httpsrv.c +++ b/examples/example_httpsrv.c @@ -27,10 +27,18 @@ #include #include #include +#include +#include #include /* NOTE: Error checking has been omitted to make it clear. */ +static bool terminated = false; + +static void sig_handler(__SG_UNUSED int signum) { + terminated = true; +} + static void req_cb(__SG_UNUSED void *cls, __SG_UNUSED struct sg_httpreq *req, struct sg_httpres *res) { sg_httpres_send(res, @@ -46,6 +54,8 @@ int main(int argc, const char *argv[]) { printf("%s \n", argv[0]); return EXIT_FAILURE; } + signal(SIGTERM, sig_handler); + signal(SIGINT, sig_handler); port = strtol(argv[1], NULL, 10); srv = sg_httpsrv_new(req_cb, NULL); if (!sg_httpsrv_listen(srv, port, false)) { @@ -55,7 +65,9 @@ int main(int argc, const char *argv[]) { fprintf(stdout, "Server running at http://localhost:%d\n", sg_httpsrv_port(srv)); fflush(stdout); - getchar(); + while (!terminated) { + usleep(100 * 1000); + } sg_httpsrv_free(srv); return EXIT_SUCCESS; } diff --git a/include/sagui.h b/include/sagui.h index 294df1eb..d4ea88ad 100644 --- a/include/sagui.h +++ b/include/sagui.h @@ -73,8 +73,8 @@ extern "C" { #endif /* __SG_FORMAT */ #define SG_VERSION_MAJOR 3 -#define SG_VERSION_MINOR 4 -#define SG_VERSION_PATCH 4 +#define SG_VERSION_MINOR 5 +#define SG_VERSION_PATCH 0 #define SG_VERSION_HEX \ ((SG_VERSION_MAJOR << 16) | (SG_VERSION_MINOR << 8) | (SG_VERSION_PATCH)) @@ -1424,6 +1424,38 @@ SG_EXTERN void sg_httpsrv_free(struct sg_httpsrv *srv); #ifdef SG_HTTPS_SUPPORT +/** + * Starts the HTTPS server. + * \param[in] srv Server handle. + * \param[in] key Memory pointer for the private key (key.pem) to be used by + * the HTTPS server. + * \param[in] pwd Password for the private key. + * \param[in] cert Memory pointer for the certificate (cert.pem) to be used by + * the HTTPS server. + * \param[in] trust Memory pointer for the certificate (ca.pem) to be used by + * the HTTPS server for client authentication. + * \param[in] dhparams Memory pointer for the Diffie Hellman parameters (dh.pem) + * to be used by the HTTPS server for key exchange. + * \param[in] priorities Memory pointer specifying the cipher algorithm. + * Default: `"NORMAL"`. + * \param[in] hostname Host name for listening to connections. + * \param[in] port Port for listening to connections. + * \param[in] backlog Maximum length of the queue of pending connections. + * Default: `511`. + * \param[in] threaded Enables/disables the threaded mode. If `true`, the + * server creates one thread per connection. + * \retval true If the server is started, `false` otherwise. If \pr{srv} is + * null, set the `errno` to `EINVAL`. + * \note If port is `0`, the operating system will assign an unused port + * randomly. + */ +SG_EXTERN bool sg_httpsrv_tls_listen4(struct sg_httpsrv *srv, const char *key, + const char *pwd, const char *cert, + const char *trust, const char *dhparams, + const char *priorities, + const char *hostname, uint16_t port, + uint32_t backlog, bool threaded); + /** * Starts the HTTPS server. * \param[in] srv Server handle. @@ -1498,6 +1530,24 @@ SG_EXTERN bool sg_httpsrv_tls_listen(struct sg_httpsrv *srv, const char *key, #endif /* SG_HTTPS_SUPPORT */ +/** + * Starts the HTTP server. + * \param[in] srv Server handle. + * \param[in] hostname Host name for listening to connections. + * \param[in] port Port for listening to connections. + * \param[in] backlog Maximum length of the queue of pending connections. + * Default: `511`. + * \param[in] threaded Enables/disables the threaded mode. If `true`, the + * server creates one thread per connection. + * \retval true If the server is started, `false` otherwise. If \pr{srv} is + * null, set the `errno` to `EINVAL`. + * \note If port is `0`, the operating system will randomly assign an unused + * port. + */ +SG_EXTERN bool sg_httpsrv_listen2(struct sg_httpsrv *srv, const char *hostname, + uint16_t port, uint32_t backlog, + bool threaded); + /** * Starts the HTTP server. * \param[in] srv Server handle. diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 74413c97..ebfcea88 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -22,7 +22,7 @@ # # Cross-platform library which helps to develop web servers or frameworks. # -# Copyright (C) 2016-2020 Silvio Clecio +# Copyright (C) 2016-2024 Silvio Clecio # # Sagui library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public @@ -65,9 +65,6 @@ list( ${SG_SOURCE_DIR}/sg_httpreq.c ${SG_SOURCE_DIR}/sg_httpres.c ${SG_SOURCE_DIR}/sg_httpsrv.c) -if(NOT HAVE_INET_NTOP) - list(APPEND SG_C_SOURCE ${SG_SOURCE_DIR}/inet_ntop.c) -endif() if(SG_PATH_ROUTING) list(APPEND SG_C_SOURCE ${SG_SOURCE_DIR}/sg_entrypoint.c ${SG_SOURCE_DIR}/sg_entrypoints.c ${SG_SOURCE_DIR}/sg_routes.c diff --git a/src/inet.h b/src/inet.h deleted file mode 100644 index 42c332ae..00000000 --- a/src/inet.h +++ /dev/null @@ -1,38 +0,0 @@ -/* _ - * ___ __ _ __ _ _ _(_) - * / __|/ _` |/ _` | | | | | - * \__ \ (_| | (_| | |_| | | - * |___/\__,_|\__, |\__,_|_| - * |___/ - * - * Cross-platform library which helps to develop web servers or frameworks. - * - * Copyright (C) 2016-2020 Silvio Clecio - * - * Sagui library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * Sagui library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with Sagui library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifndef INET_H -#define INET_H - -#ifdef _WIN32 -#include -#else /* _WIN32 */ -#include -#endif /* _WIN32 */ - -const char *inet_ntop(int af, const void *src, char *dst, socklen_t size); - -#endif /* INET_H */ diff --git a/src/inet_ntop.c b/src/inet_ntop.c deleted file mode 100644 index 9a7a47ea..00000000 --- a/src/inet_ntop.c +++ /dev/null @@ -1,194 +0,0 @@ -/* - * Copyright (c) 1996-1999 by Internet Software Consortium. - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS - * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE - * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL - * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR - * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS - * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS - * SOFTWARE. - */ - -/* - * Original code by Paul Vixie, available at glibc sources: - * https://sourceware.org/git/?p=glibc.git;a=blob_plain;f=resolv/inet_ntop.c - * - * "Saguified" by Silvio Clecio. - */ - -#include -#include -#include -#ifdef _WIN32 -#include -#else -#include -#include -#endif -#include "inet.h" - -#define NS_INT16SZ 2 -#define NS_IN6ADDRSZ 16 - -#ifdef SPRINTF_CHAR -# define SPRINTF(x) strlen(sprintf/**/x) -#else -# define SPRINTF(x) ((size_t)sprintf x) -#endif - -/* - * WARNING: Don't even consider trying to compile this on a system where - * sizeof(int) < 4. sizeof(int) > 4 is fine; all the world's not a VAX. - */ - -static const char *inet_ntop4 (const u_char *src, char *dst, socklen_t size); -static const char *inet_ntop6 (const u_char *src, char *dst, socklen_t size); - -/* char * - * inet_ntop(af, src, dst, size) - * convert a network format address to presentation format. - * return: - * pointer to presentation format address (`dst'), or NULL (see errno). - * author: - * Paul Vixie, 1996. - */ -const char * -inet_ntop (int af, const void *src, char *dst, socklen_t size) -{ - switch (af) { - case AF_INET: - return (inet_ntop4(src, dst, size)); - case AF_INET6: - return (inet_ntop6(src, dst, size)); - default: - __set_errno (EAFNOSUPPORT); - return (NULL); - } - /* NOTREACHED */ -} -libc_hidden_def (inet_ntop) - -/* const char * - * inet_ntop4(src, dst, size) - * format an IPv4 address - * return: - * `dst' (as a const) - * notes: - * (1) uses no statics - * (2) takes a u_char* not an in_addr as input - * author: - * Paul Vixie, 1996. - */ -static const char * -inet_ntop4 (const u_char *src, char *dst, socklen_t size) -{ - static const char fmt[] = "%u.%u.%u.%u"; - char tmp[sizeof "255.255.255.255"]; - - if (SPRINTF((tmp, fmt, src[0], src[1], src[2], src[3])) >= size) { - __set_errno (ENOSPC); - return (NULL); - } - return strcpy(dst, tmp); -} - -/* const char * - * inet_ntop6(src, dst, size) - * convert IPv6 binary address into presentation (printable) format - * author: - * Paul Vixie, 1996. - */ -static const char * -inet_ntop6 (const u_char *src, char *dst, socklen_t size) -{ - /* - * Note that int32_t and int16_t need only be "at least" large enough - * to contain a value of the specified size. On some systems, like - * Crays, there is no such thing as an integer variable with 16 bits. - * Keep this in mind if you think this function should have been coded - * to use pointer overlays. All the world's not a VAX. - */ - char tmp[sizeof "ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255"], *tp; - struct { int base, len; } best, cur; - u_int words[NS_IN6ADDRSZ / NS_INT16SZ]; - int i; - - /* - * Preprocess: - * Copy the input (bytewise) array into a wordwise array. - * Find the longest run of 0x00's in src[] for :: shorthanding. - */ - memset(words, '\0', sizeof words); - for (i = 0; i < NS_IN6ADDRSZ; i += 2) - words[i / 2] = (src[i] << 8) | src[i + 1]; - best.base = -1; - cur.base = -1; - best.len = 0; - cur.len = 0; - for (i = 0; i < (NS_IN6ADDRSZ / NS_INT16SZ); i++) { - if (words[i] == 0) { - if (cur.base == -1) - cur.base = i, cur.len = 1; - else - cur.len++; - } else { - if (cur.base != -1) { - if (best.base == -1 || cur.len > best.len) - best = cur; - cur.base = -1; - } - } - } - if (cur.base != -1) { - if (best.base == -1 || cur.len > best.len) - best = cur; - } - if (best.base != -1 && best.len < 2) - best.base = -1; - - /* - * Format the result. - */ - tp = tmp; - for (i = 0; i < (NS_IN6ADDRSZ / NS_INT16SZ); i++) { - /* Are we inside the best run of 0x00's? */ - if (best.base != -1 && i >= best.base && - i < (best.base + best.len)) { - if (i == best.base) - *tp++ = ':'; - continue; - } - /* Are we following an initial run of 0x00s or any real hex? */ - if (i != 0) - *tp++ = ':'; - /* Is this address an encapsulated IPv4? */ - if (i == 6 && best.base == 0 && - (best.len == 6 || (best.len == 5 && words[5] == 0xffff))) { - if (!inet_ntop4(src+12, tp, sizeof tmp - (tp - tmp))) - return (NULL); - tp += strlen(tp); - break; - } - tp += SPRINTF((tp, "%x", words[i])); - } - /* Was it a trailing run of 0x00's? */ - if (best.base != -1 && (best.base + best.len) == - (NS_IN6ADDRSZ / NS_INT16SZ)) - *tp++ = ':'; - *tp++ = '\0'; - - /* - * Check for overflow, copy, and we're done. - */ - if ((socklen_t)(tp - tmp) > size) { - __set_errno (ENOSPC); - return (NULL); - } - return strcpy(dst, tmp); -} diff --git a/src/sg_httpsrv.c b/src/sg_httpsrv.c index cc294ee5..f46029b9 100644 --- a/src/sg_httpsrv.c +++ b/src/sg_httpsrv.c @@ -7,7 +7,7 @@ * * Cross-platform library which helps to develop web servers or frameworks. * - * Copyright (C) 2016-2021 Silvio Clecio + * Copyright (C) 2016-2024 Silvio Clecio * * Sagui library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -30,6 +30,11 @@ #include #include #include +#ifndef _WIN32 +#include +#include +#include +#endif /* _WIN32 */ #include "sg_macros.h" #include "utlist.h" #include "microhttpd.h" @@ -133,34 +138,63 @@ static void sg__httpsrv_ncc(void *cls, __SG_UNUSED struct MHD_Connection *con, } } -static void sg__httpsrv_addopt(struct MHD_OptionItem ops[8], unsigned char *pos, - enum MHD_OPTION opt, intptr_t val, void *ptr) { +static void sg__httpsrv_addopt(struct MHD_OptionItem ops[14], + unsigned char *pos, enum MHD_OPTION opt, + intptr_t val, void *ptr) { ops[*pos].option = opt; ops[*pos].value = val; ops[*pos].ptr_value = ptr; (*pos)++; } -static bool sg__httpsrv_listen(struct sg_httpsrv *srv, const char *key, - const char *pwd, const char *cert, - const char *trust, const char *dhparams, - const char *priorities, uint16_t port, - bool threaded) { - struct MHD_OptionItem ops[8]; +static bool sg__httpsrv_listen2(struct sg_httpsrv *srv, const char *key, + const char *pwd, const char *cert, + const char *trust, const char *dhparams, + const char *priorities, const char *hostname, + uint16_t port, uint32_t backlog, + bool threaded) { + struct MHD_OptionItem ops[14]; + struct sockaddr_in addr; + struct sockaddr_in6 addr6; unsigned int flags; unsigned char pos = 0; + int errnum; if (!srv || !srv->upld_cb || !srv->upld_write_cb || !srv->upld_save_cb || !srv->upld_save_as_cb || !srv->uplds_dir || (srv->post_buf_size < 256)) { errno = EINVAL; return false; } - flags = MHD_USE_DUAL_STACK | MHD_USE_ITC | MHD_USE_ERROR_LOG | - MHD_ALLOW_SUSPEND_RESUME | + flags = MHD_USE_ITC | MHD_USE_ERROR_LOG | MHD_ALLOW_SUSPEND_RESUME | (threaded ? MHD_USE_INTERNAL_POLLING_THREAD | MHD_USE_THREAD_PER_CONNECTION : MHD_USE_AUTO_INTERNAL_THREAD); sg__httpsrv_addopt(ops, &pos, MHD_OPTION_EXTERNAL_LOGGER, (intptr_t) sg__httpsrv_oel, srv); + if (hostname) { + memset(&addr, 0, sizeof(addr)); + addr.sin_family = AF_INET; + addr.sin_port = htons(port); + errnum = inet_pton(AF_INET, hostname, &(addr.sin_addr)); + if (errnum == 1) { + sg__httpsrv_addopt(ops, &pos, MHD_OPTION_SOCK_ADDR, 0, + (struct sockaddr *) (&addr)); + } else { + memset(&addr6, 0, sizeof(addr6)); + addr6.sin6_family = AF_INET6; + addr6.sin6_port = htons(port); + errnum = inet_pton(AF_INET6, hostname, &(addr6.sin6_addr)); + if (errnum != 1) { + sg__httpsrv_eprintf(srv, _("Invalid host name: %s.\n"), hostname); + errno = EINVAL; + return false; + } + flags |= MHD_USE_DUAL_STACK; + sg__httpsrv_addopt(ops, &pos, MHD_OPTION_SOCK_ADDR, 0, + (struct sockaddr *) (&addr6)); + } + } else { + flags |= MHD_USE_DUAL_STACK; + } sg__httpsrv_addopt(ops, &pos, MHD_OPTION_NOTIFY_COMPLETED, (intptr_t) sg__httpsrv_rcc, srv); sg__httpsrv_addopt(ops, &pos, MHD_OPTION_NOTIFY_CONNECTION, @@ -190,6 +224,10 @@ static bool sg__httpsrv_listen(struct sg_httpsrv *srv, const char *key, if (priorities) sg__httpsrv_addopt(ops, &pos, MHD_OPTION_HTTPS_PRIORITIES, 0, (void *) priorities); + if (backlog > 0) { + sg__httpsrv_addopt(ops, &pos, MHD_OPTION_LISTEN_BACKLOG_SIZE, backlog, + NULL); + } } sg__httpsrv_addopt(ops, &pos, MHD_OPTION_END, 0, NULL); srv->handle = MHD_start_daemon(flags, port, NULL, NULL, sg__httpsrv_ahc, srv, @@ -197,6 +235,15 @@ static bool sg__httpsrv_listen(struct sg_httpsrv *srv, const char *key, return srv->handle != NULL; } +static bool sg__httpsrv_listen(struct sg_httpsrv *srv, const char *key, + const char *pwd, const char *cert, + const char *trust, const char *dhparams, + const char *priorities, uint16_t port, + bool threaded) { + return sg__httpsrv_listen2(srv, key, pwd, cert, trust, dhparams, priorities, + NULL, port, 0, threaded); +} + void sg__httpsrv_eprintf(struct sg_httpsrv *srv, const char *fmt, ...) { char err[SG_ERR_SIZE]; va_list ap; @@ -307,6 +354,18 @@ void sg_httpsrv_free(struct sg_httpsrv *srv) { #ifdef SG_HTTPS_SUPPORT +bool sg_httpsrv_tls_listen4(struct sg_httpsrv *srv, const char *key, + const char *pwd, const char *cert, + const char *trust, const char *dhparams, + const char *priorities, const char *hostname, + uint16_t port, uint32_t backlog, bool threaded) { + if (key && cert) + return sg__httpsrv_listen2(srv, key, pwd, cert, trust, dhparams, priorities, + hostname, port, backlog, threaded); + errno = EINVAL; + return false; +} + bool sg_httpsrv_tls_listen3(struct sg_httpsrv *srv, const char *key, const char *pwd, const char *cert, const char *trust, const char *dhparams, @@ -341,6 +400,12 @@ bool sg_httpsrv_tls_listen(struct sg_httpsrv *srv, const char *key, #endif /* SG_HTTPS_SUPPORT */ +bool sg_httpsrv_listen2(struct sg_httpsrv *srv, const char *hostname, + uint16_t port, uint32_t backlog, bool threaded) { + return sg__httpsrv_listen2(srv, NULL, NULL, NULL, NULL, NULL, NULL, hostname, + port, backlog, threaded); +} + bool sg_httpsrv_listen(struct sg_httpsrv *srv, uint16_t port, bool threaded) { return sg__httpsrv_listen(srv, NULL, NULL, NULL, NULL, NULL, NULL, port, threaded); diff --git a/src/sg_utils.c b/src/sg_utils.c index ab25cf20..adf5c150 100644 --- a/src/sg_utils.c +++ b/src/sg_utils.c @@ -34,19 +34,11 @@ #include "sg_macros.h" #ifdef _WIN32 #include -#include #include #include #else /* _WIN32 */ -#include -#include -#ifdef HAVE_ARPA_INET_H #include -#endif /* HAVE_ARPA_INET_H */ #endif /* _WIN32 */ -#ifndef HAVE_INET_NTOP -#include "inet.h" -#endif /* HAVE_INET_NTOP */ #include "sagui.h" #include "sg_utils.h" @@ -413,14 +405,14 @@ char *sg_tmpdir(void) { /* Sockets */ int sg_ip(const void *socket, char *buf, size_t size) { - const struct sockaddr *sa; + const struct sockaddr *addr; const struct in6_addr *addr6; size_t len; if (!socket || !buf || (ssize_t) size < 0) return EINVAL; - sa = socket; - if (sa->sa_family == AF_INET6) { - addr6 = &(((struct sockaddr_in6 *) sa)->sin6_addr); + addr = socket; + if (addr->sa_family == AF_INET6) { + addr6 = &(((struct sockaddr_in6 *) addr)->sin6_addr); if (!inet_ntop(AF_INET6, addr6, buf, size)) return errno; len = strlen("::ffff:"); @@ -428,7 +420,7 @@ int sg_ip(const void *socket, char *buf, size_t size) { memcpy(buf, buf + len, strlen(buf + len) + 1); return 0; } - if (!inet_ntop(AF_INET, &(((struct sockaddr_in *) sa)->sin_addr), buf, size)) + if (!inet_ntop(AF_INET, &(((struct sockaddr_in *) addr)->sin_addr), buf, size)) return errno; return 0; } diff --git a/test/test_httpsrv.c b/test/test_httpsrv.c index 55397148..fd596643 100644 --- a/test/test_httpsrv.c +++ b/test/test_httpsrv.c @@ -7,7 +7,7 @@ * * Cross-platform library which helps to develop web servers or frameworks. * - * Copyright (C) 2016-2021 Silvio Clecio + * Copyright (C) 2016-2024 Silvio Clecio * * Sagui library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -233,7 +233,7 @@ static void test__httpsrv_rcc(void) { } static void test__httpsrv_addopt(void) { - struct MHD_OptionItem ops[8]; + struct MHD_OptionItem ops[14]; unsigned char pos = 0; int dummy = 123; memset(ops, 0, sizeof(ops)); @@ -409,6 +409,83 @@ static void test_httpsrv_listen(struct sg_httpsrv *srv) { ASSERT(sg_httpsrv_shutdown(srv) == 0); } +static void test_httpsrv_listen2(struct sg_httpsrv *srv) { + struct sg_httpsrv *dummy_srv; + + errno = 0; + ASSERT(!sg_httpsrv_listen2(NULL, NULL, TEST_HTTPSRV_PORT, 0, true)); + ASSERT(errno == EINVAL); + dummy_srv = sg_httpsrv_new(dummy_httpreq_cb, NULL); + dummy_srv->upld_cb = NULL; + errno = 0; + ASSERT(!sg_httpsrv_listen2(dummy_srv, NULL, TEST_HTTPSRV_PORT, 0, true)); + ASSERT(errno == EINVAL); + sg_httpsrv_free(dummy_srv); + dummy_srv = sg_httpsrv_new(dummy_httpreq_cb, NULL); + dummy_srv->upld_write_cb = NULL; + errno = 0; + ASSERT(!sg_httpsrv_listen2(dummy_srv, NULL, TEST_HTTPSRV_PORT, 0, true)); + ASSERT(errno == EINVAL); + sg_httpsrv_free(dummy_srv); + dummy_srv = sg_httpsrv_new(dummy_httpreq_cb, NULL); + dummy_srv->upld_save_cb = NULL; + errno = 0; + ASSERT(!sg_httpsrv_listen2(dummy_srv, NULL, TEST_HTTPSRV_PORT, 0, true)); + ASSERT(errno == EINVAL); + sg_httpsrv_free(dummy_srv); + dummy_srv = sg_httpsrv_new(dummy_httpreq_cb, NULL); + dummy_srv->upld_save_as_cb = NULL; + errno = 0; + ASSERT(!sg_httpsrv_listen2(dummy_srv, NULL, TEST_HTTPSRV_PORT, 0, true)); + ASSERT(errno == EINVAL); + sg_httpsrv_free(dummy_srv); + dummy_srv = sg_httpsrv_new(dummy_httpreq_cb, NULL); + sg_free(dummy_srv->uplds_dir); + dummy_srv->uplds_dir = NULL; + errno = 0; + ASSERT(!sg_httpsrv_listen2(dummy_srv, NULL, TEST_HTTPSRV_PORT, 0, true)); + ASSERT(errno == EINVAL); + sg_httpsrv_free(dummy_srv); + dummy_srv = sg_httpsrv_new(dummy_httpreq_cb, NULL); + dummy_srv->post_buf_size = 255; + errno = 0; + ASSERT(!sg_httpsrv_listen2(dummy_srv, NULL, TEST_HTTPSRV_PORT, 0, true)); + ASSERT(errno == EINVAL); + sg_httpsrv_free(dummy_srv); + + ASSERT(sg_httpsrv_listen2(srv, NULL, 0, 0, true)); + ASSERT(sg_httpsrv_shutdown(srv) == 0); + + ASSERT(sg_httpsrv_listen2(srv, NULL, TEST_HTTPSRV_PORT, 0, false)); + ASSERT(MHD_get_daemon_info(srv->handle, MHD_DAEMON_INFO_FLAGS)->flags & + MHD_USE_DUAL_STACK); + ASSERT(MHD_get_daemon_info(srv->handle, MHD_DAEMON_INFO_FLAGS)->flags & + MHD_USE_ERROR_LOG); + ASSERT(MHD_get_daemon_info(srv->handle, MHD_DAEMON_INFO_FLAGS)->flags & + MHD_USE_AUTO_INTERNAL_THREAD); + ASSERT(!(MHD_get_daemon_info(srv->handle, MHD_DAEMON_INFO_FLAGS)->flags & + MHD_USE_THREAD_PER_CONNECTION)); + ASSERT(sg_httpsrv_shutdown(srv) == 0); + ASSERT(sg_httpsrv_listen2(srv, NULL, TEST_HTTPSRV_PORT, 0, true)); + ASSERT(MHD_get_daemon_info(srv->handle, MHD_DAEMON_INFO_FLAGS)->flags & + MHD_USE_DUAL_STACK); + ASSERT(MHD_get_daemon_info(srv->handle, MHD_DAEMON_INFO_FLAGS)->flags & + MHD_USE_ERROR_LOG); + ASSERT(MHD_get_daemon_info(srv->handle, MHD_DAEMON_INFO_FLAGS)->flags & + MHD_USE_AUTO_INTERNAL_THREAD); + ASSERT(MHD_get_daemon_info(srv->handle, MHD_DAEMON_INFO_FLAGS)->flags & + MHD_USE_THREAD_PER_CONNECTION); +#ifdef __linux__ + dummy_srv = + sg_httpsrv_new2(NULL, dummy_httpreq_cb, dummy_httpreq_err_cb, NULL); + errno = 0; + ASSERT(!sg_httpsrv_listen2(dummy_srv, NULL, TEST_HTTPSRV_PORT, 0, true)); + ASSERT(errno == EADDRINUSE); + sg_httpsrv_free(dummy_srv); +#endif /* __linux__ */ + ASSERT(sg_httpsrv_shutdown(srv) == 0); +} + #ifdef SG_HTTPS_SUPPORT static void test_httpsrv_tls_listen(struct sg_httpsrv *srv) { @@ -523,6 +600,17 @@ static void test_httpsrv_tls_listen2(struct sg_httpsrv *srv) { ASSERT(errno == EINVAL); } +static void test_httpsrv_tls_listen4(struct sg_httpsrv *srv) { + errno = 0; + ASSERT(!sg_httpsrv_tls_listen4(srv, NULL, "", "", "", "", "", "", + TEST_HTTPSRV_PORT, 0, false)); + ASSERT(errno == EINVAL); + errno = 0; + ASSERT(!sg_httpsrv_tls_listen4(srv, "", "", NULL, "", "", "", "", + TEST_HTTPSRV_PORT, 0, false)); + ASSERT(errno == EINVAL); +} + static void test_httpsrv_tls_listen3(struct sg_httpsrv *srv) { errno = 0; ASSERT(!sg_httpsrv_tls_listen3(srv, NULL, "", "", "", "", "", @@ -790,10 +878,12 @@ int main(void) { test_httpsrv_new(); test_httpsrv_free(); test_httpsrv_listen(srv); + test_httpsrv_listen2(srv); #ifdef SG_HTTPS_SUPPORT test_httpsrv_tls_listen(srv); test_httpsrv_tls_listen2(srv); test_httpsrv_tls_listen3(srv); + test_httpsrv_tls_listen4(srv); #endif /* SG_HTTPS_SUPPORT */ test_httpsrv_shutdown(srv); test_httpsrv_port(srv);