From 8930095c526e727f1a9e21dec115957a8aa3e96b Mon Sep 17 00:00:00 2001 From: sgourdas Date: Tue, 24 Sep 2024 20:08:11 +0300 Subject: [PATCH 1/5] Make IpMode members uppercase --- include/common.h | 2 +- include/server.h | 2 +- src/server/internalServer.cpp | 10 +++++----- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/include/common.h b/include/common.h index 995993bcb..e97d025e6 100644 --- a/include/common.h +++ b/include/common.h @@ -16,7 +16,7 @@ namespace kiwix { -enum class IpMode { ipv4, ipv6, all }; +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 d44c5207a..250bace72 100644 --- a/include/server.h +++ b/include/server.h @@ -81,7 +81,7 @@ namespace kiwix bool m_withTaskbar = true; bool m_withLibraryButton = true; bool m_blockExternalLinks = false; - IpMode m_ipMode = IpMode::ipv4; + IpMode m_ipMode = IpMode::IPV4; int m_ipConnectionLimit = 0; std::unique_ptr mp_server; }; diff --git a/src/server/internalServer.cpp b/src/server/internalServer.cpp index 476e1a18c..95a26349b 100644 --- a/src/server/internalServer.cpp +++ b/src/server/internalServer.cpp @@ -466,25 +466,25 @@ bool InternalServer::start() { sockAddr6.sin6_addr = in6addr_any; sockAddr4.sin_addr.s_addr = htonl(INADDR_ANY); } - m_addr = kiwix::getBestPublicIp(m_ipMode == IpMode::ipv6 || m_ipMode == IpMode::all); + m_addr = kiwix::getBestPublicIp(m_ipMode == IpMode::IPV6 || m_ipMode == IpMode::ALL); } else { bool ipv6 = inet_pton(AF_INET6, m_addr.c_str(), &(sockAddr6.sin6_addr.s6_addr)) == 1; bool ipv4 = inet_pton(AF_INET, m_addr.c_str(), &(sockAddr4.sin_addr.s_addr)) == 1; if (ipv6){ - m_ipMode = IpMode::all; + m_ipMode = IpMode::ALL; } else if (!ipv4) { std::cerr << "Ip address " << m_addr << " is not a valid ip address" << std::endl; return false; } } - if (m_ipMode == IpMode::all) { + if (m_ipMode == IpMode::ALL) { flags|=MHD_USE_DUAL_STACK; - } else if (m_ipMode == IpMode::ipv6) { + } else if (m_ipMode == IpMode::IPV6) { flags|=MHD_USE_IPv6; } - struct sockaddr* sockaddr = (m_ipMode==IpMode::all || m_ipMode==IpMode::ipv6) + struct sockaddr* sockaddr = (m_ipMode==IpMode::ALL || m_ipMode==IpMode::IPV6) ? (struct sockaddr*)&sockAddr6 : (struct sockaddr*)&sockAddr4; From 02ab2ce5a519890bfbb0a07310388165e91bb305 Mon Sep 17 00:00:00 2001 From: sgourdas Date: Tue, 17 Sep 2024 14:25:09 +0300 Subject: [PATCH 2/5] Make server getters const --- include/server.h | 4 ++-- src/server.cpp | 4 ++-- src/server/internalServer.h | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/include/server.h b/include/server.h index 250bace72..09689d867 100644 --- a/include/server.h +++ b/include/server.h @@ -64,8 +64,8 @@ namespace kiwix void setBlockExternalLinks(bool blockExternalLinks) { m_blockExternalLinks = blockExternalLinks; } void setIpMode(IpMode mode) { m_ipMode = mode; } - int getPort(); - std::string getAddress(); + int getPort() const; + std::string getAddress() const; IpMode getIpMode() const; protected: diff --git a/src/server.cpp b/src/server.cpp index 89491c371..207852a6c 100644 --- a/src/server.cpp +++ b/src/server.cpp @@ -75,12 +75,12 @@ void Server::setRoot(const std::string& root) } } -int Server::getPort() +int Server::getPort() const { return mp_server->getPort(); } -std::string Server::getAddress() +std::string Server::getAddress() const { return mp_server->getAddress(); } diff --git a/src/server/internalServer.h b/src/server/internalServer.h index 14694fd19..85ca2cbdb 100644 --- a/src/server/internalServer.h +++ b/src/server/internalServer.h @@ -117,8 +117,8 @@ class InternalServer { void** cont_cls); bool start(); void stop(); - std::string getAddress() { return m_addr; } - int getPort() { return m_port; } + std::string getAddress() const { return m_addr; } + int getPort() const { return m_port; } IpMode getIpMode() const { return m_ipMode; } private: // functions From 534916929d140156265d2a0ac7848205d6012de6 Mon Sep 17 00:00:00 2001 From: sgourdas Date: Thu, 19 Sep 2024 11:21:18 +0300 Subject: [PATCH 3/5] Line cleanup --- src/tools/networkTools.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/tools/networkTools.cpp b/src/tools/networkTools.cpp index 04555f661..602194eab 100644 --- a/src/tools/networkTools.cpp +++ b/src/tools/networkTools.cpp @@ -217,8 +217,7 @@ std::string getBestPublicIp(bool ipv6) { std::map interfaces = getNetworkInterfacesIPv4Or6(); #ifndef _WIN32 - const char* const prioritizedNames[] = - { "eth0", "eth1", "wlan0", "wlan1", "en0", "en1" }; + const char* const prioritizedNames[] = { "eth0", "eth1", "wlan0", "wlan1", "en0", "en1" }; for(auto name: prioritizedNames) { auto it=interfaces.find(name); if(it != interfaces.end() && !(ipv6 && (*it).second.addr6.empty())) { From b80699916d6b373ee2f62eabc26b54d0760803bf Mon Sep 17 00:00:00 2001 From: sgourdas Date: Mon, 23 Sep 2024 13:55:49 +0300 Subject: [PATCH 4/5] Refactor getBestPublicIp for both protocols --- include/common.h | 2 +- include/server.h | 10 ++++---- include/tools.h | 6 +++-- src/server.cpp | 14 ++++++++++- src/server/internalServer.cpp | 35 +++++++++++++++++----------- src/server/internalServer.h | 7 +++--- src/tools/networkTools.cpp | 44 ++++++++++++++++++----------------- test/otherTools.cpp | 10 ++++---- 8 files changed, 76 insertions(+), 52 deletions(-) diff --git a/include/common.h b/include/common.h index e97d025e6..2c680e1fe 100644 --- a/include/common.h +++ b/include/common.h @@ -16,7 +16,7 @@ namespace kiwix { -enum class IpMode { IPV4, IPV6, ALL }; +enum class IpMode { IPV4, IPV6, ALL, AUTO }; // AUTO: Server decides the protocol typedef zim::size_type size_type; typedef zim::offset_type offset_type; diff --git a/include/server.h b/include/server.h index 09689d867..2825c4fcb 100644 --- a/include/server.h +++ b/include/server.h @@ -22,7 +22,7 @@ #include #include -#include "common.h" +#include "tools.h" namespace kiwix { @@ -52,7 +52,7 @@ namespace kiwix void stop(); void setRoot(const std::string& root); - void setAddress(const std::string& addr) { m_addr = addr; } + void setAddress(const std::string& addr); void setPort(int port) { m_port = port; } void setNbThreads(int threads) { m_nbThreads = threads; } void setMultiZimSearchLimit(unsigned int limit) { m_multizimSearchLimit = limit; } @@ -65,14 +65,14 @@ namespace kiwix { m_blockExternalLinks = blockExternalLinks; } void setIpMode(IpMode mode) { m_ipMode = mode; } int getPort() const; - std::string getAddress() const; + IpAddress getAddress() const; IpMode getIpMode() const; protected: std::shared_ptr mp_library; std::shared_ptr mp_nameMapper; std::string m_root = ""; - std::string m_addr = ""; + IpAddress m_addr; std::string m_indexTemplateString = ""; int m_port = 80; int m_nbThreads = 1; @@ -81,7 +81,7 @@ namespace kiwix bool m_withTaskbar = true; bool m_withLibraryButton = true; bool m_blockExternalLinks = false; - IpMode m_ipMode = IpMode::IPV4; + IpMode m_ipMode = IpMode::AUTO; int m_ipConnectionLimit = 0; std::unique_ptr mp_server; }; diff --git a/include/tools.h b/include/tools.h index 3742f8aeb..32ef5a27a 100644 --- a/include/tools.h +++ b/include/tools.h @@ -24,6 +24,7 @@ #include #include #include +#include "common.h" namespace kiwix { @@ -214,9 +215,10 @@ std::map getNetworkInterfacesIPv4Or6(); std::map getNetworkInterfaces(); /** Provides the best IP address - * This function provides the best IP address from the list given by getNetworkInterfacesIPv4Or6() + * This function provides the best IP addresses for both ipv4 and ipv6 protocols, + * in an IpAddress struct, based on the list given by getNetworkInterfacesIPv4Or6() */ -std::string getBestPublicIp(bool ipv6); +IpAddress getBestPublicIps(); /** Provides the best IPv4 adddress * Equivalent to getBestPublicIp(false). Provided for backward compatibility diff --git a/src/server.cpp b/src/server.cpp index 207852a6c..b2db70217 100644 --- a/src/server.cpp +++ b/src/server.cpp @@ -75,12 +75,24 @@ void Server::setRoot(const std::string& root) } } +// FIXME: this method is implemented under the assumption that it is invoked only once (per object). +void Server::setAddress(const std::string& addr) +{ + if (addr.empty()) return; + + if (addr.find(':') != std::string::npos) { // IPv6 + m_addr.addr6 = (addr[0] == '[') ? addr.substr(1, addr.length() - 2) : addr; // Remove brackets if any + } else { + m_addr.addr = addr; + } +} + int Server::getPort() const { return mp_server->getPort(); } -std::string Server::getAddress() const +IpAddress Server::getAddress() const { return mp_server->getAddress(); } diff --git a/src/server/internalServer.cpp b/src/server/internalServer.cpp index 95a26349b..cee7c194a 100644 --- a/src/server/internalServer.cpp +++ b/src/server/internalServer.cpp @@ -407,7 +407,7 @@ class InternalServer::CustomizedResources : public std::map nameMapper, - std::string addr, + IpAddress addr, int port, std::string root, int nbThreads, @@ -461,21 +461,30 @@ bool InternalServer::start() { sockAddr6.sin6_family = AF_INET6; sockAddr6.sin6_port = htons(m_port); - if (m_addr.empty()) { - if (0 != INADDR_ANY) { - sockAddr6.sin6_addr = in6addr_any; - sockAddr4.sin_addr.s_addr = htonl(INADDR_ANY); - } - m_addr = kiwix::getBestPublicIp(m_ipMode == IpMode::IPV6 || m_ipMode == IpMode::ALL); + if (m_addr.addr.empty() && m_addr.addr6.empty()) { // No ip address provided + if (m_ipMode == IpMode::AUTO) m_ipMode = IpMode::ALL; + sockAddr6.sin6_addr = in6addr_any; + sockAddr4.sin_addr.s_addr = htonl(INADDR_ANY); + IpAddress bestIps = kiwix::getBestPublicIps(); + if (m_ipMode == IpMode::IPV4 || m_ipMode == IpMode::ALL) m_addr.addr = bestIps.addr; + if (m_ipMode == IpMode::IPV6 || m_ipMode == IpMode::ALL) m_addr.addr6 = bestIps.addr6; } else { - bool ipv6 = inet_pton(AF_INET6, m_addr.c_str(), &(sockAddr6.sin6_addr.s6_addr)) == 1; - bool ipv4 = inet_pton(AF_INET, m_addr.c_str(), &(sockAddr4.sin_addr.s_addr)) == 1; - if (ipv6){ - m_ipMode = IpMode::ALL; - } else if (!ipv4) { - std::cerr << "Ip address " << m_addr << " is not a valid ip address" << std::endl; + const std::string addr = !m_addr.addr.empty() ? m_addr.addr : m_addr.addr6; + + if (m_ipMode != kiwix::IpMode::AUTO) { + std::cerr << "ERROR: When an IP address is provided the IP mode must not be set" << std::endl; return false; } + + bool validV4 = inet_pton(AF_INET, m_addr.addr.c_str(), &(sockAddr4.sin_addr.s_addr)) == 1; + bool validV6 = inet_pton(AF_INET6, m_addr.addr6.c_str(), &(sockAddr6.sin6_addr.s6_addr)) == 1; + + if (!validV4 && !validV6) { + std::cerr << "ERROR: invalid IP address: " << addr << std::endl; + return false; + } + + m_ipMode = !m_addr.addr.empty() ? IpMode::IPV4 : IpMode::IPV6; } if (m_ipMode == IpMode::ALL) { diff --git a/src/server/internalServer.h b/src/server/internalServer.h index 85ca2cbdb..b0831fefe 100644 --- a/src/server/internalServer.h +++ b/src/server/internalServer.h @@ -27,6 +27,7 @@ extern "C" { #include "library.h" #include "name_mapper.h" +#include "tools.h" #include #include @@ -94,7 +95,7 @@ class InternalServer { public: InternalServer(LibraryPtr library, std::shared_ptr nameMapper, - std::string addr, + IpAddress addr, int port, std::string root, int nbThreads, @@ -117,7 +118,7 @@ class InternalServer { void** cont_cls); bool start(); void stop(); - std::string getAddress() const { return m_addr; } + IpAddress getAddress() const { return m_addr; } int getPort() const { return m_port; } IpMode getIpMode() const { return m_ipMode; } @@ -166,7 +167,7 @@ class InternalServer { typedef ConcurrentCache> SuggestionSearcherCache; private: // data - std::string m_addr; + IpAddress m_addr; int m_port; std::string m_root; // URI-encoded std::string m_rootPrefixOfDecodedURL; // URI-decoded diff --git a/src/tools/networkTools.cpp b/src/tools/networkTools.cpp index 602194eab..903c839c6 100644 --- a/src/tools/networkTools.cpp +++ b/src/tools/networkTools.cpp @@ -19,6 +19,7 @@ */ #include "tools.h" +#include "stringTools.h" #include #include @@ -62,6 +63,12 @@ size_t write_callback_to_iss(char* ptr, size_t size, size_t nmemb, void* userdat return nmemb; } +void updatePublicIpAddress(IpAddress& publicIpAddr, const IpAddress& interfaceIpAddr) +{ + if (publicIpAddr.addr.empty()) publicIpAddr.addr = interfaceIpAddr.addr; + if (publicIpAddr.addr6.empty()) publicIpAddr.addr6 = interfaceIpAddr.addr6; +} + } // unnamed namespace std::string download(const std::string& url) { @@ -85,7 +92,6 @@ std::string download(const std::string& url) { return ss.str(); } - namespace { @@ -211,39 +217,35 @@ std::map getNetworkInterfaces() { return result; } - -std::string getBestPublicIp(bool ipv6) { - IpAddress bestPublicIp = IpAddress{"127.0.0.1","::1"}; +IpAddress getBestPublicIps() { + IpAddress bestPublicIps; std::map interfaces = getNetworkInterfacesIPv4Or6(); - #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() && !(ipv6 && (*it).second.addr6.empty())) { - bestPublicIp = (*it).second; - break; + for (const auto& name : prioritizedNames) { + const auto it = interfaces.find(name); + if (it != interfaces.end()) { + updatePublicIpAddress(bestPublicIps, it->second); } } #endif - - const char* const prefixes[] = { "192.168", "172.16.", "10.0" }; - for(auto prefix : prefixes){ - for(auto& itr : interfaces) { - std::string interfaceIp(itr.second.addr); - if (interfaceIp.find(prefix) == 0 && !(ipv6 && itr.second.addr6.empty())) { - bestPublicIp = itr.second; - break; + const char* const v4prefixes[] = { "192.168", "172.16", "10.0", "169.254" }; + for (const auto& prefix : v4prefixes) { + for (const auto& [_, interfaceIps] : interfaces) { + if (kiwix::startsWith(interfaceIps.addr, prefix)) { + updatePublicIpAddress(bestPublicIps, interfaceIps); } } } - return ipv6 ? bestPublicIp.addr6 : bestPublicIp.addr; -} + updatePublicIpAddress(bestPublicIps, {"127.0.0.1", "::1"}); + + return bestPublicIps; +} std::string getBestPublicIp() { - return getBestPublicIp(false); + return getBestPublicIps().addr; } } // namespace kiwix diff --git a/test/otherTools.cpp b/test/otherTools.cpp index 3ffca0456..bac2dd2e9 100644 --- a/test/otherTools.cpp +++ b/test/otherTools.cpp @@ -18,6 +18,7 @@ */ #include "gtest/gtest.h" +#include "../include/tools.h" #include "../src/tools/otherTools.h" #include "zim/suggestion_iterator.h" #include "../src/server/i18n_utils.h" @@ -252,11 +253,8 @@ TEST(networkTools, getNetworkInterfaces) } } -TEST(networkTools, getBestPublicIp) +TEST(networkTools, getBestPublicIps) { - using kiwix::getBestPublicIp; - - std::cout << "getBestPublicIp(true) " << getBestPublicIp(true) << std::endl; - std::cout << "getBestPublicIp(false) " << getBestPublicIp(false) << std::endl; - std::cout << "getBestPublicIp() " << getBestPublicIp() << std::endl; + std::cout << "getBestPublicIps(): " << "[" << kiwix::getBestPublicIps().addr << ", " << kiwix::getBestPublicIps().addr6 << "]" << std::endl; + std::cout << "getBestPublicIp(): " << kiwix::getBestPublicIp() << std::endl; } From 95529d2c0af4bf4de3d779a7f17035be6b7f9495 Mon Sep 17 00:00:00 2001 From: sgourdas Date: Mon, 23 Sep 2024 19:18:57 +0300 Subject: [PATCH 5/5] Add ip availability check in server start --- src/server/internalServer.cpp | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/src/server/internalServer.cpp b/src/server/internalServer.cpp index cee7c194a..06ff30565 100644 --- a/src/server/internalServer.cpp +++ b/src/server/internalServer.cpp @@ -85,6 +85,19 @@ namespace kiwix { namespace { +bool ipAvailable(const std::string addr) +{ + auto interfaces = kiwix::getNetworkInterfacesIPv4Or6(); + + for (const auto& [_, interfaceIps] : interfaces) { + if ((interfaceIps.addr == addr) || (interfaceIps.addr6 == addr)) { + return true; + } + } + + return false; +} + inline std::string normalizeRootUrl(std::string rootUrl) { while ( !rootUrl.empty() && rootUrl.back() == '/' ) @@ -484,6 +497,11 @@ bool InternalServer::start() { return false; } + if (!ipAvailable(addr)) { + std::cerr << "ERROR: IP address is not available on this system: " << addr << std::endl; + return false; + } + m_ipMode = !m_addr.addr.empty() ? IpMode::IPV4 : IpMode::IPV6; }