Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor getBestPublicIp for all valid ips #1132

Merged
merged 5 commits into from
Oct 2, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion include/common.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down
12 changes: 6 additions & 6 deletions include/server.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@

#include <string>
#include <memory>
#include "common.h"
#include "tools.h"

namespace kiwix
{
Expand Down Expand Up @@ -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; }
Expand All @@ -64,15 +64,15 @@ namespace kiwix
void setBlockExternalLinks(bool blockExternalLinks)
{ m_blockExternalLinks = blockExternalLinks; }
void setIpMode(IpMode mode) { m_ipMode = mode; }
int getPort();
std::string getAddress();
int getPort() const;
IpAddress getAddress() const;
sgourdas marked this conversation as resolved.
Show resolved Hide resolved
IpMode getIpMode() const;

protected:
std::shared_ptr<Library> mp_library;
std::shared_ptr<NameMapper> 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;
Expand All @@ -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<InternalServer> mp_server;
};
Expand Down
6 changes: 4 additions & 2 deletions include/tools.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
#include <vector>
#include <map>
#include <cstdint>
#include "common.h"

namespace kiwix
{
Expand Down Expand Up @@ -214,9 +215,10 @@ std::map<std::string, IpAddress> getNetworkInterfacesIPv4Or6();
std::map<std::string, std::string> 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
sgourdas marked this conversation as resolved.
Show resolved Hide resolved
Expand Down
16 changes: 14 additions & 2 deletions src/server.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -75,12 +75,24 @@
}
}

int Server::getPort()
// 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
sgourdas marked this conversation as resolved.
Show resolved Hide resolved
} else {
m_addr.addr = addr;
}
}
sgourdas marked this conversation as resolved.
Show resolved Hide resolved

int Server::getPort() const

Check warning on line 90 in src/server.cpp

View check run for this annotation

Codecov / codecov/patch

src/server.cpp#L90

Added line #L90 was not covered by tests
{
return mp_server->getPort();
}

std::string Server::getAddress()
IpAddress Server::getAddress() const

Check warning on line 95 in src/server.cpp

View check run for this annotation

Codecov / codecov/patch

src/server.cpp#L95

Added line #L95 was not covered by tests
{
return mp_server->getAddress();
}
Expand Down
59 changes: 43 additions & 16 deletions src/server/internalServer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,19 @@
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;

Check warning on line 98 in src/server/internalServer.cpp

View check run for this annotation

Codecov / codecov/patch

src/server/internalServer.cpp#L98

Added line #L98 was not covered by tests
}

inline std::string normalizeRootUrl(std::string rootUrl)
{
while ( !rootUrl.empty() && rootUrl.back() == '/' )
Expand Down Expand Up @@ -407,7 +420,7 @@

InternalServer::InternalServer(LibraryPtr library,
std::shared_ptr<NameMapper> nameMapper,
std::string addr,
IpAddress addr,
int port,
std::string root,
int nbThreads,
Expand Down Expand Up @@ -444,47 +457,61 @@

InternalServer::~InternalServer() = default;

bool InternalServer::start() {
#ifdef _WIN32
int flags = MHD_USE_SELECT_INTERNALLY;
#else
int flags = MHD_USE_POLL_INTERNALLY;
#endif
if (m_verbose.load())
flags |= MHD_USE_DEBUG;


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) {
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);

Check warning on line 480 in src/server/internalServer.cpp

View check run for this annotation

Codecov / codecov/patch

src/server/internalServer.cpp#L479-L480

Added lines #L479 - L480 were not covered by tests
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;

Check warning on line 489 in src/server/internalServer.cpp

View check run for this annotation

Codecov / codecov/patch

src/server/internalServer.cpp#L489

Added line #L489 was not covered by tests
}

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;
sgourdas marked this conversation as resolved.
Show resolved Hide resolved

if (!validV4 && !validV6) {
std::cerr << "ERROR: invalid IP address: " << addr << std::endl;
return false;

Check warning on line 497 in src/server/internalServer.cpp

View check run for this annotation

Codecov / codecov/patch

src/server/internalServer.cpp#L497

Added line #L497 was not covered by tests
}

if (!ipAvailable(addr)) {
sgourdas marked this conversation as resolved.
Show resolved Hide resolved
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;
}

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;

Expand Down
9 changes: 5 additions & 4 deletions src/server/internalServer.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@

#include "library.h"
#include "name_mapper.h"
#include "tools.h"

#include <zim/search.h>
#include <zim/suggestion.h>
Expand Down Expand Up @@ -94,7 +95,7 @@
public:
InternalServer(LibraryPtr library,
std::shared_ptr<NameMapper> nameMapper,
std::string addr,
IpAddress addr,
int port,
std::string root,
int nbThreads,
Expand All @@ -117,8 +118,8 @@
void** cont_cls);
bool start();
void stop();
std::string getAddress() { return m_addr; }
int getPort() { return m_port; }
IpAddress getAddress() const { return m_addr; }
int getPort() const { return m_port; }

Check warning on line 122 in src/server/internalServer.h

View check run for this annotation

Codecov / codecov/patch

src/server/internalServer.h#L121-L122

Added lines #L121 - L122 were not covered by tests
IpMode getIpMode() const { return m_ipMode; }

private: // functions
Expand Down Expand Up @@ -166,7 +167,7 @@
typedef ConcurrentCache<std::string, std::shared_ptr<LockableSuggestionSearcher>> 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
Expand Down
47 changes: 24 additions & 23 deletions src/tools/networkTools.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
*/

#include "tools.h"
#include "stringTools.h"
#include <tools/networkTools.h>

#include <stdio.h>
Expand Down Expand Up @@ -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) {
Expand All @@ -85,7 +92,6 @@ std::string download(const std::string& url) {
return ss.str();
}


namespace
{

Expand Down Expand Up @@ -211,40 +217,35 @@ std::map<std::string, std::string> getNetworkInterfaces() {
return result;
}


std::string getBestPublicIp(bool ipv6) {
IpAddress bestPublicIp = IpAddress{"127.0.0.1","::1"};
IpAddress getBestPublicIps() {
IpAddress bestPublicIps;
std::map<std::string, IpAddress> 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;
const char* const prioritizedNames[] = { "eth0", "eth1", "wlan0", "wlan1", "en0", "en1" };
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" };
sgourdas marked this conversation as resolved.
Show resolved Hide resolved
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
10 changes: 4 additions & 6 deletions test/otherTools.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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;
}
Loading