Browse Source

Implement custom host name support

silvioprog 1 year ago
parent
commit
4730c7946f
11 changed files with 242 additions and 311 deletions
  1. 0 33
      CMakeLists.txt
  2. 2 5
      Dockerfile
  3. 1 8
      doxygen/Doxyfile.in
  4. 13 1
      examples/example_httpsrv.c
  5. 52 2
      include/sagui.h
  6. 1 4
      src/CMakeLists.txt
  7. 0 38
      src/inet.h
  8. 0 194
      src/inet_ntop.c
  9. 76 11
      src/sg_httpsrv.c
  10. 5 13
      src/sg_utils.c
  11. 92 2
      test/test_httpsrv.c

+ 0 - 33
CMakeLists.txt

@@ -166,39 +166,6 @@ if(UNIX AND ((NOT APPLE) AND (NOT ANDROID)))
   endif()
   endif()
 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 <stdio.h>\n#include <ws2tcpip.h>\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(${SG_INCLUDE_DIR})
 include_directories(${MHD_INCLUDE_DIR})
 include_directories(${MHD_INCLUDE_DIR})
 if(SG_HTTP_COMPRESSION)
 if(SG_HTTP_COMPRESSION)

+ 2 - 5
Dockerfile

@@ -4,8 +4,8 @@
 # SPDX-License-Identifier: MIT
 # 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
 FROM alpine:3.19.1 AS builder
 
 
@@ -15,11 +15,8 @@ RUN apk add --no-cache \
   automake \
   automake \
   clang \
   clang \
   cmake
   cmake
-
 WORKDIR /app
 WORKDIR /app
-
 COPY . /app/
 COPY . /app/
-
 RUN mkdir build && \
 RUN mkdir build && \
   cd build/ && \
   cd build/ && \
   cmake -DBUILD_SHARED_LIBS=OFF .. && \
   cmake -DBUILD_SHARED_LIBS=OFF .. && \

+ 1 - 8
doxygen/Doxyfile.in

@@ -7,7 +7,7 @@
 #
 #
 # Cross-platform library which helps to develop web servers or frameworks.
 # Cross-platform library which helps to develop web servers or frameworks.
 #
 #
-# Copyright (C) 2016-2021 Silvio Clecio <[email protected]>
+# Copyright (C) 2016-2024 Silvio Clecio <[email protected]>
 #
 #
 # Sagui library is free software; you can redistribute it and/or
 # Sagui library is free software; you can redistribute it and/or
 # modify it under the terms of the GNU Lesser General Public
 # modify it under the terms of the GNU Lesser General Public
@@ -1163,13 +1163,6 @@ CLANG_DATABASE_PATH    =
 
 
 ALPHABETICAL_INDEX     = YES
 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
 # 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
 # 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
 # can be used to specify a prefix (or a list of prefixes) that should be ignored

+ 13 - 1
examples/example_httpsrv.c

@@ -27,10 +27,18 @@
 #include <stdio.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <stdlib.h>
 #include <stdint.h>
 #include <stdint.h>
+#include <unistd.h>
+#include <signal.h>
 #include <sagui.h>
 #include <sagui.h>
 
 
 /* NOTE: Error checking has been omitted to make it clear. */
 /* 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,
 static void req_cb(__SG_UNUSED void *cls, __SG_UNUSED struct sg_httpreq *req,
                    struct sg_httpres *res) {
                    struct sg_httpres *res) {
   sg_httpres_send(res,
   sg_httpres_send(res,
@@ -46,6 +54,8 @@ int main(int argc, const char *argv[]) {
     printf("%s <PORT>\n", argv[0]);
     printf("%s <PORT>\n", argv[0]);
     return EXIT_FAILURE;
     return EXIT_FAILURE;
   }
   }
+  signal(SIGTERM, sig_handler);
+  signal(SIGINT, sig_handler);
   port = strtol(argv[1], NULL, 10);
   port = strtol(argv[1], NULL, 10);
   srv = sg_httpsrv_new(req_cb, NULL);
   srv = sg_httpsrv_new(req_cb, NULL);
   if (!sg_httpsrv_listen(srv, port, false)) {
   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",
   fprintf(stdout, "Server running at http://localhost:%d\n",
           sg_httpsrv_port(srv));
           sg_httpsrv_port(srv));
   fflush(stdout);
   fflush(stdout);
-  getchar();
+  while (!terminated) {
+    usleep(100 * 1000);
+  }
   sg_httpsrv_free(srv);
   sg_httpsrv_free(srv);
   return EXIT_SUCCESS;
   return EXIT_SUCCESS;
 }
 }

+ 52 - 2
include/sagui.h

@@ -73,8 +73,8 @@ extern "C" {
 #endif /* __SG_FORMAT */
 #endif /* __SG_FORMAT */
 
 
 #define SG_VERSION_MAJOR 3
 #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                                                         \
 #define SG_VERSION_HEX                                                         \
   ((SG_VERSION_MAJOR << 16) | (SG_VERSION_MINOR << 8) | (SG_VERSION_PATCH))
   ((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
 #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.
  * Starts the HTTPS server.
  * \param[in] srv Server handle.
  * \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 */
 #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.
  * Starts the HTTP server.
  * \param[in] srv Server handle.
  * \param[in] srv Server handle.

+ 1 - 4
src/CMakeLists.txt

@@ -22,7 +22,7 @@
 #
 #
 # Cross-platform library which helps to develop web servers or frameworks.
 # Cross-platform library which helps to develop web servers or frameworks.
 #
 #
-# Copyright (C) 2016-2020 Silvio Clecio <[email protected]>
+# Copyright (C) 2016-2024 Silvio Clecio <[email protected]>
 #
 #
 # Sagui library is free software; you can redistribute it and/or
 # Sagui library is free software; you can redistribute it and/or
 # modify it under the terms of the GNU Lesser General Public
 # 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_httpreq.c
   ${SG_SOURCE_DIR}/sg_httpres.c
   ${SG_SOURCE_DIR}/sg_httpres.c
   ${SG_SOURCE_DIR}/sg_httpsrv.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)
 if(SG_PATH_ROUTING)
   list(APPEND SG_C_SOURCE ${SG_SOURCE_DIR}/sg_entrypoint.c
   list(APPEND SG_C_SOURCE ${SG_SOURCE_DIR}/sg_entrypoint.c
        ${SG_SOURCE_DIR}/sg_entrypoints.c ${SG_SOURCE_DIR}/sg_routes.c
        ${SG_SOURCE_DIR}/sg_entrypoints.c ${SG_SOURCE_DIR}/sg_routes.c

+ 0 - 38
src/inet.h

@@ -1,38 +0,0 @@
-/*                         _
- *   ___  __ _  __ _ _   _(_)
- *  / __|/ _` |/ _` | | | | |
- *  \__ \ (_| | (_| | |_| | |
- *  |___/\__,_|\__, |\__,_|_|
- *             |___/
- *
- * Cross-platform library which helps to develop web servers or frameworks.
- *
- * Copyright (C) 2016-2020 Silvio Clecio <[email protected]>
- *
- * 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 <ws2tcpip.h>
-#else /* _WIN32 */
-#include <unistd.h>
-#endif /* _WIN32 */
-
-const char *inet_ntop(int af, const void *src, char *dst, socklen_t size);
-
-#endif /* INET_H */

+ 0 - 194
src/inet_ntop.c

@@ -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 <stdio.h>
-#include <errno.h>
-#include <string.h>
-#ifdef _WIN32
-#include <ws2tcpip.h>
-#else
-#include <unistd.h>
-#include <sys/socket.h>
-#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);
-}

+ 76 - 11
src/sg_httpsrv.c

@@ -7,7 +7,7 @@
  *
  *
  * Cross-platform library which helps to develop web servers or frameworks.
  * Cross-platform library which helps to develop web servers or frameworks.
  *
  *
- * Copyright (C) 2016-2021 Silvio Clecio <[email protected]>
+ * Copyright (C) 2016-2024 Silvio Clecio <[email protected]>
  *
  *
  * Sagui library is free software; you can redistribute it and/or
  * Sagui library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
  * modify it under the terms of the GNU Lesser General Public
@@ -30,6 +30,11 @@
 #include <string.h>
 #include <string.h>
 #include <errno.h>
 #include <errno.h>
 #include <pthread.h>
 #include <pthread.h>
+#ifndef _WIN32
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#endif /* _WIN32 */
 #include "sg_macros.h"
 #include "sg_macros.h"
 #include "utlist.h"
 #include "utlist.h"
 #include "microhttpd.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].option = opt;
   ops[*pos].value = val;
   ops[*pos].value = val;
   ops[*pos].ptr_value = ptr;
   ops[*pos].ptr_value = ptr;
   (*pos)++;
   (*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 int flags;
   unsigned char pos = 0;
   unsigned char pos = 0;
+  int errnum;
   if (!srv || !srv->upld_cb || !srv->upld_write_cb || !srv->upld_save_cb ||
   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)) {
       !srv->upld_save_as_cb || !srv->uplds_dir || (srv->post_buf_size < 256)) {
     errno = EINVAL;
     errno = EINVAL;
     return false;
     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 ?
           (threaded ?
              MHD_USE_INTERNAL_POLLING_THREAD | MHD_USE_THREAD_PER_CONNECTION :
              MHD_USE_INTERNAL_POLLING_THREAD | MHD_USE_THREAD_PER_CONNECTION :
              MHD_USE_AUTO_INTERNAL_THREAD);
              MHD_USE_AUTO_INTERNAL_THREAD);
   sg__httpsrv_addopt(ops, &pos, MHD_OPTION_EXTERNAL_LOGGER,
   sg__httpsrv_addopt(ops, &pos, MHD_OPTION_EXTERNAL_LOGGER,
                      (intptr_t) sg__httpsrv_oel, srv);
                      (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,
   sg__httpsrv_addopt(ops, &pos, MHD_OPTION_NOTIFY_COMPLETED,
                      (intptr_t) sg__httpsrv_rcc, srv);
                      (intptr_t) sg__httpsrv_rcc, srv);
   sg__httpsrv_addopt(ops, &pos, MHD_OPTION_NOTIFY_CONNECTION,
   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)
     if (priorities)
       sg__httpsrv_addopt(ops, &pos, MHD_OPTION_HTTPS_PRIORITIES, 0,
       sg__httpsrv_addopt(ops, &pos, MHD_OPTION_HTTPS_PRIORITIES, 0,
                          (void *) priorities);
                          (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);
   sg__httpsrv_addopt(ops, &pos, MHD_OPTION_END, 0, NULL);
   srv->handle = MHD_start_daemon(flags, port, NULL, NULL, sg__httpsrv_ahc, srv,
   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;
   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, ...) {
 void sg__httpsrv_eprintf(struct sg_httpsrv *srv, const char *fmt, ...) {
   char err[SG_ERR_SIZE];
   char err[SG_ERR_SIZE];
   va_list ap;
   va_list ap;
@@ -307,6 +354,18 @@ void sg_httpsrv_free(struct sg_httpsrv *srv) {
 
 
 #ifdef SG_HTTPS_SUPPORT
 #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,
 bool sg_httpsrv_tls_listen3(struct sg_httpsrv *srv, const char *key,
                             const char *pwd, const char *cert,
                             const char *pwd, const char *cert,
                             const char *trust, const char *dhparams,
                             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 */
 #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) {
 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,
   return sg__httpsrv_listen(srv, NULL, NULL, NULL, NULL, NULL, NULL, port,
                             threaded);
                             threaded);

+ 5 - 13
src/sg_utils.c

@@ -34,19 +34,11 @@
 #include "sg_macros.h"
 #include "sg_macros.h"
 #ifdef _WIN32
 #ifdef _WIN32
 #include <ws2tcpip.h>
 #include <ws2tcpip.h>
-#include <winsock2.h>
 #include <windows.h>
 #include <windows.h>
 #include <wchar.h>
 #include <wchar.h>
 #else /* _WIN32 */
 #else /* _WIN32 */
-#include <sys/socket.h>
-#include <netinet/in.h>
-#ifdef HAVE_ARPA_INET_H
 #include <arpa/inet.h>
 #include <arpa/inet.h>
-#endif /* HAVE_ARPA_INET_H */
 #endif /* _WIN32 */
 #endif /* _WIN32 */
-#ifndef HAVE_INET_NTOP
-#include "inet.h"
-#endif /* HAVE_INET_NTOP */
 #include "sagui.h"
 #include "sagui.h"
 #include "sg_utils.h"
 #include "sg_utils.h"
 
 
@@ -413,14 +405,14 @@ done:
 /* Sockets */
 /* Sockets */
 
 
 int sg_ip(const void *socket, char *buf, size_t size) {
 int sg_ip(const void *socket, char *buf, size_t size) {
-  const struct sockaddr *sa;
+  const struct sockaddr *addr;
   const struct in6_addr *addr6;
   const struct in6_addr *addr6;
   size_t len;
   size_t len;
   if (!socket || !buf || (ssize_t) size < 0)
   if (!socket || !buf || (ssize_t) size < 0)
     return EINVAL;
     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))
     if (!inet_ntop(AF_INET6, addr6, buf, size))
       return errno;
       return errno;
     len = strlen("::ffff:");
     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);
       memcpy(buf, buf + len, strlen(buf + len) + 1);
     return 0;
     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 errno;
   return 0;
   return 0;
 }
 }

+ 92 - 2
test/test_httpsrv.c

@@ -7,7 +7,7 @@
  *
  *
  * Cross-platform library which helps to develop web servers or frameworks.
  * Cross-platform library which helps to develop web servers or frameworks.
  *
  *
- * Copyright (C) 2016-2021 Silvio Clecio <[email protected]>
+ * Copyright (C) 2016-2024 Silvio Clecio <[email protected]>
  *
  *
  * Sagui library is free software; you can redistribute it and/or
  * Sagui library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
  * 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) {
 static void test__httpsrv_addopt(void) {
-  struct MHD_OptionItem ops[8];
+  struct MHD_OptionItem ops[14];
   unsigned char pos = 0;
   unsigned char pos = 0;
   int dummy = 123;
   int dummy = 123;
   memset(ops, 0, sizeof(ops));
   memset(ops, 0, sizeof(ops));
@@ -409,6 +409,83 @@ static void test_httpsrv_listen(struct sg_httpsrv *srv) {
   ASSERT(sg_httpsrv_shutdown(srv) == 0);
   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
 #ifdef SG_HTTPS_SUPPORT
 
 
 static void test_httpsrv_tls_listen(struct sg_httpsrv *srv) {
 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);
   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) {
 static void test_httpsrv_tls_listen3(struct sg_httpsrv *srv) {
   errno = 0;
   errno = 0;
   ASSERT(!sg_httpsrv_tls_listen3(srv, NULL, "", "", "", "", "",
   ASSERT(!sg_httpsrv_tls_listen3(srv, NULL, "", "", "", "", "",
@@ -790,10 +878,12 @@ int main(void) {
   test_httpsrv_new();
   test_httpsrv_new();
   test_httpsrv_free();
   test_httpsrv_free();
   test_httpsrv_listen(srv);
   test_httpsrv_listen(srv);
+  test_httpsrv_listen2(srv);
 #ifdef SG_HTTPS_SUPPORT
 #ifdef SG_HTTPS_SUPPORT
   test_httpsrv_tls_listen(srv);
   test_httpsrv_tls_listen(srv);
   test_httpsrv_tls_listen2(srv);
   test_httpsrv_tls_listen2(srv);
   test_httpsrv_tls_listen3(srv);
   test_httpsrv_tls_listen3(srv);
+  test_httpsrv_tls_listen4(srv);
 #endif /* SG_HTTPS_SUPPORT */
 #endif /* SG_HTTPS_SUPPORT */
   test_httpsrv_shutdown(srv);
   test_httpsrv_shutdown(srv);
   test_httpsrv_port(srv);
   test_httpsrv_port(srv);