WebDNS/server/src/net.h

261 lines
7.5 KiB
C++

#pragma once
#include <string>
#include <cstring>
#include <deque>
#include <vector>
#include <tuple>
#ifdef WIN32
#include <WS2tcpip.h>
#include <WinSock2.h>
#include <Windows.h>
#include <in6addr.h>
#else
#include <arpa/inet.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <netdb.h>
#endif
namespace net {
inline std::string to_string(const in6_addr& address) {
char buffer[INET6_ADDRSTRLEN];
if(!inet_ntop(AF_INET6, (void*) &address, buffer, INET6_ADDRSTRLEN)) return "";
return std::string(buffer);
}
inline std::string to_string(const in_addr& address) {
char buffer[INET_ADDRSTRLEN];
if(!inet_ntop(AF_INET, (void*) &address, buffer, INET_ADDRSTRLEN)) return "";
return std::string(buffer);
}
inline uint16_t port(const sockaddr_storage& address) {
switch(address.ss_family) {
case AF_INET:
return htons(((sockaddr_in*) &address)->sin_port);
case AF_INET6:
return htons(((sockaddr_in6*) &address)->sin6_port);
default:
return 0;
}
}
inline std::string to_string(const sockaddr_storage& address, bool port = true) {
switch(address.ss_family) {
case AF_INET:
return to_string(((sockaddr_in*) &address)->sin_addr) + (port ? ":" + std::to_string(htons(((sockaddr_in*) &address)->sin_port)) : "");
case AF_INET6:
return to_string(((sockaddr_in6*) &address)->sin6_addr) + (port ? ":" + std::to_string(htons(((sockaddr_in6*) &address)->sin6_port)) : "");
default:
return "unknown_type";
}
}
inline socklen_t address_size(const sockaddr_storage& address) {
switch (address.ss_family) {
case AF_INET: return sizeof(sockaddr_in);
case AF_INET6: return sizeof(sockaddr_in6);
default: return 0;
}
}
inline bool address_equal(const sockaddr_storage& a, const sockaddr_storage& b) {
if(a.ss_family != b.ss_family) return false;
if(a.ss_family == AF_INET) return ((sockaddr_in*) &a)->sin_addr.s_addr == ((sockaddr_in*) &b)->sin_addr.s_addr;
else if(a.ss_family == AF_INET6) {
#ifdef WIN32
return memcmp(((sockaddr_in6*) &a)->sin6_addr.u.Byte, ((sockaddr_in6*) &b)->sin6_addr.u.Byte, 16) == 0;
#else
return memcmp(((sockaddr_in6*) &a)->sin6_addr.__in6_u.__u6_addr8, ((sockaddr_in6*) &b)->sin6_addr.__in6_u.__u6_addr8, 16) == 0;
#endif
}
return false;
}
inline bool address_equal_ranged(const sockaddr_storage& a, const sockaddr_storage& b, uint8_t range) {
if(a.ss_family != b.ss_family) return false;
if(a.ss_family == AF_INET) {
auto address_a = ((sockaddr_in*) &a)->sin_addr.s_addr;
auto address_b = ((sockaddr_in*) &b)->sin_addr.s_addr;
if(range > 32)
range = 32;
range = (uint8_t) (32 - range);
address_a <<= range;
address_b <<= range;
return address_a == address_b;
} else if(a.ss_family == AF_INET6) {
#ifdef WIN32
throw std::runtime_error("not implemented");
//FIXME: Implement me!
#elif defined(__x86_64__) && false
static_assert(sizeof(__int128) == 16);
auto address_a = (__int128) ((sockaddr_in6*) &a)->sin6_addr.__in6_u.__u6_addr32;
auto address_b = (__int128) ((sockaddr_in6*) &b)->sin6_addr.__in6_u.__u6_addr32;
if(range > 128)
range = 128;
range = (uint8_t) (128 - range);
address_a <<= range;
address_b <<= range;
return address_a == address_b;
#else
static_assert(sizeof(uint64_t) == 8);
if(range > 128)
range = 128;
range = (uint8_t) (128 - range);
auto address_ah = (uint64_t) (((sockaddr_in6*) &a)->sin6_addr.__in6_u.__u6_addr8 + 0);
auto address_al = (uint64_t) (((sockaddr_in6*) &a)->sin6_addr.__in6_u.__u6_addr8 + 8);
auto address_bh = (uint64_t) (((sockaddr_in6*) &b)->sin6_addr.__in6_u.__u6_addr8 + 0);
auto address_bl = (uint64_t) (((sockaddr_in6*) &b)->sin6_addr.__in6_u.__u6_addr8 + 8);
if(range > 64) {
/* only lower counts */
return (address_al << (range - 64)) == (address_bl << (range - 64));
} else {
return address_al == address_bl &&(address_bh << (range - 64)) == (address_ah << (range - 64));
}
#endif
}
return false;
}
inline bool is_ipv6(const std::string& str) {
sockaddr_in6 sa{};
return inet_pton(AF_INET6, str.c_str(), &(sa.sin6_addr)) != 0;
}
inline bool is_ipv4(const std::string& str) {
sockaddr_in sa{};
return inet_pton(AF_INET, str.c_str(), &(sa.sin_addr)) != 0;
}
inline bool is_anybind(sockaddr_storage& storage) {
if(storage.ss_family == AF_INET) {
auto data = (sockaddr_in*) &storage;
return data->sin_addr.s_addr == 0;
} else if(storage.ss_family == AF_INET6) {
auto data = (sockaddr_in6*) &storage;
#ifdef WIN32
auto& blocks = data->sin6_addr.u.Word;
return
blocks[0] == 0 &&
blocks[1] == 0 &&
blocks[2] == 0 &&
blocks[3] == 0 &&
blocks[4] == 0 &&
blocks[5] == 0 &&
blocks[6] == 0 &&
blocks[7] == 0;
#else
auto& blocks = data->sin6_addr.__in6_u.__u6_addr32;
return blocks[0] == 0 && blocks[1] == 0 && blocks[2] == 0 && blocks[3] == 0;
#endif
}
return false;
}
inline bool resolve_address(const std::string& address, sockaddr_storage& result) {
if(is_ipv4(address)) {
sockaddr_in s{};
s.sin_port = 0;
s.sin_family = AF_INET;
auto record = gethostbyname(address.c_str());
if(!record)
return false;
s.sin_addr.s_addr = ((in_addr*) record->h_addr)->s_addr;
memcpy(&result, &s, sizeof(s));
return true;
} else if(is_ipv6(address)) {
sockaddr_in6 s{};
s.sin6_family = AF_INET6;
s.sin6_port = 0;
s.sin6_flowinfo = 0;
s.sin6_scope_id = 0;
#ifdef WIN32
auto record = gethostbyname(address.c_str());
#else
auto record = gethostbyname2(address.c_str(), AF_INET6);
#endif
if(!record) return false;
s.sin6_addr = *(in6_addr*) record->h_addr;
memcpy(&result, &s, sizeof(s));
return true;
} else if(address == "[::]" || address == "::") {
sockaddr_in6 s{};
s.sin6_family = AF_INET6;
s.sin6_port = 0;
s.sin6_flowinfo = 0;
s.sin6_scope_id = 0;
memcpy(&s.sin6_addr, &in6addr_any, sizeof(in6_addr));
memcpy(&result, &s, sizeof(s));
return true;
}
return false;
}
namespace helpers {
inline void strip(std::string& message) {
while(!message.empty()) {
if(message[0] == ' ')
message = message.substr(1);
else if(message[message.length() - 1] == ' ')
message = message.substr(0, message.length() - 1);
else break;
}
}
inline std::deque<std::string> split(const std::string& message, char delimiter) {
std::deque<std::string> result{};
size_t found, index = 0;
do {
found = message.find(delimiter, index);
result.push_back(message.substr(index, found - index));
index = found + 1;
} while(index != 0);
return result;
}
}
inline std::vector<std::tuple<std::string, sockaddr_storage, std::string>> resolve_bindings(const std::string& bindings, uint16_t port) {
auto binding_list = helpers::split(bindings, ',');
std::vector<std::tuple<std::string, sockaddr_storage, std::string>> result;
result.reserve(binding_list.size());
for(auto& address : binding_list) {
helpers::strip(address);
sockaddr_storage element{};
memset(&element, 0, sizeof(element));
if(!resolve_address(address, element)) {
result.emplace_back(address, element, "address resolve failed");
continue;
}
if(element.ss_family == AF_INET) {
((sockaddr_in*) &element)->sin_port = htons(port);
} else if(element.ss_family == AF_INET6) {
((sockaddr_in6*) &element)->sin6_port = htons(port);
}
result.emplace_back(address, element, "");
}
return result;
}
}