This repository has been archived by the owner on Mar 26, 2024. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #2 from Cach30verfl0w/develop
Implement cross-platform DNS resolver
- Loading branch information
Showing
7 changed files
with
457 additions
and
34 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
Oops, something went wrong.