commit da9f4fd6f2f37346d1cae368ec7683506df2287a
parent a03074c3f799f6c681719ddb09e669c44c52caf1
Author: Evgeny Grin (Karlson2k) <k2k@narod.ru>
Date: Thu, 15 Jun 2017 10:37:40 +0300
MHD_start_daemon(): added port autodetection if '0' (autoselect) is used for port number,
Added new MHD_FEATURE value MHD_FEATURE_AUTODETECT_BIND_PORT.
Diffstat:
3 files changed, 219 insertions(+), 5 deletions(-)
diff --git a/configure.ac b/configure.ac
@@ -756,6 +756,119 @@ AC_CHECK_MEMBER([struct sockaddr_in.sin_len],
#endif
])
+AC_CHECK_DECLS([getsockname],
+ [
+ AC_CHECK_FUNCS([getsockname],
+ [
+ AC_CACHE_CHECK([[whether getsockname() is usable]], [[mhc_cv_getsockname_usable]],
+ [
+ AC_RUN_IFELSE(
+ [
+ AC_LANG_SOURCE(
+ [[
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+#ifdef HAVE_SYS_SOCKET_H
+#include <sys/socket.h>
+#endif
+#ifdef HAVE_WINSOCK2_H
+#include <winsock2.h>
+#endif
+#ifdef HAVE_WS2TCPIP_H
+#include <ws2tcpip.h>
+#endif
+#ifdef HAVE_NETINET_IN_H
+#include <netinet/in.h>
+#endif
+#ifdef HAVE_NETINET_IP_H
+#include <netinet/ip.h>
+#endif
+#ifdef HAVE_ARPA_INET_H
+#include <arpa/inet.h>
+#endif
+
+
+static void zr_mem(void *ptr, socklen_t size)
+{ char *mem = ptr; while(size--) {mem[0] = 0; mem++;} }
+
+int main(void)
+{
+ const socklen_t c_addr_size = (socklen_t)sizeof(struct sockaddr_in);
+ struct sockaddr_in sa;
+ socklen_t addr_size;
+ int ret = 1;
+#if !defined(_WIN32) || defined(__CYGWIN__)
+ int sckt;
+ const int invld_sckt = -1;
+#else
+ SOCKET sckt;
+ const SOCKET invld_sckt = INVALID_SOCKET;
+ WSADATA wsa_data;
+
+ if (0 != WSAStartup(MAKEWORD(2, 2), &wsa_data) || MAKEWORD(2, 2) != wsa_data.wVersion)
+ return 20;
+#endif
+
+ sckt = socket (PF_INET, SOCK_STREAM, 0);
+ if (invld_sckt != sckt)
+ {
+ zr_mem(&sa, c_addr_size);
+ sa.sin_family = AF_INET;
+#if HAVE_SOCKADDR_IN_SIN_LEN
+ sa.sin_len = c_addr_size;
+#endif
+ if (0 == bind (sckt, (struct sockaddr *)&sa, c_addr_size))
+ {
+ if (0 == listen (sckt, 1))
+ {
+ addr_size = c_addr_size;
+ if (0 == getsockname (sckt, (struct sockaddr *)&sa, &addr_size))
+ {
+ if (c_addr_size >= addr_size)
+ {
+ if (0 != ntohs(sa.sin_port))
+ { ret = 0;
+ } else ret = 7;
+ } else ret = 6;
+ } else ret = 5;
+ } else ret = 4;
+ } else ret = 3;
+ } else ret = 2;
+#if !defined(_WIN32) || defined(__CYGWIN__)
+ close (sckt);
+#else
+ closesocket (sckt);
+ WSACleanup();
+#endif
+ return ret;
+}
+ ]]
+ )
+ ],
+ [[mhc_cv_getsockname_usable='yes']],
+ [[mhc_cv_getsockname_usable='no']],
+ [[mhc_cv_getsockname_usable='assuming yes']]
+ )
+ ]
+ )
+ AS_VAR_IF([[mhc_cv_getsockname_usable]], [["no"]], [:],
+ [AC_DEFINE([[MHD_USE_GETSOCKNAME]], [[1]], [Define if you have usable `getsockname' function.])])
+ ]
+ )
+ ], [],
+ [[
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+#ifdef HAVE_SYS_SOCKET_H
+#include <sys/socket.h>
+#endif
+#ifdef HAVE_WINSOCK2_H
+#include <winsock2.h>
+#endif
+ ]]
+)
# Check for inter-thread signaling type
AC_ARG_ENABLE([[itc]],
diff --git a/src/include/microhttpd.h b/src/include/microhttpd.h
@@ -2095,7 +2095,11 @@ typedef int
* Start a webserver on the given port.
*
* @param flags combination of `enum MHD_FLAG` values
- * @param port port to bind to (in host byte order)
+ * @param port port to bind to (in host byte order),
+ * use '0' to bind to random free port,
+ * ignored if MHD_OPTION_SOCK_ADDR or
+ * MHD_OPTION_LISTEN_SOCKET is provided
+ * or MHD_USE_NO_LISTEN_SOCKET is specified
* @param apc callback to call to check which clients
* will be allowed to connect; you can pass NULL
* in which case connections from any IP will be
@@ -2121,7 +2125,11 @@ MHD_start_daemon_va (unsigned int flags,
* #MHD_start_daemon_va.
*
* @param flags combination of `enum MHD_FLAG` values
- * @param port port to bind to
+ * @param port port to bind to (in host byte order),
+ * use '0' to bind to random free port,
+ * ignored if MHD_OPTION_SOCK_ADDR or
+ * MHD_OPTION_LISTEN_SOCKET is provided
+ * or MHD_USE_NO_LISTEN_SOCKET is specified
* @param apc callback to call to check which clients
* will be allowed to connect; you can pass NULL
* in which case connections from any IP will be
@@ -3425,7 +3433,12 @@ enum MHD_FEATURE
* It's always safe to use same file FD in multiple responses if MHD
* is run in any single thread mode.
*/
- MHD_FEATURE_RESPONSES_SHARED_FD = 18
+ MHD_FEATURE_RESPONSES_SHARED_FD = 18,
+
+ /**
+ * Get whether MHD support automatic detection of bind port number.
+ */
+ MHD_FEATURE_AUTODETECT_BIND_PORT = 19
};
diff --git a/src/microhttpd/daemon.c b/src/microhttpd/daemon.c
@@ -4346,7 +4346,11 @@ unescape_wrapper (void *cls,
* #MHD_start_daemon_va.
*
* @param flags combination of `enum MHD_FLAG` values
- * @param port port to bind to
+ * @param port port to bind to (in host byte order),
+ * use '0' to bind to random free port,
+ * ignored if MHD_OPTION_SOCK_ADDR or
+ * MHD_OPTION_LISTEN_SOCKET is provided
+ * or MHD_USE_NO_LISTEN_SOCKET is specified
* @param apc callback to call to check which clients
* will be allowed to connect; you can pass NULL
* in which case connections from any IP will be
@@ -5082,7 +5086,11 @@ setup_epoll_to_listen (struct MHD_Daemon *daemon)
* Start a webserver on the given port.
*
* @param flags combination of `enum MHD_FLAG` values
- * @param port port to bind to (in host byte order)
+ * @param port port to bind to (in host byte order),
+ * use '0' to bind to random free port,
+ * ignored if MHD_OPTION_SOCK_ADDR or
+ * MHD_OPTION_LISTEN_SOCKET is provided
+ * or MHD_USE_NO_LISTEN_SOCKET is specified
* @param apc callback to call to check which clients
* will be allowed to connect; you can pass NULL
* in which case connections from any IP will be
@@ -5621,6 +5629,80 @@ MHD_start_daemon_va (unsigned int flags,
listen_fd = daemon->listen_fd;
}
+ if ( (0 == daemon->port) &&
+ (0 == (*pflags & MHD_USE_NO_LISTEN_SOCKET)) )
+ { /* Get port number. */
+ struct sockaddr *realaddr;
+#ifdef MHD_POSIX_SOCKETS
+ socklen_t alloc_len;
+#endif /* MHD_POSIX_SOCKETS */
+#ifdef HAVE_INET6
+ if (0 != (*pflags & MHD_USE_IPv6))
+ {
+ memset (&servaddr6,
+ 0,
+ sizeof (struct sockaddr_in6));
+ servaddr6.sin6_family = AF_INET6;
+#ifdef HAVE_SOCKADDR_IN_SIN_LEN
+ servaddr6.sin6_len = sizeof (struct sockaddr_in6);
+#endif /* HAVE_SOCKADDR_IN_SIN_LEN */
+ addrlen = (socklen_t) sizeof (struct sockaddr_in6);
+ realaddr = (struct sockaddr *) &servaddr6;
+ }
+ else
+#else /* ! HAVE_INET6 */
+ if (1)
+#endif /* ! HAVE_INET6 */
+ {
+ memset (&servaddr4,
+ 0,
+ sizeof (struct sockaddr_in));
+ servaddr4.sin_family = AF_INET;
+#ifdef HAVE_SOCKADDR_IN_SIN_LEN
+ servaddr4.sin_len = sizeof (struct sockaddr_in);
+#endif /* HAVE_SOCKADDR_IN_SIN_LEN */
+ addrlen = (socklen_t) sizeof (struct sockaddr_in);
+ realaddr = (struct sockaddr *) &servaddr4;
+ }
+#ifdef MHD_POSIX_SOCKETS
+ alloc_len = addrlen;
+#endif /* MHD_POSIX_SOCKETS */
+ if (0 != getsockname (listen_fd, realaddr, &addrlen))
+ {
+#ifdef HAVE_MESSAGES
+ MHD_DLOG (daemon,
+ _("Failed to get listen port number: %s\n"),
+ MHD_socket_last_strerr_ ());
+#endif /* HAVE_MESSAGES */
+ }
+#ifdef MHD_POSIX_SOCKETS
+ else if (alloc_len < addrlen)
+ {
+#ifdef HAVE_MESSAGES
+ MHD_DLOG (daemon,
+ _("Failed to get listen port number due to small buffer\n"));
+#endif /* HAVE_MESSAGES */
+ }
+#endif /* MHD_POSIX_SOCKETS */
+ else
+ {
+#ifdef HAVE_INET6
+ if (0 != (*pflags & MHD_USE_IPv6))
+ {
+ mhd_assert (AF_INET6 == servaddr6.sin6_family);
+ daemon->port = ntohs(servaddr6.sin6_port);
+ }
+ else
+#else /* ! HAVE_INET6 */
+ if (1)
+#endif /* ! HAVE_INET6 */
+ {
+ mhd_assert (AF_INET == servaddr4.sin_family);
+ daemon->port = ntohs(servaddr4.sin_port);
+ }
+ }
+ }
+
if ( (MHD_INVALID_SOCKET != listen_fd) &&
(! MHD_socket_nonblocking_ (listen_fd)) )
{
@@ -6425,6 +6507,12 @@ MHD_is_feature_supported(enum MHD_FEATURE feature)
#else
return MHD_NO;
#endif
+ case MHD_FEATURE_AUTODETECT_BIND_PORT:
+#ifdef MHD_USE_GETSOCKNAME
+ return MHD_YES;
+#else
+ return MHD_NO;
+#endif
}
return MHD_NO;
}