A lots of updates
This commit is contained in:
parent
7e87ec772e
commit
bc94c8d31a
@ -133,6 +133,7 @@ else()
|
|||||||
endfunction()
|
endfunction()
|
||||||
|
|
||||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++17 -static-libgcc -static-libstdc++")
|
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++17 -static-libgcc -static-libstdc++")
|
||||||
|
set(CMAKE_SHARED_LIBRARY_CXX_FLAGS "") # Disable -fPIC (We dont want any relonking with build in electron libraries
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
setup_nodejs()
|
setup_nodejs()
|
||||||
@ -168,3 +169,8 @@ function(build_crash_handler)
|
|||||||
add_subdirectory(crash_handler)
|
add_subdirectory(crash_handler)
|
||||||
endfunction()
|
endfunction()
|
||||||
build_crash_handler()
|
build_crash_handler()
|
||||||
|
|
||||||
|
function(build_dns)
|
||||||
|
add_subdirectory(dns)
|
||||||
|
endfunction()
|
||||||
|
build_dns()
|
||||||
|
9
native/dist/ext_nan/NanEventCallback.h
vendored
9
native/dist/ext_nan/NanEventCallback.h
vendored
@ -84,6 +84,15 @@ namespace Nan {
|
|||||||
struct callback_wrap {
|
struct callback_wrap {
|
||||||
std::shared_ptr<callback_scoped<Args...>> handle;
|
std::shared_ptr<callback_scoped<Args...>> handle;
|
||||||
|
|
||||||
|
void call_cpy(Args... args, bool no_throw = false) {
|
||||||
|
if(!this->handle) {
|
||||||
|
if(no_throw)
|
||||||
|
return;
|
||||||
|
throw std::bad_function_call();
|
||||||
|
}
|
||||||
|
handle->callback(std::forward<Args>(args)...);
|
||||||
|
}
|
||||||
|
|
||||||
void call(Args&&... args, bool no_throw = false) {
|
void call(Args&&... args, bool no_throw = false) {
|
||||||
if(!this->handle) {
|
if(!this->handle) {
|
||||||
if(no_throw)
|
if(no_throw)
|
||||||
|
19
native/dns/CMakeLists.txt
Normal file
19
native/dns/CMakeLists.txt
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
set(MODULE_NAME "teaclient_dns")
|
||||||
|
|
||||||
|
set(SOURCE_FILES ${SOURCE_FILES} src/resolver.cpp src/types.cpp src/response.cpp utils.cpp)
|
||||||
|
|
||||||
|
find_package(Libevent REQUIRED)
|
||||||
|
include_directories(${LIBEVENT_INCLUDE_DIRS})
|
||||||
|
message("EVENT: ${LIBEVENT_INCLUDE_DIRS}")
|
||||||
|
|
||||||
|
#find_package(Unbound REQUIRED)
|
||||||
|
#include_directories(${Unbound_INCLUDE_DIR})
|
||||||
|
|
||||||
|
add_nodejs_module(${MODULE_NAME} binding.cc ${SOURCE_FILES})
|
||||||
|
target_link_libraries(${MODULE_NAME} /home/wolverindev/TeaSpeak-Client/third_party/unbound/build/lib/libunbound.a ${LIBEVENT_STATIC_LIBRARIES} pthread)
|
||||||
|
|
||||||
|
add_executable(DNS-Test ${SOURCE_FILES} test/main.cpp)
|
||||||
|
target_link_libraries(DNS-Test /home/wolverindev/TeaSpeak-Client/third_party/unbound/build/lib/libunbound.a ssl crypto ${LIBEVENT_STATIC_LIBRARIES} pthread)
|
||||||
|
|
||||||
|
add_executable(DNS-Test2 test/crash.cpp)
|
||||||
|
target_link_libraries(DNS-Test2 /home/wolverindev/TeaSpeak-Client/third_party/unbound/build/lib/libunbound.a ssl crypto ${LIBEVENT_STATIC_LIBRARIES} pthread)
|
96
native/dns/binding.cc
Normal file
96
native/dns/binding.cc
Normal file
@ -0,0 +1,96 @@
|
|||||||
|
#include <v8.h>
|
||||||
|
#include <nan.h>
|
||||||
|
#include <node.h>
|
||||||
|
#include <iostream>
|
||||||
|
#include <mutex>
|
||||||
|
#include <event2/thread.h>
|
||||||
|
|
||||||
|
#include "./src/resolver.h"
|
||||||
|
#include "./utils.h"
|
||||||
|
|
||||||
|
using namespace std;
|
||||||
|
|
||||||
|
#include "NanException.h"
|
||||||
|
#include "NanEventCallback.h"
|
||||||
|
|
||||||
|
std::unique_ptr<tc::dns::Resolver> resolver{nullptr};
|
||||||
|
|
||||||
|
NAN_METHOD(initialize) {
|
||||||
|
if(resolver) {
|
||||||
|
Nan::ThrowError("already initialized");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
//evthread_use_pthreads();
|
||||||
|
resolver = make_unique<tc::dns::Resolver>();
|
||||||
|
|
||||||
|
string error;
|
||||||
|
if(!resolver->initialize(error, true, true)) {
|
||||||
|
Nan::ThrowError(error.c_str());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
NAN_METHOD(query_connect_address) {
|
||||||
|
if(!resolver) {
|
||||||
|
Nan::ThrowError("initialize resolver first!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(info.Length() != 3 || !info[0]->IsString() || !info[1]->IsNumber() || !info[2]->IsFunction()) {
|
||||||
|
Nan::ThrowError("invalid arguments");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto host = Nan::Utf8String{info[0]->ToString()};
|
||||||
|
auto port = info[1]->ToNumber(Nan::GetCurrentContext()).ToLocalChecked()->NumberValue();
|
||||||
|
|
||||||
|
auto js_callback = make_unique<Nan::Callback>(info[2].As<v8::Function>());
|
||||||
|
auto begin = chrono::system_clock::now();
|
||||||
|
auto callback = Nan::async_callback([js_callback = std::move(js_callback), begin] (bool success, std::string message, tc::dns::ServerAddress response) {
|
||||||
|
Nan::HandleScope scope{};
|
||||||
|
auto isolate = Nan::GetCurrentContext()->GetIsolate();
|
||||||
|
|
||||||
|
v8::Local<v8::Value> argv[1];
|
||||||
|
if(!success) {
|
||||||
|
argv[0] = v8::String::NewFromOneByte(isolate, (uint8_t*) message.c_str()).ToLocalChecked();
|
||||||
|
} else {
|
||||||
|
auto js_data = Nan::New<v8::Object>();
|
||||||
|
Nan::Set(js_data,
|
||||||
|
v8::String::NewFromUtf8(isolate, "host").ToLocalChecked(),
|
||||||
|
v8::String::NewFromUtf8(isolate, response.host.c_str()).ToLocalChecked()
|
||||||
|
);
|
||||||
|
Nan::Set(js_data,
|
||||||
|
v8::String::NewFromUtf8(isolate, "port").ToLocalChecked(),
|
||||||
|
Nan::New<v8::Number>(response.port)
|
||||||
|
);
|
||||||
|
Nan::Set(js_data,
|
||||||
|
v8::String::NewFromUtf8(isolate, "timing").ToLocalChecked(),
|
||||||
|
Nan::New<v8::Number>(chrono::floor<chrono::milliseconds>(chrono::system_clock::now() - begin).count())
|
||||||
|
);
|
||||||
|
|
||||||
|
argv[0] = js_data;
|
||||||
|
}
|
||||||
|
js_callback->Call(1, argv);
|
||||||
|
}).option_destroyed_execute(true);
|
||||||
|
|
||||||
|
tc::dns::cr(*resolver,
|
||||||
|
tc::dns::ServerAddress{ *host, (uint16_t) port },
|
||||||
|
[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));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
__attribute__((visibility("default"))) NAN_MODULE_INIT(init) {
|
||||||
|
Nan::Set(target,
|
||||||
|
v8::String::NewFromUtf8(Nan::GetCurrentContext()->GetIsolate(), "resolve_cr").ToLocalChecked(),
|
||||||
|
Nan::GetFunction(Nan::New<v8::FunctionTemplate>(query_connect_address)).ToLocalChecked()
|
||||||
|
);
|
||||||
|
|
||||||
|
Nan::Set(target,
|
||||||
|
v8::String::NewFromUtf8(Nan::GetCurrentContext()->GetIsolate(), "initialize").ToLocalChecked(),
|
||||||
|
Nan::GetFunction(Nan::New<v8::FunctionTemplate>(initialize)).ToLocalChecked()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
NODE_MODULE(MODULE_NAME, init)
|
677
native/dns/src/resolver.cpp
Normal file
677
native/dns/src/resolver.cpp
Normal file
@ -0,0 +1,677 @@
|
|||||||
|
#include "./resolver.h"
|
||||||
|
#include "./response.h"
|
||||||
|
|
||||||
|
#include <cassert>
|
||||||
|
#include <functional>
|
||||||
|
#include <event.h>
|
||||||
|
#include <unbound.h>
|
||||||
|
#include <unbound-event.h>
|
||||||
|
#include <iostream>
|
||||||
|
#include <cstring>
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
|
#include <fcntl.h> /* for TSDNS */
|
||||||
|
#include <unistd.h>
|
||||||
|
#ifdef WIN32
|
||||||
|
#include <ws2tcpip.h>
|
||||||
|
#define SOCK_NONBLOCK (0)
|
||||||
|
#define MSG_DONTWAIT (0)
|
||||||
|
#else
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
using namespace std;
|
||||||
|
using namespace tc::dns;
|
||||||
|
|
||||||
|
Resolver::Resolver() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
Resolver::~Resolver() {
|
||||||
|
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));
|
||||||
|
|
||||||
|
this->ub_ctx = ub_ctx_create_event(this->event.base);
|
||||||
|
if(!this->ub_ctx) {
|
||||||
|
this->finalize();
|
||||||
|
error = "failed to create ub context";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Add /etc/hosts */
|
||||||
|
auto err = !hosts ? 0 : ub_ctx_hosts((struct ub_ctx*) this->ub_ctx, nullptr);
|
||||||
|
if(err != 0) {
|
||||||
|
cerr << "Failed to add hosts file: " << ub_strerror(err) << endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Add resolv.conf */
|
||||||
|
err = !resolv ? 0 : ub_ctx_resolvconf((struct ub_ctx*) this->ub_ctx, nullptr);
|
||||||
|
if(err != 0) {
|
||||||
|
cerr << "Failed to add hosts file: " << ub_strerror(err) << endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
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) {
|
||||||
|
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);
|
||||||
|
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!
|
||||||
|
void Resolver::destroy_dns_request(Resolver::dns_request *request) {
|
||||||
|
assert(this_thread::get_id() == this->event.loop.get_id() || !this->event.loop_active);
|
||||||
|
|
||||||
|
{
|
||||||
|
lock_guard lock{this->request_lock};
|
||||||
|
this->dns_requests.erase(std::find(this->dns_requests.begin(), this->dns_requests.end(), request), this->dns_requests.end());
|
||||||
|
}
|
||||||
|
|
||||||
|
if(request->register_event) {
|
||||||
|
event_del_noblock(request->register_event);
|
||||||
|
event_free(request->register_event);
|
||||||
|
request->register_event = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(request->timeout_event) {
|
||||||
|
event_del_noblock(request->timeout_event);
|
||||||
|
event_free(request->timeout_event);
|
||||||
|
request->timeout_event = nullptr;
|
||||||
|
}
|
||||||
|
delete request;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Resolver::destroy_tsdns_request(Resolver::tsdns_request *request) {
|
||||||
|
assert(this_thread::get_id() == this->event.loop.get_id() || !this->event.loop_active);
|
||||||
|
|
||||||
|
{
|
||||||
|
lock_guard lock{this->request_lock};
|
||||||
|
this->tsdns_requests.erase(std::find(this->tsdns_requests.begin(), this->tsdns_requests.end(), request), this->tsdns_requests.end());
|
||||||
|
}
|
||||||
|
|
||||||
|
if(request->event_read) {
|
||||||
|
event_del_noblock(request->event_read);
|
||||||
|
event_free(request->event_read);
|
||||||
|
request->event_read = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(request->event_write) {
|
||||||
|
event_del_noblock(request->event_write);
|
||||||
|
event_free(request->event_write);
|
||||||
|
request->event_write = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(request->timeout_event) {
|
||||||
|
event_del_noblock(request->timeout_event);
|
||||||
|
event_free(request->timeout_event);
|
||||||
|
request->timeout_event = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(request->socket > 0) {
|
||||||
|
#ifndef WIN32
|
||||||
|
::shutdown(request->socket, SHUT_RDWR);
|
||||||
|
#endif
|
||||||
|
::close(request->socket);
|
||||||
|
request->socket = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
delete request;
|
||||||
|
}
|
||||||
|
|
||||||
|
//--------------- DNS
|
||||||
|
void Resolver::resolve_dns(const char *name, const rrtype::value &rrtype, const rrclass::value &rrclass, const std::chrono::microseconds& timeout, const dns_callback_t& callback) {
|
||||||
|
if(!this->event.loop_active) {
|
||||||
|
callback(ResultState::INITIALISATION_FAILED, 3, nullptr);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto request = new dns_request{};
|
||||||
|
request->resolver = this;
|
||||||
|
|
||||||
|
request->callback = callback;
|
||||||
|
request->host = name;
|
||||||
|
request->rrtype = rrtype;
|
||||||
|
request->rrclass = rrclass;
|
||||||
|
|
||||||
|
request->timeout_event = evtimer_new(this->event.base, [](evutil_socket_t, short, void *_request) {
|
||||||
|
auto request = static_cast<dns_request*>(_request);
|
||||||
|
request->resolver->evtimer_dns_callback(request);
|
||||||
|
}, request);
|
||||||
|
|
||||||
|
request->register_event = evuser_new(this->event.base, [](evutil_socket_t, short, void *_request) {
|
||||||
|
auto request = static_cast<dns_request*>(_request);
|
||||||
|
|
||||||
|
auto errc = ub_resolve_event(request->resolver->ub_ctx, request->host.c_str(), (int) request->rrtype, (int) request->rrclass, (void*) request, [](void* _request, int a, void* b, int c, int d, char* e) {
|
||||||
|
auto request = static_cast<dns_request*>(_request);
|
||||||
|
request->resolver->ub_callback(request, a, b, c, d, e);
|
||||||
|
}, &request->ub_id);
|
||||||
|
|
||||||
|
if(errc != 0) {
|
||||||
|
request->callback(ResultState::INITIALISATION_FAILED, errc, nullptr);
|
||||||
|
request->resolver->destroy_dns_request(request);
|
||||||
|
}
|
||||||
|
}, request);
|
||||||
|
|
||||||
|
if(!request->timeout_event || !request->register_event) {
|
||||||
|
callback(ResultState::INITIALISATION_FAILED, 2, nullptr);
|
||||||
|
|
||||||
|
if(request->timeout_event)
|
||||||
|
event_free(request->timeout_event);
|
||||||
|
|
||||||
|
if(request->register_event)
|
||||||
|
event_free(request->register_event);
|
||||||
|
|
||||||
|
delete request;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Lock here all requests so the event loop cant already delete the request
|
||||||
|
*/
|
||||||
|
unique_lock rlock{this->request_lock};
|
||||||
|
|
||||||
|
{
|
||||||
|
auto errc = event_add(request->timeout_event, nullptr);
|
||||||
|
//TODO: Check for error
|
||||||
|
|
||||||
|
evuser_trigger(request->register_event);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
auto seconds = chrono::floor<chrono::seconds>(timeout);
|
||||||
|
auto microseconds = chrono::ceil<chrono::microseconds>(timeout - seconds);
|
||||||
|
|
||||||
|
timeval tv{seconds.count(), microseconds.count()};
|
||||||
|
auto errc = event_add(request->timeout_event, &tv);
|
||||||
|
|
||||||
|
//TODO: Check for error
|
||||||
|
}
|
||||||
|
|
||||||
|
this->dns_requests.push_back(request);
|
||||||
|
rlock.unlock();
|
||||||
|
|
||||||
|
/* Activate the event loop */
|
||||||
|
this->event.condition.notify_one();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Resolver::evtimer_dns_callback(tc::dns::Resolver::dns_request *request) {
|
||||||
|
if(request->ub_id > 0) {
|
||||||
|
auto errc = ub_cancel(this->ub_ctx, request->ub_id);
|
||||||
|
if(errc != 0) {
|
||||||
|
cerr << "Failed to cancel DNS request " << request->ub_id << " after timeout (" << errc << "/" << ub_strerror(errc) << ")!" << endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
request->callback(ResultState::DNS_TIMEOUT, 0, nullptr);
|
||||||
|
this->destroy_dns_request(request);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Resolver::ub_callback(dns_request* request, int rcode, void *packet, int packet_length, int sec, char *why_bogus) {
|
||||||
|
if(rcode != 0) {
|
||||||
|
request->callback(ResultState::DNS_FAIL, rcode, nullptr);
|
||||||
|
} else {
|
||||||
|
auto callback = request->callback;
|
||||||
|
auto data = std::unique_ptr<DNSResponse>(new DNSResponse{(uint8_t) sec, why_bogus, packet, (size_t) packet_length});
|
||||||
|
callback(ResultState::SUCCESS, 0, std::move(data));
|
||||||
|
}
|
||||||
|
|
||||||
|
this->destroy_dns_request(request);
|
||||||
|
}
|
||||||
|
|
||||||
|
thread_local std::vector<size_t> visited_links;
|
||||||
|
std::string DNSResponseBuffer::parse_dns_dn(std::string &error, size_t &index, bool allow_compression) {
|
||||||
|
if(allow_compression) {
|
||||||
|
visited_links.clear();
|
||||||
|
visited_links.reserve(8);
|
||||||
|
|
||||||
|
if(std::find(visited_links.begin(), visited_links.end(), index) != visited_links.end()) {
|
||||||
|
error = "circular link detected";
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
visited_links.push_back(index);
|
||||||
|
}
|
||||||
|
|
||||||
|
error.clear();
|
||||||
|
|
||||||
|
string result;
|
||||||
|
result.reserve(256); //Max length is 253
|
||||||
|
|
||||||
|
while(true) {
|
||||||
|
if(index + 1 > this->length) {
|
||||||
|
error = "truncated data (missing code)";
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto code = this->buffer[index++];
|
||||||
|
if(code == 0) break;
|
||||||
|
|
||||||
|
if((code >> 6U) == 3) {
|
||||||
|
if(!allow_compression) {
|
||||||
|
error = "found link, but links are not allowed";
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto lower_addr = this->buffer[index++];
|
||||||
|
if(index + 1 > this->length) {
|
||||||
|
error = "truncated data (missing lower link address)";
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t addr = ((code & 0x3FU) << 8U) | lower_addr;
|
||||||
|
if(addr >= this->length) {
|
||||||
|
error = "invalid link address";
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
auto tail = this->parse_dns_dn(error, addr, true);
|
||||||
|
if(!error.empty())
|
||||||
|
goto exit;
|
||||||
|
|
||||||
|
if(!result.empty())
|
||||||
|
result += "." + tail;
|
||||||
|
else
|
||||||
|
result = tail;
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
if(code > 63) {
|
||||||
|
error = "max domain label length is 63 characters";
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!result.empty())
|
||||||
|
result += ".";
|
||||||
|
|
||||||
|
if(index + code >= this->length) {
|
||||||
|
error = "truncated data (domain label)";
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
result.append((const char*) (this->buffer + index), code);
|
||||||
|
index += code;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
exit:
|
||||||
|
if(allow_compression) visited_links.pop_back();
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
DNSResponseBuffer::~DNSResponseBuffer() {
|
||||||
|
::free(this->buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
DNSResponse::DNSResponse(uint8_t secure_state, const char* bogus, void *packet, size_t size) {
|
||||||
|
this->bogus = bogus ? std::string{bogus} : std::string{"packet is secure"};
|
||||||
|
this->secure_state = secure_state;
|
||||||
|
|
||||||
|
this->packet = make_shared<DNSResponseBuffer>();
|
||||||
|
this->packet->buffer = (uint8_t*) malloc(size);
|
||||||
|
this->packet->length = size;
|
||||||
|
|
||||||
|
memcpy(this->packet->buffer, packet, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
response::DNSHeader DNSResponse::header() const {
|
||||||
|
return response::DNSHeader{this};
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DNSResponse::parse(std::string &error) {
|
||||||
|
if(this->is_parsed) {
|
||||||
|
error = this->parse_error;
|
||||||
|
return error.empty();
|
||||||
|
}
|
||||||
|
error.clear();
|
||||||
|
this->is_parsed = true;
|
||||||
|
|
||||||
|
auto header = this->header();
|
||||||
|
size_t index = 12; /* 12 bits for the header */
|
||||||
|
|
||||||
|
{
|
||||||
|
auto count = header.query_count();
|
||||||
|
this->parsed_queries.reserve(count);
|
||||||
|
|
||||||
|
for(size_t idx = 0; idx < count; idx++) {
|
||||||
|
auto dn = this->packet->parse_dns_dn(error, index, true);
|
||||||
|
if(!error.empty()) {
|
||||||
|
error = "failed to parse query " + to_string(idx) + " dn: " + error; // NOLINT(performance-inefficient-string-concatenation)
|
||||||
|
goto error_exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(index + 4 > this->packet_length()) {
|
||||||
|
error = "truncated data for query " + to_string(index);
|
||||||
|
goto error_exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto type = (rrtype::value) ntohs(*(uint16_t*) (this->packet->buffer + index));
|
||||||
|
index += 2;
|
||||||
|
|
||||||
|
auto klass = (rrclass::value) ntohs(*(uint16_t*) (this->packet->buffer + index));
|
||||||
|
index += 2;
|
||||||
|
|
||||||
|
this->parsed_queries.emplace_back(new response::DNSQuery{dn, type, klass});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
auto count = header.answer_count();
|
||||||
|
this->parsed_answers.reserve(count);
|
||||||
|
|
||||||
|
for(size_t idx = 0; idx < count; idx++) {
|
||||||
|
this->parsed_answers.push_back(this->parse_rr(error, index, true));
|
||||||
|
if(!error.empty()) {
|
||||||
|
error = "failed to parse answer " + to_string(idx) + ": " + error; // NOLINT(performance-inefficient-string-concatenation)
|
||||||
|
goto error_exit;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
auto count = header.authority_count();
|
||||||
|
this->parsed_authorities.reserve(count);
|
||||||
|
|
||||||
|
for(size_t idx = 0; idx < count; idx++) {
|
||||||
|
this->parsed_authorities.push_back(this->parse_rr(error, index, true));
|
||||||
|
if(!error.empty()) {
|
||||||
|
error = "failed to parse authority " + to_string(idx) + ": " + error; // NOLINT(performance-inefficient-string-concatenation)
|
||||||
|
goto error_exit;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
auto count = header.additional_count();
|
||||||
|
this->parsed_additionals.reserve(count);
|
||||||
|
|
||||||
|
for(size_t idx = 0; idx < count; idx++) {
|
||||||
|
this->parsed_additionals.push_back(this->parse_rr(error, index, true));
|
||||||
|
if(!error.empty()) {
|
||||||
|
error = "failed to parse additional " + to_string(idx) + ": " + error; // NOLINT(performance-inefficient-string-concatenation)
|
||||||
|
goto error_exit;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
|
||||||
|
error_exit:
|
||||||
|
this->parsed_queries.clear();
|
||||||
|
this->parsed_answers.clear();
|
||||||
|
this->parsed_authorities.clear();
|
||||||
|
this->parsed_additionals.clear();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<response::DNSResourceRecords> DNSResponse::parse_rr(std::string &error, size_t &index, bool allow_compressed) {
|
||||||
|
auto dn = this->packet->parse_dns_dn(error, index, allow_compressed);
|
||||||
|
if(!error.empty()) {
|
||||||
|
error = "failed to parse rr dn: " + error; // NOLINT(performance-inefficient-string-concatenation)
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(index + 10 > this->packet_length()) {
|
||||||
|
error = "truncated header";
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto type = (rrtype::value) ntohs(*(uint16_t*) (this->packet->buffer + index));
|
||||||
|
index += 2;
|
||||||
|
|
||||||
|
auto klass = (rrclass::value) ntohs(*(uint16_t*) (this->packet->buffer + index));
|
||||||
|
index += 2;
|
||||||
|
|
||||||
|
auto ttl = ntohl(*(uint32_t*) (this->packet->buffer + index));
|
||||||
|
index += 4;
|
||||||
|
|
||||||
|
auto payload_length = ntohs(*(uint16_t*) (this->packet->buffer + index));
|
||||||
|
index += 2;
|
||||||
|
|
||||||
|
if(index + payload_length > this->packet_length()) {
|
||||||
|
error = "truncated body";
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto response = std::shared_ptr<response::DNSResourceRecords>(new response::DNSResourceRecords{this->packet, index, payload_length, ttl, dn, type, klass});
|
||||||
|
index += payload_length;
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
|
||||||
|
//---------------------- TSDNS
|
||||||
|
void Resolver::resolve_tsdns(const char *query, const sockaddr_storage& server_address, const std::chrono::microseconds& timeout, const tc::dns::Resolver::tsdns_callback_t &callback) {
|
||||||
|
/* create the socket */
|
||||||
|
auto socket = ::socket(server_address.ss_family, SOCK_STREAM | SOCK_NONBLOCK, 0);
|
||||||
|
if(socket <= 0) {
|
||||||
|
callback(ResultState::INITIALISATION_FAILED, -1, "failed to allocate socket: " + to_string(errno) + "/" + strerror(errno));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef WIN32
|
||||||
|
u_long enabled = 0;
|
||||||
|
auto non_block_rs = ioctlsocket(this->_socket, FIONBIO, &enabled);
|
||||||
|
if (non_block_rs != NO_ERROR) {
|
||||||
|
::close(socket);
|
||||||
|
callback(ResultState::INITIALISATION_FAILED, -2, "failed to enable nonblock: " + to_string(errno) + "/" + strerror(errno));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
int opt = 1;
|
||||||
|
setsockopt(socket, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(int));
|
||||||
|
|
||||||
|
auto request = new tsdns_request{};
|
||||||
|
|
||||||
|
request->resolver = this;
|
||||||
|
request->callback = callback;
|
||||||
|
request->socket = socket;
|
||||||
|
request->timeout_event = evtimer_new(this->event.base, [](evutil_socket_t, short, void *_request) {
|
||||||
|
auto request = static_cast<tsdns_request*>(_request);
|
||||||
|
request->resolver->evtimer_tsdns_callback(request);
|
||||||
|
}, request);
|
||||||
|
|
||||||
|
request->event_read = event_new(this->event.base, socket, EV_READ | EV_PERSIST, [](evutil_socket_t, short, void *_request){
|
||||||
|
auto request = static_cast<tsdns_request*>(_request);
|
||||||
|
request->resolver->event_tsdns_read(request);
|
||||||
|
}, request);
|
||||||
|
request->event_write = event_new(this->event.base, socket, EV_WRITE, [](evutil_socket_t, short, void *_request){
|
||||||
|
auto request = static_cast<tsdns_request*>(_request);
|
||||||
|
request->resolver->event_tsdns_write(request);
|
||||||
|
}, request);
|
||||||
|
|
||||||
|
if(!request->timeout_event || !request->event_write || !request->event_read) {
|
||||||
|
callback(ResultState::INITIALISATION_FAILED, -3, "");
|
||||||
|
this->destroy_tsdns_request(request);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
request->write_buffer = query;
|
||||||
|
request->write_buffer += "\n\r\r\r\n";
|
||||||
|
|
||||||
|
int result = ::connect(socket, reinterpret_cast<const sockaddr *> (&server_address), sizeof(server_address));
|
||||||
|
if (result < 0) {
|
||||||
|
#ifdef WIN32
|
||||||
|
auto error = WSAGetLastError();
|
||||||
|
|
||||||
|
if(error != WSAEWOULDBLOCK) {
|
||||||
|
/*
|
||||||
|
* TODO!
|
||||||
|
wchar_t *s = nullptr;
|
||||||
|
FormatMessageW(
|
||||||
|
FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
|
||||||
|
nullptr,
|
||||||
|
error,
|
||||||
|
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
|
||||||
|
(LPWSTR)&s,
|
||||||
|
0,
|
||||||
|
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");
|
||||||
|
this->destroy_tsdns_request(request);
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
if(errno != EINPROGRESS) {
|
||||||
|
callback(ResultState::TSDNS_CONNECTION_FAIL, -1, "Failed to connect with code: " + to_string(errno) + "/" + strerror(errno));
|
||||||
|
this->destroy_tsdns_request(request);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
event_add(request->event_write, nullptr);
|
||||||
|
event_add(request->event_read, nullptr);
|
||||||
|
|
||||||
|
{
|
||||||
|
auto seconds = chrono::floor<chrono::seconds>(timeout);
|
||||||
|
auto microseconds = chrono::ceil<chrono::microseconds>(timeout - seconds);
|
||||||
|
|
||||||
|
timeval tv{seconds.count(), microseconds.count()};
|
||||||
|
auto errc = event_add(request->timeout_event, &tv);
|
||||||
|
|
||||||
|
//TODO: Check for error
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
lock_guard lock{this->request_lock};
|
||||||
|
this->tsdns_requests.push_back(request);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Activate the event loop */
|
||||||
|
this->event.condition.notify_one();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Resolver::evtimer_tsdns_callback(Resolver::tsdns_request *request) {
|
||||||
|
request->callback(ResultState::DNS_TIMEOUT, 0, "");
|
||||||
|
this->destroy_tsdns_request(request);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Resolver::event_tsdns_read(Resolver::tsdns_request *request) {
|
||||||
|
int64_t buffer_length = 1024;
|
||||||
|
char buffer[1024];
|
||||||
|
|
||||||
|
buffer_length = recv(request->socket, buffer, (int) buffer_length, MSG_DONTWAIT);
|
||||||
|
if(buffer_length < 0) {
|
||||||
|
#ifdef WIN32
|
||||||
|
auto error = WSAGetLastError();
|
||||||
|
if(error != WSAEWOULDBLOCK)
|
||||||
|
return;
|
||||||
|
request->callback(ResultState::TSDNS_CONNECTION_FAIL, -2, "read failed: " + to_string(error));
|
||||||
|
#else
|
||||||
|
if(errno == EAGAIN)
|
||||||
|
return;
|
||||||
|
request->callback(ResultState::TSDNS_CONNECTION_FAIL, -2, "read failed: " + to_string(errno) + "/" + strerror(errno));
|
||||||
|
#endif
|
||||||
|
this->destroy_tsdns_request(request);
|
||||||
|
return;
|
||||||
|
} else if(buffer_length == 0) {
|
||||||
|
if(request->read_buffer.empty()) {
|
||||||
|
request->callback(ResultState::TSDNS_EMPTY_RESPONSE, 0, "");
|
||||||
|
} else {
|
||||||
|
request->callback(ResultState::SUCCESS, 0, request->read_buffer);
|
||||||
|
}
|
||||||
|
this->destroy_tsdns_request(request);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
lock_guard lock{request->buffer_lock};
|
||||||
|
request->read_buffer.append(buffer, buffer_length);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Resolver::event_tsdns_write(Resolver::tsdns_request *request) {
|
||||||
|
lock_guard lock{request->buffer_lock};
|
||||||
|
if(request->write_buffer.empty())
|
||||||
|
return;
|
||||||
|
|
||||||
|
auto written = send(request->socket, request->write_buffer.data(), min(request->write_buffer.size(), 1024UL), MSG_DONTWAIT);
|
||||||
|
if(written < 0) {
|
||||||
|
#ifdef WIN32
|
||||||
|
auto error = WSAGetLastError();
|
||||||
|
if(error != WSAEWOULDBLOCK)
|
||||||
|
return;
|
||||||
|
request->callback(ResultState::TSDNS_CONNECTION_FAIL, -4, "write failed: " + to_string(error));
|
||||||
|
#else
|
||||||
|
if(errno == EAGAIN)
|
||||||
|
return;
|
||||||
|
request->callback(ResultState::TSDNS_CONNECTION_FAIL, -4, "write failed: " + to_string(errno) + "/" + strerror(errno));
|
||||||
|
#endif
|
||||||
|
this->destroy_tsdns_request(request);
|
||||||
|
return;
|
||||||
|
} else if(written == 0) {
|
||||||
|
request->callback(ResultState::TSDNS_CONNECTION_FAIL, -5, "remote peer hang up");
|
||||||
|
this->destroy_tsdns_request(request);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(written == request->write_buffer.size())
|
||||||
|
request->write_buffer.clear();
|
||||||
|
else {
|
||||||
|
request->write_buffer = request->write_buffer.substr(written);
|
||||||
|
event_add(request->event_write, nullptr);
|
||||||
|
}
|
||||||
|
}
|
161
native/dns/src/resolver.h
Normal file
161
native/dns/src/resolver.h
Normal file
@ -0,0 +1,161 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <thread>
|
||||||
|
#include <mutex>
|
||||||
|
#include <functional>
|
||||||
|
#include <condition_variable>
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
|
#include <event.h>
|
||||||
|
|
||||||
|
#include "./types.h"
|
||||||
|
|
||||||
|
struct ub_ctx;
|
||||||
|
namespace tc::dns {
|
||||||
|
namespace response {
|
||||||
|
class DNSHeader;
|
||||||
|
class DNSQuery;
|
||||||
|
class DNSResourceRecords;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct DNSResponseBuffer {
|
||||||
|
uint8_t* buffer{nullptr};
|
||||||
|
size_t length{0};
|
||||||
|
|
||||||
|
~DNSResponseBuffer();
|
||||||
|
std::string parse_dns_dn(std::string& /* error */, size_t& /* index */, bool /* compression allowed */);
|
||||||
|
};
|
||||||
|
|
||||||
|
class Resolver;
|
||||||
|
class DNSResponse {
|
||||||
|
friend class Resolver;
|
||||||
|
public:
|
||||||
|
typedef std::vector<std::shared_ptr<response::DNSResourceRecords>> rr_list_t;
|
||||||
|
typedef std::vector<std::shared_ptr<response::DNSQuery>> q_list_t;
|
||||||
|
|
||||||
|
DNSResponse(const DNSResponse&) = delete;
|
||||||
|
DNSResponse(DNSResponse&&) = delete;
|
||||||
|
|
||||||
|
bool parse(std::string& /* error */);
|
||||||
|
|
||||||
|
[[nodiscard]] inline const std::string why_bogus() const { return this->bogus; }
|
||||||
|
|
||||||
|
[[nodiscard]] inline const uint8_t* packet_data() const { return this->packet->buffer; }
|
||||||
|
[[nodiscard]] inline size_t packet_length() const { return this->packet->length; }
|
||||||
|
|
||||||
|
[[nodiscard]] inline bool is_secure() const { return this->secure_state > 0; }
|
||||||
|
[[nodiscard]] inline bool is_secure_dnssec() const { return this->secure_state == 2; }
|
||||||
|
|
||||||
|
[[nodiscard]] response::DNSHeader header() const;
|
||||||
|
|
||||||
|
[[nodiscard]] q_list_t queries() const { return this->parsed_queries; }
|
||||||
|
[[nodiscard]] rr_list_t answers() const { return this->parsed_answers; }
|
||||||
|
[[nodiscard]] rr_list_t authorities() const { return this->parsed_authorities; }
|
||||||
|
[[nodiscard]] rr_list_t additionals() const { return this->parsed_additionals; }
|
||||||
|
private:
|
||||||
|
DNSResponse(uint8_t /* secure state */, const char* /* bogus */, void* /* packet */, size_t /* length */);
|
||||||
|
|
||||||
|
std::shared_ptr<response::DNSResourceRecords> parse_rr(std::string& /* error */, size_t& index, bool /* compression allowed dn */);
|
||||||
|
|
||||||
|
std::string bogus;
|
||||||
|
uint8_t secure_state{0};
|
||||||
|
|
||||||
|
std::shared_ptr<DNSResponseBuffer> packet{nullptr};
|
||||||
|
|
||||||
|
bool is_parsed{false};
|
||||||
|
std::string parse_error{};
|
||||||
|
|
||||||
|
q_list_t parsed_queries;
|
||||||
|
rr_list_t parsed_answers;
|
||||||
|
rr_list_t parsed_authorities;
|
||||||
|
rr_list_t parsed_additionals;
|
||||||
|
};
|
||||||
|
|
||||||
|
class Resolver {
|
||||||
|
public:
|
||||||
|
struct ResultState {
|
||||||
|
enum value : uint8_t {
|
||||||
|
SUCCESS = 0,
|
||||||
|
|
||||||
|
INITIALISATION_FAILED = 0x01,
|
||||||
|
|
||||||
|
DNS_TIMEOUT = 0x10,
|
||||||
|
DNS_FAIL = 0x11, /* error detail is a DNS error code */
|
||||||
|
|
||||||
|
TSDNS_CONNECTION_FAIL = 0x20,
|
||||||
|
TSDNS_EMPTY_RESPONSE = 0x21,
|
||||||
|
|
||||||
|
ABORT = 0xFF /* request has been aborted */
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef std::function<void(ResultState::value /* error */, int /* error detail */, std::unique_ptr<DNSResponse> /* response */)> dns_callback_t;
|
||||||
|
typedef std::function<void(ResultState::value /* error */, int /* error detail */, const std::string& /* response */)> tsdns_callback_t;
|
||||||
|
|
||||||
|
Resolver();
|
||||||
|
virtual ~Resolver();
|
||||||
|
|
||||||
|
bool initialize(std::string& /* error */, bool /* use hosts */, bool /* use resolv */);
|
||||||
|
void finalize();
|
||||||
|
|
||||||
|
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 */);
|
||||||
|
private:
|
||||||
|
struct dns_request {
|
||||||
|
Resolver* resolver{nullptr};
|
||||||
|
|
||||||
|
int ub_id{0};
|
||||||
|
struct ::event* timeout_event{nullptr};
|
||||||
|
struct ::event* register_event{nullptr};
|
||||||
|
|
||||||
|
std::string host;
|
||||||
|
dns::rrtype::value rrtype{dns::rrtype::Unassigned};
|
||||||
|
dns::rrclass::value rrclass{dns::rrclass::IN};
|
||||||
|
|
||||||
|
dns_callback_t callback{};
|
||||||
|
};
|
||||||
|
|
||||||
|
struct tsdns_request {
|
||||||
|
Resolver* resolver{nullptr};
|
||||||
|
|
||||||
|
int socket{0};
|
||||||
|
|
||||||
|
struct ::event* timeout_event{nullptr};
|
||||||
|
struct ::event* event_read{nullptr};
|
||||||
|
struct ::event* event_write{nullptr};
|
||||||
|
|
||||||
|
std::mutex buffer_lock{};
|
||||||
|
std::string write_buffer{};
|
||||||
|
std::string read_buffer{};
|
||||||
|
|
||||||
|
tsdns_callback_t callback{};
|
||||||
|
};
|
||||||
|
|
||||||
|
struct {
|
||||||
|
bool loop_active{false};
|
||||||
|
std::condition_variable condition{};
|
||||||
|
std::mutex lock{};
|
||||||
|
std::thread loop{};
|
||||||
|
event_base* base = nullptr;
|
||||||
|
} event;
|
||||||
|
|
||||||
|
struct ub_ctx* ub_ctx = nullptr;
|
||||||
|
|
||||||
|
std::vector<tsdns_request*> tsdns_requests{};
|
||||||
|
std::vector<dns_request*> dns_requests{};
|
||||||
|
std::recursive_mutex request_lock{}; /* this is recursive because due to the instance callback resolve_dns could be called recursively */
|
||||||
|
|
||||||
|
void destroy_dns_request(dns_request*);
|
||||||
|
void destroy_tsdns_request(tsdns_request*);
|
||||||
|
|
||||||
|
void event_loop_runner();
|
||||||
|
|
||||||
|
void evtimer_tsdns_callback(tsdns_request* /* request */);
|
||||||
|
void evtimer_dns_callback(dns_request* /* request */);
|
||||||
|
|
||||||
|
void event_tsdns_write(tsdns_request* /* request */);
|
||||||
|
void event_tsdns_read(tsdns_request* /* request */);
|
||||||
|
|
||||||
|
void ub_callback(dns_request* /* request */, int /* rcode */, void* /* packet */, int /* packet_len */, int /* sec */, char* /* why_bogus */);
|
||||||
|
};
|
||||||
|
}
|
86
native/dns/src/response.cpp
Normal file
86
native/dns/src/response.cpp
Normal file
@ -0,0 +1,86 @@
|
|||||||
|
#include "./response.h"
|
||||||
|
#include "./resolver.h"
|
||||||
|
|
||||||
|
#ifdef WIN32
|
||||||
|
#include <Windows.h>
|
||||||
|
#include <in6addr.h>
|
||||||
|
#else
|
||||||
|
#include <arpa/inet.h>
|
||||||
|
#include <netinet/in.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
using namespace tc::dns::response;
|
||||||
|
using namespace tc::dns::response::rrparser;
|
||||||
|
|
||||||
|
inline std::string to_string(const in6_addr& address) {
|
||||||
|
char buffer[INET6_ADDRSTRLEN];
|
||||||
|
if(!inet_ntop(AF_INET6, (void*) &address, buffer, INET6_ADDRSTRLEN)) return "";
|
||||||
|
return std::string(buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline std::string to_string(const in_addr& address) {
|
||||||
|
char buffer[INET_ADDRSTRLEN];
|
||||||
|
if(!inet_ntop(AF_INET, (void*) &address, buffer, INET_ADDRSTRLEN)) return "";
|
||||||
|
return std::string(buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint16_t DNSHeader::field(int index) const {
|
||||||
|
return ((uint16_t*) this->response->packet_data())[index];
|
||||||
|
}
|
||||||
|
|
||||||
|
DNSResourceRecords::DNSResourceRecords(std::shared_ptr<DNSResponseBuffer> packet, size_t payload_offset, size_t length, uint32_t ttl, std::string name, rrtype::value type, rrclass::value klass)
|
||||||
|
: offset{payload_offset}, length{length}, ttl{ttl}, name{std::move(name)}, type{type}, klass{klass} {
|
||||||
|
this->packet = std::move(packet);
|
||||||
|
}
|
||||||
|
|
||||||
|
const uint8_t* DNSResourceRecords::payload_data() const {
|
||||||
|
return this->packet->buffer + this->offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
//---------------- AAAA
|
||||||
|
std::string AAAA::address_string() {
|
||||||
|
return to_string(this->address());
|
||||||
|
}
|
||||||
|
|
||||||
|
in6_addr AAAA::address() {
|
||||||
|
return {
|
||||||
|
.__in6_u = {
|
||||||
|
.__u6_addr32 = {
|
||||||
|
((uint32_t*) this->handle->payload_data())[0],
|
||||||
|
((uint32_t*) this->handle->payload_data())[1],
|
||||||
|
((uint32_t*) this->handle->payload_data())[2],
|
||||||
|
((uint32_t*) this->handle->payload_data())[3]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
//---------------- SRV
|
||||||
|
bool SRV::is_valid() {
|
||||||
|
if(this->handle->payload_length() < 7)
|
||||||
|
return false;
|
||||||
|
size_t index = this->handle->payload_offset() + 6;
|
||||||
|
std::string error{};
|
||||||
|
this->handle->dns_packet()->parse_dns_dn(error, index, true);
|
||||||
|
return error.empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string SRV::target_hostname() {
|
||||||
|
size_t index = this->handle->payload_offset() + 6;
|
||||||
|
std::string error{};
|
||||||
|
return this->handle->dns_packet()->parse_dns_dn(error, index, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
//---------------- All types with a name
|
||||||
|
bool named_base::is_valid() {
|
||||||
|
size_t index = this->handle->payload_offset();
|
||||||
|
std::string error{};
|
||||||
|
this->handle->dns_packet()->parse_dns_dn(error, index, true);
|
||||||
|
return error.empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string named_base::name() {
|
||||||
|
size_t index = this->handle->payload_offset();
|
||||||
|
std::string error{};
|
||||||
|
return this->handle->dns_packet()->parse_dns_dn(error, index, true);
|
||||||
|
}
|
145
native/dns/src/response.h
Normal file
145
native/dns/src/response.h
Normal file
@ -0,0 +1,145 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
#include <string>
|
||||||
|
#include <cstdint>
|
||||||
|
#include <netinet/in.h>
|
||||||
|
|
||||||
|
#include "./types.h"
|
||||||
|
|
||||||
|
namespace tc::dns {
|
||||||
|
class DNSResponse;
|
||||||
|
class DNSResponseBuffer;
|
||||||
|
|
||||||
|
namespace response {
|
||||||
|
class DNSHeader {
|
||||||
|
friend class tc::dns::DNSResponse;
|
||||||
|
public:
|
||||||
|
[[nodiscard]] inline size_t id() const { return this->field(0); }
|
||||||
|
|
||||||
|
[[nodiscard]] inline bool is_answer() const { return this->field(1) & 0x1UL; }
|
||||||
|
[[nodiscard]] inline bool is_authoritative_answer() const { return (uint8_t) ((this->field(1) >> 5UL) & 0x01UL); }
|
||||||
|
[[nodiscard]] inline bool is_truncation() const { return (uint8_t) ((this->field(1) >> 6UL) & 0x01UL); }
|
||||||
|
[[nodiscard]] inline bool is_recursion_desired() const { return (uint8_t) ((this->field(1) >> 7UL) & 0x01UL); }
|
||||||
|
[[nodiscard]] inline bool is_recursion_available() const { return (uint8_t) ((this->field(1) >> 8UL) & 0x01UL); }
|
||||||
|
|
||||||
|
[[nodiscard]] inline uint8_t query_type() const { return (uint8_t) ((this->field(1) >> 1UL) & 0x07UL); }
|
||||||
|
[[nodiscard]] inline uint8_t response_code() const { return (uint8_t) ((this->field(1) >> 12UL) & 0x07UL); }
|
||||||
|
|
||||||
|
[[nodiscard]] inline uint16_t query_count() const { return ntohs(this->field(2)); }
|
||||||
|
[[nodiscard]] inline uint16_t answer_count() const { return ntohs(this->field(3)); }
|
||||||
|
[[nodiscard]] inline uint16_t authority_count() const { return ntohs(this->field(4)); }
|
||||||
|
[[nodiscard]] inline uint16_t additional_count() const { return htons(this->field(5)); }
|
||||||
|
private:
|
||||||
|
[[nodiscard]] uint16_t field(int index) const;
|
||||||
|
|
||||||
|
explicit DNSHeader(const DNSResponse* response) : response{response} {}
|
||||||
|
const DNSResponse* response{nullptr};
|
||||||
|
};
|
||||||
|
|
||||||
|
class DNSQuery {
|
||||||
|
friend class tc::dns::DNSResponse;
|
||||||
|
public:
|
||||||
|
[[nodiscard]] inline std::string qname() const { return this->name; }
|
||||||
|
[[nodiscard]] inline rrtype::value qtype() const { return this->type; }
|
||||||
|
[[nodiscard]] inline rrclass::value qclass() const { return this->klass; }
|
||||||
|
private:
|
||||||
|
DNSQuery(std::string name, rrtype::value type, rrclass::value klass) : name{std::move(name)}, type{type}, klass{klass} {}
|
||||||
|
|
||||||
|
std::string name;
|
||||||
|
rrtype::value type;
|
||||||
|
rrclass::value klass;
|
||||||
|
};
|
||||||
|
|
||||||
|
class DNSResourceRecords {
|
||||||
|
friend class tc::dns::DNSResponse;
|
||||||
|
public:
|
||||||
|
[[nodiscard]] inline std::string qname() const { return this->name; }
|
||||||
|
[[nodiscard]] inline rrtype::value atype() const { return this->type; }
|
||||||
|
[[nodiscard]] inline rrclass::value aclass() const { return this->klass; }
|
||||||
|
[[nodiscard]] inline uint16_t attl() const { return this->ttl; }
|
||||||
|
|
||||||
|
[[nodiscard]] const uint8_t* payload_data() const;
|
||||||
|
[[nodiscard]] inline size_t payload_length() const { return this->length; }
|
||||||
|
[[nodiscard]] inline size_t payload_offset() const { return this->offset; }
|
||||||
|
|
||||||
|
[[nodiscard]] inline std::shared_ptr<DNSResponseBuffer> dns_packet() const { return this->packet; }
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
[[nodiscard]] inline T parse() const {
|
||||||
|
if(T::type != this->type)
|
||||||
|
throw std::logic_error{"parser type mismatch"};
|
||||||
|
return T{this};
|
||||||
|
}
|
||||||
|
private:
|
||||||
|
DNSResourceRecords(std::shared_ptr<DNSResponseBuffer>, size_t, size_t, uint32_t, std::string , rrtype::value, rrclass::value);
|
||||||
|
|
||||||
|
std::shared_ptr<DNSResponseBuffer> packet{nullptr};
|
||||||
|
size_t offset{0};
|
||||||
|
size_t length{0};
|
||||||
|
|
||||||
|
uint32_t ttl;
|
||||||
|
|
||||||
|
std::string name;
|
||||||
|
rrtype::value type;
|
||||||
|
rrclass::value klass;
|
||||||
|
};
|
||||||
|
|
||||||
|
namespace rrparser {
|
||||||
|
struct base {
|
||||||
|
protected:
|
||||||
|
explicit base(const DNSResourceRecords* handle) : handle{handle} {}
|
||||||
|
const DNSResourceRecords* handle{nullptr};
|
||||||
|
};
|
||||||
|
|
||||||
|
struct named_base : public base {
|
||||||
|
public:
|
||||||
|
[[nodiscard]] bool is_valid();
|
||||||
|
[[nodiscard]] std::string name();
|
||||||
|
protected:
|
||||||
|
explicit named_base(const DNSResourceRecords* handle) : base{handle} {}
|
||||||
|
};
|
||||||
|
|
||||||
|
#define define_parser(name, base, ...) \
|
||||||
|
struct name : public base { \
|
||||||
|
friend class response::DNSResourceRecords; \
|
||||||
|
public: \
|
||||||
|
static constexpr auto type = rrtype::name; \
|
||||||
|
__VA_ARGS__ \
|
||||||
|
private: \
|
||||||
|
explicit name(const DNSResourceRecords* handle) : base{handle} {} \
|
||||||
|
}
|
||||||
|
|
||||||
|
define_parser(A, base,
|
||||||
|
[[nodiscard]] inline bool is_valid() { return this->handle->payload_length() == 4; }
|
||||||
|
[[nodiscard]] inline std::string address_string() {
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
[[nodiscard]] inline in_addr address() {
|
||||||
|
return {*(uint32_t*) this->handle->payload_data()};
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
define_parser(AAAA, base,
|
||||||
|
[[nodiscard]] inline bool is_valid() { return this->handle->payload_length() == 16; }
|
||||||
|
[[nodiscard]] std::string address_string();
|
||||||
|
[[nodiscard]] in6_addr address();
|
||||||
|
);
|
||||||
|
|
||||||
|
define_parser(SRV, base,
|
||||||
|
[[nodiscard]] bool is_valid();
|
||||||
|
|
||||||
|
[[nodiscard]] inline uint16_t priority() { return ntohs(((uint16_t*) this->handle->payload_data())[0]); }
|
||||||
|
[[nodiscard]] inline uint16_t weight() { return ntohs(((uint16_t*) this->handle->payload_data())[1]); }
|
||||||
|
[[nodiscard]] inline uint16_t target_port() { return ntohs(((uint16_t*) this->handle->payload_data())[2]); }
|
||||||
|
[[nodiscard]] std::string target_hostname();
|
||||||
|
);
|
||||||
|
|
||||||
|
define_parser(CNAME, named_base);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
102
native/dns/src/types.cpp
Normal file
102
native/dns/src/types.cpp
Normal file
@ -0,0 +1,102 @@
|
|||||||
|
#include "types.h"
|
||||||
|
|
||||||
|
std::map<tc::dns::rrtype::value, const char*> tc::dns::rrtype::names{
|
||||||
|
{tc::dns::rrtype::A, "A"},
|
||||||
|
{tc::dns::rrtype::NS, "NS"},
|
||||||
|
{tc::dns::rrtype::MD, "MD"},
|
||||||
|
{tc::dns::rrtype::MF, "MF"},
|
||||||
|
{tc::dns::rrtype::CNAME, "CNAME"},
|
||||||
|
{tc::dns::rrtype::SOA, "SOA"},
|
||||||
|
{tc::dns::rrtype::MB, "MB"},
|
||||||
|
{tc::dns::rrtype::MG, "MG"},
|
||||||
|
{tc::dns::rrtype::MR, "MR"},
|
||||||
|
{tc::dns::rrtype::NULL_, "NULL_"},
|
||||||
|
{tc::dns::rrtype::WKS, "WKS"},
|
||||||
|
{tc::dns::rrtype::PTR, "PTR"},
|
||||||
|
{tc::dns::rrtype::HINFO, "HINFO"},
|
||||||
|
{tc::dns::rrtype::MINFO, "MINFO"},
|
||||||
|
{tc::dns::rrtype::MX, "MX"},
|
||||||
|
{tc::dns::rrtype::TXT, "TXT"},
|
||||||
|
{tc::dns::rrtype::RP, "RP"},
|
||||||
|
{tc::dns::rrtype::AFSDB, "AFSDB"},
|
||||||
|
{tc::dns::rrtype::X25, "X25"},
|
||||||
|
{tc::dns::rrtype::ISDN, "ISDN"},
|
||||||
|
{tc::dns::rrtype::RT, "RT"},
|
||||||
|
{tc::dns::rrtype::NSAP, "NSAP"},
|
||||||
|
{tc::dns::rrtype::NSAP_PTR, "NSAP_PTR"},
|
||||||
|
{tc::dns::rrtype::SIG, "SIG"},
|
||||||
|
{tc::dns::rrtype::KEY, "KEY"},
|
||||||
|
{tc::dns::rrtype::PX, "PX"},
|
||||||
|
{tc::dns::rrtype::GPOS, "GPOS"},
|
||||||
|
{tc::dns::rrtype::AAAA, "AAAA"},
|
||||||
|
{tc::dns::rrtype::LOC, "LOC"},
|
||||||
|
{tc::dns::rrtype::NXT, "NXT"},
|
||||||
|
{tc::dns::rrtype::EID, "EID"},
|
||||||
|
{tc::dns::rrtype::NIMLOC, "NIMLOC"},
|
||||||
|
{tc::dns::rrtype::SRV, "SRV"},
|
||||||
|
{tc::dns::rrtype::ATMA, "ATMA"},
|
||||||
|
{tc::dns::rrtype::NAPTR, "NAPTR"},
|
||||||
|
{tc::dns::rrtype::KX, "KX"},
|
||||||
|
{tc::dns::rrtype::CERT, "CERT"},
|
||||||
|
{tc::dns::rrtype::A6, "A6"},
|
||||||
|
{tc::dns::rrtype::DNAME, "DNAME"},
|
||||||
|
{tc::dns::rrtype::SINK, "SINK"},
|
||||||
|
{tc::dns::rrtype::OPT, "OPT"},
|
||||||
|
{tc::dns::rrtype::APL, "APL"},
|
||||||
|
{tc::dns::rrtype::DS, "DS"},
|
||||||
|
{tc::dns::rrtype::SSHFP, "SSHFP"},
|
||||||
|
{tc::dns::rrtype::IPSECKEY, "IPSECKEY"},
|
||||||
|
{tc::dns::rrtype::RRSIG, "RRSIG"},
|
||||||
|
{tc::dns::rrtype::NSEC, "NSEC"},
|
||||||
|
{tc::dns::rrtype::DNSKEY, "DNSKEY"},
|
||||||
|
{tc::dns::rrtype::DHCID, "DHCID"},
|
||||||
|
{tc::dns::rrtype::NSEC3, "NSEC3"},
|
||||||
|
{tc::dns::rrtype::NSEC3PARAM, "NSEC3PARAM"},
|
||||||
|
{tc::dns::rrtype::TLSA, "TLSA"},
|
||||||
|
{tc::dns::rrtype::SMIMEA, "SMIMEA"},
|
||||||
|
{tc::dns::rrtype::Unassigned, "Unassigned"},
|
||||||
|
{tc::dns::rrtype::HIP, "HIP"},
|
||||||
|
{tc::dns::rrtype::NINFO, "NINFO"},
|
||||||
|
{tc::dns::rrtype::RKEY, "RKEY"},
|
||||||
|
{tc::dns::rrtype::TALINK, "TALINK"},
|
||||||
|
{tc::dns::rrtype::CDS, "CDS"},
|
||||||
|
{tc::dns::rrtype::CDNSKEY, "CDNSKEY"},
|
||||||
|
{tc::dns::rrtype::OPENPGPKEY, "OPENPGPKEY"},
|
||||||
|
{tc::dns::rrtype::CSYNC, "CSYNC"},
|
||||||
|
{tc::dns::rrtype::ZONEMD, "ZONEMD"},
|
||||||
|
{tc::dns::rrtype::SPF, "SPF"},
|
||||||
|
{tc::dns::rrtype::UINFO, "UINFO"},
|
||||||
|
{tc::dns::rrtype::UID, "UID"},
|
||||||
|
{tc::dns::rrtype::GID, "GID"},
|
||||||
|
{tc::dns::rrtype::UNSPEC, "UNSPEC"},
|
||||||
|
{tc::dns::rrtype::NID, "NID"},
|
||||||
|
{tc::dns::rrtype::L32, "L32"},
|
||||||
|
{tc::dns::rrtype::L64, "L64"},
|
||||||
|
{tc::dns::rrtype::LP, "LP"},
|
||||||
|
{tc::dns::rrtype::EUI48, "EUI48"},
|
||||||
|
{tc::dns::rrtype::EUI64, "EUI64"},
|
||||||
|
{tc::dns::rrtype::TKEY, "TKEY"},
|
||||||
|
{tc::dns::rrtype::TSIG, "TSIG"},
|
||||||
|
{tc::dns::rrtype::IXFR, "IXFR"},
|
||||||
|
{tc::dns::rrtype::AXFR, "AXFR"},
|
||||||
|
{tc::dns::rrtype::MAILB, "MAILB"},
|
||||||
|
{tc::dns::rrtype::MAILA, "MAILA"},
|
||||||
|
{tc::dns::rrtype::ANY, "ANY"},
|
||||||
|
{tc::dns::rrtype::URI, "URI"},
|
||||||
|
{tc::dns::rrtype::CAA, "CAA"},
|
||||||
|
{tc::dns::rrtype::AVC, "AVC"},
|
||||||
|
{tc::dns::rrtype::DOA, "DOA"},
|
||||||
|
{tc::dns::rrtype::AMTRELAY, "AMTRELAY"},
|
||||||
|
{tc::dns::rrtype::TA, "TA"},
|
||||||
|
{tc::dns::rrtype::DLV, "DLV"},
|
||||||
|
{tc::dns::rrtype::Reserved, "Reserved"},
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
std::map<tc::dns::rrclass::value, const char*> tc::dns::rrclass::names{
|
||||||
|
{tc::dns::rrclass::IN, "IN"},
|
||||||
|
{tc::dns::rrclass::CH, "CH"},
|
||||||
|
{tc::dns::rrclass::HS, "HS"},
|
||||||
|
{tc::dns::rrclass::QCLASS_ANY, "QCLASS_ANY"},
|
||||||
|
{tc::dns::rrclass::QCLASS_NONE, "QCLASS_NONE"},
|
||||||
|
};
|
134
native/dns/src/types.h
Normal file
134
native/dns/src/types.h
Normal file
@ -0,0 +1,134 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <map>
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
|
namespace tc::dns {
|
||||||
|
//https://www.iana.org/assignments/dns-parameters/dns-parameters.xhtml#dns-parameters-2
|
||||||
|
struct rrtype {
|
||||||
|
enum value : uint32_t {
|
||||||
|
A = 1, // a host address,[RFC1035],
|
||||||
|
NS = 2, // an authoritative name server,[RFC1035],
|
||||||
|
MD = 3, // a mail destination (OBSOLETE - use MX),[RFC1035],
|
||||||
|
MF = 4, // a mail forwarder (OBSOLETE - use MX),[RFC1035],
|
||||||
|
CNAME = 5, // the canonical name for an alias,[RFC1035],
|
||||||
|
SOA = 6, // marks the start of a zone of authority,[RFC1035],
|
||||||
|
MB = 7, // a mailbox domain name (EXPERIMENTAL),[RFC1035],
|
||||||
|
MG = 8, // a mail group member (EXPERIMENTAL),[RFC1035],
|
||||||
|
MR = 9, // a mail rename domain name (EXPERIMENTAL),[RFC1035],
|
||||||
|
NULL_ = 10, // a null RR (EXPERIMENTAL),[RFC1035],
|
||||||
|
WKS = 11, // a well known service description,[RFC1035],
|
||||||
|
PTR = 12, // a domain name pointer,[RFC1035],
|
||||||
|
HINFO = 13, // host information,[RFC1035],
|
||||||
|
MINFO = 14, // mailbox or mail list information,[RFC1035],
|
||||||
|
MX = 15, // mail exchange,[RFC1035],
|
||||||
|
TXT = 16, // text strings,[RFC1035],
|
||||||
|
RP = 17, // for Responsible Person,[RFC1183],
|
||||||
|
AFSDB = 18, // for AFS Data Base location,[RFC1183][RFC5864],
|
||||||
|
X25 = 19, // for X.25 PSDN address,[RFC1183],
|
||||||
|
ISDN = 20, // for ISDN address,[RFC1183],
|
||||||
|
RT = 21, // for Route Through,[RFC1183],
|
||||||
|
NSAP = 22, // "for NSAP address, NSAP style A record",[RFC1706],
|
||||||
|
NSAP_PTR = 23, // "for domain name pointer, NSAP style",[RFC1348][RFC1637][RFC1706],
|
||||||
|
SIG = 24, // for security signature,[RFC4034][RFC3755][RFC2535][RFC2536][RFC2537][RFC2931][RFC3110][RFC3008],
|
||||||
|
KEY = 25, // for security key,[RFC4034][RFC3755][RFC2535][RFC2536][RFC2537][RFC2539][RFC3008][RFC3110],
|
||||||
|
PX = 26, // X.400 mail mapping information,[RFC2163],
|
||||||
|
GPOS = 27, // Geographical Position,[RFC1712],
|
||||||
|
AAAA = 28, // IP6 Address,[RFC3596],
|
||||||
|
LOC = 29, // Location Information,[RFC1876],
|
||||||
|
NXT = 30, // Next Domain (OBSOLETE),[RFC3755][RFC2535],
|
||||||
|
EID = 31, // Endpoint Identifier,[Michael_Patton][http://ana-3.lcs.mit.edu/~jnc/nimrod/dns.txt],
|
||||||
|
NIMLOC = 32, // Nimrod Locator,[1][Michael_Patton][http://ana-3.lcs.mit.edu/~jnc/nimrod/dns.txt],
|
||||||
|
SRV = 33, // Server Selection,[1][RFC2782],
|
||||||
|
ATMA = 34, // ATM Address,"[ ATM Forum Technical Committee, ""ATM Name System, V2.0"", Doc ID: AF-DANS-0152.000, July 2000. Available from and held in escrow by IANA.]",
|
||||||
|
NAPTR = 35, // Naming Authority Pointer,[RFC2915][RFC2168][RFC3403],
|
||||||
|
KX = 36, // Key Exchanger,[RFC2230],
|
||||||
|
CERT = 37, //CERT, // [RFC4398],
|
||||||
|
A6 = 38, // A6 (OBSOLETE - use AAAA),[RFC3226][RFC2874][RFC6563],
|
||||||
|
DNAME = 39, //DNAME, // [RFC6672],
|
||||||
|
SINK = 40, //SINK, // [Donald_E_Eastlake][http://tools.ietf.org/html/draft-eastlake-kitchen-sink],
|
||||||
|
OPT = 41, //OPT, // [RFC6891][RFC3225],
|
||||||
|
APL = 42, //APL, // [RFC3123],
|
||||||
|
DS = 43, // Delegation Signer,[RFC4034][RFC3658],
|
||||||
|
SSHFP = 44, // SSH Key Fingerprint,[RFC4255],
|
||||||
|
IPSECKEY = 45, //IPSECKEY, // [RFC4025],
|
||||||
|
RRSIG = 46, //RRSIG, // [RFC4034][RFC3755],
|
||||||
|
NSEC = 47, //NSEC, // [RFC4034][RFC3755],
|
||||||
|
DNSKEY = 48, //DNSKEY, // [RFC4034][RFC3755],
|
||||||
|
DHCID = 49, //DHCID, // [RFC4701],
|
||||||
|
NSEC3 = 50, //NSEC3, // [RFC5155],
|
||||||
|
NSEC3PARAM = 51, //NSEC3PARAM, // [RFC5155],
|
||||||
|
TLSA = 52, //TLSA, // [RFC6698],
|
||||||
|
SMIMEA = 53, // S/MIME cert association,[RFC8162],SMIMEA/smimea-completed-template
|
||||||
|
Unassigned = 54, // ,
|
||||||
|
HIP = 55, // Host Identity Protocol,[RFC8005],
|
||||||
|
NINFO = 56, //NINFO [Jim_Reid], // NINFO/ninfo-completed-template
|
||||||
|
RKEY = 57, //RKEY [Jim_Reid], // RKEY/rkey-completed-template
|
||||||
|
TALINK = 58, // Trust Anchor LINK,[Wouter_Wijngaards],TALINK/talink-completed-template
|
||||||
|
CDS = 59, // Child DS,[RFC7344],CDS/cds-completed-template
|
||||||
|
CDNSKEY = 60, // DNSKEY(s) the Child wants reflected in DS,[RFC7344],
|
||||||
|
OPENPGPKEY = 61, // OpenPGP Key,[RFC7929],OPENPGPKEY/openpgpkey-completed-template
|
||||||
|
CSYNC = 62, // Child-To-Parent Synchronization,[RFC7477],
|
||||||
|
ZONEMD = 63, // message digest for DNS zone,[draft-wessels-dns-zone-digest],ZONEMD/zonemd-completed-template
|
||||||
|
//Unassigned = 64-98,
|
||||||
|
SPF = 99, // [RFC7208],
|
||||||
|
UINFO = 100, // [IANA-Reserved],
|
||||||
|
UID = 101, // [IANA-Reserved],
|
||||||
|
GID = 102, // [IANA-Reserved],
|
||||||
|
UNSPEC = 103, // [IANA-Reserved],
|
||||||
|
NID = 104, //[RFC6742], // ILNP/nid-completed-template
|
||||||
|
L32 = 105, //[RFC6742], // ILNP/l32-completed-template
|
||||||
|
L64 = 106, //[RFC6742], // ILNP/l64-completed-template
|
||||||
|
LP = 107, //[RFC6742], // ILNP/lp-completed-template
|
||||||
|
EUI48 = 108, // an EUI-48 address,[RFC7043],EUI48/eui48-completed-template
|
||||||
|
EUI64 = 109, // an EUI-64 address,[RFC7043],EUI64/eui64-completed-template
|
||||||
|
//Unassigned = 110-248, // ,
|
||||||
|
TKEY = 249, // Transaction Key,[RFC2930],
|
||||||
|
TSIG = 250, // Transaction Signature,[RFC2845],
|
||||||
|
IXFR = 251, // incremental transfer,[RFC1995],
|
||||||
|
AXFR = 252, // transfer of an entire zone,[RFC1035][RFC5936],
|
||||||
|
MAILB = 253, // "mailbox-related RRs (MB, MG or MR)",[RFC1035],
|
||||||
|
MAILA = 254, // mail agent RRs (OBSOLETE - see MX),[RFC1035],
|
||||||
|
ANY = 255, // A request for some or all records the server has available,[RFC1035][RFC6895][RFC8482],
|
||||||
|
URI = 256, //URI [RFC7553], // URI/uri-completed-template
|
||||||
|
CAA = 257, // Certification Authority Restriction,[RFC-ietf-lamps-rfc6844bis-07],CAA/caa-completed-template
|
||||||
|
AVC = 258, // Application Visibility and Control,[Wolfgang_Riedel],AVC/avc-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
|
||||||
|
//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,
|
||||||
|
// Information Networking Institute, Carnegie Mellon University, April 2004.]",
|
||||||
|
DLV = 32769, // DNSSEC Lookaside Validation,[RFC4431],
|
||||||
|
//Unassigned = 32770-65279,, // ,
|
||||||
|
//Private use,65280-65534,,,,
|
||||||
|
Reserved = 65535,
|
||||||
|
};
|
||||||
|
|
||||||
|
static std::map<value, const char*> names;
|
||||||
|
|
||||||
|
static const char* name(const value& value) {
|
||||||
|
if(names.count(value) > 0)
|
||||||
|
return names.at(value);
|
||||||
|
return "unknown";
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct rrclass {
|
||||||
|
enum value : uint32_t {
|
||||||
|
IN = 1, /* Internet */
|
||||||
|
CH = 3, /* Chaos */
|
||||||
|
HS = 4, /* Hesiod */
|
||||||
|
|
||||||
|
QCLASS_NONE = 254,
|
||||||
|
QCLASS_ANY = 255
|
||||||
|
};
|
||||||
|
|
||||||
|
static std::map<value, const char*> names;
|
||||||
|
|
||||||
|
static const char* name(const value& value) {
|
||||||
|
if(names.count(value) > 0)
|
||||||
|
return names.at(value);
|
||||||
|
return "unknown";
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
66
native/dns/test/crash.cpp
Normal file
66
native/dns/test/crash.cpp
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
#include <mutex>
|
||||||
|
#include <condition_variable>
|
||||||
|
#include <cassert>
|
||||||
|
#include <thread>
|
||||||
|
#include <event.h>
|
||||||
|
#include <unbound.h>
|
||||||
|
#include <unbound-event.h>
|
||||||
|
|
||||||
|
#define ITERATIONS (2000)
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
/* Setting up all libevent stuff */
|
||||||
|
std::mutex ev_lock{};
|
||||||
|
std::condition_variable ev_condition{};
|
||||||
|
auto ev_base = event_base_new();
|
||||||
|
|
||||||
|
std::thread ev_loop = std::thread([&]{
|
||||||
|
/* Wait until we should start the event loop */
|
||||||
|
{
|
||||||
|
std::unique_lock lock{ev_lock};
|
||||||
|
ev_condition.wait(lock);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* executing the event loop until no events are registered */
|
||||||
|
event_base_loop(ev_base, 0);
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
/* Setting up libunbound */
|
||||||
|
auto ctx = ub_ctx_create_event(ev_base);
|
||||||
|
assert(ctx);
|
||||||
|
|
||||||
|
auto errc = ub_ctx_hosts(ctx, nullptr);
|
||||||
|
assert(errc == 0);
|
||||||
|
|
||||||
|
errc = ub_ctx_resolvconf(ctx, nullptr);
|
||||||
|
assert(errc == 0);
|
||||||
|
|
||||||
|
int callback_call_count{0};
|
||||||
|
for(int i = 0; i < ITERATIONS; i++){
|
||||||
|
auto host = std::to_string(i) + "ts.teaspeak.de";
|
||||||
|
errc = ub_resolve_event(ctx, host.c_str(), 33, 1, (void*) &callback_call_count, [](void* _ccc, int, void*, int, int, char*) {
|
||||||
|
*static_cast<int*>(_ccc) += 1;
|
||||||
|
}, nullptr);
|
||||||
|
assert(errc == 0);
|
||||||
|
|
||||||
|
if(i == 0) {
|
||||||
|
//CASE 1: CRASH
|
||||||
|
//Start the event loop as soon the first resolve has been scheduled
|
||||||
|
ev_condition.notify_one();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//CASE 2: NO CRASH
|
||||||
|
//Start the event loop after all resolves have been scheduled
|
||||||
|
//ev_condition.notify_one();
|
||||||
|
|
||||||
|
/* Hang up until no more events */
|
||||||
|
ev_loop.join();
|
||||||
|
assert(ITERATIONS == callback_call_count);
|
||||||
|
|
||||||
|
ub_ctx_delete(ctx);
|
||||||
|
event_base_free(ev_base);
|
||||||
|
}
|
52
native/dns/test/main.cpp
Normal file
52
native/dns/test/main.cpp
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
#include <iostream>
|
||||||
|
#include <event2/thread.h>
|
||||||
|
#include <cstring>
|
||||||
|
#include <variant>
|
||||||
|
#include <random>
|
||||||
|
#include <deque>
|
||||||
|
#include <cassert>
|
||||||
|
#include <unbound-event.h>
|
||||||
|
|
||||||
|
#include "../src/response.h"
|
||||||
|
#include "../src/resolver.h"
|
||||||
|
#include "../utils.h"
|
||||||
|
|
||||||
|
using namespace tc::dns;
|
||||||
|
using namespace std;
|
||||||
|
|
||||||
|
namespace parser = response::rrparser;
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
evthread_use_pthreads();
|
||||||
|
//evthread_enable_lock_debugging();
|
||||||
|
|
||||||
|
std::string error{};
|
||||||
|
Resolver resolver{};
|
||||||
|
|
||||||
|
if(!resolver.initialize(error, true, true)) {
|
||||||
|
cerr << "failed to initialize resolver: " << error << endl;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto begin = chrono::system_clock::now();
|
||||||
|
for(int i = 0; i < 250; i++) {
|
||||||
|
cr(resolver, {
|
||||||
|
to_string(i) + "ts.twerion.net",
|
||||||
|
9922
|
||||||
|
}, [begin](bool success, auto data) {
|
||||||
|
auto end = chrono::system_clock::now();
|
||||||
|
cout << "Success: " << success << " Time: " << chrono::ceil<chrono::milliseconds>(end - begin).count() << "ms" << endl;
|
||||||
|
if(success) {
|
||||||
|
auto address = std::get<ServerAddress>(data);
|
||||||
|
cout << "Target: " << address.host << ":" << address.port << endl;
|
||||||
|
} else {
|
||||||
|
cout << "Message: " << std::get<std::string>(data) << endl;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
this_thread::sleep_for(chrono::seconds{8});
|
||||||
|
resolver.finalize();
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
27
native/dns/test/main.ts
Normal file
27
native/dns/test/main.ts
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
/// <reference path="../exports/exports.d.ts" />
|
||||||
|
|
||||||
|
console.log("HELLO WORLD");
|
||||||
|
module.paths.push("../../build/linux_x64");
|
||||||
|
module.paths.push("../../build/win32_64");
|
||||||
|
|
||||||
|
const original_require = require;
|
||||||
|
require = (module => original_require("/home/wolverindev/TeaSpeak-Client/client/native/build/linux_x64/" + module + ".node")) as any;
|
||||||
|
import * as handle from "teaclient_dns";
|
||||||
|
require = original_require;
|
||||||
|
|
||||||
|
handle.initialize();
|
||||||
|
console.log("INITS");
|
||||||
|
handle.resolve_cr("voice.teamspeak.com", 9987, result => {
|
||||||
|
console.log("Result: " + JSON.stringify(result));
|
||||||
|
});
|
||||||
|
handle.resolve_cr("ts.twerion.net", 9987, result => {
|
||||||
|
console.log("Result: " + JSON.stringify(result));
|
||||||
|
});
|
||||||
|
handle.resolve_cr("twerion.net", 9987, result => {
|
||||||
|
console.log("Result: " + JSON.stringify(result));
|
||||||
|
});
|
||||||
|
/*
|
||||||
|
handle.resolve_cr("localhost", 9987, result => {
|
||||||
|
console.log("Result: " + JSON.stringify(result));
|
||||||
|
});
|
||||||
|
*/
|
497
native/dns/utils.cpp
Normal file
497
native/dns/utils.cpp
Normal file
@ -0,0 +1,497 @@
|
|||||||
|
#include "./utils.h"
|
||||||
|
#include "./src/response.h"
|
||||||
|
|
||||||
|
#include <map>
|
||||||
|
#include <vector>
|
||||||
|
#include <deque>
|
||||||
|
#include <tuple>
|
||||||
|
#include <cassert>
|
||||||
|
#include <mutex>
|
||||||
|
#include <memory>
|
||||||
|
#include <iostream>
|
||||||
|
#include <random>
|
||||||
|
#include <cstring>
|
||||||
|
#include <arpa/inet.h>
|
||||||
|
|
||||||
|
using namespace tc::dns;
|
||||||
|
namespace parser = tc::dns::response::rrparser;
|
||||||
|
|
||||||
|
//-------------------------- IP-Resolve
|
||||||
|
struct CrIpStatus {
|
||||||
|
std::mutex pending_lock;
|
||||||
|
uint8_t pending{0};
|
||||||
|
|
||||||
|
ServerAddress original{"", 0};
|
||||||
|
|
||||||
|
std::tuple<bool, std::string> a{false, "unset"};
|
||||||
|
std::tuple<bool, std::string> aaaa{false, "unset"};
|
||||||
|
|
||||||
|
cr_callback_t callback;
|
||||||
|
std::function<void(const std::shared_ptr<CrIpStatus>&)> finish_callback;
|
||||||
|
|
||||||
|
void one_finished(const std::shared_ptr<CrIpStatus>& _this) {
|
||||||
|
assert(&*_this == this);
|
||||||
|
|
||||||
|
std::lock_guard lock{this->pending_lock};
|
||||||
|
if(--pending == 0)
|
||||||
|
this->finish_callback(_this);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
void cr_ip_finish(const std::shared_ptr<CrIpStatus>& status) {
|
||||||
|
if(std::get<0>(status->a)) {
|
||||||
|
status->callback(true, ServerAddress{std::get<1>(status->a), status->original.port});
|
||||||
|
} else if(std::get<0>(status->aaaa)) {
|
||||||
|
status->callback(true, ServerAddress{std::get<1>(status->aaaa), status->original.port});
|
||||||
|
} 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) + "}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void tc::dns::cr_ip(Resolver& resolver, const ServerAddress& address, const cr_callback_t& callback) {
|
||||||
|
auto status = std::make_shared<CrIpStatus>();
|
||||||
|
|
||||||
|
status->original = address;
|
||||||
|
status->finish_callback = cr_ip_finish;
|
||||||
|
status->callback = callback;
|
||||||
|
|
||||||
|
/* general pending so we could finish our method */
|
||||||
|
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){
|
||||||
|
if(state != 0) {
|
||||||
|
status->a = {false, "A query failed. State: " + std::to_string(state) + ", Code: " + std::to_string(code)};
|
||||||
|
status->one_finished(status);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
std::string error;
|
||||||
|
if(!data->parse(error)) {
|
||||||
|
status->a = {false, "A query failed. State: " + std::to_string(state) + ", Code: " + std::to_string(code)};
|
||||||
|
status->one_finished(status);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for(const auto& answer : data->answers()) {
|
||||||
|
if(answer->atype() != rrtype::A){
|
||||||
|
std::cerr << "Received a non A record answer in A query!" << std::endl;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto data = answer->parse<parser::A>();
|
||||||
|
if(!data.is_valid())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
status->a = {true, data.address_string()};
|
||||||
|
status->one_finished(status);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
status->a = {false, "empty response"};
|
||||||
|
status->one_finished(status);
|
||||||
|
});
|
||||||
|
|
||||||
|
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){
|
||||||
|
if(state != 0) {
|
||||||
|
status->aaaa = {false, "AAAA query failed. State: " + std::to_string(state) + ", Code: " + std::to_string(code)};
|
||||||
|
status->one_finished(status);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string error;
|
||||||
|
if(!data->parse(error)) {
|
||||||
|
status->aaaa = {false, "failed to parse AAAA query reponse: " + error};
|
||||||
|
status->one_finished(status);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for(const auto& answer : data->answers()) {
|
||||||
|
if(answer->atype() != rrtype::AAAA){
|
||||||
|
std::cerr << "Received a non AAAA record answer in AAAA query!" << std::endl;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto data = answer->parse<parser::AAAA>();
|
||||||
|
if(!data.is_valid())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
status->aaaa = {true, data.address_string()};
|
||||||
|
status->one_finished(status);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
status->aaaa = {false, "empty response"};
|
||||||
|
status->one_finished(status);
|
||||||
|
return;
|
||||||
|
});
|
||||||
|
|
||||||
|
status->one_finished(status);
|
||||||
|
}
|
||||||
|
|
||||||
|
//-------------------------- SRV-Resolve
|
||||||
|
static std::random_device srv_rnd_dev;
|
||||||
|
static std::mt19937 srv_rnd(srv_rnd_dev());
|
||||||
|
|
||||||
|
/* 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) {
|
||||||
|
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){
|
||||||
|
if(state != 0) {
|
||||||
|
callback(false, "SRV query failed. State: " + std::to_string(state) + ", Code: " + std::to_string(code));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string error;
|
||||||
|
if(!data->parse(error)) {
|
||||||
|
callback(false, "failed to parse srv query reponse: " + error);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct SrvEntry {
|
||||||
|
uint16_t weight;
|
||||||
|
std::string target;
|
||||||
|
uint16_t port;
|
||||||
|
};
|
||||||
|
std::map<uint16_t, std::vector<SrvEntry>> entries{};
|
||||||
|
|
||||||
|
auto answers = data->answers();
|
||||||
|
for(const auto& answer : answers) {
|
||||||
|
if(answer->atype() != rrtype::SRV) {
|
||||||
|
std::cerr << "Received a non SRV record answer in SRV query (" << rrtype::name(answer->atype()) << ")!" << std::endl;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto srv = answer->parse<parser::SRV>();
|
||||||
|
entries[srv.priority()].push_back({srv.weight(), srv.target_hostname(), srv.target_port()});
|
||||||
|
}
|
||||||
|
|
||||||
|
if(entries.empty()) {
|
||||||
|
callback(false, "empty response");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::deque<std::tuple<uint16_t, SrvEntry>> results{};
|
||||||
|
for(auto [priority, pentries] : entries) {
|
||||||
|
int64_t count = 0;
|
||||||
|
for(const auto& entry : pentries)
|
||||||
|
count += std::max((size_t) entry.weight, 1UL);
|
||||||
|
|
||||||
|
std::uniform_int_distribution<std::mt19937::result_type> dist(0, count - 1);
|
||||||
|
auto index = dist(srv_rnd);
|
||||||
|
|
||||||
|
count = 0;
|
||||||
|
for(const auto& entry : pentries) {
|
||||||
|
count += std::max((size_t) entry.weight, 1UL);
|
||||||
|
if(count > index) {
|
||||||
|
count = -1;
|
||||||
|
results.emplace_back(priority, entry);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
assert(!results.empty());
|
||||||
|
|
||||||
|
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
|
||||||
|
auto target = std::get<1>(*results.begin());
|
||||||
|
cr_ip(resolver, {
|
||||||
|
target.target,
|
||||||
|
target.port == 0 ? address.port : target.port
|
||||||
|
}, [callback](bool success, auto response) {
|
||||||
|
if(!success) {
|
||||||
|
//TODO: Use the backup stuff?
|
||||||
|
callback(false, "failed to resolve dns pointer: " + std::get<std::string>(response));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
callback(true, std::get<ServerAddress>(response));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
//-------------------------- TSDNS-Resolve
|
||||||
|
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);
|
||||||
|
cr_srv(resolver, {root, 0}, [&resolver, callback, address](bool success, auto data){
|
||||||
|
if(!success) {
|
||||||
|
callback(false, "failed to resolve tsdns address: " + std::get<std::string>(data));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
auto tsdns_host = std::get<ServerAddress>(data);
|
||||||
|
|
||||||
|
sockaddr_storage tsdns_address{};
|
||||||
|
memset(&tsdns_address, 0, sizeof(tsdns_address));
|
||||||
|
|
||||||
|
auto tsdns_a6 = (sockaddr_in6*) &tsdns_address;
|
||||||
|
auto tsdns_a4 = (sockaddr_in*) &tsdns_address;
|
||||||
|
if(inet_pton(AF_INET6, tsdns_host.host.c_str(), &tsdns_a6->sin6_addr) == 1) {
|
||||||
|
tsdns_a6->sin6_family = AF_INET6;
|
||||||
|
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) {
|
||||||
|
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;
|
||||||
|
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) {
|
||||||
|
if(error == Resolver::ResultState::SUCCESS) {
|
||||||
|
if(response == "404")
|
||||||
|
callback(false, "no record found for " + query);
|
||||||
|
else {
|
||||||
|
std::string host{response};
|
||||||
|
std::string port{"$PORT"};
|
||||||
|
|
||||||
|
//TODO: Backup IP-Addresses?
|
||||||
|
if(host.find(',') != -1)
|
||||||
|
host = std::string{host.begin(), host.begin() + host.find(',')};
|
||||||
|
|
||||||
|
auto colon_index = host.rfind(':');
|
||||||
|
if(colon_index > 0 && (host[colon_index - 1] == ']' || host.find(':') == colon_index)) {
|
||||||
|
port = host.substr(colon_index + 1);
|
||||||
|
host = host.substr(0, colon_index);
|
||||||
|
}
|
||||||
|
|
||||||
|
ServerAddress resp{host, 0};
|
||||||
|
if(port == "$PORT") {
|
||||||
|
resp.port = address.port;
|
||||||
|
} else {
|
||||||
|
try {
|
||||||
|
resp.port = stoul(port);
|
||||||
|
} catch(std::exception& ex) {
|
||||||
|
callback(false, "failed to parse response: " + response + " Failed to parse port: " + port);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
callback(true, resp);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
callback(false, "query failed. Code: " + std::to_string(error) + "," + std::to_string(detail) + ": " + response);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}, "_tsdns._tcp");
|
||||||
|
}
|
||||||
|
|
||||||
|
//-------------------------- Full-Resolve
|
||||||
|
|
||||||
|
struct CrStatus {
|
||||||
|
enum State {
|
||||||
|
PENDING,
|
||||||
|
FAILED,
|
||||||
|
SUCCESS
|
||||||
|
};
|
||||||
|
|
||||||
|
std::recursive_mutex pending_lock; /* do_done could be called recursively because DNS request could answer instant! */
|
||||||
|
uint8_t pending{0};
|
||||||
|
bool finished{false};
|
||||||
|
|
||||||
|
tc::dns::ServerAddress address;
|
||||||
|
tc::dns::cr_callback_t callback;
|
||||||
|
|
||||||
|
~CrStatus() {
|
||||||
|
assert(this->pending == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void do_done(const std::shared_ptr<CrStatus>& _this) {
|
||||||
|
std::lock_guard lock{pending_lock};
|
||||||
|
this->finished |= this->try_answer(_this); //May invokes next DNS query
|
||||||
|
|
||||||
|
assert(this->pending > 0);
|
||||||
|
if(--pending == 0 && !this->finished) { //Order matters we have to decrease pensing!
|
||||||
|
this->callback(false, "no results");
|
||||||
|
this->finished = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef std::tuple<bool, std::function<void(const std::shared_ptr<CrStatus>&)>> flagged_executor_t;
|
||||||
|
|
||||||
|
flagged_executor_t execute_subsrv_ts;
|
||||||
|
std::tuple<State, std::string, tc::dns::ServerAddress> subsrv_ts;
|
||||||
|
|
||||||
|
flagged_executor_t execute_subsrv_ts3;
|
||||||
|
std::tuple<State, std::string, tc::dns::ServerAddress> subsrv_ts3;
|
||||||
|
|
||||||
|
flagged_executor_t execute_tsdns;
|
||||||
|
std::tuple<State, std::string, tc::dns::ServerAddress> tsdns;
|
||||||
|
|
||||||
|
flagged_executor_t execute_subdomain;
|
||||||
|
std::tuple<State, std::string, tc::dns::ServerAddress> subdomain;
|
||||||
|
|
||||||
|
//Only after subsrc and tsdns failed
|
||||||
|
flagged_executor_t execute_rootsrv;
|
||||||
|
std::tuple<State, std::string, tc::dns::ServerAddress> rootsrv;
|
||||||
|
|
||||||
|
//Only after subsrc and tsdns failed
|
||||||
|
flagged_executor_t execute_rootdomain;
|
||||||
|
std::tuple<State, std::string, tc::dns::ServerAddress> rootdomain;
|
||||||
|
|
||||||
|
#define try_answer_test(element, executor) \
|
||||||
|
if(std::get<0>(element) == State::SUCCESS) { \
|
||||||
|
this->callback(true, std::get<2>(element)); \
|
||||||
|
return true; \
|
||||||
|
} else if(std::get<0>(element) == State::PENDING) { \
|
||||||
|
if(!std::get<0>(executor)) { \
|
||||||
|
std::get<0>(executor) = true; \
|
||||||
|
if(!!std::get<1>(executor)) { \
|
||||||
|
std::get<1>(executor)(_this); \
|
||||||
|
return false; \
|
||||||
|
} else { \
|
||||||
|
std::get<1>(element) = "No executor"; \
|
||||||
|
std::get<0>(element) = State::FAILED; \
|
||||||
|
} \
|
||||||
|
} else \
|
||||||
|
return false; \
|
||||||
|
}
|
||||||
|
|
||||||
|
bool try_answer(const std::shared_ptr<CrStatus>& _this) {
|
||||||
|
if(this->finished)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
try_answer_test(this->subsrv_ts, this->execute_subsrv_ts);
|
||||||
|
try_answer_test(this->subsrv_ts3, this->execute_subsrv_ts3);
|
||||||
|
try_answer_test(this->tsdns, this->execute_tsdns);
|
||||||
|
try_answer_test(this->subdomain, this->execute_subdomain);
|
||||||
|
try_answer_test(this->rootsrv, this->execute_rootsrv);
|
||||||
|
try_answer_test(this->rootdomain, this->execute_rootdomain);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
void tc::dns::cr(Resolver& resolver, const tc::dns::ServerAddress& address, const tc::dns::cr_callback_t& callback) {
|
||||||
|
auto status = std::make_shared<CrStatus>();
|
||||||
|
status->address = address;
|
||||||
|
status->callback = callback;
|
||||||
|
status->pending++;
|
||||||
|
|
||||||
|
status->execute_subsrv_ts = {
|
||||||
|
false,
|
||||||
|
[&resolver](const std::shared_ptr<CrStatus>& status) {
|
||||||
|
//std::cout << "Execute subsrc ts" << std::endl;
|
||||||
|
status->pending++;
|
||||||
|
tc::dns::cr_srv(resolver, status->address, [status](bool success, auto data) {
|
||||||
|
if(success) {
|
||||||
|
status->subsrv_ts = {CrStatus::SUCCESS, "", std::get<ServerAddress>(data)};
|
||||||
|
} else {
|
||||||
|
status->subsrv_ts = {CrStatus::FAILED, std::get<std::string>(data), {}};
|
||||||
|
}
|
||||||
|
status->do_done(status);
|
||||||
|
}, "_ts._udp");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
/* execute */
|
||||||
|
std::get<0>(status->execute_subsrv_ts) = true;
|
||||||
|
|
||||||
|
status->execute_subsrv_ts3 = {
|
||||||
|
false,
|
||||||
|
[&resolver](const std::shared_ptr<CrStatus>& status) {
|
||||||
|
//std::cout << "Execute subsrc ts3" << std::endl;
|
||||||
|
status->pending++;
|
||||||
|
tc::dns::cr_srv(resolver, status->address, [status](bool success, auto data) {
|
||||||
|
if(success) {
|
||||||
|
status->subsrv_ts3 = {CrStatus::SUCCESS, "", std::get<ServerAddress>(data)};
|
||||||
|
} else {
|
||||||
|
status->subsrv_ts3 = {CrStatus::FAILED, std::get<std::string>(data), {}};
|
||||||
|
}
|
||||||
|
status->do_done(status);
|
||||||
|
}, "_ts3._udp");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
/* execute */
|
||||||
|
std::get<0>(status->execute_subsrv_ts3) = true;
|
||||||
|
|
||||||
|
status->execute_subdomain = {
|
||||||
|
false,
|
||||||
|
[&resolver](const std::shared_ptr<CrStatus>& status) {
|
||||||
|
//std::cout << "Execute subdomain" << std::endl;
|
||||||
|
status->pending++;
|
||||||
|
tc::dns::cr_ip(resolver, status->address, [status](bool success, auto data) {
|
||||||
|
if(success) {
|
||||||
|
status->subdomain = {CrStatus::SUCCESS, "", std::get<ServerAddress>(data)};
|
||||||
|
} else {
|
||||||
|
status->subdomain = {CrStatus::FAILED, std::get<std::string>(data), {}};
|
||||||
|
}
|
||||||
|
status->do_done(status);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
/* execute */
|
||||||
|
//Will be autoamticall be executed after the SRV stuff
|
||||||
|
//std::get<0>(status->execute_subdomain) = true;
|
||||||
|
|
||||||
|
status->execute_tsdns = {
|
||||||
|
false,
|
||||||
|
[&resolver](const std::shared_ptr<CrStatus>& status) {
|
||||||
|
//std::cout << "Execute tsdns" << std::endl;
|
||||||
|
status->pending++;
|
||||||
|
|
||||||
|
tc::dns::cr_tsdns(resolver, status->address, [status](bool success, auto data) {
|
||||||
|
if(success) {
|
||||||
|
status->tsdns = {CrStatus::SUCCESS, "", std::get<ServerAddress>(data)};
|
||||||
|
} else {
|
||||||
|
status->tsdns = {CrStatus::FAILED, std::get<std::string>(data), {}};
|
||||||
|
}
|
||||||
|
status->do_done(status);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
/* execute */
|
||||||
|
//Execute the TSDNS request right at the beginning because it could hang sometimes
|
||||||
|
std::get<0>(status->execute_tsdns) = true;
|
||||||
|
|
||||||
|
auto root_domain = tc::dns::domain_root(status->address.host);
|
||||||
|
if(root_domain != status->address.host) {
|
||||||
|
status->execute_rootsrv = {
|
||||||
|
false,
|
||||||
|
[&resolver](const std::shared_ptr<CrStatus>& status) {
|
||||||
|
//std::cout << "Execute root srv" << std::endl;
|
||||||
|
status->pending++;
|
||||||
|
|
||||||
|
tc::dns::cr_srv(resolver, {
|
||||||
|
tc::dns::domain_root(status->address.host),
|
||||||
|
status->address.port
|
||||||
|
}, [status](bool success, auto data) {
|
||||||
|
if(success) {
|
||||||
|
status->rootsrv = {CrStatus::SUCCESS, "", std::get<ServerAddress>(data)};
|
||||||
|
} else {
|
||||||
|
status->rootsrv = {CrStatus::FAILED, std::get<std::string>(data), {}};
|
||||||
|
}
|
||||||
|
status->do_done(status);
|
||||||
|
}, "_ts3._udp");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
status->execute_rootdomain = {
|
||||||
|
false,
|
||||||
|
[&resolver](const std::shared_ptr<CrStatus>& status) {
|
||||||
|
//std::cout << "Execute root domain" << std::endl;
|
||||||
|
status->pending++;
|
||||||
|
|
||||||
|
tc::dns::cr_ip(resolver,{
|
||||||
|
tc::dns::domain_root(status->address.host),
|
||||||
|
status->address.port
|
||||||
|
}, [status](bool success, auto data) {
|
||||||
|
if(success) {
|
||||||
|
status->rootdomain = {CrStatus::SUCCESS, "", std::get<ServerAddress>(data)};
|
||||||
|
} else {
|
||||||
|
status->rootdomain = {CrStatus::FAILED, std::get<std::string>(data), {}};
|
||||||
|
}
|
||||||
|
status->do_done(status);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Only execute after every executor has been registered! */
|
||||||
|
std::get<1>(status->execute_subsrv_ts)(status);
|
||||||
|
std::get<1>(status->execute_subsrv_ts3)(status);
|
||||||
|
//std::get<1>(status->execute_subdomain)(status);
|
||||||
|
std::get<1>(status->execute_tsdns)(status);
|
||||||
|
|
||||||
|
status->do_done(status);
|
||||||
|
}
|
33
native/dns/utils.h
Normal file
33
native/dns/utils.h
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <cstddef>
|
||||||
|
#include <string>
|
||||||
|
#include <functional>
|
||||||
|
#include <variant>
|
||||||
|
#include "./src/resolver.h"
|
||||||
|
|
||||||
|
namespace tc::dns {
|
||||||
|
struct ServerAddress {
|
||||||
|
std::string host;
|
||||||
|
uint16_t port;
|
||||||
|
};
|
||||||
|
typedef std::function<void(bool /* success */, const std::variant<std::string, ServerAddress>& /* data */)> cr_callback_t;
|
||||||
|
|
||||||
|
inline std::string domain_root(std::string input) {
|
||||||
|
size_t dot_count = 0;
|
||||||
|
auto it = input.rbegin();
|
||||||
|
while(it != input.rend()) {
|
||||||
|
if(*it == '.' && ++dot_count == 2)
|
||||||
|
break;
|
||||||
|
it++;
|
||||||
|
}
|
||||||
|
std::string result{};
|
||||||
|
result.append(input.begin() + std::distance(it, input.rend()), input.end());
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
extern void cr_ip(Resolver& resolver, const ServerAddress& address, const cr_callback_t& callback);
|
||||||
|
extern void cr_srv(Resolver& resolver, const ServerAddress& address, const cr_callback_t& callback, const std::string& application = "_ts._udp");
|
||||||
|
extern void cr_tsdns(Resolver& resolver, const ServerAddress& address, const cr_callback_t& callback);
|
||||||
|
extern void cr(Resolver& resolver, const tc::dns::ServerAddress& address, const tc::dns::cr_callback_t& callback);
|
||||||
|
}
|
@ -58,6 +58,7 @@ endif()
|
|||||||
|
|
||||||
add_nodejs_module(${MODULE_NAME} ${SOURCE_FILES} ${NODEJS_SOURCE_FILES})
|
add_nodejs_module(${MODULE_NAME} ${SOURCE_FILES} ${NODEJS_SOURCE_FILES})
|
||||||
target_link_libraries(${MODULE_NAME} ${NODEJS_LIBRARIES})
|
target_link_libraries(${MODULE_NAME} ${NODEJS_LIBRARIES})
|
||||||
|
#target_compile_options(${MODULE_NAME} PUBLIC "-fPIC")
|
||||||
|
|
||||||
find_package(TomMath REQUIRED)
|
find_package(TomMath REQUIRED)
|
||||||
include_directories(${TomMath_INCLUDE_DIR})
|
include_directories(${TomMath_INCLUDE_DIR})
|
||||||
@ -132,7 +133,7 @@ else()
|
|||||||
set(REQUIRED_LIBRARIES ${REQUIRED_LIBRARIES}
|
set(REQUIRED_LIBRARIES ${REQUIRED_LIBRARIES}
|
||||||
libstdc++fs.a
|
libstdc++fs.a
|
||||||
asound
|
asound
|
||||||
jack
|
jack.a
|
||||||
pthread
|
pthread
|
||||||
)
|
)
|
||||||
endif()
|
endif()
|
||||||
|
@ -101,15 +101,16 @@ bool AudioInput::recording() {
|
|||||||
|
|
||||||
void AudioInput::stop() {
|
void AudioInput::stop() {
|
||||||
lock_guard lock(this->input_stream_lock);
|
lock_guard lock(this->input_stream_lock);
|
||||||
if(this->input_stream)
|
if(this->input_stream) {
|
||||||
Pa_StopStream(this->input_stream);
|
Pa_AbortStream(this->input_stream);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void AudioInput::close_device() {
|
void AudioInput::close_device() {
|
||||||
lock_guard lock(this->input_stream_lock);
|
lock_guard lock(this->input_stream_lock);
|
||||||
if(this->input_stream) {
|
if(this->input_stream) {
|
||||||
/* TODO: Test for errors */
|
/* TODO: Test for errors */
|
||||||
Pa_StopStream(this->input_stream);
|
Pa_AbortStream(this->input_stream);
|
||||||
Pa_CloseStream(this->input_stream);
|
Pa_CloseStream(this->input_stream);
|
||||||
|
|
||||||
this->input_stream = nullptr;
|
this->input_stream = nullptr;
|
||||||
|
@ -99,7 +99,7 @@ namespace tc {
|
|||||||
struct {
|
struct {
|
||||||
pow_state::value state;
|
pow_state::value state;
|
||||||
|
|
||||||
uint32_t client_build_timestamp = 173265950 /* TS3 */; /* needs to be lower than 173265950 for old stuff, else new protocol */
|
uint32_t client_ts3_build_timestamp = 173265950 /* TS3 */; /* needs to be lower than 173265950 for old stuff, else new protocol */
|
||||||
uint8_t client_control_data[4] = {0,0,0,0};
|
uint8_t client_control_data[4] = {0,0,0,0};
|
||||||
uint8_t server_control_data[16] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
|
uint8_t server_control_data[16] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
|
||||||
uint8_t server_data[100];
|
uint8_t server_data[100];
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
#include "ServerConnection.h"
|
#include "ServerConnection.h"
|
||||||
#include "Socket.h"
|
#include "Socket.h"
|
||||||
#include "../logger.h"
|
#include "../logger.h"
|
||||||
|
#include "Error.h"
|
||||||
|
|
||||||
#include <misc/endianness.h>
|
#include <misc/endianness.h>
|
||||||
#include <protocol/buffers.h>
|
#include <protocol/buffers.h>
|
||||||
@ -61,18 +62,30 @@ void ProtocolHandler::handlePacketInit(const std::shared_ptr<ts::protocol::Serve
|
|||||||
this->acknowledge_handler.reset(); /* we don't need an ack anymore for our init packet */
|
this->acknowledge_handler.reset(); /* we don't need an ack anymore for our init packet */
|
||||||
|
|
||||||
if(packet_state == pow_state::COOKIE_SET) {
|
if(packet_state == pow_state::COOKIE_SET) {
|
||||||
if(data.length() != 21)
|
if(data.length() != 21 && data.length() != 5) {
|
||||||
|
log_trace(category::connection, tr("[POW] Dropping cookie packet (got {} bytes expect 21 or 5 bytes)"), data.length());
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
memcpy(&this->pow.server_control_data[0], &data[1], 16);
|
if(data.length() == 21) {
|
||||||
//TODO test client data reserved bytes
|
memcpy(&this->pow.server_control_data[0], &data[1], 16);
|
||||||
|
//TODO test client data reserved bytes
|
||||||
|
} else {
|
||||||
|
auto errc = ntohl(*(uint32_t*) &data[1]);
|
||||||
|
auto err = ts::findError(errc);
|
||||||
|
|
||||||
|
log_error(category::connection, tr("[POW] Received error code: {:x} ({})"), errc, err.message);
|
||||||
|
this->handle->call_connect_result.call(this->handle->errors.register_error(tr("received error: ") + to_string(errc) + " (" + err.message + ")"), true);
|
||||||
|
this->handle->close_connection();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
/* send puzzle get request */
|
/* send puzzle get request */
|
||||||
{
|
{
|
||||||
this->pow.state = pow_state::PUZZLE_SET; /* next expected packet state */
|
this->pow.state = pow_state::PUZZLE_SET; /* next expected packet state */
|
||||||
|
|
||||||
uint8_t response_buffer[25];
|
uint8_t response_buffer[25];
|
||||||
le2be32(this->pow.client_build_timestamp, &response_buffer[0]);
|
le2be32(this->pow.client_ts3_build_timestamp, &response_buffer[0]);
|
||||||
response_buffer[4] = pow_state::PUZZLE_GET;
|
response_buffer[4] = pow_state::PUZZLE_GET;
|
||||||
|
|
||||||
memcpy(&response_buffer[5], this->pow.server_control_data, 16);
|
memcpy(&response_buffer[5], this->pow.server_control_data, 16);
|
||||||
@ -86,8 +99,11 @@ void ProtocolHandler::handlePacketInit(const std::shared_ptr<ts::protocol::Serve
|
|||||||
|
|
||||||
return;
|
return;
|
||||||
} else if(packet_state == pow_state::PUZZLE_SET) {
|
} else if(packet_state == pow_state::PUZZLE_SET) {
|
||||||
if(data.length() != 1 + 64 * 2 + 4 + 100)
|
constexpr auto expected_bytes = 1 + 64 * 2 + 4 + 100;
|
||||||
|
if(data.length() != 1 + 64 * 2 + 4 + 100) {
|
||||||
|
log_trace(category::connection, tr("[POW] Dropping puzzle packet (got {} bytes expect {} bytes)"), data.length(), expected_bytes);
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
mp_int point_x{}, point_n{}, result{};
|
mp_int point_x{}, point_n{}, result{};
|
||||||
if(mp_read_unsigned_bin(&point_x, (u_char*) &data[1], 64) < 0)
|
if(mp_read_unsigned_bin(&point_x, (u_char*) &data[1], 64) < 0)
|
||||||
@ -110,7 +126,7 @@ void ProtocolHandler::handlePacketInit(const std::shared_ptr<ts::protocol::Serve
|
|||||||
size_t response_buffer_length = 301 + command.size();
|
size_t response_buffer_length = 301 + command.size();
|
||||||
auto response_buffer = buffer::allocate_buffer(response_buffer_length);
|
auto response_buffer = buffer::allocate_buffer(response_buffer_length);
|
||||||
|
|
||||||
le2be32(this->pow.client_build_timestamp, &response_buffer[0]);
|
le2be32(this->pow.client_ts3_build_timestamp, &response_buffer[0]);
|
||||||
response_buffer[4] = pow_state::PUZZLE_SOLVE;
|
response_buffer[4] = pow_state::PUZZLE_SOLVE;
|
||||||
memcpy(&response_buffer[5], &data[1], 64 * 2 + 100 + 4);
|
memcpy(&response_buffer[5], &data[1], 64 * 2 + 100 + 4);
|
||||||
|
|
||||||
@ -132,13 +148,15 @@ void ProtocolHandler::handlePacketInit(const std::shared_ptr<ts::protocol::Serve
|
|||||||
void ProtocolHandler::pow_send_cookie_get() {
|
void ProtocolHandler::pow_send_cookie_get() {
|
||||||
this->pow.state = pow_state::COOKIE_SET; /* next expected packet state */
|
this->pow.state = pow_state::COOKIE_SET; /* next expected packet state */
|
||||||
|
|
||||||
if((this->pow.client_control_data[0] & 0x01) == 0) {
|
if((this->pow.client_control_data[0] & 0x01U) == 0) {
|
||||||
generate_random(this->pow.client_control_data, 4);
|
generate_random(this->pow.client_control_data, 4);
|
||||||
this->pow.client_control_data[0] |= 0x01;
|
this->pow.client_control_data[0] |= 0x01U;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this->pow.client_ts3_build_timestamp = floor < seconds > ((system_clock::now() - hours{24}).time_since_epoch()).count();
|
||||||
|
|
||||||
uint8_t response_buffer[21];
|
uint8_t response_buffer[21];
|
||||||
le2be32(this->pow.client_build_timestamp, &response_buffer[0]);
|
le2be32(this->pow.client_ts3_build_timestamp, &response_buffer[0]);
|
||||||
response_buffer[4] = pow_state::COOKIE_GET;
|
response_buffer[4] = pow_state::COOKIE_GET;
|
||||||
memset(&response_buffer[5], 0, 4);
|
memset(&response_buffer[5], 0, 4);
|
||||||
memcpy(&response_buffer[9], &this->pow.client_control_data, 4);
|
memcpy(&response_buffer[9], &this->pow.client_control_data, 4);
|
||||||
|
@ -11,7 +11,7 @@ import {NativeServerConnection} from "teaclient_connection";
|
|||||||
//remote_host: "54.36.232.11", /* the beast */
|
//remote_host: "54.36.232.11", /* the beast */
|
||||||
//remote_host: "79.133.54.207", /* gommehd.net */
|
//remote_host: "79.133.54.207", /* gommehd.net */
|
||||||
|
|
||||||
const target_address = "localhost";
|
const target_address = "51.68.181.92";
|
||||||
const { host, port } = {
|
const { host, port } = {
|
||||||
host: target_address.split(":")[0],
|
host: target_address.split(":")[0],
|
||||||
port: target_address.split(":").length > 1 ? parseInt(target_address.split(":")[1]) : 9987
|
port: target_address.split(":").length > 1 ? parseInt(target_address.split(":")[1]) : 9987
|
||||||
@ -117,30 +117,8 @@ class Bot {
|
|||||||
|
|
||||||
|
|
||||||
const bot_list = [];
|
const bot_list = [];
|
||||||
for(let index = 0; index < 20; index++) {
|
for(let index = 0; index < 1; index++) {
|
||||||
const bot = new Bot();
|
const bot = new Bot();
|
||||||
bot_list.push(bot);
|
bot_list.push(bot);
|
||||||
bot.connect();
|
bot.connect();
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
import * as net from "net";
|
|
||||||
import * as tls from "tls";
|
|
||||||
import * as https from "https";
|
|
||||||
process.env["NODE_TLS_REJECT_UNAUTHORIZED"] = 0;
|
|
||||||
|
|
||||||
const run = async () => {
|
|
||||||
try {
|
|
||||||
console.log("request");
|
|
||||||
const response = await new Promise((resolve, reject) => {
|
|
||||||
https.get("https://localhost:30303", resolve).on('error', reject);
|
|
||||||
});
|
|
||||||
|
|
||||||
console.log("done");
|
|
||||||
//console.log("response: %o", response);
|
|
||||||
} catch(error) {
|
|
||||||
console.log("error: %o", error);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
setInterval(run, 10);
|
|
||||||
*/
|
|
Loading…
x
Reference in New Issue
Block a user