From a6cf161341e6b8a11bd06ca01b727da8d17a45be Mon Sep 17 00:00:00 2001 From: Aryan Arora Date: Sat, 20 Apr 2024 15:28:35 +0530 Subject: [PATCH 1/2] add ipv6 support to HTTP daemon --- include/server.h | 3 + include/tools.h | 23 +++- src/server.cpp | 6 ++ src/server/internalServer.cpp | 37 +++++-- src/server/internalServer.h | 3 + src/tools/networkTools.cpp | 193 +++++++++++++++++++++------------- 6 files changed, 177 insertions(+), 88 deletions(-) diff --git a/include/server.h b/include/server.h index d1981c8c2..1633aefe0 100644 --- a/include/server.h +++ b/include/server.h @@ -62,8 +62,10 @@ namespace kiwix { m_withTaskbar = withTaskbar; m_withLibraryButton = withLibraryButton; } void setBlockExternalLinks(bool blockExternalLinks) { m_blockExternalLinks = blockExternalLinks; } + void setIPv6(bool ipv6) { m_ipv6 = ipv6; } int getPort(); std::string getAddress(); + bool isAddressIPv6(); protected: std::shared_ptr mp_library; @@ -78,6 +80,7 @@ namespace kiwix bool m_withTaskbar = true; bool m_withLibraryButton = true; bool m_blockExternalLinks = false; + bool m_ipv6 = false; int m_ipConnectionLimit = 0; std::unique_ptr mp_server; }; diff --git a/include/tools.h b/include/tools.h index 2ea9e8b29..ce42ae44e 100644 --- a/include/tools.h +++ b/include/tools.h @@ -25,6 +25,11 @@ #include #include +struct ip_addr{ + std::string addr; + std::string addr6; +}; + namespace kiwix { typedef std::pair LangNameCodePair; typedef std::vector FeedLanguages; @@ -215,16 +220,28 @@ bool fileReadable(const std::string& path); */ std::string getMimeTypeForFile(const std::string& filename); +/** Provides all available network interfaces on Windows + * + * This function provides the available IPv4 and IPv6 network interfaces + */ +std::map getNetworkInterfacesWin(); + +/** Provides all available network interfaces on Posix + * + * This function provides the available IPv4 and IPv6 network interfaces + */ +std::map getNetworkInterfacesPosix(); + /** Provides all available network interfaces * - * This function provides the available IPv4 network interfaces + * This function provides the available IPv4 and IPv6 network interfaces */ -std::map getNetworkInterfaces(); +std::map getNetworkInterfaces(); /** Provides the best IP address * This function provides the best IP address from the list given by getNetworkInterfaces */ -std::string getBestPublicIp(); +std::string getBestPublicIp(bool ipv6); /** Converts file size to human readable format. * diff --git a/src/server.cpp b/src/server.cpp index 1a4ecfa9e..53d938871 100644 --- a/src/server.cpp +++ b/src/server.cpp @@ -51,6 +51,7 @@ bool Server::start() { m_withTaskbar, m_withLibraryButton, m_blockExternalLinks, + m_ipv6, m_indexTemplateString, m_ipConnectionLimit)); return mp_server->start(); @@ -84,4 +85,9 @@ std::string Server::getAddress() return mp_server->getAddress(); } +bool Server::isAddressIPv6() +{ + return mp_server->isAddressIPv6(); +} + } diff --git a/src/server/internalServer.cpp b/src/server/internalServer.cpp index 910ade8a4..560059d47 100644 --- a/src/server/internalServer.cpp +++ b/src/server/internalServer.cpp @@ -416,6 +416,7 @@ InternalServer::InternalServer(LibraryPtr library, bool withTaskbar, bool withLibraryButton, bool blockExternalLinks, + bool ipv6, std::string indexTemplateString, int ipConnectionLimit) : m_addr(addr), @@ -428,6 +429,7 @@ InternalServer::InternalServer(LibraryPtr library, m_withTaskbar(withTaskbar), m_withLibraryButton(withLibraryButton), m_blockExternalLinks(blockExternalLinks), + m_ipv6(ipv6), m_indexTemplateString(indexTemplateString.empty() ? RESOURCE::templates::index_html : indexTemplateString), m_ipConnectionLimit(ipConnectionLimit), mp_daemon(nullptr), @@ -451,28 +453,43 @@ bool InternalServer::start() { if (m_verbose.load()) flags |= MHD_USE_DEBUG; - struct sockaddr_in sockAddr; - memset(&sockAddr, 0, sizeof(sockAddr)); - sockAddr.sin_family = AF_INET; - sockAddr.sin_port = htons(m_port); + + struct sockaddr_in sockAddr4={0}; + sockAddr4.sin_family = AF_INET; + sockAddr4.sin_port = htons(m_port); + struct sockaddr_in6 sockAddr6={0}; + sockAddr6.sin6_family = AF_INET6; + sockAddr6.sin6_port = htons(m_port); + if (m_addr.empty()) { if (0 != INADDR_ANY) { - sockAddr.sin_addr.s_addr = htonl(INADDR_ANY); + sockAddr6.sin6_addr = in6addr_any; + sockAddr4.sin_addr.s_addr = htonl(INADDR_ANY); } - m_addr = kiwix::getBestPublicIp(); - } else { - if (inet_pton(AF_INET, m_addr.c_str(), &(sockAddr.sin_addr.s_addr)) == 0) { - std::cerr << "Ip address " << m_addr << " is not a valid ip address" << std::endl; + m_addr = kiwix::getBestPublicIp(m_ipv6); + std::cout< handle_request(const RequestContext& request); @@ -174,6 +176,7 @@ class InternalServer { bool m_withTaskbar; bool m_withLibraryButton; bool m_blockExternalLinks; + bool m_ipv6; std::string m_indexTemplateString; int m_ipConnectionLimit; struct MHD_Daemon* mp_daemon; diff --git a/src/tools/networkTools.cpp b/src/tools/networkTools.cpp index 191eb8543..b113f810d 100644 --- a/src/tools/networkTools.cpp +++ b/src/tools/networkTools.cpp @@ -32,12 +32,14 @@ #include #ifdef _WIN32 +#include #include #include #include #else #include -#include +#include +#include #include #include #include @@ -75,90 +77,131 @@ std::string kiwix::download(const std::string& url) { return ss.str(); } -std::map kiwix::getNetworkInterfaces() { - std::map interfaces; - #ifdef _WIN32 - SOCKET sd = WSASocket(AF_INET, SOCK_DGRAM, 0, 0, 0, 0); - if (sd == INVALID_SOCKET) { - std::cerr << "Failed to get a socket. Error " << WSAGetLastError() << std::endl; - return interfaces; - } - INTERFACE_INFO InterfaceList[20]; - unsigned long nBytesReturned; - if (WSAIoctl(sd, SIO_GET_INTERFACE_LIST, 0, 0, &InterfaceList, - sizeof(InterfaceList), &nBytesReturned, 0, 0) == SOCKET_ERROR) { - std::cerr << "Failed calling WSAIoctl: error " << WSAGetLastError() << std::endl; - return interfaces; - } +std::map kiwix::getNetworkInterfacesWin() { + std::map interfaces; - int nNumInterfaces = nBytesReturned / sizeof(INTERFACE_INFO); - for (int i = 0; i < nNumInterfaces; ++i) { - sockaddr_in *pAddress; - pAddress = (sockaddr_in *) & (InterfaceList[i].iiAddress.AddressIn); - if(pAddress->sin_family == AF_INET) { - /* Add to the map */ - std::string interfaceName = std::string(inet_ntoa(pAddress->sin_addr)); - interfaces[interfaceName] = interfaceName; - } - } -#else - /* Get Network interfaces information */ - char buf[16384]; - struct ifconf ifconf; - int fd = socket(PF_INET, SOCK_DGRAM, 0); /* Only IPV4 */ - ifconf.ifc_len = sizeof(buf); - ifconf.ifc_buf=buf; - if(ioctl(fd, SIOCGIFCONF, &ifconf)!=0) { - perror("ioctl(SIOCGIFCONF)"); - } + const int working_buffer_size = 15000; + const int max_tries = 3; - /* Go through each interface */ - struct ifreq *ifreq; - ifreq = ifconf.ifc_req; - for (int i = 0; i < ifconf.ifc_len; ) { - if (ifreq->ifr_addr.sa_family == AF_INET) { - /* Get the network interface ip */ - char host[128] = { 0 }; - const int error = getnameinfo(&(ifreq->ifr_addr), sizeof(ifreq->ifr_addr), - host, sizeof(host), - 0, 0, NI_NUMERICHOST); - if (!error) { - std::string interfaceName = std::string(ifreq->ifr_name); - std::string interfaceIp = std::string(host); - /* Add to the map */ - interfaces[interfaceName] = interfaceIp; - } else { - perror("getnameinfo()"); - } + ULONG flags = GAA_FLAG_INCLUDE_PREFIX; + + // default to unspecified address family (both) + ULONG family = AF_UNSPEC; + + ULONG outBufLen = working_buffer_size; + ULONG Iterations = 0; + DWORD dwRetVal = 0; + PIP_ADAPTER_ADDRESSES interfacesHead = NULL; + + // Successively allocate the required memory until GetAdaptersAddresses does not + // results in ERROR_BUFFER_OVERFLOW for a maximum of max_tries + do{ + + interfacesHead = (IP_ADAPTER_ADDRESSES *) malloc(outBufLen); + if (interfacesHead == NULL) { + std::cerr << "Memory allocation failed for IP_ADAPTER_ADDRESSES struct" << std::endl; + return interfaces; } - /* some systems have ifr_addr.sa_len and adjust the length that - * way, but not mine. weird */ - size_t len; -#ifndef __linux__ - len = IFNAMSIZ + ifreq->ifr_addr.sa_len; -#else - len = sizeof(*ifreq); -#endif - ifreq = (struct ifreq*)((char*)ifreq+len); - i += len; + dwRetVal = GetAdaptersAddresses(family, flags, NULL, interfacesHead, &outBufLen); + + } while ((dwRetVal == ERROR_BUFFER_OVERFLOW) && (Iterations < max_tries)); + + if (dwRetVal == NO_ERROR) { + PIP_ADAPTER_UNICAST_ADDRESS pUnicast = NULL; + unsigned int i = 0; + for (PIP_ADAPTER_ADDRESSES temp = interfacesHead; temp != NULL; + temp = temp->Next) { + pUnicast = temp->FirstUnicastAddress; + if (pUnicast != NULL) { + for (i = 0; pUnicast != NULL; i++){ + if (pUnicast->Address.lpSockaddr->sa_family == AF_INET) + { + sockaddr_in *si = (sockaddr_in *)(pUnicast->Address.lpSockaddr); + char host[INET_ADDRSTRLEN]={0}; + inet_ntop(AF_INET, &(si->sin_addr), host, sizeof(host)); + interfaces[temp->AdapterName].addr=host; + } + else if (pUnicast->Address.lpSockaddr->sa_family == AF_INET6) + { + sockaddr_in6 *si = (sockaddr_in6 *)(pUnicast->Address.lpSockaddr); + char host[INET6_ADDRSTRLEN]={0}; + inet_ntop(AF_INET6, &(si->sin6_addr), host, sizeof(host)); + if (!IN6_IS_ADDR_LINKLOCAL(&(si->sin6_addr))) + interfaces[temp->AdapterName].addr6=host; + } + pUnicast = pUnicast->Next; + } + } + } + } else { + std::cerr << "Call to GetAdaptersAddresses failed with error: "<< dwRetVal << std::endl; } -#endif + + if (interfacesHead) free(interfacesHead); + return interfaces; } -std::string kiwix::getBestPublicIp() { - auto interfaces = getNetworkInterfaces(); +#else + +std::map kiwix::getNetworkInterfacesPosix() { + std::map interfaces; + + struct ifaddrs *interfacesHead; + if (getifaddrs(&interfacesHead) == -1) { + perror("getifaddrs"); + } + + for (ifaddrs *temp = interfacesHead; temp != NULL; temp = temp->ifa_next) { + if (temp->ifa_addr == NULL) continue; + + if (temp->ifa_addr->sa_family == AF_INET) { + sockaddr_in *si = (sockaddr_in *)(temp->ifa_addr); + char host[INET_ADDRSTRLEN] = {0}; + inet_ntop(AF_INET, &(si->sin_addr), host, sizeof(host)); + interfaces[temp->ifa_name].addr=host; + } else if (temp->ifa_addr->sa_family == AF_INET6) { + sockaddr_in6 *si = (sockaddr_in6 *)(temp->ifa_addr); + char host[INET6_ADDRSTRLEN] = {0}; + inet_ntop(AF_INET6, &(si->sin6_addr), host, sizeof(host)); + if (!IN6_IS_ADDR_LINKLOCAL(&(si->sin6_addr))) + interfaces[temp->ifa_name].addr6=host; + } + } + + freeifaddrs(interfacesHead); + + return interfaces; +} + +#endif + +std::map kiwix::getNetworkInterfaces() { + std::map interfaces; + +#ifdef _WIN32 + return getNetworkInterfacesWin(); +#else + return getNetworkInterfacesPosix(); +#endif + +} + +std::string kiwix::getBestPublicIp(bool ipv6) { + ip_addr bestPublicIp = ip_addr{"127.0.0.1","::1"}; + std::map interfaces = getNetworkInterfaces(); #ifndef _WIN32 const char* const prioritizedNames[] = { "eth0", "eth1", "wlan0", "wlan1", "en0", "en1" }; for(auto name: prioritizedNames) { - auto it = interfaces.find(name); - if(it != interfaces.end()) { - return it->second; + auto it=interfaces.find(name); + if(it != interfaces.end() && !(ipv6 && (*it).second.addr6.empty())) { + bestPublicIp = (*it).second; + break; } } #endif @@ -166,12 +209,12 @@ std::string kiwix::getBestPublicIp() { const char* const prefixes[] = { "192.168", "172.16.", "10.0" }; for(auto prefix : prefixes){ for(auto& itr : interfaces) { - auto interfaceIp = itr.second; - if (interfaceIp.find(prefix) == 0) { - return interfaceIp; + std::string interfaceIp(itr.second.addr); + if (interfaceIp.find(prefix) == 0 && !(ipv6 && itr.second.addr6.empty())) { + bestPublicIp = itr.second; + break; } } } - - return "127.0.0.1"; + return ipv6 ? bestPublicIp.addr6 : bestPublicIp.addr; } From b7eadf95bf2e8b7e1c1abf272dd37dda58bdbe6a Mon Sep 17 00:00:00 2001 From: Aryan Arora Date: Wed, 24 Apr 2024 15:41:40 +0530 Subject: [PATCH 2/2] handle ip modes & add compilation flags for windows build --- include/common.h | 1 + include/server.h | 7 ++++--- include/tools.h | 18 +++--------------- meson.build | 11 ++++------- src/server.cpp | 6 +++--- src/server/internalServer.cpp | 33 +++++++++++++++++++-------------- src/server/internalServer.h | 6 +++--- src/tools/networkTools.cpp | 19 +++++++------------ 8 files changed, 44 insertions(+), 57 deletions(-) diff --git a/include/common.h b/include/common.h index 5df556e9d..995993bcb 100644 --- a/include/common.h +++ b/include/common.h @@ -16,6 +16,7 @@ namespace kiwix { +enum class IpMode { ipv4, ipv6, all }; typedef zim::size_type size_type; typedef zim::offset_type offset_type; diff --git a/include/server.h b/include/server.h index 1633aefe0..d44c5207a 100644 --- a/include/server.h +++ b/include/server.h @@ -22,6 +22,7 @@ #include #include +#include "common.h" namespace kiwix { @@ -62,10 +63,10 @@ namespace kiwix { m_withTaskbar = withTaskbar; m_withLibraryButton = withLibraryButton; } void setBlockExternalLinks(bool blockExternalLinks) { m_blockExternalLinks = blockExternalLinks; } - void setIPv6(bool ipv6) { m_ipv6 = ipv6; } + void setIpMode(IpMode mode) { m_ipMode = mode; } int getPort(); std::string getAddress(); - bool isAddressIPv6(); + IpMode getIpMode() const; protected: std::shared_ptr mp_library; @@ -80,7 +81,7 @@ namespace kiwix bool m_withTaskbar = true; bool m_withLibraryButton = true; bool m_blockExternalLinks = false; - bool m_ipv6 = false; + IpMode m_ipMode = IpMode::ipv4; int m_ipConnectionLimit = 0; std::unique_ptr mp_server; }; diff --git a/include/tools.h b/include/tools.h index ce42ae44e..8ac60312e 100644 --- a/include/tools.h +++ b/include/tools.h @@ -25,12 +25,12 @@ #include #include -struct ip_addr{ +namespace kiwix { +struct IpAddress{ std::string addr; std::string addr6; }; -namespace kiwix { typedef std::pair LangNameCodePair; typedef std::vector FeedLanguages; typedef std::vector FeedCategories; @@ -220,23 +220,11 @@ bool fileReadable(const std::string& path); */ std::string getMimeTypeForFile(const std::string& filename); -/** Provides all available network interfaces on Windows - * - * This function provides the available IPv4 and IPv6 network interfaces - */ -std::map getNetworkInterfacesWin(); - -/** Provides all available network interfaces on Posix - * - * This function provides the available IPv4 and IPv6 network interfaces - */ -std::map getNetworkInterfacesPosix(); - /** Provides all available network interfaces * * This function provides the available IPv4 and IPv6 network interfaces */ -std::map getNetworkInterfaces(); +std::map getNetworkInterfaces(); /** Provides the best IP address * This function provides the best IP address from the list given by getNetworkInterfaces diff --git a/meson.build b/meson.build index 50c8afe4e..582ef5eff 100644 --- a/meson.build +++ b/meson.build @@ -48,7 +48,10 @@ if host_machine.system() == 'windows' and static_deps endif if host_machine.system() == 'windows' - add_project_arguments('-DNOMINMAX', language: 'cpp') + add_project_arguments('-DNOMINMAX', language: 'cpp') + extra_libs += ['-liphlpapi'] +else + extra_link_args = [] endif all_deps = [thread_dep, libicu_dep, libzim_dep, pugixml_dep, libcurl_dep, microhttpd_dep, zlib_dep, xapian_dep] @@ -58,12 +61,6 @@ inc = include_directories('include', extra_include) conf = configuration_data() conf.set('LIBKIWIX_VERSION', '"@0@"'.format(meson.project_version())) -if build_machine.system() == 'windows' - extra_link_args = ['-lshlwapi', '-lwinmm'] -else - extra_link_args = [] -endif - subdir('include') subdir('scripts') subdir('static') diff --git a/src/server.cpp b/src/server.cpp index 53d938871..89491c371 100644 --- a/src/server.cpp +++ b/src/server.cpp @@ -51,7 +51,7 @@ bool Server::start() { m_withTaskbar, m_withLibraryButton, m_blockExternalLinks, - m_ipv6, + m_ipMode, m_indexTemplateString, m_ipConnectionLimit)); return mp_server->start(); @@ -85,9 +85,9 @@ std::string Server::getAddress() return mp_server->getAddress(); } -bool Server::isAddressIPv6() +IpMode Server::getIpMode() const { - return mp_server->isAddressIPv6(); + return mp_server->getIpMode(); } } diff --git a/src/server/internalServer.cpp b/src/server/internalServer.cpp index 560059d47..ccc94a49b 100644 --- a/src/server/internalServer.cpp +++ b/src/server/internalServer.cpp @@ -416,7 +416,7 @@ InternalServer::InternalServer(LibraryPtr library, bool withTaskbar, bool withLibraryButton, bool blockExternalLinks, - bool ipv6, + IpMode ipMode, std::string indexTemplateString, int ipConnectionLimit) : m_addr(addr), @@ -429,7 +429,7 @@ InternalServer::InternalServer(LibraryPtr library, m_withTaskbar(withTaskbar), m_withLibraryButton(withLibraryButton), m_blockExternalLinks(blockExternalLinks), - m_ipv6(ipv6), + m_ipMode(ipMode), m_indexTemplateString(indexTemplateString.empty() ? RESOURCE::templates::index_html : indexTemplateString), m_ipConnectionLimit(ipConnectionLimit), mp_daemon(nullptr), @@ -466,22 +466,27 @@ bool InternalServer::start() { sockAddr6.sin6_addr = in6addr_any; sockAddr4.sin_addr.s_addr = htonl(INADDR_ANY); } - m_addr = kiwix::getBestPublicIp(m_ipv6); - std::cout< handle_request(const RequestContext& request); @@ -176,7 +176,7 @@ class InternalServer { bool m_withTaskbar; bool m_withLibraryButton; bool m_blockExternalLinks; - bool m_ipv6; + IpMode m_ipMode; std::string m_indexTemplateString; int m_ipConnectionLimit; struct MHD_Daemon* mp_daemon; diff --git a/src/tools/networkTools.cpp b/src/tools/networkTools.cpp index b113f810d..c1b61335f 100644 --- a/src/tools/networkTools.cpp +++ b/src/tools/networkTools.cpp @@ -79,8 +79,8 @@ std::string kiwix::download(const std::string& url) { #ifdef _WIN32 -std::map kiwix::getNetworkInterfacesWin() { - std::map interfaces; +std::map getNetworkInterfacesWin() { + std::map interfaces; const int working_buffer_size = 15000; const int max_tries = 3; @@ -98,7 +98,6 @@ std::map kiwix::getNetworkInterfacesWin() { // Successively allocate the required memory until GetAdaptersAddresses does not // results in ERROR_BUFFER_OVERFLOW for a maximum of max_tries do{ - interfacesHead = (IP_ADAPTER_ADDRESSES *) malloc(outBufLen); if (interfacesHead == NULL) { std::cerr << "Memory allocation failed for IP_ADAPTER_ADDRESSES struct" << std::endl; @@ -106,7 +105,6 @@ std::map kiwix::getNetworkInterfacesWin() { } dwRetVal = GetAdaptersAddresses(family, flags, NULL, interfacesHead, &outBufLen); - } while ((dwRetVal == ERROR_BUFFER_OVERFLOW) && (Iterations < max_tries)); if (dwRetVal == NO_ERROR) { @@ -147,8 +145,8 @@ std::map kiwix::getNetworkInterfacesWin() { #else -std::map kiwix::getNetworkInterfacesPosix() { - std::map interfaces; +std::map getNetworkInterfacesPosix() { + std::map interfaces; struct ifaddrs *interfacesHead; if (getifaddrs(&interfacesHead) == -1) { @@ -179,20 +177,17 @@ std::map kiwix::getNetworkInterfacesPosix() { #endif -std::map kiwix::getNetworkInterfaces() { - std::map interfaces; - +std::map kiwix::getNetworkInterfaces() { #ifdef _WIN32 return getNetworkInterfacesWin(); #else return getNetworkInterfacesPosix(); #endif - } std::string kiwix::getBestPublicIp(bool ipv6) { - ip_addr bestPublicIp = ip_addr{"127.0.0.1","::1"}; - std::map interfaces = getNetworkInterfaces(); + kiwix::IpAddress bestPublicIp = kiwix::IpAddress{"127.0.0.1","::1"}; + std::map interfaces = getNetworkInterfaces(); #ifndef _WIN32 const char* const prioritizedNames[] =