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; }