Skip to content

Commit

Permalink
add ipv6 support
Browse files Browse the repository at this point in the history
  • Loading branch information
aryanA101a committed Apr 20, 2024
1 parent eb328ed commit bd17e90
Show file tree
Hide file tree
Showing 6 changed files with 139 additions and 86 deletions.
2 changes: 2 additions & 0 deletions include/server.h
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ 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();

Expand All @@ -78,6 +79,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<InternalServer> mp_server;
};
Expand Down
11 changes: 8 additions & 3 deletions include/tools.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,11 @@
#include <map>
#include <cstdint>

struct ip_addr{
std::string addr;
std::string addr6;
};

namespace kiwix {
typedef std::pair<std::string, std::string> LangNameCodePair;
typedef std::vector<LangNameCodePair> FeedLanguages;
Expand Down Expand Up @@ -211,14 +216,14 @@ std::string getMimeTypeForFile(const std::string& filename);

/** 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<std::string, std::string> getNetworkInterfaces();
std::map<std::string,ip_addr> getNetworkInterfaces();

/** Provides the best IP address
* This function provides the best IP address from the list given by getNetworkInterfaces
*/
std::string getBestPublicIp();
ip_addr getBestPublicIp(bool ipv6);

/** Converts file size to human readable format.
*
Expand Down
1 change: 1 addition & 0 deletions src/server.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ bool Server::start() {
m_withTaskbar,
m_withLibraryButton,
m_blockExternalLinks,
m_ipv6,
m_indexTemplateString,
m_ipConnectionLimit));
return mp_server->start();
Expand Down
49 changes: 36 additions & 13 deletions src/server/internalServer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -416,6 +416,7 @@ InternalServer::InternalServer(LibraryPtr library,
bool withTaskbar,
bool withLibraryButton,
bool blockExternalLinks,
bool ipv6,
std::string indexTemplateString,
int ipConnectionLimit) :
m_addr(addr),
Expand All @@ -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),
Expand All @@ -451,28 +453,49 @@ 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);
if (m_addr.empty()) {
if (0 != INADDR_ANY) {
sockAddr.sin_addr.s_addr = htonl(INADDR_ANY);
}
m_addr = kiwix::getBestPublicIp();

struct sockaddr_in sockAddr4={0};
struct sockaddr_in6 sockAddr6={0};


if (m_ipv6) {
flags|=MHD_USE_DUAL_STACK;
sockAddr6.sin6_family = AF_INET6;
sockAddr6.sin6_port = htons(m_port);
if (m_addr.empty()) {
if (0 != INADDR_ANY) {
sockAddr6.sin6_addr = in6addr_any;
}
m_addr = (kiwix::getBestPublicIp(m_ipv6)).addr6;
} else {
if (inet_pton(AF_INET6, m_addr.c_str(), &(sockAddr6.sin6_addr.s6_addr)) == 0) {
std::cerr << "Ip address " << m_addr << " is not a valid ipv6 address" << std::endl;
return false;
}
}
} 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;
sockAddr4.sin_family = AF_INET;
sockAddr4.sin_port = htons(m_port);
if (m_addr.empty()) {
if (0 != INADDR_ANY) {
sockAddr4.sin_addr.s_addr = htonl(INADDR_ANY);
}
m_addr = (kiwix::getBestPublicIp(m_ipv6)).addr;
} else{
if (inet_pton(AF_INET, m_addr.c_str(), &(sockAddr4.sin_addr.s_addr)) == 0){
std::cerr << "Ip address " << m_addr << " is not a valid ipv4 address" << std::endl;
return false;
}
}
}
}

mp_daemon = MHD_start_daemon(flags,
m_port,
NULL,
NULL,
&staticHandlerCallback,
this,
MHD_OPTION_SOCK_ADDR, &sockAddr,
MHD_OPTION_SOCK_ADDR, m_ipv6?(struct sockaddr*)&sockAddr6:(struct sockaddr*)&sockAddr4,
MHD_OPTION_THREAD_POOL_SIZE, m_nbThreads,
MHD_OPTION_PER_IP_CONNECTION_LIMIT, m_ipConnectionLimit,
MHD_OPTION_END);
Expand Down
2 changes: 2 additions & 0 deletions src/server/internalServer.h
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,7 @@ class InternalServer {
bool withTaskbar,
bool withLibraryButton,
bool blockExternalLinks,
bool ipv6,
std::string indexTemplateString,
int ipConnectionLimit);
virtual ~InternalServer();
Expand Down Expand Up @@ -174,6 +175,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;
Expand Down
160 changes: 90 additions & 70 deletions src/tools/networkTools.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -32,12 +32,14 @@
#include <stdexcept>

#ifdef _WIN32
#include <iphlpapi.h>
#include <winsock2.h>
#include <ws2tcpip.h>
#include <iostream>
#else
#include <unistd.h>
#include <sys/ioctl.h>
#include <arpa/inet.h>
#include <ifaddrs.h>
#include <sys/socket.h>
#include <net/if.h>
#include <netdb.h>
Expand Down Expand Up @@ -75,103 +77,121 @@ std::string kiwix::download(const std::string& url) {
return ss.str();
}

std::map<std::string, std::string> kiwix::getNetworkInterfaces() {
std::map<std::string, std::string> interfaces;
std::map<std::string,ip_addr> kiwix::getNetworkInterfaces() {
std::map<std::string,ip_addr> 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;
}
const int working_buffer_size = 15000;
const int max_tries = 3;

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

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;
// Successively allocate the required memory until GetAdaptersAddresses does not
// results in ERROR_BUFFER_OVERFLOW for a maximum of max_tries
do{

Check notice on line 100 in src/tools/networkTools.cpp

View check run for this annotation

codefactor.io / CodeFactor

src/tools/networkTools.cpp#L100

Redundant blank line at the start of a code block should be deleted. (whitespace/blank_line)
interfacesHead = (IP_ADAPTER_ADDRESSES *) malloc(outBufLen);
if (interfacesHead == NULL) {
std::cerr << "Memory allocation failed for IP_ADAPTER_ADDRESSES struct" << std::endl;
return interfaces;
}

dwRetVal = GetAdaptersAddresses(family, flags, NULL, interfacesHead, &outBufLen);

Check notice on line 108 in src/tools/networkTools.cpp

View check run for this annotation

codefactor.io / CodeFactor

src/tools/networkTools.cpp#L108

Redundant blank line at the end of a code block should be deleted. (whitespace/blank_line)
} 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;
}

if (interfacesHead) free(interfacesHead);

#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)");

struct ifaddrs *interfacesHead;
if (getifaddrs(&interfacesHead) == -1) {
perror("getifaddrs");
}

/* 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()");
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;
}
}

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

freeifaddrs(interfacesHead);
#endif
return interfaces;
}

std::string kiwix::getBestPublicIp() {
auto interfaces = getNetworkInterfaces();
ip_addr kiwix::getBestPublicIp(bool ipv6) {
std::map<std::string,ip_addr> 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())) {
return (*it).second;
}
}
#endif

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())) {
return itr.second;
}
}
}

return "127.0.0.1";
return ip_addr{"127.0.0.1","::1"};
}

0 comments on commit bd17e90

Please sign in to comment.