Skip to content
This repository has been archived by the owner on Mar 26, 2024. It is now read-only.

Commit

Permalink
Merge pull request #2 from Cach30verfl0w/develop
Browse files Browse the repository at this point in the history
Implement cross-platform DNS resolver
  • Loading branch information
KitsuneAlex authored Aug 16, 2023
2 parents e402381 + e191ad5 commit dbbe2bf
Show file tree
Hide file tree
Showing 7 changed files with 457 additions and 34 deletions.
19 changes: 10 additions & 9 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -16,18 +16,19 @@ cmx_add_library(kstd-platform-static STATIC "${CMAKE_CURRENT_SOURCE_DIR}/src")
target_include_directories(kstd-platform-static PUBLIC "${CMAKE_SOURCE_DIR}/include")
cmx_include_kstd_core(kstd-platform-static PUBLIC)

if (CMX_PLATFORM_UNIX)
target_link_libraries(kstd-platform PUBLIC -lresolv)
target_link_libraries(kstd-platform-static PUBLIC -lresolv)
endif()

if (CMX_PLATFORM_LINUX)
target_link_libraries(kstd-platform PUBLIC -ldl -lrt)
target_link_libraries(kstd-platform-static PUBLIC -ldl -lrt)
endif ()

# Tests
if (KSTD_PLATFORM_BUILD_TESTS)
cmx_add_tests(kstd-platform-tests "${CMAKE_SOURCE_DIR}/test")
cmx_include_gtest(kstd-platform-tests PRIVATE)
target_link_libraries(kstd-platform-tests PRIVATE kstd-platform-static)
add_dependencies(kstd-platform-tests kstd-platform-static)
endif ()

if (CMX_PLATFORM_LINUX)
target_link_libraries(kstd-platform PUBLIC -ldl -lrt)
target_link_libraries(kstd-platform-static PUBLIC -ldl -lrt)
if (KSTD_PLATFORM_BUILD_TESTS)
target_link_libraries(kstd-platform-tests PRIVATE -ldl -lrt)
endif ()
endif ()
100 changes: 100 additions & 0 deletions include/kstd/platform/dns.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
// Copyright 2023 Karma Krafts & associates
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

/**
* @author Cedric Hammes
* @since 12/08/2023
*/

#pragma once

#include "kstd/platform/platform.hpp"
#include <fmt/format.h>
#include <initializer_list>
#include <kstd/bitflags.hpp>
#include <kstd/result.hpp>
#include <regex>
#include <stdexcept>
#include <string>

#ifdef PLATFORM_WINDOWS
#define WIN32_LEAN_AND_MEAN
#include <WinDNS.h>
#include <Windows.h>
#include <kstd/option.hpp>
#else
#include <arpa/inet.h>
#include <arpa/nameser.h>
#include <netinet/in.h>
#include <resolv.h>
#endif

namespace kstd::platform {

#if defined(PLATFORM_WINDOWS)
enum class RecordType : kstd::u16 {
A = DNS_TYPE_A,
AAAA = DNS_TYPE_AAAA
};

struct IP4Array {
DWORD address_count;
std::array<IP4_ADDRESS, 4> addresses;
};
#elif defined(PLATFORM_LINUX)
enum class RecordType : kstd::u16 {
A = T_A,
AAAA = T_AAAA
};
#else
enum class RecordType : kstd::u16 {
A = __ns_type::ns_t_a,
AAAA = __ns_type::ns_t_aaaa
};
#endif

class Resolver final {
#ifdef PLATFORM_WINDOWS
kstd::Option<IP4Array> _dns_addresses;
#else
std::vector<std::string> _dns_addresses;
#endif

public:
Resolver(std::vector<std::string> dns_addresses);
Resolver();
~Resolver();
KSTD_DEFAULT_MOVE(Resolver, Resolver)
KSTD_NO_COPY(Resolver, Resolver)

[[nodiscard]] auto resolve(const std::string& address, RecordType type) noexcept -> kstd::Result<std::string>;
};

inline auto is_ipv4_address(const std::string& address) noexcept -> bool {
static auto s_pattern = std::regex(R"(([0-9]{1,3})\.([0-9]{1,3})\.([0-9]{1,3})\.([0-9]{1,3}))");
return std::regex_match(address, s_pattern);
}

inline auto is_ipv6_address(const std::string& address) noexcept -> bool {
static auto s_pattern = std::regex(
"(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-"
"F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){"
"1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}"
"|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,"
"4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\\.){3,3}(25["
"0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]"
")\\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))");
return std::regex_match(address, s_pattern);
}
}// namespace kstd::platform
85 changes: 85 additions & 0 deletions src/linux/dns.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
// Copyright 2023 Karma Krafts & associates
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

/**
* @author Cedric Hammes
* @since 12/08/2023
*/

#ifdef PLATFORM_LINUX

#include "kstd/platform/dns.hpp"

namespace kstd::platform {
Resolver::Resolver(const std::vector<std::string> dns_addresses) :
_dns_addresses {dns_addresses} {
if(dns_addresses.size() == 0) {
throw std::runtime_error("Unable to initialize list of DNS servers: No DNS server specified");
}
}

Resolver::Resolver() :
_dns_addresses {} {
}

Resolver::~Resolver() {
}

auto Resolver::resolve(const std::string& address, const RecordType type) noexcept -> kstd::Result<std::string> {
if(address == "localhost") {
switch(type) {
case RecordType::A: return kstd::Result<std::string> {"127.0.0.1"};
case RecordType::AAAA: return kstd::Result<std::string> {"::1"};
}
}

// Init resolv API
res_init();

// Generate custom nameserver list if needed
if(_dns_addresses.size() > 0) {
_res.nscount = _dns_addresses.size();
for(int i = 0; i < _res.nscount; ++i) {
if(is_ipv4_address(_dns_addresses[i])) {
_res.nsaddr_list[i].sin_family = AF_INET;
}
else if(is_ipv6_address(_dns_addresses[i])) {
_res.nsaddr_list[i].sin_family = AF_INET6;
}
else {
return kstd::Error {fmt::format("Unable to resolve address of {}: Illegal DNS server address {}",
address, _dns_addresses[i])};
}
_res.nsaddr_list[i].sin_addr.s_addr = ::inet_addr(_dns_addresses[i].c_str());
_res.nsaddr_list[i].sin_port = htons(53);
}
}

// Send DNS request
std::array<u_char, 4096> response_buffer {'\0'};
kstd::isize response_length = ::res_query(address.c_str(), C_IN, static_cast<int>(type), response_buffer.data(),
sizeof(response_buffer));

if(response_length < 0) {
return kstd::Error {fmt::format("Unable to resolve address of {}: {}", address, get_last_error())};
}

if(response_length == 0) {
return kstd::Error {fmt::format("Unable to resolve address of {}: There is no response", address)};
}
return std::string {response_buffer.cbegin(), response_buffer.cbegin() + response_length + 1};
}
}// namespace kstd::platform

#endif
85 changes: 85 additions & 0 deletions src/macos/dns.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
// Copyright 2023 Karma Krafts & associates
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

/**
* @author Cedric Hammes
* @since 12/08/2023
*/

#ifdef PLATFORM_APPLE

#include "kstd/platform/dns.hpp"

namespace kstd::platform {
Resolver::Resolver(const std::vector<std::string> dns_addresses) :
_dns_addresses {dns_addresses} {
if(dns_addresses.size() == 0) {
throw std::runtime_error("Unable to initialize list of DNS servers: No DNS server specified");
}
}

Resolver::Resolver() :
_dns_addresses {} {
}

Resolver::~Resolver() {
}

auto Resolver::resolve(const std::string& address, const RecordType type) noexcept -> kstd::Result<std::string> {
if(address == "localhost") {
switch(type) {
case RecordType::A: return kstd::Result<std::string> {"127.0.0.1"};
case RecordType::AAAA: return kstd::Result<std::string> {"::1"};
}
}

// Init resolv API
res_init();

// Generate custom nameserver list if needed
if(_dns_addresses.size() > 0) {
_res.nscount = _dns_addresses.size();
for(int i = 0; i < _res.nscount; ++i) {
if(is_ipv4_address(_dns_addresses[i])) {
_res.nsaddr_list[i].sin_family = AF_INET;
}
else if(is_ipv6_address(_dns_addresses[i])) {
_res.nsaddr_list[i].sin_family = AF_INET6;
}
else {
return kstd::Error {fmt::format("Unable to resolve address of {}: Illegal DNS server address {}",
address, _dns_addresses[i])};
}
_res.nsaddr_list[i].sin_addr.s_addr = ::inet_addr(_dns_addresses[i].c_str());
_res.nsaddr_list[i].sin_port = htons(53);
}
}

// Send DNS request
std::array<u_char, 4096> response_buffer {'\0'};
kstd::isize response_length = ::res_query(address.c_str(), __ns_class::ns_c_in, static_cast<int>(type),
response_buffer.data(), sizeof(response_buffer));

if(response_length < 0) {
return kstd::Error {fmt::format("Unable to resolve address {}: {}", address, get_last_error())};
}

if(response_length == 0) {
return kstd::Error {fmt::format("Unable to resolve address {}: There is no response", address)};
}
return std::string {response_buffer.cbegin(), response_buffer.cbegin() + response_length + 1};
}
}// namespace kstd::platform

#endif
Loading

0 comments on commit dbbe2bf

Please sign in to comment.