libmicrohttpd

HTTP/1.x server C library (MHD 1.x, stable)
Log | Files | Refs | Submodules | README | LICENSE

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:
Mconfigure.ac | 113+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/include/microhttpd.h | 19++++++++++++++++---
Msrc/microhttpd/daemon.c | 92+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--
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; }