Updated DNS module for windows (may broke linux support)
This commit is contained in:
parent
6c618d189f
commit
273c3b966e
2
github
2
github
@ -1 +1 @@
|
|||||||
Subproject commit 14645dca78396c915ad4ad122d532f24fdfd2969
|
Subproject commit a1490ab0d97f65e27845fec6daf88be8ed6d1014
|
@ -1,17 +1,26 @@
|
|||||||
set(MODULE_NAME "teaclient_dns")
|
set(MODULE_NAME "teaclient_dns")
|
||||||
|
|
||||||
set(SOURCE_FILES ${SOURCE_FILES} src/resolver.cpp src/types.cpp src/response.cpp utils.cpp src/resolver_linux.cpp)
|
set(SOURCE_FILES ${SOURCE_FILES} src/resolver.cpp src/types.cpp src/response.cpp utils.cpp)
|
||||||
|
if (WIN32)
|
||||||
|
set(SOURCE_FILES ${SOURCE_FILES} src/resolver_windows.cpp)
|
||||||
|
else()
|
||||||
|
set(SOURCE_FILES ${SOURCE_FILES} src/resolver_linux.cpp src/resolver_windows.cpp)
|
||||||
|
endif()
|
||||||
|
|
||||||
find_package(Libevent REQUIRED)
|
find_package(Libevent REQUIRED)
|
||||||
include_directories(${LIBEVENT_INCLUDE_DIRS})
|
include_directories(${LIBEVENT_INCLUDE_DIRS})
|
||||||
|
|
||||||
find_package(unbound REQUIRED)
|
if(NOT WIN32)
|
||||||
|
find_package(unbound REQUIRED)
|
||||||
|
endif()
|
||||||
|
|
||||||
add_nodejs_module(${MODULE_NAME} binding.cc ${SOURCE_FILES})
|
add_nodejs_module(${MODULE_NAME} binding.cc ${SOURCE_FILES})
|
||||||
target_link_libraries(${MODULE_NAME} unbound::static ${LIBEVENT_STATIC_LIBRARIES} pthread)
|
|
||||||
|
|
||||||
add_executable(DNS-Test ${SOURCE_FILES} test/main.cpp)
|
add_executable(DNS-Test ${SOURCE_FILES} test/main.cpp)
|
||||||
target_link_libraries(DNS-Test unbound::static ssl crypto ${LIBEVENT_STATIC_LIBRARIES} pthread)
|
|
||||||
|
|
||||||
add_executable(DNS-Test2 test/crash.cpp)
|
if (WIN32)
|
||||||
target_link_libraries(DNS-Test2 unbound::static ssl crypto ${LIBEVENT_STATIC_LIBRARIES} pthread)
|
target_link_libraries(${MODULE_NAME} ${LIBEVENT_STATIC_LIBRARIES} Ws2_32.lib Ntdll.lib Dnsapi.lib)
|
||||||
|
target_link_libraries(DNS-Test ${LIBEVENT_STATIC_LIBRARIES} Ws2_32.lib Ntdll.lib Dnsapi.lib)
|
||||||
|
else()
|
||||||
|
target_link_libraries(${MODULE_NAME} unbound::static ${LIBEVENT_STATIC_LIBRARIES} pthread)
|
||||||
|
target_link_libraries(DNS-Test unbound::static ssl crypto ${LIBEVENT_STATIC_LIBRARIES} pthread)
|
||||||
|
endif()
|
@ -21,7 +21,11 @@ NAN_METHOD(initialize) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
//evthread_use_pthreads();
|
#ifdef WIN32
|
||||||
|
evthread_use_windows_threads();
|
||||||
|
#else
|
||||||
|
evthread_use_pthreads();
|
||||||
|
#endif
|
||||||
resolver = make_unique<tc::dns::Resolver>();
|
resolver = make_unique<tc::dns::Resolver>();
|
||||||
|
|
||||||
string error;
|
string error;
|
||||||
@ -77,11 +81,11 @@ NAN_METHOD(query_connect_address) {
|
|||||||
tc::dns::cr(*resolver,
|
tc::dns::cr(*resolver,
|
||||||
tc::dns::ServerAddress{ *host, (uint16_t) port },
|
tc::dns::ServerAddress{ *host, (uint16_t) port },
|
||||||
[callback = std::move(callback)] (bool success, std::variant<std::string, tc::dns::ServerAddress> data) mutable {
|
[callback = std::move(callback)] (bool success, std::variant<std::string, tc::dns::ServerAddress> data) mutable {
|
||||||
callback.call_cpy(success, success ? "" : std::get<std::string>(data), !success ? tc::dns::ServerAddress{"", 0} : std::get<tc::dns::ServerAddress>(data));
|
callback.call_cpy(success, success ? "" : std::get<std::string>(data), !success ? tc::dns::ServerAddress{"", 0} : std::get<tc::dns::ServerAddress>(data), false);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
__attribute__((visibility("default"))) NAN_MODULE_INIT(init) {
|
NAN_MODULE_INIT(init) {
|
||||||
Nan::Set(target,
|
Nan::Set(target,
|
||||||
v8::String::NewFromUtf8(Nan::GetCurrentContext()->GetIsolate(), "resolve_cr").ToLocalChecked(),
|
v8::String::NewFromUtf8(Nan::GetCurrentContext()->GetIsolate(), "resolve_cr").ToLocalChecked(),
|
||||||
Nan::GetFunction(Nan::New<v8::FunctionTemplate>(query_connect_address)).ToLocalChecked()
|
Nan::GetFunction(Nan::New<v8::FunctionTemplate>(query_connect_address)).ToLocalChecked()
|
||||||
|
@ -4,19 +4,19 @@
|
|||||||
#include <cassert>
|
#include <cassert>
|
||||||
#include <functional>
|
#include <functional>
|
||||||
#include <event.h>
|
#include <event.h>
|
||||||
#include <unbound.h>
|
|
||||||
#include <unbound-event.h>
|
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
|
|
||||||
#include <fcntl.h> /* for TSDNS */
|
#include <fcntl.h> /* for TSDNS */
|
||||||
#include <unistd.h>
|
|
||||||
#ifdef WIN32
|
#ifdef WIN32
|
||||||
#include <ws2tcpip.h>
|
#include <ws2tcpip.h>
|
||||||
#define SOCK_NONBLOCK (0)
|
#define SOCK_NONBLOCK (0)
|
||||||
#define MSG_DONTWAIT (0)
|
#define MSG_DONTWAIT (0)
|
||||||
#else
|
#else
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <unbound.h>
|
||||||
|
#include <unbound-event.h>
|
||||||
#include <sys/socket.h>
|
#include <sys/socket.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@ -29,6 +29,77 @@ Resolver::~Resolver() {
|
|||||||
this->finalize();
|
this->finalize();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool Resolver::initialize(std::string &error, bool hosts, bool resolv) {
|
||||||
|
if(this->event.loop_active)
|
||||||
|
this->finalize();
|
||||||
|
|
||||||
|
this->event.loop_active = true;
|
||||||
|
this->event.base = event_base_new();
|
||||||
|
if(!this->event.base) {
|
||||||
|
error = "failed to allcoate event base";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
this->event.loop = std::thread(std::bind(&Resolver::event_loop_runner, this));
|
||||||
|
|
||||||
|
return this->initialize_platform(error, hosts, resolv);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Resolver::finalize() {
|
||||||
|
this->event.loop_active = false;
|
||||||
|
if(this->event.base) {
|
||||||
|
auto ret = event_base_loopexit(this->event.base, nullptr);
|
||||||
|
if(ret != 0) {
|
||||||
|
cerr << "Failed to exit event base loop: " << ret << endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
this->event.condition.notify_one();
|
||||||
|
if(this->event.loop.joinable())
|
||||||
|
this->event.loop.join();
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
unique_lock lock(this->request_lock);
|
||||||
|
auto dns_list = std::move(this->dns_requests);
|
||||||
|
auto tsdns_list = std::move(this->tsdns_requests);
|
||||||
|
|
||||||
|
for(auto entry : dns_list) {
|
||||||
|
entry->callback(ResultState::ABORT, 0, nullptr);
|
||||||
|
this->destroy_dns_request(entry);
|
||||||
|
}
|
||||||
|
|
||||||
|
for(auto entry : tsdns_list) {
|
||||||
|
entry->callback(ResultState::ABORT, 0, "");
|
||||||
|
this->destroy_tsdns_request(entry);
|
||||||
|
}
|
||||||
|
lock.unlock();
|
||||||
|
}
|
||||||
|
|
||||||
|
this->finalize_platform(); /* keep the event base allocated until platform depend finalize has been done */
|
||||||
|
if(this->event.base) {
|
||||||
|
event_base_free(this->event.base);
|
||||||
|
this->event.base = nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Resolver::event_loop_runner() {
|
||||||
|
while(true) {
|
||||||
|
{
|
||||||
|
unique_lock lock{this->event.lock};
|
||||||
|
if(!this->event.loop_active)
|
||||||
|
break;
|
||||||
|
|
||||||
|
this->event.condition.wait(lock);
|
||||||
|
if(!this->event.loop_active)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
event_base_loop(this->event.base, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void Resolver::destroy_tsdns_request(Resolver::tsdns_request *request) {
|
void Resolver::destroy_tsdns_request(Resolver::tsdns_request *request) {
|
||||||
assert(this_thread::get_id() == this->event.loop.get_id() || !this->event.loop_active);
|
assert(this_thread::get_id() == this->event.loop.get_id() || !this->event.loop_active);
|
||||||
|
|
||||||
@ -58,8 +129,10 @@ void Resolver::destroy_tsdns_request(Resolver::tsdns_request *request) {
|
|||||||
if(request->socket > 0) {
|
if(request->socket > 0) {
|
||||||
#ifndef WIN32
|
#ifndef WIN32
|
||||||
::shutdown(request->socket, SHUT_RDWR);
|
::shutdown(request->socket, SHUT_RDWR);
|
||||||
#endif
|
|
||||||
::close(request->socket);
|
::close(request->socket);
|
||||||
|
#else
|
||||||
|
closesocket(request->socket);
|
||||||
|
#endif
|
||||||
request->socket = 0;
|
request->socket = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -71,28 +144,42 @@ void Resolver::resolve_tsdns(const char *query, const sockaddr_storage& server_a
|
|||||||
/* create the socket */
|
/* create the socket */
|
||||||
auto socket = ::socket(server_address.ss_family, SOCK_STREAM | SOCK_NONBLOCK, 0);
|
auto socket = ::socket(server_address.ss_family, SOCK_STREAM | SOCK_NONBLOCK, 0);
|
||||||
if(socket <= 0) {
|
if(socket <= 0) {
|
||||||
callback(ResultState::INITIALISATION_FAILED, -1, "failed to allocate socket: " + to_string(errno) + "/" + strerror(errno));
|
#ifdef WIN32
|
||||||
|
char buf[1024];
|
||||||
|
strerror_s(buf, errno);
|
||||||
|
std::string strerr{buf};
|
||||||
|
#else
|
||||||
|
std::string strerr{strerror(errno)};
|
||||||
|
#endif
|
||||||
|
callback(ResultState::INITIALISATION_FAILED, -1, "failed to allocate socket: " + to_string(errno) + "/" + strerr);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef WIN32
|
#ifdef WIN32
|
||||||
u_long enabled = 0;
|
u_long enabled = 0;
|
||||||
auto non_block_rs = ioctlsocket(this->_socket, FIONBIO, &enabled);
|
auto non_block_rs = ioctlsocket(socket, FIONBIO, &enabled);
|
||||||
if (non_block_rs != NO_ERROR) {
|
if (non_block_rs != NO_ERROR) {
|
||||||
::close(socket);
|
#ifdef WIN32
|
||||||
callback(ResultState::INITIALISATION_FAILED, -2, "failed to enable nonblock: " + to_string(errno) + "/" + strerror(errno));
|
char buf[1024];
|
||||||
|
strerror_s(buf, errno);
|
||||||
|
std::string strerr{buf};
|
||||||
|
#else
|
||||||
|
std::string strerr{strerror(errno)};
|
||||||
|
#endif
|
||||||
|
closesocket(socket);
|
||||||
|
callback(ResultState::INITIALISATION_FAILED, -2, "failed to enable nonblock: " + to_string(errno) + "/" + strerr);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
#endif
|
#else
|
||||||
|
int opt = 1;
|
||||||
int opt = 1;
|
|
||||||
setsockopt(socket, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(int));
|
setsockopt(socket, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(int));
|
||||||
|
#endif
|
||||||
|
|
||||||
auto request = new tsdns_request{};
|
auto request = new tsdns_request{};
|
||||||
|
|
||||||
request->resolver = this;
|
request->resolver = this;
|
||||||
request->callback = callback;
|
request->callback = callback;
|
||||||
request->socket = socket;
|
request->socket = (int) socket;
|
||||||
request->timeout_event = evtimer_new(this->event.base, [](evutil_socket_t, short, void *_request) {
|
request->timeout_event = evtimer_new(this->event.base, [](evutil_socket_t, short, void *_request) {
|
||||||
auto request = static_cast<tsdns_request*>(_request);
|
auto request = static_cast<tsdns_request*>(_request);
|
||||||
request->resolver->evtimer_tsdns_callback(request);
|
request->resolver->evtimer_tsdns_callback(request);
|
||||||
@ -122,23 +209,21 @@ void Resolver::resolve_tsdns(const char *query, const sockaddr_storage& server_a
|
|||||||
auto error = WSAGetLastError();
|
auto error = WSAGetLastError();
|
||||||
|
|
||||||
if(error != WSAEWOULDBLOCK) {
|
if(error != WSAEWOULDBLOCK) {
|
||||||
/*
|
char* s = nullptr;
|
||||||
* TODO!
|
FormatMessageA(
|
||||||
wchar_t *s = nullptr;
|
|
||||||
FormatMessageW(
|
|
||||||
FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
|
FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
|
||||||
nullptr,
|
nullptr,
|
||||||
error,
|
error,
|
||||||
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
|
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
|
||||||
(LPWSTR)&s,
|
(LPSTR) &s,
|
||||||
0,
|
0,
|
||||||
nullptr
|
nullptr
|
||||||
);
|
);
|
||||||
fprintf(stdout, "Connect failed with code %d. Error: %ld/%S\n", result, error, s);
|
|
||||||
LocalFree(s);
|
|
||||||
*/
|
|
||||||
|
|
||||||
callback(ResultState::TSDNS_CONNECTION_FAIL, -1, "Failed to connect");
|
std::string message{s};
|
||||||
|
LocalFree(s);
|
||||||
|
|
||||||
|
callback(ResultState::TSDNS_CONNECTION_FAIL, -1, "Failed to connect: " + message);
|
||||||
this->destroy_tsdns_request(request);
|
this->destroy_tsdns_request(request);
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
@ -157,7 +242,7 @@ void Resolver::resolve_tsdns(const char *query, const sockaddr_storage& server_a
|
|||||||
auto seconds = chrono::floor<chrono::seconds>(timeout);
|
auto seconds = chrono::floor<chrono::seconds>(timeout);
|
||||||
auto microseconds = chrono::ceil<chrono::microseconds>(timeout - seconds);
|
auto microseconds = chrono::ceil<chrono::microseconds>(timeout - seconds);
|
||||||
|
|
||||||
timeval tv{seconds.count(), microseconds.count()};
|
timeval tv{(long) seconds.count(), (long) microseconds.count()};
|
||||||
auto errc = event_add(request->timeout_event, &tv);
|
auto errc = event_add(request->timeout_event, &tv);
|
||||||
|
|
||||||
//TODO: Check for error
|
//TODO: Check for error
|
||||||
@ -214,7 +299,7 @@ void Resolver::event_tsdns_write(Resolver::tsdns_request *request) {
|
|||||||
if(request->write_buffer.empty())
|
if(request->write_buffer.empty())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
auto written = send(request->socket, request->write_buffer.data(), min(request->write_buffer.size(), 1024UL), MSG_DONTWAIT);
|
auto written = send(request->socket, request->write_buffer.data(), (int) min(request->write_buffer.size(), 1024UL), MSG_DONTWAIT);
|
||||||
if(written < 0) {
|
if(written < 0) {
|
||||||
#ifdef WIN32
|
#ifdef WIN32
|
||||||
auto error = WSAGetLastError();
|
auto error = WSAGetLastError();
|
||||||
|
@ -9,8 +9,10 @@
|
|||||||
#include <event.h>
|
#include <event.h>
|
||||||
|
|
||||||
#include "./types.h"
|
#include "./types.h"
|
||||||
#ifndef WIN32
|
#ifdef WIN32
|
||||||
struct ub_ctx;
|
#include <WinDNS.h>
|
||||||
|
#else
|
||||||
|
struct ub_ctx;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
namespace tc::dns {
|
namespace tc::dns {
|
||||||
@ -22,7 +24,8 @@ namespace tc::dns {
|
|||||||
|
|
||||||
struct DNSResponseData {
|
struct DNSResponseData {
|
||||||
#ifdef WIN32
|
#ifdef WIN32
|
||||||
//IMPLEMENT ME!
|
bool wide_string{false};
|
||||||
|
DNS_QUERY_RESULT data;
|
||||||
#else
|
#else
|
||||||
uint8_t* buffer{nullptr};
|
uint8_t* buffer{nullptr};
|
||||||
size_t length{0};
|
size_t length{0};
|
||||||
@ -68,11 +71,10 @@ namespace tc::dns {
|
|||||||
|
|
||||||
std::string bogus;
|
std::string bogus;
|
||||||
uint8_t secure_state{0};
|
uint8_t secure_state{0};
|
||||||
|
|
||||||
std::shared_ptr<DNSResponseData> data{nullptr};
|
|
||||||
#else
|
#else
|
||||||
|
DNSResponse(std::shared_ptr<DNSResponseData>);
|
||||||
#endif
|
#endif
|
||||||
|
std::shared_ptr<DNSResponseData> data{nullptr};
|
||||||
|
|
||||||
bool is_parsed{false};
|
bool is_parsed{false};
|
||||||
std::string parse_error{};
|
std::string parse_error{};
|
||||||
@ -93,6 +95,7 @@ namespace tc::dns {
|
|||||||
|
|
||||||
DNS_TIMEOUT = 0x10,
|
DNS_TIMEOUT = 0x10,
|
||||||
DNS_FAIL = 0x11, /* error detail is a DNS error code */
|
DNS_FAIL = 0x11, /* error detail is a DNS error code */
|
||||||
|
DNS_API_FAIL = 0x12,
|
||||||
|
|
||||||
TSDNS_CONNECTION_FAIL = 0x20,
|
TSDNS_CONNECTION_FAIL = 0x20,
|
||||||
TSDNS_EMPTY_RESPONSE = 0x21,
|
TSDNS_EMPTY_RESPONSE = 0x21,
|
||||||
@ -113,21 +116,59 @@ namespace tc::dns {
|
|||||||
void resolve_dns(const char* /* name */, const rrtype::value& /* rrtype */, const rrclass::value& /* rrclass */, const std::chrono::microseconds& /* timeout */, const dns_callback_t& /* callback */);
|
void resolve_dns(const char* /* name */, const rrtype::value& /* rrtype */, const rrclass::value& /* rrclass */, const std::chrono::microseconds& /* timeout */, const dns_callback_t& /* callback */);
|
||||||
void resolve_tsdns(const char* /* name */, const sockaddr_storage& /* server */, const std::chrono::microseconds& /* timeout */, const tsdns_callback_t& /* callback */);
|
void resolve_tsdns(const char* /* name */, const sockaddr_storage& /* server */, const std::chrono::microseconds& /* timeout */, const tsdns_callback_t& /* callback */);
|
||||||
private:
|
private:
|
||||||
#ifndef WIN32
|
#ifdef WIN32
|
||||||
|
struct dns_request;
|
||||||
|
struct dns_old_request_data {
|
||||||
|
/* request might me nullptr if its beeing timeouted */
|
||||||
|
struct dns_request* request{nullptr}; /* protected by lock */
|
||||||
|
std::mutex* lock{nullptr};
|
||||||
|
};
|
||||||
|
|
||||||
|
struct {
|
||||||
|
HMODULE libhandle{nullptr};
|
||||||
|
|
||||||
|
DNS_STATUS (*DnsQueryEx)(
|
||||||
|
_In_ PDNS_QUERY_REQUEST pQueryRequest,
|
||||||
|
_Inout_ PDNS_QUERY_RESULT pQueryResults,
|
||||||
|
_Inout_opt_ PDNS_QUERY_CANCEL pCancelHandle
|
||||||
|
) = nullptr;
|
||||||
|
|
||||||
|
DNS_STATUS (*DnsCancelQuery)(
|
||||||
|
_In_ PDNS_QUERY_CANCEL pCancelHandle
|
||||||
|
) = nullptr;
|
||||||
|
} dnsapi;
|
||||||
|
#endif
|
||||||
|
|
||||||
struct dns_request {
|
struct dns_request {
|
||||||
Resolver* resolver{nullptr};
|
Resolver* resolver{nullptr};
|
||||||
|
|
||||||
int ub_id{0};
|
|
||||||
struct ::event* timeout_event{nullptr};
|
|
||||||
struct ::event* register_event{nullptr};
|
|
||||||
|
|
||||||
std::string host;
|
std::string host;
|
||||||
dns::rrtype::value rrtype{dns::rrtype::Unassigned};
|
dns::rrtype::value rrtype{dns::rrtype::Unassigned};
|
||||||
dns::rrclass::value rrclass{dns::rrclass::IN};
|
dns::rrclass::value rrclass{dns::rrclass::IN};
|
||||||
|
|
||||||
dns_callback_t callback{};
|
dns_callback_t callback{};
|
||||||
|
#ifdef WIN32
|
||||||
|
std::wstring whost;
|
||||||
|
|
||||||
|
/* windows 8 or newer */
|
||||||
|
DNS_QUERY_REQUEST dns_query;
|
||||||
|
DNS_QUERY_RESULT dns_result;
|
||||||
|
DNS_QUERY_CANCEL dns_cancel;
|
||||||
|
|
||||||
|
/* for old stuff */
|
||||||
|
//std::thread resolve_thread;
|
||||||
|
std::mutex* threaded_lock{nullptr};
|
||||||
|
struct dns_old_request_data* thread_data{nullptr}; /* protected by threaded_lock */
|
||||||
|
#else
|
||||||
|
|
||||||
|
int ub_id{0};
|
||||||
|
struct ::event* register_event{nullptr};
|
||||||
|
#endif
|
||||||
|
struct ::event* timeout_event{nullptr};
|
||||||
|
struct ::event* processed_event{nullptr};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#ifndef WIN32
|
||||||
struct ub_ctx* ub_ctx = nullptr;
|
struct ub_ctx* ub_ctx = nullptr;
|
||||||
#endif
|
#endif
|
||||||
struct tsdns_request {
|
struct tsdns_request {
|
||||||
@ -156,21 +197,22 @@ namespace tc::dns {
|
|||||||
|
|
||||||
|
|
||||||
std::vector<tsdns_request*> tsdns_requests{};
|
std::vector<tsdns_request*> tsdns_requests{};
|
||||||
#ifndef WIN32
|
|
||||||
std::vector<dns_request*> dns_requests{};
|
std::vector<dns_request*> dns_requests{};
|
||||||
#endif
|
|
||||||
std::recursive_mutex request_lock{}; /* this is recursive because due to the instance callback resolve_dns could be called recursively */
|
std::recursive_mutex request_lock{}; /* this is recursive because due to the instance callback resolve_dns could be called recursively */
|
||||||
|
|
||||||
#ifndef WIN32
|
bool initialize_platform(std::string& /* error */, bool /* use hosts */, bool /* use resolv */);
|
||||||
|
void finalize_platform();
|
||||||
|
|
||||||
void destroy_dns_request(dns_request*);
|
void destroy_dns_request(dns_request*);
|
||||||
#endif
|
|
||||||
void destroy_tsdns_request(tsdns_request*);
|
void destroy_tsdns_request(tsdns_request*);
|
||||||
|
|
||||||
void event_loop_runner();
|
void event_loop_runner();
|
||||||
|
|
||||||
#ifndef WIN32
|
|
||||||
void evtimer_dns_callback(dns_request* /* request */);
|
void evtimer_dns_callback(dns_request* /* request */);
|
||||||
|
#ifndef WIN32
|
||||||
void ub_callback(dns_request* /* request */, int /* rcode */, void* /* packet */, int /* packet_len */, int /* sec */, char* /* why_bogus */);
|
void ub_callback(dns_request* /* request */, int /* rcode */, void* /* packet */, int /* packet_len */, int /* sec */, char* /* why_bogus */);
|
||||||
|
#else
|
||||||
|
void dns_callback(dns_request* /* request */);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
void evtimer_tsdns_callback(tsdns_request* /* request */);
|
void evtimer_tsdns_callback(tsdns_request* /* request */);
|
||||||
|
@ -16,18 +16,7 @@
|
|||||||
using namespace std;
|
using namespace std;
|
||||||
using namespace tc::dns;
|
using namespace tc::dns;
|
||||||
|
|
||||||
bool Resolver::initialize(std::string &error, bool hosts, bool resolv) {
|
bool Resolver::initialize_platform(std::string &error, bool hosts, bool resolv) {
|
||||||
if(this->event.loop_active)
|
|
||||||
this->finalize();
|
|
||||||
|
|
||||||
this->event.loop_active = true;
|
|
||||||
this->event.base = event_base_new();
|
|
||||||
if(!this->event.base) {
|
|
||||||
error = "failed to allcoate event base";
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
this->event.loop = std::thread(std::bind(&Resolver::event_loop_runner, this));
|
|
||||||
|
|
||||||
this->ub_ctx = ub_ctx_create_event(this->event.base);
|
this->ub_ctx = ub_ctx_create_event(this->event.base);
|
||||||
if(!this->ub_ctx) {
|
if(!this->ub_ctx) {
|
||||||
this->finalize();
|
this->finalize();
|
||||||
@ -50,63 +39,9 @@ bool Resolver::initialize(std::string &error, bool hosts, bool resolv) {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Resolver::finalize() {
|
void Resolver::finalize_platform() {
|
||||||
this->event.loop_active = false;
|
|
||||||
if(this->event.base) {
|
|
||||||
auto ret = event_base_loopexit(this->event.base, nullptr);
|
|
||||||
if(ret != 0) {
|
|
||||||
cerr << "Failed to exit event base loop: " << ret << endl;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
this->event.condition.notify_one();
|
|
||||||
if(this->event.loop.joinable())
|
|
||||||
this->event.loop.join();
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
unique_lock lock(this->request_lock);
|
|
||||||
auto dns_list = std::move(this->dns_requests);
|
|
||||||
auto tsdns_list = std::move(this->tsdns_requests);
|
|
||||||
|
|
||||||
for(auto entry : dns_list) {
|
|
||||||
ub_cancel(this->ub_ctx, entry->ub_id);
|
|
||||||
|
|
||||||
entry->callback(ResultState::ABORT, 0, nullptr);
|
|
||||||
this->destroy_dns_request(entry);
|
|
||||||
}
|
|
||||||
|
|
||||||
for(auto entry : tsdns_list) {
|
|
||||||
entry->callback(ResultState::ABORT, 0, "");
|
|
||||||
this->destroy_tsdns_request(entry);
|
|
||||||
}
|
|
||||||
lock.unlock();
|
|
||||||
}
|
|
||||||
|
|
||||||
ub_ctx_delete((struct ub_ctx*) this->ub_ctx);
|
ub_ctx_delete((struct ub_ctx*) this->ub_ctx);
|
||||||
this->ub_ctx = nullptr;
|
this->ub_ctx = nullptr;
|
||||||
|
|
||||||
if(this->event.base) {
|
|
||||||
event_base_free(this->event.base);
|
|
||||||
this->event.base = nullptr;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Resolver::event_loop_runner() {
|
|
||||||
while(true) {
|
|
||||||
{
|
|
||||||
unique_lock lock{this->event.lock};
|
|
||||||
if(!this->event.loop_active)
|
|
||||||
break;
|
|
||||||
|
|
||||||
this->event.condition.wait(lock);
|
|
||||||
if(!this->event.loop_active)
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
event_base_loop(this->event.base, 0);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//Call only within the event loop!
|
//Call only within the event loop!
|
||||||
@ -118,6 +53,9 @@ void Resolver::destroy_dns_request(Resolver::dns_request *request) {
|
|||||||
this->dns_requests.erase(std::find(this->dns_requests.begin(), this->dns_requests.end(), request), this->dns_requests.end());
|
this->dns_requests.erase(std::find(this->dns_requests.begin(), this->dns_requests.end(), request), this->dns_requests.end());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(!this->event.loop_active)
|
||||||
|
ub_cancel(this->ub_ctx, entry->ub_id);
|
||||||
|
|
||||||
if(request->register_event) {
|
if(request->register_event) {
|
||||||
event_del_noblock(request->register_event);
|
event_del_noblock(request->register_event);
|
||||||
event_free(request->register_event);
|
event_free(request->register_event);
|
||||||
|
@ -1,9 +1,14 @@
|
|||||||
#include "./response.h"
|
#include "./response.h"
|
||||||
#include "./resolver.h"
|
#include "./resolver.h"
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
#include <codecvt>
|
||||||
#ifdef WIN32
|
#ifdef WIN32
|
||||||
#include <Windows.h>
|
#include <WS2tcpip.h>
|
||||||
#include <in6addr.h>
|
#include <in6addr.h>
|
||||||
|
#include <ip2string.h>
|
||||||
|
#include <inaddr.h>
|
||||||
|
#include <WinDNS.h>
|
||||||
#else
|
#else
|
||||||
#include <arpa/inet.h>
|
#include <arpa/inet.h>
|
||||||
#include <netinet/in.h>
|
#include <netinet/in.h>
|
||||||
@ -12,18 +17,7 @@
|
|||||||
using namespace tc::dns::response;
|
using namespace tc::dns::response;
|
||||||
using namespace tc::dns::response::rrparser;
|
using namespace tc::dns::response::rrparser;
|
||||||
|
|
||||||
inline std::string to_string(const in6_addr& address) {
|
#ifndef WIN32
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
uint16_t DNSHeader::field(int index) const {
|
uint16_t DNSHeader::field(int index) const {
|
||||||
return ((uint16_t*) this->response->packet_data())[index];
|
return ((uint16_t*) this->response->packet_data())[index];
|
||||||
}
|
}
|
||||||
@ -36,17 +30,83 @@ DNSResourceRecords::DNSResourceRecords(std::shared_ptr<DNSResponseData> packet,
|
|||||||
const uint8_t* DNSResourceRecords::payload_data() const {
|
const uint8_t* DNSResourceRecords::payload_data() const {
|
||||||
return this->data->buffer + this->offset;
|
return this->data->buffer + this->offset;
|
||||||
}
|
}
|
||||||
|
#else
|
||||||
|
DNSResourceRecords::DNSResourceRecords(std::shared_ptr<tc::dns::DNSResponseData> data, PDNS_RECORDA rdata) : nrecord{rdata}, data{std::move(data)} { }
|
||||||
|
bool DNSResourceRecords::is_wide_string() const {
|
||||||
|
return this->data->wide_string;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
bool A::is_valid() {
|
||||||
|
#ifdef WIN32
|
||||||
|
return true;
|
||||||
|
#else
|
||||||
|
return this->handle->payload_length() == 4;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
in_addr A::address() {
|
||||||
|
#ifdef WIN32
|
||||||
|
in_addr result{};
|
||||||
|
result.S_un.S_addr = this->handle->native_record()->Data.A.IpAddress;
|
||||||
|
return result;
|
||||||
|
#else
|
||||||
|
//TODO: Attention: Unaligned access
|
||||||
|
return {*(uint32_t*) this->handle->payload_data()};
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string A::address_string() {
|
||||||
|
#ifdef WIN32
|
||||||
|
struct in_addr address = this->address();
|
||||||
|
char buffer[17];
|
||||||
|
RtlIpv4AddressToStringA(&address, buffer);
|
||||||
|
return std::string{buffer};
|
||||||
|
#else
|
||||||
|
auto _1 = this->handle->payload_data()[0],
|
||||||
|
_2 = this->handle->payload_data()[1],
|
||||||
|
_3 = this->handle->payload_data()[2],
|
||||||
|
_4 = this->handle->payload_data()[3];
|
||||||
|
return std::to_string(_1) + "." + std::to_string(_2) + "." + std::to_string(_3) + "." + std::to_string(_4);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
//---------------- AAAA
|
//---------------- AAAA
|
||||||
|
#ifdef WIN32
|
||||||
|
bool AAAA::is_valid() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
std::string AAAA::address_string() {
|
std::string AAAA::address_string() {
|
||||||
return to_string(this->address());
|
struct in6_addr address = this->address();
|
||||||
|
char buffer[47];
|
||||||
|
RtlIpv6AddressToStringA(&address, buffer); //Supported for Win7 as well and not only above 8.1 like inet_ntop
|
||||||
|
return std::string{buffer};
|
||||||
|
}
|
||||||
|
|
||||||
|
in6_addr AAAA::address() {
|
||||||
|
in6_addr result{};
|
||||||
|
memcpy(result.u.Byte, this->handle->native_record()->Data.AAAA.Ip6Address.IP6Byte, 16);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
bool AAAA::is_valid() {
|
||||||
|
return this->handle->payload_length() == 16;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string AAAA::address_string() {
|
||||||
|
auto address = this->address();
|
||||||
|
|
||||||
|
char buffer[INET6_ADDRSTRLEN];
|
||||||
|
if(!inet_ntop(AF_INET6, (void*) &address, buffer, INET6_ADDRSTRLEN)) return "";
|
||||||
|
return std::string(buffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
in6_addr AAAA::address() {
|
in6_addr AAAA::address() {
|
||||||
return {
|
return {
|
||||||
.__in6_u = {
|
.__in6_u = {
|
||||||
.__u6_addr32 = {
|
.__u6_addr32 = {
|
||||||
//Attention unaligned memory access
|
//TODO: Attention unaligned memory access
|
||||||
((uint32_t*) this->handle->payload_data())[0],
|
((uint32_t*) this->handle->payload_data())[0],
|
||||||
((uint32_t*) this->handle->payload_data())[1],
|
((uint32_t*) this->handle->payload_data())[1],
|
||||||
((uint32_t*) this->handle->payload_data())[2],
|
((uint32_t*) this->handle->payload_data())[2],
|
||||||
@ -55,8 +115,23 @@ in6_addr AAAA::address() {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
//---------------- SRV
|
//---------------- SRV
|
||||||
|
#ifdef WIN32
|
||||||
|
bool SRV::is_valid() { return true; }
|
||||||
|
std::string SRV::target_hostname() {
|
||||||
|
if(this->handle->is_wide_string()) {
|
||||||
|
auto result = std::wstring{ ((PDNS_RECORDW) this->handle->native_record())->Data.Srv.pNameTarget };
|
||||||
|
return std::string{result.begin(), result.end()};
|
||||||
|
} else {
|
||||||
|
return std::string{ this->handle->native_record()->Data.Srv.pNameTarget };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
uint16_t SRV::priority() { return this->handle->native_record()->Data.SRV.wPriority; }
|
||||||
|
uint16_t SRV::weight() { return this->handle->native_record()->Data.SRV.wWeight; }
|
||||||
|
uint16_t SRV::target_port() { return this->handle->native_record()->Data.SRV.wPort; }
|
||||||
|
#else
|
||||||
bool SRV::is_valid() {
|
bool SRV::is_valid() {
|
||||||
if(this->handle->payload_length() < 7)
|
if(this->handle->payload_length() < 7)
|
||||||
return false;
|
return false;
|
||||||
@ -72,16 +147,35 @@ std::string SRV::target_hostname() {
|
|||||||
return this->handle->dns_data()->parse_dns_dn(error, index, true);
|
return this->handle->dns_data()->parse_dns_dn(error, index, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
uint16_t SRV::priority() { return ntohs(((uint16_t*) this->handle->payload_data())[0]); }
|
||||||
|
uint16_t SRV::weight() { return ntohs(((uint16_t*) this->handle->payload_data())[1]); }
|
||||||
|
uint16_t SRV::target_port() { return ntohs(((uint16_t*) this->handle->payload_data())[2]); }
|
||||||
|
#endif
|
||||||
|
|
||||||
//---------------- All types with a name
|
//---------------- All types with a name
|
||||||
bool named_base::is_valid() {
|
bool named_base::is_valid() {
|
||||||
|
#ifdef WIN32
|
||||||
|
return true;
|
||||||
|
#else
|
||||||
size_t index = this->handle->payload_offset();
|
size_t index = this->handle->payload_offset();
|
||||||
std::string error{};
|
std::string error{};
|
||||||
this->handle->dns_data()->parse_dns_dn(error, index, true);
|
this->handle->dns_data()->parse_dns_dn(error, index, true);
|
||||||
return error.empty();
|
return error.empty();
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string named_base::name() {
|
std::string named_base::name() {
|
||||||
size_t index = this->handle->payload_offset();
|
#ifdef WIN32
|
||||||
|
if(this->handle->is_wide_string()) {
|
||||||
|
auto result = std::wstring{ ((PDNS_RECORDW) this->handle->native_record())->Data.Cname.pNameHost };
|
||||||
|
return std::string{result.begin(), result.end()};
|
||||||
|
} else {
|
||||||
|
return std::string{ this->handle->native_record()->Data.Cname.pNameHost };
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
size_t index = this->handle->payload_offset();
|
||||||
std::string error{};
|
std::string error{};
|
||||||
return this->handle->dns_data()->parse_dns_dn(error, index, true);
|
return this->handle->dns_data()->parse_dns_dn(error, index, true);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
@ -3,13 +3,20 @@
|
|||||||
#include <memory>
|
#include <memory>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <netinet/in.h>
|
#ifdef WIN32
|
||||||
|
#include <WinSock2.h>
|
||||||
|
#include <Windows.h>
|
||||||
|
#include <WinDNS.h>
|
||||||
|
#else
|
||||||
|
#include <netinet/in.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
#include "./types.h"
|
#include "./types.h"
|
||||||
|
|
||||||
|
struct in6_addr;
|
||||||
namespace tc::dns {
|
namespace tc::dns {
|
||||||
class DNSResponse;
|
class DNSResponse;
|
||||||
class DNSResponseData;
|
struct DNSResponseData;
|
||||||
|
|
||||||
namespace response {
|
namespace response {
|
||||||
#ifndef WIN32
|
#ifndef WIN32
|
||||||
@ -57,16 +64,32 @@ namespace tc::dns {
|
|||||||
friend class tc::dns::DNSResponse;
|
friend class tc::dns::DNSResponse;
|
||||||
public:
|
public:
|
||||||
[[nodiscard]] inline std::string qname() const {
|
[[nodiscard]] inline std::string qname() const {
|
||||||
|
#ifdef WIN32
|
||||||
|
return std::string{this->nrecord->pName};
|
||||||
|
#else
|
||||||
return this->name;
|
return this->name;
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
[[nodiscard]] inline rrtype::value atype() const {
|
[[nodiscard]] inline rrtype::value atype() const {
|
||||||
|
#ifdef WIN32
|
||||||
|
return static_cast<rrtype::value>(this->nrecord->wType);
|
||||||
|
#else
|
||||||
return this->type;
|
return this->type;
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
[[nodiscard]] inline rrclass::value aclass() const {
|
[[nodiscard]] inline rrclass::value aclass() const {
|
||||||
return this->klass;
|
#ifdef WIN32
|
||||||
|
return static_cast<rrclass::value>(1);
|
||||||
|
#else
|
||||||
|
return this->klass;
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
[[nodiscard]] inline uint16_t attl() const {
|
[[nodiscard]] inline uint16_t attl() const {
|
||||||
|
#ifdef WIN32
|
||||||
|
return (uint16_t) this->nrecord->dwTtl;
|
||||||
|
#else
|
||||||
return this->ttl;
|
return this->ttl;
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifndef WIN32
|
#ifndef WIN32
|
||||||
@ -75,6 +98,7 @@ namespace tc::dns {
|
|||||||
[[nodiscard]] inline size_t payload_offset() const { return this->offset; }
|
[[nodiscard]] inline size_t payload_offset() const { return this->offset; }
|
||||||
#else
|
#else
|
||||||
[[nodiscard]] inline PDNS_RECORDA native_record() const { return this->nrecord; }
|
[[nodiscard]] inline PDNS_RECORDA native_record() const { return this->nrecord; }
|
||||||
|
[[nodiscard]] bool is_wide_string() const;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
[[nodiscard]] inline std::shared_ptr<DNSResponseData> dns_data() const {
|
[[nodiscard]] inline std::shared_ptr<DNSResponseData> dns_data() const {
|
||||||
@ -83,7 +107,7 @@ namespace tc::dns {
|
|||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
[[nodiscard]] inline T parse() const {
|
[[nodiscard]] inline T parse() const {
|
||||||
if(T::type != this->type)
|
if(T::type != this->atype())
|
||||||
throw std::logic_error{"parser type mismatch"};
|
throw std::logic_error{"parser type mismatch"};
|
||||||
return T{this};
|
return T{this};
|
||||||
}
|
}
|
||||||
@ -134,21 +158,14 @@ namespace tc::dns {
|
|||||||
}
|
}
|
||||||
|
|
||||||
define_parser(A, base,
|
define_parser(A, base,
|
||||||
[[nodiscard]] inline bool is_valid() { return this->handle->payload_length() == 4; }
|
[[nodiscard]] bool is_valid();
|
||||||
[[nodiscard]] inline std::string address_string() {
|
[[nodiscard]] std::string address_string();
|
||||||
auto _1 = this->handle->payload_data()[0],
|
|
||||||
_2 = this->handle->payload_data()[1],
|
[[nodiscard]] in_addr address();
|
||||||
_3 = this->handle->payload_data()[2],
|
|
||||||
_4 = this->handle->payload_data()[3];
|
|
||||||
return std::to_string(_1) + "." + std::to_string(_2) + "." + std::to_string(_3) + "." + std::to_string(_4);
|
|
||||||
}
|
|
||||||
[[nodiscard]] inline in_addr address() {
|
|
||||||
return {*(uint32_t*) this->handle->payload_data()};
|
|
||||||
}
|
|
||||||
);
|
);
|
||||||
|
|
||||||
define_parser(AAAA, base,
|
define_parser(AAAA, base,
|
||||||
[[nodiscard]] inline bool is_valid() { return this->handle->payload_length() == 16; }
|
[[nodiscard]] bool is_valid();
|
||||||
[[nodiscard]] std::string address_string();
|
[[nodiscard]] std::string address_string();
|
||||||
[[nodiscard]] in6_addr address();
|
[[nodiscard]] in6_addr address();
|
||||||
);
|
);
|
||||||
@ -156,9 +173,9 @@ namespace tc::dns {
|
|||||||
define_parser(SRV, base,
|
define_parser(SRV, base,
|
||||||
[[nodiscard]] bool is_valid();
|
[[nodiscard]] bool is_valid();
|
||||||
|
|
||||||
[[nodiscard]] inline uint16_t priority() { return ntohs(((uint16_t*) this->handle->payload_data())[0]); }
|
[[nodiscard]] uint16_t priority();
|
||||||
[[nodiscard]] inline uint16_t weight() { return ntohs(((uint16_t*) this->handle->payload_data())[1]); }
|
[[nodiscard]] uint16_t weight();
|
||||||
[[nodiscard]] inline uint16_t target_port() { return ntohs(((uint16_t*) this->handle->payload_data())[2]); }
|
[[nodiscard]] uint16_t target_port();
|
||||||
[[nodiscard]] std::string target_hostname();
|
[[nodiscard]] std::string target_hostname();
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -71,7 +71,7 @@ namespace tc::dns {
|
|||||||
CSYNC = 62, // Child-To-Parent Synchronization,[RFC7477],
|
CSYNC = 62, // Child-To-Parent Synchronization,[RFC7477],
|
||||||
ZONEMD = 63, // message digest for DNS zone,[draft-wessels-dns-zone-digest],ZONEMD/zonemd-completed-template
|
ZONEMD = 63, // message digest for DNS zone,[draft-wessels-dns-zone-digest],ZONEMD/zonemd-completed-template
|
||||||
//Unassigned = 64-98,
|
//Unassigned = 64-98,
|
||||||
SPF = 99, // [RFC7208],
|
SPF = 99, // [RFC7208],
|
||||||
UINFO = 100, // [IANA-Reserved],
|
UINFO = 100, // [IANA-Reserved],
|
||||||
UID = 101, // [IANA-Reserved],
|
UID = 101, // [IANA-Reserved],
|
||||||
GID = 102, // [IANA-Reserved],
|
GID = 102, // [IANA-Reserved],
|
||||||
@ -83,7 +83,7 @@ namespace tc::dns {
|
|||||||
EUI48 = 108, // an EUI-48 address,[RFC7043],EUI48/eui48-completed-template
|
EUI48 = 108, // an EUI-48 address,[RFC7043],EUI48/eui48-completed-template
|
||||||
EUI64 = 109, // an EUI-64 address,[RFC7043],EUI64/eui64-completed-template
|
EUI64 = 109, // an EUI-64 address,[RFC7043],EUI64/eui64-completed-template
|
||||||
//Unassigned = 110-248, // ,
|
//Unassigned = 110-248, // ,
|
||||||
TKEY = 249, // Transaction Key,[RFC2930],
|
TKEY = 249, // Transaction Key,[RFC2930],
|
||||||
TSIG = 250, // Transaction Signature,[RFC2845],
|
TSIG = 250, // Transaction Signature,[RFC2845],
|
||||||
IXFR = 251, // incremental transfer,[RFC1995],
|
IXFR = 251, // incremental transfer,[RFC1995],
|
||||||
AXFR = 252, // transfer of an entire zone,[RFC1035][RFC5936],
|
AXFR = 252, // transfer of an entire zone,[RFC1035][RFC5936],
|
||||||
@ -96,12 +96,12 @@ namespace tc::dns {
|
|||||||
DOA = 259, // Digital Object Architecture,[draft-durand-doa-over-dns],DOA/doa-completed-template
|
DOA = 259, // Digital Object Architecture,[draft-durand-doa-over-dns],DOA/doa-completed-template
|
||||||
AMTRELAY = 260, // Automatic Multicast Tunneling Relay,[draft-ietf-mboned-driad-amt-discovery],AMTRELAY/amtrelay-completed-template
|
AMTRELAY = 260, // Automatic Multicast Tunneling Relay,[draft-ietf-mboned-driad-amt-discovery],AMTRELAY/amtrelay-completed-template
|
||||||
//Unassigned = 261-32767,
|
//Unassigned = 261-32767,
|
||||||
TA = 32768, // DNSSEC Trust Authorities,"[Sam_Weiler][http://cameo.library.cmu.edu/][ Deploying DNSSEC Without a Signed Root. Technical Report 1999-19,
|
TA = 32768, // DNSSEC Trust Authorities,"[Sam_Weiler][http://cameo.library.cmu.edu/][ Deploying DNSSEC Without a Signed Root. Technical Report 1999-19,
|
||||||
// Information Networking Institute, Carnegie Mellon University, April 2004.]",
|
// Information Networking Institute, Carnegie Mellon University, April 2004.]",
|
||||||
DLV = 32769, // DNSSEC Lookaside Validation,[RFC4431],
|
DLV = 32769, // DNSSEC Lookaside Validation,[RFC4431],
|
||||||
//Unassigned = 32770-65279,, // ,
|
//Unassigned = 32770-65279,, // ,
|
||||||
//Private use,65280-65534,,,,
|
//Private use,65280-65534,,,,
|
||||||
Reserved = 65535,
|
Reserved = 65535,
|
||||||
};
|
};
|
||||||
|
|
||||||
static std::map<value, const char*> names;
|
static std::map<value, const char*> names;
|
||||||
@ -114,13 +114,17 @@ namespace tc::dns {
|
|||||||
};
|
};
|
||||||
|
|
||||||
struct rrclass {
|
struct rrclass {
|
||||||
|
#ifdef IN
|
||||||
|
#undef IN
|
||||||
|
#endif
|
||||||
|
|
||||||
enum value : uint32_t {
|
enum value : uint32_t {
|
||||||
IN = 1, /* Internet */
|
IN = 1, /* Internet */
|
||||||
CH = 3, /* Chaos */
|
CH = 3, /* Chaos */
|
||||||
HS = 4, /* Hesiod */
|
HS = 4, /* Hesiod */
|
||||||
|
|
||||||
QCLASS_NONE = 254,
|
QCLASS_NONE = 254,
|
||||||
QCLASS_ANY = 255
|
QCLASS_ANY = 255,
|
||||||
};
|
};
|
||||||
|
|
||||||
static std::map<value, const char*> names;
|
static std::map<value, const char*> names;
|
||||||
|
@ -5,7 +5,6 @@
|
|||||||
#include <random>
|
#include <random>
|
||||||
#include <deque>
|
#include <deque>
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
#include <unbound-event.h>
|
|
||||||
|
|
||||||
#include "../src/response.h"
|
#include "../src/response.h"
|
||||||
#include "../src/resolver.h"
|
#include "../src/resolver.h"
|
||||||
@ -17,7 +16,20 @@ using namespace std;
|
|||||||
namespace parser = response::rrparser;
|
namespace parser = response::rrparser;
|
||||||
|
|
||||||
int main() {
|
int main() {
|
||||||
|
#ifdef WIN32
|
||||||
|
{
|
||||||
|
WSADATA wsaData;
|
||||||
|
auto error = WSAStartup(MAKEWORD(2, 2), &wsaData);
|
||||||
|
if (error != 0) {
|
||||||
|
wprintf(L"WSAStartup failed with %d\n", error);
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
evthread_use_windows_threads();
|
||||||
|
#else
|
||||||
evthread_use_pthreads();
|
evthread_use_pthreads();
|
||||||
|
#endif
|
||||||
//evthread_enable_lock_debugging();
|
//evthread_enable_lock_debugging();
|
||||||
|
|
||||||
std::string error{};
|
std::string error{};
|
||||||
@ -29,7 +41,7 @@ int main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
auto begin = chrono::system_clock::now();
|
auto begin = chrono::system_clock::now();
|
||||||
for(int i = 0; i < 250; i++) {
|
for(int i = 0; i < 1; i++) {
|
||||||
cr(resolver, {
|
cr(resolver, {
|
||||||
to_string(i) + "ts.twerion.net",
|
to_string(i) + "ts.twerion.net",
|
||||||
9922
|
9922
|
||||||
@ -45,8 +57,10 @@ int main() {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
this_thread::sleep_for(chrono::seconds{8});
|
this_thread::sleep_for(chrono::seconds{5});
|
||||||
|
cout << "Timeout" << endl;
|
||||||
resolver.finalize();
|
resolver.finalize();
|
||||||
|
cout << "Exit" << endl;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
@ -11,122 +11,128 @@
|
|||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <random>
|
#include <random>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
|
|
||||||
|
#ifndef WIN32
|
||||||
#include <arpa/inet.h>
|
#include <arpa/inet.h>
|
||||||
|
#else
|
||||||
|
#include <Ws2ipdef.h>
|
||||||
|
#include <ip2string.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
using namespace tc::dns;
|
using namespace tc::dns;
|
||||||
namespace parser = tc::dns::response::rrparser;
|
namespace parser = tc::dns::response::rrparser;
|
||||||
|
|
||||||
//-------------------------- IP-Resolve
|
//-------------------------- IP-Resolve
|
||||||
struct CrIpStatus {
|
struct CrIpStatus {
|
||||||
std::mutex pending_lock;
|
std::mutex pending_lock;
|
||||||
uint8_t pending{0};
|
uint8_t pending{0};
|
||||||
|
|
||||||
ServerAddress original{"", 0};
|
ServerAddress original{"", 0};
|
||||||
|
|
||||||
std::tuple<bool, std::string> a{false, "unset"};
|
std::tuple<bool, std::string> a{false, "unset"};
|
||||||
std::tuple<bool, std::string> aaaa{false, "unset"};
|
std::tuple<bool, std::string> aaaa{false, "unset"};
|
||||||
|
|
||||||
cr_callback_t callback;
|
cr_callback_t callback;
|
||||||
std::function<void(const std::shared_ptr<CrIpStatus>&)> finish_callback;
|
std::function<void(const std::shared_ptr<CrIpStatus>&)> finish_callback;
|
||||||
|
|
||||||
void one_finished(const std::shared_ptr<CrIpStatus>& _this) {
|
void one_finished(const std::shared_ptr<CrIpStatus>& _this) {
|
||||||
assert(&*_this == this);
|
assert(&*_this == this);
|
||||||
|
|
||||||
std::lock_guard lock{this->pending_lock};
|
std::lock_guard lock{this->pending_lock};
|
||||||
if(--pending == 0)
|
if(--pending == 0)
|
||||||
this->finish_callback(_this);
|
this->finish_callback(_this);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
void cr_ip_finish(const std::shared_ptr<CrIpStatus>& status) {
|
void cr_ip_finish(const std::shared_ptr<CrIpStatus>& status) {
|
||||||
if(std::get<0>(status->a)) {
|
if(std::get<0>(status->a)) {
|
||||||
status->callback(true, ServerAddress{std::get<1>(status->a), status->original.port});
|
status->callback(true, ServerAddress{std::get<1>(status->a), status->original.port});
|
||||||
} else if(std::get<0>(status->aaaa)) {
|
} else if(std::get<0>(status->aaaa)) {
|
||||||
status->callback(true, ServerAddress{std::get<1>(status->aaaa), status->original.port});
|
status->callback(true, ServerAddress{std::get<1>(status->aaaa), status->original.port});
|
||||||
} else {
|
} else {
|
||||||
status->callback(false, "failed to resolve an IP for " + status->original.host + ": A{" + std::get<1>(status->a) + "} AAAA{" + std::get<1>(status->aaaa) + "}");
|
status->callback(false, "failed to resolve an IP for " + status->original.host + ": A{" + std::get<1>(status->a) + "} AAAA{" + std::get<1>(status->aaaa) + "}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void tc::dns::cr_ip(Resolver& resolver, const ServerAddress& address, const cr_callback_t& callback) {
|
void tc::dns::cr_ip(Resolver& resolver, const ServerAddress& address, const cr_callback_t& callback) {
|
||||||
auto status = std::make_shared<CrIpStatus>();
|
auto status = std::make_shared<CrIpStatus>();
|
||||||
|
|
||||||
status->original = address;
|
status->original = address;
|
||||||
status->finish_callback = cr_ip_finish;
|
status->finish_callback = cr_ip_finish;
|
||||||
status->callback = callback;
|
status->callback = callback;
|
||||||
|
|
||||||
/* general pending so we could finish our method */
|
/* general pending so we could finish our method */
|
||||||
status->pending++;
|
status->pending++;
|
||||||
|
|
||||||
status->pending++;
|
status->pending++;
|
||||||
resolver.resolve_dns(address.host.c_str(), rrtype::A, rrclass::IN, std::chrono::seconds{5}, [status, &resolver](Resolver::ResultState::value state, int code, std::unique_ptr<DNSResponse> data){
|
resolver.resolve_dns(address.host.c_str(), rrtype::A, rrclass::IN, std::chrono::seconds{5}, [status, &resolver](Resolver::ResultState::value state, int code, std::unique_ptr<DNSResponse> data){
|
||||||
if(state != 0) {
|
if(state != 0) {
|
||||||
status->a = {false, "A query failed. State: " + std::to_string(state) + ", Code: " + std::to_string(code)};
|
status->a = {false, "A query failed. State: " + std::to_string(state) + ", Code: " + std::to_string(code)};
|
||||||
status->one_finished(status);
|
status->one_finished(status);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
std::string error;
|
std::string error;
|
||||||
if(!data->parse(error)) {
|
if(!data->parse(error)) {
|
||||||
status->a = {false, "A query failed. State: " + std::to_string(state) + ", Code: " + std::to_string(code)};
|
status->a = {false, "A query failed. State: " + std::to_string(state) + ", Code: " + std::to_string(code)};
|
||||||
status->one_finished(status);
|
status->one_finished(status);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
for(const auto& answer : data->answers()) {
|
for(const auto& answer : data->answers()) {
|
||||||
if(answer->atype() != rrtype::A){
|
if(answer->atype() != rrtype::A){
|
||||||
std::cerr << "Received a non A record answer in A query!" << std::endl;
|
std::cerr << "Received a non A record answer in A query!" << std::endl;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto data = answer->parse<parser::A>();
|
auto data = answer->parse<parser::A>();
|
||||||
if(!data.is_valid())
|
if(!data.is_valid())
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
status->a = {true, data.address_string()};
|
status->a = {true, data.address_string()};
|
||||||
status->one_finished(status);
|
status->one_finished(status);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
status->a = {false, "empty response"};
|
status->a = {false, "empty response"};
|
||||||
status->one_finished(status);
|
status->one_finished(status);
|
||||||
});
|
});
|
||||||
|
|
||||||
status->pending++;
|
status->pending++;
|
||||||
resolver.resolve_dns(address.host.c_str(), rrtype::AAAA, rrclass::IN, std::chrono::seconds{5}, [status, &resolver](Resolver::ResultState::value state, int code, std::unique_ptr<DNSResponse> data){
|
resolver.resolve_dns(address.host.c_str(), rrtype::AAAA, rrclass::IN, std::chrono::seconds{5}, [status, &resolver](Resolver::ResultState::value state, int code, std::unique_ptr<DNSResponse> data){
|
||||||
if(state != 0) {
|
if(state != 0) {
|
||||||
status->aaaa = {false, "AAAA query failed. State: " + std::to_string(state) + ", Code: " + std::to_string(code)};
|
status->aaaa = {false, "AAAA query failed. State: " + std::to_string(state) + ", Code: " + std::to_string(code)};
|
||||||
status->one_finished(status);
|
status->one_finished(status);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string error;
|
std::string error;
|
||||||
if(!data->parse(error)) {
|
if(!data->parse(error)) {
|
||||||
status->aaaa = {false, "failed to parse AAAA query reponse: " + error};
|
status->aaaa = {false, "failed to parse AAAA query reponse: " + error};
|
||||||
status->one_finished(status);
|
status->one_finished(status);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
for(const auto& answer : data->answers()) {
|
for(const auto& answer : data->answers()) {
|
||||||
if(answer->atype() != rrtype::AAAA){
|
if(answer->atype() != rrtype::AAAA){
|
||||||
std::cerr << "Received a non AAAA record answer in AAAA query!" << std::endl;
|
std::cerr << "Received a non AAAA record answer in AAAA query!" << std::endl;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto data = answer->parse<parser::AAAA>();
|
auto data = answer->parse<parser::AAAA>();
|
||||||
if(!data.is_valid())
|
if(!data.is_valid())
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
status->aaaa = {true, data.address_string()};
|
status->aaaa = {true, data.address_string()};
|
||||||
status->one_finished(status);
|
status->one_finished(status);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
status->aaaa = {false, "empty response"};
|
status->aaaa = {false, "empty response"};
|
||||||
status->one_finished(status);
|
status->one_finished(status);
|
||||||
return;
|
return;
|
||||||
});
|
});
|
||||||
|
|
||||||
status->one_finished(status);
|
status->one_finished(status);
|
||||||
}
|
}
|
||||||
|
|
||||||
//-------------------------- SRV-Resolve
|
//-------------------------- SRV-Resolve
|
||||||
@ -135,205 +141,218 @@ static std::mt19937 srv_rnd(srv_rnd_dev());
|
|||||||
|
|
||||||
/* connect resolve for TS3 srv records */
|
/* connect resolve for TS3 srv records */
|
||||||
void tc::dns::cr_srv(Resolver& resolver, const ServerAddress& address, const cr_callback_t& callback, const std::string& application) {
|
void tc::dns::cr_srv(Resolver& resolver, const ServerAddress& address, const cr_callback_t& callback, const std::string& application) {
|
||||||
auto query = application + "." + address.host;
|
auto query = application + "." + address.host;
|
||||||
resolver.resolve_dns(query.c_str(), rrtype::SRV, rrclass::IN, std::chrono::seconds{5}, [callback, address, &resolver](Resolver::ResultState::value state, int code, std::unique_ptr<DNSResponse> data){
|
resolver.resolve_dns(query.c_str(), rrtype::SRV, rrclass::IN, std::chrono::seconds{5}, [callback, address, &resolver](Resolver::ResultState::value state, int code, std::unique_ptr<DNSResponse> data){
|
||||||
if(state != 0) {
|
if(state != 0) {
|
||||||
callback(false, "SRV query failed. State: " + std::to_string(state) + ", Code: " + std::to_string(code));
|
callback(false, "SRV query failed. State: " + std::to_string(state) + ", Code: " + std::to_string(code));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string error;
|
std::string error;
|
||||||
if(!data->parse(error)) {
|
if(!data->parse(error)) {
|
||||||
callback(false, "failed to parse srv query reponse: " + error);
|
callback(false, "failed to parse srv query reponse: " + error);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct SrvEntry {
|
struct SrvEntry {
|
||||||
uint16_t weight;
|
uint16_t weight;
|
||||||
std::string target;
|
std::string target;
|
||||||
uint16_t port;
|
uint16_t port;
|
||||||
};
|
};
|
||||||
std::map<uint16_t, std::vector<SrvEntry>> entries{};
|
std::map<uint16_t, std::vector<SrvEntry>> entries{};
|
||||||
|
|
||||||
auto answers = data->answers();
|
auto answers = data->answers();
|
||||||
for(const auto& answer : answers) {
|
for(const auto& answer : answers) {
|
||||||
if(answer->atype() != rrtype::SRV) {
|
if(answer->atype() != rrtype::SRV) {
|
||||||
std::cerr << "Received a non SRV record answer in SRV query (" << rrtype::name(answer->atype()) << ")!" << std::endl;
|
std::cerr << "Received a non SRV record answer in SRV query (" << rrtype::name(answer->atype()) << ")!" << std::endl;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto srv = answer->parse<parser::SRV>();
|
auto srv = answer->parse<parser::SRV>();
|
||||||
entries[srv.priority()].push_back({srv.weight(), srv.target_hostname(), srv.target_port()});
|
entries[srv.priority()].push_back({srv.weight(), srv.target_hostname(), srv.target_port()});
|
||||||
}
|
}
|
||||||
|
|
||||||
if(entries.empty()) {
|
if(entries.empty()) {
|
||||||
callback(false, "empty response");
|
callback(false, "empty response");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::deque<std::tuple<uint16_t, SrvEntry>> results{};
|
std::deque<std::tuple<uint16_t, SrvEntry>> results{};
|
||||||
for(auto [priority, pentries] : entries) {
|
for(auto [priority, pentries] : entries) {
|
||||||
int64_t count = 0;
|
uint32_t count = 0;
|
||||||
for(const auto& entry : pentries)
|
for(const auto& entry : pentries) {
|
||||||
count += std::max((size_t) entry.weight, 1UL);
|
count += max((size_t) entry.weight, 1UL);
|
||||||
|
}
|
||||||
|
|
||||||
std::uniform_int_distribution<std::mt19937::result_type> dist(0, count - 1);
|
std::uniform_int_distribution<std::mt19937::result_type> dist(0, (uint32_t) (count - 1));
|
||||||
auto index = dist(srv_rnd);
|
auto index = dist(srv_rnd);
|
||||||
|
|
||||||
count = 0;
|
count = 0;
|
||||||
for(const auto& entry : pentries) {
|
for(const auto& entry : pentries) {
|
||||||
count += std::max((size_t) entry.weight, 1UL);
|
count += max((size_t) entry.weight, 1UL);
|
||||||
if(count > index) {
|
if(count > index) {
|
||||||
count = -1;
|
count = -1;
|
||||||
results.emplace_back(priority, entry);
|
results.emplace_back(priority, entry);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
assert(!results.empty());
|
assert(!results.empty());
|
||||||
|
|
||||||
std::sort(results.begin(), results.end(), [](const auto& a, const auto& b) { return std::get<0>(a) < std::get<0>(b); });
|
std::sort(results.begin(), results.end(), [](const auto& a, const auto& b) { return std::get<0>(a) < std::get<0>(b); });
|
||||||
|
|
||||||
//TODO: Resolve backup stuff as well
|
//TODO: Resolve backup stuff as well
|
||||||
auto target = std::get<1>(*results.begin());
|
auto target = std::get<1>(*results.begin());
|
||||||
cr_ip(resolver, {
|
cr_ip(resolver, {
|
||||||
target.target,
|
target.target,
|
||||||
target.port == 0 ? address.port : target.port
|
target.port == 0 ? address.port : target.port
|
||||||
}, [callback](bool success, auto response) {
|
}, [callback](bool success, auto response) {
|
||||||
if(!success) {
|
if(!success) {
|
||||||
//TODO: Use the backup stuff?
|
//TODO: Use the backup stuff?
|
||||||
callback(false, "failed to resolve dns pointer: " + std::get<std::string>(response));
|
callback(false, "failed to resolve dns pointer: " + std::get<std::string>(response));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
callback(true, std::get<ServerAddress>(response));
|
callback(true, std::get<ServerAddress>(response));
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
//-------------------------- TSDNS-Resolve
|
//-------------------------- TSDNS-Resolve
|
||||||
void tc::dns::cr_tsdns(tc::dns::Resolver &resolver, const tc::dns::ServerAddress &address, const tc::dns::cr_callback_t &callback) {
|
void tc::dns::cr_tsdns(tc::dns::Resolver &resolver, const tc::dns::ServerAddress &address, const tc::dns::cr_callback_t &callback) {
|
||||||
auto root = domain_root(address.host);
|
auto root = domain_root(address.host);
|
||||||
cr_srv(resolver, {root, 0}, [&resolver, callback, address](bool success, auto data){
|
cr_srv(resolver, {root, 0}, [&resolver, callback, address](bool success, auto data){
|
||||||
if(!success) {
|
if(!success) {
|
||||||
callback(false, "failed to resolve tsdns address: " + std::get<std::string>(data));
|
callback(false, "failed to resolve tsdns address: " + std::get<std::string>(data));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
auto tsdns_host = std::get<ServerAddress>(data);
|
auto tsdns_host = std::get<ServerAddress>(data);
|
||||||
|
|
||||||
sockaddr_storage tsdns_address{};
|
sockaddr_storage tsdns_address{};
|
||||||
memset(&tsdns_address, 0, sizeof(tsdns_address));
|
memset(&tsdns_address, 0, sizeof(tsdns_address));
|
||||||
|
|
||||||
auto tsdns_a6 = (sockaddr_in6*) &tsdns_address;
|
auto tsdns_a6 = (sockaddr_in6*) &tsdns_address;
|
||||||
auto tsdns_a4 = (sockaddr_in*) &tsdns_address;
|
auto tsdns_a4 = (sockaddr_in*) &tsdns_address;
|
||||||
if(inet_pton(AF_INET6, tsdns_host.host.c_str(), &tsdns_a6->sin6_addr) == 1) {
|
#ifdef WIN32
|
||||||
tsdns_a6->sin6_family = AF_INET6;
|
PCSTR terminator{nullptr};
|
||||||
tsdns_a6->sin6_port = htons(tsdns_host.port == 0 ? 41144 : tsdns_host.port);
|
if(RtlIpv6StringToAddressA(tsdns_host.host.c_str(), &terminator, (in6_addr*) &tsdns_a6->sin6_addr) == 0)
|
||||||
} else if(inet_pton(AF_INET, tsdns_host.host.c_str(), &(tsdns_a4->sin_addr)) == 1) {
|
#else
|
||||||
tsdns_a4->sin_family = AF_INET;
|
if(inet_pton(AF_INET6, tsdns_host.host.c_str(), &tsdns_a6->sin6_addr) == 1)
|
||||||
tsdns_a4->sin_port = htons(tsdns_host.port == 0 ? 41144 : tsdns_host.port);
|
#endif
|
||||||
} else {
|
{
|
||||||
callback(false, "invalid tsdns host: " + tsdns_host.host);
|
tsdns_a6->sin6_family = AF_INET6;
|
||||||
return;
|
tsdns_a6->sin6_port = htons(tsdns_host.port == 0 ? 41144 : tsdns_host.port);
|
||||||
}
|
}
|
||||||
|
#ifdef WIN32
|
||||||
|
else if(RtlIpv4StringToAddressA(tsdns_host.host.c_str(), false, &terminator, (in_addr*) &tsdns_a4->sin_addr) == 0)
|
||||||
|
#else
|
||||||
|
else if(inet_pton(AF_INET, tsdns_host.host.c_str(), &(tsdns_a4->sin_addr)) == 1)
|
||||||
|
#endif
|
||||||
|
{
|
||||||
|
tsdns_a4->sin_family = AF_INET;
|
||||||
|
tsdns_a4->sin_port = htons(tsdns_host.port == 0 ? 41144 : tsdns_host.port);
|
||||||
|
} else {
|
||||||
|
callback(false, "invalid tsdns host: " + tsdns_host.host);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
auto query = address.host;
|
auto query = address.host;
|
||||||
std::transform(query.begin(), query.end(), query.begin(), tolower);
|
std::transform(query.begin(), query.end(), query.begin(), tolower);
|
||||||
resolver.resolve_tsdns(query.c_str(), tsdns_address, std::chrono::seconds{5}, [callback, query, address](Resolver::ResultState::value error, int detail, const std::string& response) {
|
resolver.resolve_tsdns(query.c_str(), tsdns_address, std::chrono::seconds{5}, [callback, query, address](Resolver::ResultState::value error, int detail, const std::string& response) {
|
||||||
if(error == Resolver::ResultState::SUCCESS) {
|
if(error == Resolver::ResultState::SUCCESS) {
|
||||||
if(response == "404")
|
if(response == "404")
|
||||||
callback(false, "no record found for " + query);
|
callback(false, "no record found for " + query);
|
||||||
else {
|
else {
|
||||||
std::string host{response};
|
std::string host{response};
|
||||||
std::string port{"$PORT"};
|
std::string port{"$PORT"};
|
||||||
|
|
||||||
//TODO: Backup IP-Addresses?
|
//TODO: Backup IP-Addresses?
|
||||||
if(host.find(',') != -1)
|
if(host.find(',') != -1)
|
||||||
host = std::string{host.begin(), host.begin() + host.find(',')};
|
host = std::string{host.begin(), host.begin() + host.find(',')};
|
||||||
|
|
||||||
auto colon_index = host.rfind(':');
|
auto colon_index = host.rfind(':');
|
||||||
if(colon_index > 0 && (host[colon_index - 1] == ']' || host.find(':') == colon_index)) {
|
if(colon_index > 0 && (host[colon_index - 1] == ']' || host.find(':') == colon_index)) {
|
||||||
port = host.substr(colon_index + 1);
|
port = host.substr(colon_index + 1);
|
||||||
host = host.substr(0, colon_index);
|
host = host.substr(0, colon_index);
|
||||||
}
|
}
|
||||||
|
|
||||||
ServerAddress resp{host, 0};
|
ServerAddress resp{host, 0};
|
||||||
if(port == "$PORT") {
|
if(port == "$PORT") {
|
||||||
resp.port = address.port;
|
resp.port = address.port;
|
||||||
} else {
|
} else {
|
||||||
try {
|
try {
|
||||||
resp.port = stoul(port);
|
resp.port = (uint16_t) stoul(port);
|
||||||
} catch(std::exception& ex) {
|
} catch(const std::exception&) {
|
||||||
callback(false, "failed to parse response: " + response + " Failed to parse port: " + port);
|
callback(false, "failed to parse response: " + response + " Failed to parse port: " + port);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
callback(true, resp);
|
callback(true, resp);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
callback(false, "query failed. Code: " + std::to_string(error) + "," + std::to_string(detail) + ": " + response);
|
callback(false, "query failed. Code: " + std::to_string(error) + "," + std::to_string(detail) + ": " + response);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}, "_tsdns._tcp");
|
}, "_tsdns._tcp");
|
||||||
}
|
}
|
||||||
|
|
||||||
//-------------------------- Full-Resolve
|
//-------------------------- Full-Resolve
|
||||||
|
|
||||||
struct CrStatus {
|
struct CrStatus {
|
||||||
enum State {
|
enum State {
|
||||||
PENDING,
|
PENDING,
|
||||||
FAILED,
|
FAILED,
|
||||||
SUCCESS
|
SUCCESS
|
||||||
};
|
};
|
||||||
|
|
||||||
std::recursive_mutex pending_lock; /* do_done could be called recursively because DNS request could answer instant! */
|
std::recursive_mutex pending_lock; /* do_done could be called recursively because DNS request could answer instant! */
|
||||||
uint8_t pending{0};
|
uint8_t pending{0};
|
||||||
bool finished{false};
|
bool finished{false};
|
||||||
|
|
||||||
tc::dns::ServerAddress address;
|
tc::dns::ServerAddress address;
|
||||||
tc::dns::cr_callback_t callback;
|
tc::dns::cr_callback_t callback;
|
||||||
|
|
||||||
~CrStatus() {
|
~CrStatus() {
|
||||||
assert(this->pending == 0);
|
assert(this->pending == 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
void do_done(const std::shared_ptr<CrStatus>& _this) {
|
void do_done(const std::shared_ptr<CrStatus>& _this) {
|
||||||
std::lock_guard lock{pending_lock};
|
std::lock_guard lock{pending_lock};
|
||||||
this->finished |= this->try_answer(_this); //May invokes next DNS query
|
this->finished |= this->try_answer(_this); //May invokes next DNS query
|
||||||
|
|
||||||
assert(this->pending > 0);
|
assert(this->pending > 0);
|
||||||
if(--pending == 0 && !this->finished) { //Order matters we have to decrease pensing!
|
if(--pending == 0 && !this->finished) { //Order matters we have to decrease pensing!
|
||||||
this->callback(false, "no results");
|
this->callback(false, "no results");
|
||||||
this->finished = true;
|
this->finished = true;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
typedef std::tuple<bool, std::function<void(const std::shared_ptr<CrStatus>&)>> flagged_executor_t;
|
typedef std::tuple<bool, std::function<void(const std::shared_ptr<CrStatus>&)>> flagged_executor_t;
|
||||||
|
|
||||||
flagged_executor_t execute_subsrv_ts;
|
flagged_executor_t execute_subsrv_ts;
|
||||||
std::tuple<State, std::string, tc::dns::ServerAddress> subsrv_ts;
|
std::tuple<State, std::string, tc::dns::ServerAddress> subsrv_ts;
|
||||||
|
|
||||||
flagged_executor_t execute_subsrv_ts3;
|
flagged_executor_t execute_subsrv_ts3;
|
||||||
std::tuple<State, std::string, tc::dns::ServerAddress> subsrv_ts3;
|
std::tuple<State, std::string, tc::dns::ServerAddress> subsrv_ts3;
|
||||||
|
|
||||||
flagged_executor_t execute_tsdns;
|
flagged_executor_t execute_tsdns;
|
||||||
std::tuple<State, std::string, tc::dns::ServerAddress> tsdns;
|
std::tuple<State, std::string, tc::dns::ServerAddress> tsdns;
|
||||||
|
|
||||||
flagged_executor_t execute_subdomain;
|
flagged_executor_t execute_subdomain;
|
||||||
std::tuple<State, std::string, tc::dns::ServerAddress> subdomain;
|
std::tuple<State, std::string, tc::dns::ServerAddress> subdomain;
|
||||||
|
|
||||||
//Only after subsrc and tsdns failed
|
//Only after subsrc and tsdns failed
|
||||||
flagged_executor_t execute_rootsrv;
|
flagged_executor_t execute_rootsrv;
|
||||||
std::tuple<State, std::string, tc::dns::ServerAddress> rootsrv;
|
std::tuple<State, std::string, tc::dns::ServerAddress> rootsrv;
|
||||||
|
|
||||||
//Only after subsrc and tsdns failed
|
//Only after subsrc and tsdns failed
|
||||||
flagged_executor_t execute_rootdomain;
|
flagged_executor_t execute_rootdomain;
|
||||||
std::tuple<State, std::string, tc::dns::ServerAddress> rootdomain;
|
std::tuple<State, std::string, tc::dns::ServerAddress> rootdomain;
|
||||||
|
|
||||||
#define try_answer_test(element, executor) \
|
#define try_answer_test(element, executor) \
|
||||||
if(std::get<0>(element) == State::SUCCESS) { \
|
if(std::get<0>(element) == State::SUCCESS) { \
|
||||||
this->callback(true, std::get<2>(element)); \
|
this->call_answer(std::get<2>(element)); \
|
||||||
return true; \
|
return true; \
|
||||||
} else if(std::get<0>(element) == State::PENDING) { \
|
} else if(std::get<0>(element) == State::PENDING) { \
|
||||||
if(!std::get<0>(executor)) { \
|
if(!std::get<0>(executor)) { \
|
||||||
@ -349,149 +368,171 @@ struct CrStatus {
|
|||||||
return false; \
|
return false; \
|
||||||
}
|
}
|
||||||
|
|
||||||
bool try_answer(const std::shared_ptr<CrStatus>& _this) {
|
bool try_answer(const std::shared_ptr<CrStatus>& _this) {
|
||||||
if(this->finished)
|
if(this->finished)
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
try_answer_test(this->subsrv_ts, this->execute_subsrv_ts);
|
try_answer_test(this->subsrv_ts, this->execute_subsrv_ts);
|
||||||
try_answer_test(this->subsrv_ts3, this->execute_subsrv_ts3);
|
try_answer_test(this->subsrv_ts3, this->execute_subsrv_ts3);
|
||||||
try_answer_test(this->tsdns, this->execute_tsdns);
|
try_answer_test(this->tsdns, this->execute_tsdns);
|
||||||
try_answer_test(this->subdomain, this->execute_subdomain);
|
try_answer_test(this->subdomain, this->execute_subdomain);
|
||||||
try_answer_test(this->rootsrv, this->execute_rootsrv);
|
try_answer_test(this->rootsrv, this->execute_rootsrv);
|
||||||
try_answer_test(this->rootdomain, this->execute_rootdomain);
|
try_answer_test(this->rootdomain, this->execute_rootdomain);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#define answer_log(element, executor) \
|
||||||
|
if(!std::get<0>(executor)) \
|
||||||
|
std::cout << #element << ": not executed" << std::endl; \
|
||||||
|
else if(std::get<0>(element) == State::PENDING) \
|
||||||
|
std::cout << #element << ": pending" << std::endl; \
|
||||||
|
else if(std::get<0>(element) == State::FAILED) \
|
||||||
|
std::cout << #element << ": failed: " << std::get<1>(element) << std::endl; \
|
||||||
|
else \
|
||||||
|
std::cout << #element << ": success: " << std::get<2>(element).host << ":" << std::get<2>(element).port << std::endl;
|
||||||
|
|
||||||
|
void call_answer(const tc::dns::ServerAddress& data) {
|
||||||
|
answer_log(this->subsrv_ts, this->execute_subsrv_ts);
|
||||||
|
answer_log(this->subsrv_ts3, this->execute_subsrv_ts3);
|
||||||
|
answer_log(this->tsdns, this->execute_tsdns);
|
||||||
|
answer_log(this->subdomain, this->execute_subdomain);
|
||||||
|
answer_log(this->rootsrv, this->execute_rootsrv);
|
||||||
|
answer_log(this->rootdomain, this->execute_rootdomain);
|
||||||
|
|
||||||
|
//TODO: Print data
|
||||||
|
this->callback(true, data);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
void tc::dns::cr(Resolver& resolver, const tc::dns::ServerAddress& address, const tc::dns::cr_callback_t& callback) {
|
void tc::dns::cr(Resolver& resolver, const tc::dns::ServerAddress& address, const tc::dns::cr_callback_t& callback) {
|
||||||
auto status = std::make_shared<CrStatus>();
|
auto status = std::make_shared<CrStatus>();
|
||||||
status->address = address;
|
status->address = address;
|
||||||
status->callback = callback;
|
status->callback = callback;
|
||||||
status->pending++;
|
status->pending++;
|
||||||
|
|
||||||
status->execute_subsrv_ts = {
|
status->execute_subsrv_ts = {
|
||||||
false,
|
false,
|
||||||
[&resolver](const std::shared_ptr<CrStatus>& status) {
|
[&resolver](const std::shared_ptr<CrStatus>& status) {
|
||||||
//std::cout << "Execute subsrc ts" << std::endl;
|
//std::cout << "Execute subsrc ts" << std::endl;
|
||||||
status->pending++;
|
status->pending++;
|
||||||
tc::dns::cr_srv(resolver, status->address, [status](bool success, auto data) {
|
tc::dns::cr_srv(resolver, status->address, [status](bool success, auto data) {
|
||||||
if(success) {
|
if(success) {
|
||||||
status->subsrv_ts = {CrStatus::SUCCESS, "", std::get<ServerAddress>(data)};
|
status->subsrv_ts = {CrStatus::SUCCESS, "", std::get<ServerAddress>(data)};
|
||||||
} else {
|
} else {
|
||||||
status->subsrv_ts = {CrStatus::FAILED, std::get<std::string>(data), {}};
|
status->subsrv_ts = {CrStatus::FAILED, std::get<std::string>(data), {}};
|
||||||
}
|
}
|
||||||
status->do_done(status);
|
status->do_done(status);
|
||||||
}, "_ts._udp");
|
}, "_ts._udp");
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
/* execute */
|
/* execute */
|
||||||
std::get<0>(status->execute_subsrv_ts) = true;
|
std::get<0>(status->execute_subsrv_ts) = true;
|
||||||
|
|
||||||
status->execute_subsrv_ts3 = {
|
status->execute_subsrv_ts3 = {
|
||||||
false,
|
false,
|
||||||
[&resolver](const std::shared_ptr<CrStatus>& status) {
|
[&resolver](const std::shared_ptr<CrStatus>& status) {
|
||||||
//std::cout << "Execute subsrc ts3" << std::endl;
|
//std::cout << "Execute subsrc ts3" << std::endl;
|
||||||
status->pending++;
|
status->pending++;
|
||||||
tc::dns::cr_srv(resolver, status->address, [status](bool success, auto data) {
|
tc::dns::cr_srv(resolver, status->address, [status](bool success, auto data) {
|
||||||
if(success) {
|
if(success) {
|
||||||
status->subsrv_ts3 = {CrStatus::SUCCESS, "", std::get<ServerAddress>(data)};
|
status->subsrv_ts3 = {CrStatus::SUCCESS, "", std::get<ServerAddress>(data)};
|
||||||
} else {
|
} else {
|
||||||
status->subsrv_ts3 = {CrStatus::FAILED, std::get<std::string>(data), {}};
|
status->subsrv_ts3 = {CrStatus::FAILED, std::get<std::string>(data), {}};
|
||||||
}
|
}
|
||||||
status->do_done(status);
|
status->do_done(status);
|
||||||
}, "_ts3._udp");
|
}, "_ts3._udp");
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
/* execute */
|
/* execute */
|
||||||
std::get<0>(status->execute_subsrv_ts3) = true;
|
std::get<0>(status->execute_subsrv_ts3) = true;
|
||||||
|
|
||||||
status->execute_subdomain = {
|
status->execute_subdomain = {
|
||||||
false,
|
false,
|
||||||
[&resolver](const std::shared_ptr<CrStatus>& status) {
|
[&resolver](const std::shared_ptr<CrStatus>& status) {
|
||||||
//std::cout << "Execute subdomain" << std::endl;
|
//std::cout << "Execute subdomain" << std::endl;
|
||||||
status->pending++;
|
status->pending++;
|
||||||
tc::dns::cr_ip(resolver, status->address, [status](bool success, auto data) {
|
tc::dns::cr_ip(resolver, status->address, [status](bool success, auto data) {
|
||||||
if(success) {
|
if(success) {
|
||||||
status->subdomain = {CrStatus::SUCCESS, "", std::get<ServerAddress>(data)};
|
status->subdomain = {CrStatus::SUCCESS, "", std::get<ServerAddress>(data)};
|
||||||
} else {
|
} else {
|
||||||
status->subdomain = {CrStatus::FAILED, std::get<std::string>(data), {}};
|
status->subdomain = {CrStatus::FAILED, std::get<std::string>(data), {}};
|
||||||
}
|
}
|
||||||
status->do_done(status);
|
status->do_done(status);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
/* execute */
|
/* execute */
|
||||||
//Will be autoamticall be executed after the SRV stuff
|
//Will be autoamticall be executed after the SRV stuff
|
||||||
//std::get<0>(status->execute_subdomain) = true;
|
//std::get<0>(status->execute_subdomain) = true;
|
||||||
|
|
||||||
status->execute_tsdns = {
|
status->execute_tsdns = {
|
||||||
false,
|
false,
|
||||||
[&resolver](const std::shared_ptr<CrStatus>& status) {
|
[&resolver](const std::shared_ptr<CrStatus>& status) {
|
||||||
//std::cout << "Execute tsdns" << std::endl;
|
//std::cout << "Execute tsdns" << std::endl;
|
||||||
status->pending++;
|
status->pending++;
|
||||||
|
|
||||||
tc::dns::cr_tsdns(resolver, status->address, [status](bool success, auto data) {
|
tc::dns::cr_tsdns(resolver, status->address, [status](bool success, auto data) {
|
||||||
if(success) {
|
if(success) {
|
||||||
status->tsdns = {CrStatus::SUCCESS, "", std::get<ServerAddress>(data)};
|
status->tsdns = {CrStatus::SUCCESS, "", std::get<ServerAddress>(data)};
|
||||||
} else {
|
} else {
|
||||||
status->tsdns = {CrStatus::FAILED, std::get<std::string>(data), {}};
|
status->tsdns = {CrStatus::FAILED, std::get<std::string>(data), {}};
|
||||||
}
|
}
|
||||||
status->do_done(status);
|
status->do_done(status);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
/* execute */
|
/* execute */
|
||||||
//Execute the TSDNS request right at the beginning because it could hang sometimes
|
//Execute the TSDNS request right at the beginning because it could hang sometimes
|
||||||
std::get<0>(status->execute_tsdns) = true;
|
std::get<0>(status->execute_tsdns) = true;
|
||||||
|
|
||||||
auto root_domain = tc::dns::domain_root(status->address.host);
|
auto root_domain = tc::dns::domain_root(status->address.host);
|
||||||
if(root_domain != status->address.host) {
|
if(root_domain != status->address.host) {
|
||||||
status->execute_rootsrv = {
|
status->execute_rootsrv = {
|
||||||
false,
|
false,
|
||||||
[&resolver](const std::shared_ptr<CrStatus>& status) {
|
[&resolver](const std::shared_ptr<CrStatus>& status) {
|
||||||
//std::cout << "Execute root srv" << std::endl;
|
//std::cout << "Execute root srv" << std::endl;
|
||||||
status->pending++;
|
status->pending++;
|
||||||
|
|
||||||
tc::dns::cr_srv(resolver, {
|
tc::dns::cr_srv(resolver, {
|
||||||
tc::dns::domain_root(status->address.host),
|
tc::dns::domain_root(status->address.host),
|
||||||
status->address.port
|
status->address.port
|
||||||
}, [status](bool success, auto data) {
|
}, [status](bool success, auto data) {
|
||||||
if(success) {
|
if(success) {
|
||||||
status->rootsrv = {CrStatus::SUCCESS, "", std::get<ServerAddress>(data)};
|
status->rootsrv = {CrStatus::SUCCESS, "", std::get<ServerAddress>(data)};
|
||||||
} else {
|
} else {
|
||||||
status->rootsrv = {CrStatus::FAILED, std::get<std::string>(data), {}};
|
status->rootsrv = {CrStatus::FAILED, std::get<std::string>(data), {}};
|
||||||
}
|
}
|
||||||
status->do_done(status);
|
status->do_done(status);
|
||||||
}, "_ts3._udp");
|
}, "_ts3._udp");
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
status->execute_rootdomain = {
|
status->execute_rootdomain = {
|
||||||
false,
|
false,
|
||||||
[&resolver](const std::shared_ptr<CrStatus>& status) {
|
[&resolver](const std::shared_ptr<CrStatus>& status) {
|
||||||
//std::cout << "Execute root domain" << std::endl;
|
//std::cout << "Execute root domain" << std::endl;
|
||||||
status->pending++;
|
status->pending++;
|
||||||
|
|
||||||
tc::dns::cr_ip(resolver,{
|
tc::dns::cr_ip(resolver,{
|
||||||
tc::dns::domain_root(status->address.host),
|
tc::dns::domain_root(status->address.host),
|
||||||
status->address.port
|
status->address.port
|
||||||
}, [status](bool success, auto data) {
|
}, [status](bool success, auto data) {
|
||||||
if(success) {
|
if(success) {
|
||||||
status->rootdomain = {CrStatus::SUCCESS, "", std::get<ServerAddress>(data)};
|
status->rootdomain = {CrStatus::SUCCESS, "", std::get<ServerAddress>(data)};
|
||||||
} else {
|
} else {
|
||||||
status->rootdomain = {CrStatus::FAILED, std::get<std::string>(data), {}};
|
status->rootdomain = {CrStatus::FAILED, std::get<std::string>(data), {}};
|
||||||
}
|
}
|
||||||
status->do_done(status);
|
status->do_done(status);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Only execute after every executor has been registered! */
|
/* Only execute after every executor has been registered! */
|
||||||
std::get<1>(status->execute_subsrv_ts)(status);
|
std::get<1>(status->execute_subsrv_ts)(status);
|
||||||
std::get<1>(status->execute_subsrv_ts3)(status);
|
std::get<1>(status->execute_subsrv_ts3)(status);
|
||||||
//std::get<1>(status->execute_subdomain)(status);
|
//std::get<1>(status->execute_subdomain)(status);
|
||||||
std::get<1>(status->execute_tsdns)(status);
|
std::get<1>(status->execute_tsdns)(status);
|
||||||
|
|
||||||
status->do_done(status);
|
status->do_done(status);
|
||||||
}
|
}
|
Loading…
Reference in New Issue
Block a user