diff --git a/TeaDNS/CMakeLists.txt b/TeaDNS/CMakeLists.txt new file mode 100644 index 0000000..3f55ce6 --- /dev/null +++ b/TeaDNS/CMakeLists.txt @@ -0,0 +1,30 @@ +cmake_minimum_required(VERSION 3.15) +project(TeaWebDNS) + +set(CMAKE_CXX_STANDARD 17) + +#Setup the compiler (Cant be done within a function!) +if (MSVC) + set(CompilerFlags + CMAKE_C_FLAGS_DEBUG + CMAKE_C_FLAGS_MINSIZEREL + CMAKE_C_FLAGS_RELEASE + CMAKE_C_FLAGS_RELWITHDEBINFO + CMAKE_CXX_FLAGS_DEBUG + CMAKE_CXX_FLAGS_MINSIZEREL + CMAKE_CXX_FLAGS_RELEASE + CMAKE_CXX_FLAGS_RELWITHDEBINFO + ) + foreach(CompilerFlag ${CompilerFlags}) + string(REPLACE "/MD" "/MT" ${CompilerFlag} "${${CompilerFlag}}") + endforeach() +else() + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++17 -static-libgcc -static-libstdc++") +endif() + +# Require libevent +find_package(Libevent REQUIRED) +include_directories(${LIBEVENT_INCLUDE_DIRS}) + +add_executable(TeaWebDNS main.cpp src/server.cpp) +target_link_libraries(TeaWebDNS ${LIBEVENT_STATIC_LIBRARIES} pthread) \ No newline at end of file diff --git a/TeaDNS/server/CMakeLists.txt b/TeaDNS/server/CMakeLists.txt new file mode 100644 index 0000000..065e5d9 --- /dev/null +++ b/TeaDNS/server/CMakeLists.txt @@ -0,0 +1,32 @@ +cmake_minimum_required(VERSION 3.15) +project(TeaWebDNS) + +set(CMAKE_CXX_STANDARD 17) + +#Setup the compiler (Cant be done within a function!) +if (MSVC) + set(CompilerFlags + CMAKE_C_FLAGS_DEBUG + CMAKE_C_FLAGS_MINSIZEREL + CMAKE_C_FLAGS_RELEASE + CMAKE_C_FLAGS_RELWITHDEBINFO + CMAKE_CXX_FLAGS_DEBUG + CMAKE_CXX_FLAGS_MINSIZEREL + CMAKE_CXX_FLAGS_RELEASE + CMAKE_CXX_FLAGS_RELWITHDEBINFO + ) + foreach(CompilerFlag ${CompilerFlags}) + string(REPLACE "/MD" "/MT" ${CompilerFlag} "${${CompilerFlag}}") + endforeach() +else() + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++17 -static-libgcc -static-libstdc++") +endif() + +# Require libevent +find_package(Libevent REQUIRED) +include_directories(${LIBEVENT_INCLUDE_DIRS}) + +add_subdirectory(util) + +add_executable(TeaWebDNS main.cpp server/src/server.cpp server/src/handler.cpp) +target_link_libraries(TeaWebDNS ${LIBEVENT_STATIC_LIBRARIES} pthread teadns__parser) \ No newline at end of file diff --git a/TeaDNS/server/main.cpp b/TeaDNS/server/main.cpp new file mode 100644 index 0000000..b774e69 --- /dev/null +++ b/TeaDNS/server/main.cpp @@ -0,0 +1,38 @@ +#include +#include +#include "server/src/server.h" +#include "server/src/handler.h" + +using namespace ts::dns; + +std::vector bindings() { + std::vector result{}; + + { + sockaddr_in& any_v4{reinterpret_cast(result.emplace_back())}; + memset(&any_v4, 0, sizeof(sockaddr_in)); + + any_v4.sin_family = AF_INET; + any_v4.sin_port = htons(1222); //htons(53); + any_v4.sin_addr.s_addr = (1UL << 24U) | 127U; + } + + return result; +} + +int main() { + std::string error{}; + + auto handler = std::make_shared(); + WebDNSServer server{handler}; + + if(!server.start(bindings(), error)) { + for(auto& binding : server.bindings()) + std::cout << " - " << binding->error << "\n"; + std::cerr << "Failed to start server: " << error << "\n"; + return 1; + } + + std::this_thread::sleep_for(std::chrono::seconds{100}); + return 0; +} \ No newline at end of file diff --git a/TeaDNS/server/src/handler.cpp b/TeaDNS/server/src/handler.cpp new file mode 100644 index 0000000..39cab45 --- /dev/null +++ b/TeaDNS/server/src/handler.cpp @@ -0,0 +1,8 @@ +#include +#include "handler.h" +#include "net.h" + +void ts::dns::WebDNSHandler::handle_message(const sockaddr_storage &address, void *buffer, size_t size) { + + std::cout << "Received " << size << " bytes from " << net::to_string(address, true) << "\n"; +} diff --git a/TeaDNS/server/src/handler.h b/TeaDNS/server/src/handler.h new file mode 100644 index 0000000..4682217 --- /dev/null +++ b/TeaDNS/server/src/handler.h @@ -0,0 +1,17 @@ +#pragma once + +#include + +namespace ts::dns { + class DNSHandler { + public: + virtual void handle_message(const sockaddr_storage& /* address */, void* /* buffer */, size_t /* buffer size */) = 0; + }; + + class WebDNSHandler : public DNSHandler { + public: + void handle_message(const sockaddr_storage &storage, void *pVoid, size_t size) override; + + private: + }; +} \ No newline at end of file diff --git a/TeaDNS/server/src/net.h b/TeaDNS/server/src/net.h new file mode 100644 index 0000000..8e34145 --- /dev/null +++ b/TeaDNS/server/src/net.h @@ -0,0 +1,261 @@ +#pragma once + +#include +#include +#include +#include +#include + +#ifdef WIN32 + #include + #include + #include + #include +#else + #include + #include + #include + #include +#endif + +namespace net { + 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); + } + + inline uint16_t port(const sockaddr_storage& address) { + switch(address.ss_family) { + case AF_INET: + return htons(((sockaddr_in*) &address)->sin_port); + case AF_INET6: + return htons(((sockaddr_in6*) &address)->sin6_port); + default: + return 0; + } + } + + inline std::string to_string(const sockaddr_storage& address, bool port = true) { + switch(address.ss_family) { + case AF_INET: + return to_string(((sockaddr_in*) &address)->sin_addr) + (port ? ":" + std::to_string(htons(((sockaddr_in*) &address)->sin_port)) : ""); + case AF_INET6: + return to_string(((sockaddr_in6*) &address)->sin6_addr) + (port ? ":" + std::to_string(htons(((sockaddr_in6*) &address)->sin6_port)) : ""); + default: + return "unknown_type"; + } + } + + inline socklen_t address_size(const sockaddr_storage& address) { + switch (address.ss_family) { + case AF_INET: return sizeof(sockaddr_in); + case AF_INET6: return sizeof(sockaddr_in6); + default: return 0; + } + } + + inline bool address_equal(const sockaddr_storage& a, const sockaddr_storage& b) { + if(a.ss_family != b.ss_family) return false; + if(a.ss_family == AF_INET) return ((sockaddr_in*) &a)->sin_addr.s_addr == ((sockaddr_in*) &b)->sin_addr.s_addr; + else if(a.ss_family == AF_INET6) { +#ifdef WIN32 + return memcmp(((sockaddr_in6*) &a)->sin6_addr.u.Byte, ((sockaddr_in6*) &b)->sin6_addr.u.Byte, 16) == 0; +#else + return memcmp(((sockaddr_in6*) &a)->sin6_addr.__in6_u.__u6_addr8, ((sockaddr_in6*) &b)->sin6_addr.__in6_u.__u6_addr8, 16) == 0; +#endif + } + return false; + } + + inline bool address_equal_ranged(const sockaddr_storage& a, const sockaddr_storage& b, uint8_t range) { + if(a.ss_family != b.ss_family) return false; + if(a.ss_family == AF_INET) { + auto address_a = ((sockaddr_in*) &a)->sin_addr.s_addr; + auto address_b = ((sockaddr_in*) &b)->sin_addr.s_addr; + + if(range > 32) + range = 32; + + range = (uint8_t) (32 - range); + + address_a <<= range; + address_b <<= range; + + return address_a == address_b; + } else if(a.ss_family == AF_INET6) { +#ifdef WIN32 + throw std::runtime_error("not implemented"); + //FIXME: Implement me! +#elif defined(__x86_64__) && false + static_assert(sizeof(__int128) == 16); + auto address_a = (__int128) ((sockaddr_in6*) &a)->sin6_addr.__in6_u.__u6_addr32; + auto address_b = (__int128) ((sockaddr_in6*) &b)->sin6_addr.__in6_u.__u6_addr32; + + if(range > 128) + range = 128; + range = (uint8_t) (128 - range); + + address_a <<= range; + address_b <<= range; + + return address_a == address_b; +#else + static_assert(sizeof(uint64_t) == 8); + + if(range > 128) + range = 128; + range = (uint8_t) (128 - range); + + + auto address_ah = (uint64_t) (((sockaddr_in6*) &a)->sin6_addr.__in6_u.__u6_addr8 + 0); + auto address_al = (uint64_t) (((sockaddr_in6*) &a)->sin6_addr.__in6_u.__u6_addr8 + 8); + auto address_bh = (uint64_t) (((sockaddr_in6*) &b)->sin6_addr.__in6_u.__u6_addr8 + 0); + auto address_bl = (uint64_t) (((sockaddr_in6*) &b)->sin6_addr.__in6_u.__u6_addr8 + 8); + + if(range > 64) { + /* only lower counts */ + return (address_al << (range - 64)) == (address_bl << (range - 64)); + } else { + return address_al == address_bl &&(address_bh << (range - 64)) == (address_ah << (range - 64)); + } +#endif + } + return false; + } + + + inline bool is_ipv6(const std::string& str) { + sockaddr_in6 sa{}; + return inet_pton(AF_INET6, str.c_str(), &(sa.sin6_addr)) != 0; + } + + inline bool is_ipv4(const std::string& str) { + sockaddr_in sa{}; + return inet_pton(AF_INET, str.c_str(), &(sa.sin_addr)) != 0; + } + + inline bool is_anybind(sockaddr_storage& storage) { + if(storage.ss_family == AF_INET) { + auto data = (sockaddr_in*) &storage; + return data->sin_addr.s_addr == 0; + } else if(storage.ss_family == AF_INET6) { + auto data = (sockaddr_in6*) &storage; +#ifdef WIN32 + auto& blocks = data->sin6_addr.u.Word; + return + blocks[0] == 0 && + blocks[1] == 0 && + blocks[2] == 0 && + blocks[3] == 0 && + blocks[4] == 0 && + blocks[5] == 0 && + blocks[6] == 0 && + blocks[7] == 0; +#else + auto& blocks = data->sin6_addr.__in6_u.__u6_addr32; + return blocks[0] == 0 && blocks[1] == 0 && blocks[2] == 0 && blocks[3] == 0; +#endif + } + return false; + } + + inline bool resolve_address(const std::string& address, sockaddr_storage& result) { + if(is_ipv4(address)) { + sockaddr_in s{}; + s.sin_port = 0; + s.sin_family = AF_INET; + + auto record = gethostbyname(address.c_str()); + if(!record) + return false; + s.sin_addr.s_addr = ((in_addr*) record->h_addr)->s_addr; + + memcpy(&result, &s, sizeof(s)); + return true; + } else if(is_ipv6(address)) { + sockaddr_in6 s{}; + s.sin6_family = AF_INET6; + s.sin6_port = 0; + s.sin6_flowinfo = 0; + s.sin6_scope_id = 0; + +#ifdef WIN32 + auto record = gethostbyname(address.c_str()); +#else + auto record = gethostbyname2(address.c_str(), AF_INET6); +#endif + if(!record) return false; + s.sin6_addr = *(in6_addr*) record->h_addr; + + memcpy(&result, &s, sizeof(s)); + return true; + } else if(address == "[::]" || address == "::") { + sockaddr_in6 s{}; + s.sin6_family = AF_INET6; + s.sin6_port = 0; + s.sin6_flowinfo = 0; + s.sin6_scope_id = 0; + + memcpy(&s.sin6_addr, &in6addr_any, sizeof(in6_addr)); + memcpy(&result, &s, sizeof(s)); + return true; + } + + return false; + } + + namespace helpers { + inline void strip(std::string& message) { + while(!message.empty()) { + if(message[0] == ' ') + message = message.substr(1); + else if(message[message.length() - 1] == ' ') + message = message.substr(0, message.length() - 1); + else break; + } + } + + inline std::deque split(const std::string& message, char delimiter) { + std::deque result{}; + size_t found, index = 0; + do { + found = message.find(delimiter, index); + result.push_back(message.substr(index, found - index)); + index = found + 1; + } while(index != 0); + return result; + } + } + + inline std::vector> resolve_bindings(const std::string& bindings, uint16_t port) { + auto binding_list = helpers::split(bindings, ','); + std::vector> result; + result.reserve(binding_list.size()); + + for(auto& address : binding_list) { + helpers::strip(address); + + sockaddr_storage element{}; + memset(&element, 0, sizeof(element)); + if(!resolve_address(address, element)) { + result.emplace_back(address, element, "address resolve failed"); + continue; + } + + if(element.ss_family == AF_INET) { + ((sockaddr_in*) &element)->sin_port = htons(port); + } else if(element.ss_family == AF_INET6) { + ((sockaddr_in6*) &element)->sin6_port = htons(port); + } + result.emplace_back(address, element, ""); + } + return result; + } +} \ No newline at end of file diff --git a/TeaDNS/server/src/server.cpp b/TeaDNS/server/src/server.cpp new file mode 100644 index 0000000..ec456d1 --- /dev/null +++ b/TeaDNS/server/src/server.cpp @@ -0,0 +1,254 @@ +#include "./server.h" +#include "./handler.h" +#include "./net.h" + +#include +#include +#include +#include +#include +#include +#include + +using namespace ts::dns; + +WebDNSServer::~WebDNSServer() { + this->stop(); +} + +bool WebDNSServer::start(const std::vector &bindings, std::string &error) { + size_t successful_binds{0}; + + std::lock_guard lock{this->bind_lock}; + if(this->started) { + error = "server already bound"; + return false; + } + + this->event_base = event_base_new(); + if(!this->event_base) { + error = "failed to spawn event base"; + goto error_exit; + } + + this->event_base_ticker = evtimer_new(this->event_base, [](auto, auto, void* server){ + reinterpret_cast(server)->event_cb_timer(); + }, this); + if(!this->event_base_ticker) { + error = "failed to spawn heartbeat event"; + goto error_exit; + } + + this->_bindings.resize(bindings.size()); + for(size_t index = 0; index < bindings.size(); index++) { + auto& binding = *(this->_bindings[index] = std::make_unique()); + binding.server = this; + memcpy(&binding.address, &bindings[index], sizeof(sockaddr_storage)); + + if(!this->bind(binding, error)) + binding.error = error; + else + successful_binds++; + } + + if(!successful_binds) { + error = "failed to bind to any address"; + goto error_exit; + } + + { + timeval timeout{10, 0}; + event_add(this->event_base_ticker, &timeout); + } + + this->event_base_executor = std::thread(std::bind(&WebDNSServer::event_executor, this)); + this->started = true; + return true; + + error_exit: + if(this->event_base_ticker) { + event_del_block(this->event_base_ticker); + event_free(this->event_base_ticker); + this->event_base_ticker = nullptr; + } + + if(this->event_base) { + event_base_free(this->event_base); + this->event_base = nullptr; + } + return false; +} + +void WebDNSServer::stop() { + std::unique_lock lock{this->bind_lock}; + if(!this->started) + return; + + this->started = false; + assert(this->event_base); //Must be set else the started flag was invalid + assert(this->event_base_ticker); //Must be set else the started flag was invalid + + event_base_loopexit(this->event_base, nullptr); + + timeval timeout{0, 0}; + event_add(this->event_base_ticker, &timeout); + + //TODO: timeout? + lock.unlock(); + this->event_base_executor.join(); + lock.lock(); + + for(auto& binding : this->bindings()) + this->unbind(*binding); + + event_free(this->event_base_ticker); + this->event_base_ticker = nullptr; +} + +bool WebDNSServer::bind(Binding &binding, std::string &error) { + binding.socket = socket(binding.address.ss_family, SOCK_DGRAM, 0); + if(binding.socket < 2) { + error = "failed to create socket"; + return false; + } + int enable{1}; + + if(binding.address.ss_family == AF_INET6) { + if(setsockopt(binding.socket, IPPROTO_IPV6, IPV6_RECVPKTINFO, &enable, sizeof(enable)) < 0) { + error = "failed to enable packet info (v6) (" + std::to_string(errno) + "/" + strerror(errno) + ")"; + goto cleanup_exit; + } + if(setsockopt(binding.socket, IPPROTO_IPV6, IPV6_V6ONLY, &enable, sizeof(enable)) < 0) { + error = "failed to enable ip v6 only (" + std::to_string(errno) + "/" + strerror(errno) + ")"; + goto cleanup_exit; + } + } else { + if(setsockopt(binding.socket, IPPROTO_IP, IP_PKTINFO, &enable, sizeof(enable)) < 0) { + error = "failed to enable packet info (" + std::to_string(errno) + "/" + strerror(errno) + ")"; + goto cleanup_exit; + } + } + + if(::bind(binding.socket, (const sockaddr*) &binding.address, sizeof(binding.address)) < 0) { + error = "failed to bind: " + std::to_string(errno) + "/" + strerror(errno); + goto cleanup_exit; + } + + if(fcntl(binding.socket, F_SETFL, fcntl(binding.socket, F_GETFL, 0) | O_NONBLOCK) < 0) { + error = "failed to enable noblock"; + goto cleanup_exit; + } + +#ifdef WIN32 + u_long enabled = 0; + auto non_block_rs = ioctlsocket(binding.socket, FIONBIO, &enabled); + if (non_block_rs != NO_ERROR) { + error = "failed to enable nonblock"; + goto cleanup_exit; + } +#endif + + binding.read_event = event_new(this->event_base, binding.socket, EV_READ | EV_PERSIST, &WebDNSServer::event_cb_read, &binding); + if(!binding.read_event) { + error = "failed to create read event"; + goto cleanup_exit; + } + + binding.write_event = event_new(this->event_base, binding.socket, EV_WRITE, &WebDNSServer::event_cb_write, &binding); + if(!binding.read_event) { + error = "failed to create write event"; + goto cleanup_exit; + } + + event_add(binding.read_event, nullptr); + return true; + + cleanup_exit: + if(binding.read_event) { + event_del_noblock(binding.read_event); + event_free(binding.read_event); + binding.read_event = nullptr; + } + + if(binding.write_event) { + event_del_noblock(binding.write_event); + event_free(binding.write_event); + binding.write_event = nullptr; + } + return false; +} + +void WebDNSServer::unbind(Binding &binding) { + if(binding.read_event) { + event_del_block(binding.read_event); + event_free(binding.read_event); + binding.read_event = nullptr; + } + + if(binding.write_event) { + event_del_block(binding.write_event); + event_free(binding.write_event); + binding.write_event = nullptr; + } + + if(binding.socket) { + ::shutdown(binding.socket, SHUT_RDWR); + ::close(binding.socket); + + binding.socket = 0; + } +} + +void WebDNSServer::event_executor() { + do { + event_base_dispatch(this->event_base); + } while(event_base_got_exit(this->event_base)); +} + +void WebDNSServer::event_cb_timer() { + std::lock_guard lock{this->bind_lock}; + if(this->started) { + timeval timeout{10, 0}; + event_add(this->event_base_ticker, &timeout); + } +} + +void WebDNSServer::event_cb_read(evutil_socket_t fd, short, void *ptr_binding) { + auto binding = reinterpret_cast(ptr_binding); + auto server = binding->server; + + sockaddr_storage source_address{}; + socklen_t source_address_length{0}; + + ssize_t read_length{-1}; + size_t buffer_length = 1600; /* IPv6 MTU is ~1.5k */ + char buffer[1600]; + + size_t read_count = 0; + while(true) { //TODO: Some kind of timeout + source_address_length = sizeof(sockaddr_storage); + read_length = recvfrom(fd, (char*) buffer, buffer_length, MSG_DONTWAIT, (struct sockaddr*) &source_address, &source_address_length); + if(read_length <= 0) { + if(errno == EAGAIN) + break; + + std::cerr << "Failed to receive data: " << errno << "/" << strerror(errno) << "\n"; + break; /* this should never happen! */ + } + + read_count++; + //buffer, (size_t) read_length + + auto handler = server->handler; + if(handler) + handler->handle_message(source_address, buffer, read_length); + else + std::cerr << "Dropping " << read_length << " bytes from " << net::to_string(source_address, true) << " because we've no handler\n"; + } +} + +void WebDNSServer::event_cb_write(evutil_socket_t fd, short, void *ptr_binding) { + auto binding = reinterpret_cast(ptr_binding); + auto server = binding->server; + +} \ No newline at end of file diff --git a/TeaDNS/server/src/server.h b/TeaDNS/server/src/server.h new file mode 100644 index 0000000..d15ab9e --- /dev/null +++ b/TeaDNS/server/src/server.h @@ -0,0 +1,54 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include + +namespace ts::dns { + class DNSHandler; + class WebDNSServer { + public: + struct Binding { + WebDNSServer* server{nullptr}; + + sockaddr_storage address{}; + int socket{0}; + std::string error{}; + + inline bool active() const { return this->socket != 0; } + + event* read_event{nullptr}; + event* write_event{nullptr}; + }; + + WebDNSServer(const std::shared_ptr& handler) : handler{handler} {}; + virtual ~WebDNSServer(); + + bool start(const std::vector& /* bindings */, std::string& /* error */); + void stop(); + + const std::vector>& bindings() const { return this->_bindings; } + private: + std::shared_ptr handler; + + std::mutex bind_lock{}; + std::vector> _bindings; + bool started{false}; + + std::thread event_base_executor{}; + struct event* event_base_ticker{nullptr}; + struct event_base* event_base{nullptr}; + + bool bind(Binding& /* binding */, std::string& /* error */); + void unbind(Binding& /* binding */); + + void event_cb_timer(); + static void event_cb_read(evutil_socket_t fd, short, void *binding); + static void event_cb_write(evutil_socket_t fd, short, void *binding); + void event_executor(); + }; +} \ No newline at end of file diff --git a/TeaDNS/util/CMakeLists.txt b/TeaDNS/util/CMakeLists.txt new file mode 100644 index 0000000..32215ed --- /dev/null +++ b/TeaDNS/util/CMakeLists.txt @@ -0,0 +1,5 @@ +project(TeaDNS-Parser) + +add_library(teadns__parser INTERFACE) +target_sources(teadns__parser INTERFACE src/response.cpp src/types.cpp) +target_include_directories(teadns__parser INTERFACE include) \ No newline at end of file diff --git a/TeaDNS/util/include/teadns/builder.h b/TeaDNS/util/include/teadns/builder.h new file mode 100644 index 0000000..25cbb6d --- /dev/null +++ b/TeaDNS/util/include/teadns/builder.h @@ -0,0 +1,8 @@ +// +// Created by wolverindev on 21.11.19. +// + +#ifndef TEAWEBDNS_BUILDER_H +#define TEAWEBDNS_BUILDER_H + +#endif //TEAWEBDNS_BUILDER_H diff --git a/TeaDNS/util/include/teadns/parser.h b/TeaDNS/util/include/teadns/parser.h new file mode 100644 index 0000000..9f51509 --- /dev/null +++ b/TeaDNS/util/include/teadns/parser.h @@ -0,0 +1,254 @@ +#pragma once + +#include +#include +#include +#include + +#ifdef WIN32 + #include + #include + #include +#else + #include +#endif + +#include "./types.h" + +struct in6_addr; +namespace tc::dns { + namespace response { + class DNSHeader; + class DNSQuery; + class DNSResourceRecords; + } + + class DNSResponse; + struct DNSResponseData; + + struct DNSResponseData { +#ifdef WIN32 + bool wide_string{false}; + DNS_QUERY_RESULT data{}; +#else + uint8_t* buffer{nullptr}; + size_t length{0}; + + std::string parse_dns_dn(std::string& /* error */, size_t& /* index */, bool /* compression allowed */); +#endif + + ~DNSResponseData(); + }; + + class DNSResponse { + friend class Resolver; + public: + typedef std::vector> rr_list_t; + typedef std::vector> q_list_t; + + DNSResponse(const DNSResponse&) = delete; + DNSResponse(DNSResponse&&) = delete; + + bool parse(std::string& /* error */); +#ifndef WIN32 + [[nodiscard]] inline const std::string why_bogus() const { return this->bogus; } + + [[nodiscard]] inline const uint8_t* packet_data() const { return this->data->buffer; } + [[nodiscard]] inline size_t packet_length() const { return this->data->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; +#endif + [[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: +#ifndef WIN32 + DNSResponse(uint8_t /* secure state */, const char* /* bogus */, void* /* packet */, size_t /* length */); + + std::shared_ptr parse_rr(std::string& /* error */, size_t& index, bool /* compression allowed dn */); + + std::string bogus; + uint8_t secure_state{0}; +#else + DNSResponse(std::shared_ptr); +#endif + std::shared_ptr data{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; + }; + + namespace response { + #ifndef WIN32 + 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}; + }; + #endif + + 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 { +#ifdef WIN32 + return std::string{this->nrecord->pName}; +#else + return this->name; +#endif + } + [[nodiscard]] inline rrtype::value atype() const { +#ifdef WIN32 + return static_cast(this->nrecord->wType); +#else + return this->type; +#endif + } + [[nodiscard]] inline rrclass::value aclass() const { +#ifdef WIN32 + return static_cast(1); +#else + return this->klass; +#endif + } + [[nodiscard]] inline uint16_t attl() const { +#ifdef WIN32 + return (uint16_t) this->nrecord->dwTtl; +#else + return this->ttl; +#endif + } + + #ifndef WIN32 + [[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; } + #else + [[nodiscard]] inline PDNS_RECORDA native_record() const { return this->nrecord; } + [[nodiscard]] bool is_wide_string() const; + #endif + + [[nodiscard]] inline std::shared_ptr dns_data() const { + return this->data; + } + + template + [[nodiscard]] inline T parse() const { + if(T::type != this->atype()) + throw std::logic_error{"parser type mismatch"}; + return T{this}; + } + private: + std::shared_ptr data{nullptr}; + + #ifdef WIN32 + DNSResourceRecords(std::shared_ptr, PDNS_RECORDA); + + PDNS_RECORDA nrecord{nullptr}; + #else + DNSResourceRecords(std::shared_ptr, size_t, size_t, uint32_t, std::string , rrtype::value, rrclass::value); + + size_t offset{0}; + size_t length{0}; + + uint32_t ttl; + + std::string name; + rrtype::value type; + rrclass::value klass; + #endif + }; + + 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]] bool is_valid(); + [[nodiscard]] std::string address_string(); + + [[nodiscard]] in_addr address(); + ); + + define_parser(AAAA, base, + [[nodiscard]] bool is_valid(); + [[nodiscard]] std::string address_string(); + [[nodiscard]] in6_addr address(); + ); + + define_parser(SRV, base, + [[nodiscard]] bool is_valid(); + + [[nodiscard]] uint16_t priority(); + [[nodiscard]] uint16_t weight(); + [[nodiscard]] uint16_t target_port(); + [[nodiscard]] std::string target_hostname(); + ); + + define_parser(CNAME, named_base); + }; + } +} \ No newline at end of file diff --git a/TeaDNS/util/include/teadns/types.h b/TeaDNS/util/include/teadns/types.h new file mode 100644 index 0000000..7813ddc --- /dev/null +++ b/TeaDNS/util/include/teadns/types.h @@ -0,0 +1,138 @@ +#pragma once + +#include +#include + +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 names; + + static const char* name(const value& value) { + if(names.count(value) > 0) + return names.at(value); + return "unknown"; + } + }; + + struct rrclass { +#ifdef IN + #undef IN +#endif + + enum value : uint32_t { + IN = 1, /* Internet */ + CH = 3, /* Chaos */ + HS = 4, /* Hesiod */ + + QCLASS_NONE = 254, + QCLASS_ANY = 255, + }; + + static std::map names; + + static const char* name(const value& value) { + if(names.count(value) > 0) + return names.at(value); + return "unknown"; + } + }; +} \ No newline at end of file diff --git a/TeaDNS/util/src/builder.cpp b/TeaDNS/util/src/builder.cpp new file mode 100644 index 0000000..c704f55 --- /dev/null +++ b/TeaDNS/util/src/builder.cpp @@ -0,0 +1,4 @@ +// +// Created by wolverindev on 21.11.19. +// + diff --git a/TeaDNS/util/src/parser.cpp b/TeaDNS/util/src/parser.cpp new file mode 100644 index 0000000..2a96623 --- /dev/null +++ b/TeaDNS/util/src/parser.cpp @@ -0,0 +1,403 @@ +#include "teadns/parser.h" + +#include +#include +#include + +#ifdef WIN32 + #include + #include + #include + #include + #include +#else + #include + #include +#include + +#endif + +using namespace tc::dns; +using namespace tc::dns::response; +using namespace tc::dns::response::rrparser; + +thread_local std::vector visited_links; +std::string DNSResponseData::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(); + + std::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; +} + +DNSResponseData::~DNSResponseData() { + ::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->data = std::make_shared(); + this->data->buffer = (uint8_t*) malloc(size); + this->data->length = size; + + memcpy(this->data->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->data->parse_dns_dn(error, index, true); + if(!error.empty()) { + error = "failed to parse query " + std::to_string(idx) + " dn: " + error; // NOLINT(performance-inefficient-string-concatenation) + goto error_exit; + } + + if(index + 4 > this->packet_length()) { + error = "truncated data for query " + std::to_string(index); + goto error_exit; + } + + auto type = (rrtype::value) ntohs(*(uint16_t*) (this->data->buffer + index)); + index += 2; + + auto klass = (rrclass::value) ntohs(*(uint16_t*) (this->data->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 " + std::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 " + std::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 " + std::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 DNSResponse::parse_rr(std::string &error, size_t &index, bool allow_compressed) { + auto dn = this->data->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->data->buffer + index)); + index += 2; + + auto klass = (rrclass::value) ntohs(*(uint16_t*) (this->data->buffer + index)); + index += 2; + + auto ttl = ntohl(*(uint32_t*) (this->data->buffer + index)); + index += 4; + + auto payload_length = ntohs(*(uint16_t*) (this->data->buffer + index)); + index += 2; + + if(index + payload_length > this->packet_length()) { + error = "truncated body"; + return nullptr; + } + + auto response = std::shared_ptr(new response::DNSResourceRecords{this->data, index, payload_length, ttl, dn, type, klass}); + index += payload_length; + return response; +} + + +#ifndef WIN32 +uint16_t DNSHeader::field(int index) const { + return ((uint16_t*) this->response->packet_data())[index]; +} + +DNSResourceRecords::DNSResourceRecords(std::shared_ptr 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->data = std::move(packet); +} + +const uint8_t* DNSResourceRecords::payload_data() const { + return this->data->buffer + this->offset; +} +#else +DNSResourceRecords::DNSResourceRecords(std::shared_ptr data, PDNS_RECORDA rdata) : nrecord{rdata}, data{std::move(data)} { } +bool DNSResourceRecords::is_wide_string() const { + return this->data->wide_string; +} +#endif + +bool A::is_valid() { +#ifdef WIN32 + return true; +#else + return this->handle->payload_length() == 4; +#endif +} + +in_addr A::address() { +#ifdef WIN32 + in_addr result{}; + result.S_un.S_addr = this->handle->native_record()->Data.A.IpAddress; + return result; +#else + //TODO: Attention: Unaligned access + return {*(uint32_t*) this->handle->payload_data()}; +#endif +} + +std::string A::address_string() { +#ifdef WIN32 + struct in_addr address = this->address(); + char buffer[17]; + RtlIpv4AddressToStringA(&address, buffer); + return std::string{buffer}; +#else + auto _1 = this->handle->payload_data()[0], + _2 = this->handle->payload_data()[1], + _3 = this->handle->payload_data()[2], + _4 = this->handle->payload_data()[3]; + return std::to_string(_1) + "." + std::to_string(_2) + "." + std::to_string(_3) + "." + std::to_string(_4); +#endif +} + +//---------------- AAAA +#ifdef WIN32 +bool AAAA::is_valid() { + return true; +} + +std::string AAAA::address_string() { + struct in6_addr address = this->address(); + char buffer[47]; + RtlIpv6AddressToStringA(&address, buffer); //Supported for Win7 as well and not only above 8.1 like inet_ntop + return std::string{buffer}; +} + +in6_addr AAAA::address() { + in6_addr result{}; + memcpy(result.u.Byte, this->handle->native_record()->Data.AAAA.Ip6Address.IP6Byte, 16); + return result; +} +#else +bool AAAA::is_valid() { + return this->handle->payload_length() == 16; +} + +std::string AAAA::address_string() { + auto address = this->address(); + + char buffer[INET6_ADDRSTRLEN]; + if(!inet_ntop(AF_INET6, (void*) &address, buffer, INET6_ADDRSTRLEN)) return ""; + return std::string(buffer); +} + +in6_addr AAAA::address() { + return { + .__in6_u = { + .__u6_addr32 = { + //TODO: Attention unaligned memory access + ((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] + } + } + }; +} +#endif + +//---------------- SRV +#ifdef WIN32 +bool SRV::is_valid() { return true; } +std::string SRV::target_hostname() { + if(this->handle->is_wide_string()) { + auto result = std::wstring{ ((PDNS_RECORDW) this->handle->native_record())->Data.Srv.pNameTarget }; + return std::string{result.begin(), result.end()}; + } else { + return std::string{ this->handle->native_record()->Data.Srv.pNameTarget }; + } +} +uint16_t SRV::priority() { return this->handle->native_record()->Data.SRV.wPriority; } +uint16_t SRV::weight() { return this->handle->native_record()->Data.SRV.wWeight; } +uint16_t SRV::target_port() { return this->handle->native_record()->Data.SRV.wPort; } +#else +bool SRV::is_valid() { + if(this->handle->payload_length() < 7) + return false; + size_t index = this->handle->payload_offset() + 6; + std::string error{}; + this->handle->dns_data()->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_data()->parse_dns_dn(error, index, true); +} + + +uint16_t SRV::priority() { return ntohs(((uint16_t*) this->handle->payload_data())[0]); } +uint16_t SRV::weight() { return ntohs(((uint16_t*) this->handle->payload_data())[1]); } +uint16_t SRV::target_port() { return ntohs(((uint16_t*) this->handle->payload_data())[2]); } +#endif + +//---------------- All types with a name +bool named_base::is_valid() { +#ifdef WIN32 + return true; +#else + size_t index = this->handle->payload_offset(); + std::string error{}; + this->handle->dns_data()->parse_dns_dn(error, index, true); + return error.empty(); +#endif +} + +std::string named_base::name() { +#ifdef WIN32 + if(this->handle->is_wide_string()) { + auto result = std::wstring{ ((PDNS_RECORDW) this->handle->native_record())->Data.Cname.pNameHost }; + return std::string{result.begin(), result.end()}; + } else { + return std::string{ this->handle->native_record()->Data.Cname.pNameHost }; + } +#else + size_t index = this->handle->payload_offset(); + std::string error{}; + return this->handle->dns_data()->parse_dns_dn(error, index, true); +#endif +} diff --git a/TeaDNS/util/src/types.cpp b/TeaDNS/util/src/types.cpp new file mode 100644 index 0000000..1f6f36d --- /dev/null +++ b/TeaDNS/util/src/types.cpp @@ -0,0 +1,102 @@ +#include "teadns/types.h" + +std::map 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::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"}, +}; \ No newline at end of file diff --git a/TeaSpeak b/TeaSpeak index 44fe958..c4795f5 160000 --- a/TeaSpeak +++ b/TeaSpeak @@ -1 +1 @@ -Subproject commit 44fe958f312ecfa9c1612706f393a6c8ddd30c98 +Subproject commit c4795f5f74f87cdd386336f080cbcff15f0f5325