Updated DNS module for windows (may broke linux support)

This commit is contained in:
WolverinDEV 2019-10-25 20:23:46 +01:00
parent 6c618d189f
commit 273c3b966e
11 changed files with 782 additions and 534 deletions

2
github

@ -1 +1 @@
Subproject commit 14645dca78396c915ad4ad122d532f24fdfd2969 Subproject commit a1490ab0d97f65e27845fec6daf88be8ed6d1014

View File

@ -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})
if(NOT WIN32)
find_package(unbound REQUIRED) 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()

View File

@ -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()

View File

@ -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();

View File

@ -9,7 +9,9 @@
#include <event.h> #include <event.h>
#include "./types.h" #include "./types.h"
#ifndef WIN32 #ifdef WIN32
#include <WinDNS.h>
#else
struct ub_ctx; struct ub_ctx;
#endif #endif
@ -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 */);

View File

@ -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);

View File

@ -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() {
#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(); 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
} }

View File

@ -3,13 +3,20 @@
#include <memory> #include <memory>
#include <string> #include <string>
#include <cstdint> #include <cstdint>
#ifdef WIN32
#include <WinSock2.h>
#include <Windows.h>
#include <WinDNS.h>
#else
#include <netinet/in.h> #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 {
#ifdef WIN32
return static_cast<rrclass::value>(1);
#else
return this->klass; 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();
); );

View File

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

View File

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

View File

@ -11,7 +11,13 @@
#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;
@ -173,16 +179,17 @@ void tc::dns::cr_srv(Resolver& resolver, const ServerAddress& address, const cr_
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);
@ -226,10 +233,22 @@ void tc::dns::cr_tsdns(tc::dns::Resolver &resolver, const tc::dns::ServerAddress
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
PCSTR terminator{nullptr};
if(RtlIpv6StringToAddressA(tsdns_host.host.c_str(), &terminator, (in6_addr*) &tsdns_a6->sin6_addr) == 0)
#else
if(inet_pton(AF_INET6, tsdns_host.host.c_str(), &tsdns_a6->sin6_addr) == 1)
#endif
{
tsdns_a6->sin6_family = AF_INET6; tsdns_a6->sin6_family = AF_INET6;
tsdns_a6->sin6_port = htons(tsdns_host.port == 0 ? 41144 : tsdns_host.port); tsdns_a6->sin6_port = htons(tsdns_host.port == 0 ? 41144 : tsdns_host.port);
} else if(inet_pton(AF_INET, tsdns_host.host.c_str(), &(tsdns_a4->sin_addr)) == 1) { }
#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_family = AF_INET;
tsdns_a4->sin_port = htons(tsdns_host.port == 0 ? 41144 : tsdns_host.port); tsdns_a4->sin_port = htons(tsdns_host.port == 0 ? 41144 : tsdns_host.port);
} else { } else {
@ -262,8 +281,8 @@ void tc::dns::cr_tsdns(tc::dns::Resolver &resolver, const tc::dns::ServerAddress
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;
} }
@ -333,7 +352,7 @@ struct CrStatus {
#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)) { \
@ -361,6 +380,28 @@ struct CrStatus {
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) {