First commit
This commit is contained in:
commit
f4ab19ed1c
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
cmake-build-*/**
|
||||||
|
.idea/*
|
26
CMakeLists.txt
Normal file
26
CMakeLists.txt
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
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()
|
||||||
|
|
||||||
|
add_subdirectory(util)
|
||||||
|
add_subdirectory(server)
|
9
server/CMakeLists.txt
Normal file
9
server/CMakeLists.txt
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
project(TeaWebDNS)
|
||||||
|
|
||||||
|
# Require libevent
|
||||||
|
find_package(Libevent REQUIRED)
|
||||||
|
include_directories(${LIBEVENT_INCLUDE_DIRS})
|
||||||
|
message(${LIBEVENT_INCLUDE_DIRS})
|
||||||
|
|
||||||
|
add_executable(TeaWebDNS ./main.cpp src/server.cpp src/handler.cpp)
|
||||||
|
target_link_libraries(TeaWebDNS ${LIBEVENT_STATIC_LIBRARIES} pthread teadns__parser)
|
66
server/main.cpp
Normal file
66
server/main.cpp
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
#include <iostream>
|
||||||
|
#include <cstring>
|
||||||
|
#include <event2/thread.h>
|
||||||
|
#include "src/server.h"
|
||||||
|
#include "src/handler.h"
|
||||||
|
|
||||||
|
using namespace ts::dns;
|
||||||
|
|
||||||
|
std::vector<sockaddr_storage> bindings(uint16_t port) {
|
||||||
|
std::vector<sockaddr_storage> result{};
|
||||||
|
|
||||||
|
{
|
||||||
|
sockaddr_in& any_v4{reinterpret_cast<sockaddr_in&>(result.emplace_back())};
|
||||||
|
memset(&any_v4, 0, sizeof(sockaddr_in));
|
||||||
|
|
||||||
|
any_v4.sin_family = AF_INET;
|
||||||
|
any_v4.sin_port = htons(port); //htons(53);
|
||||||
|
any_v4.sin_addr.s_addr = (1UL << 24U) | 127U;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
__asm__(".symver getrandom,getrandom@GLIBC_2.2.5");
|
||||||
|
int main(int argc, char** argv) {
|
||||||
|
evthread_use_pthreads();
|
||||||
|
|
||||||
|
if(argc < 2) {
|
||||||
|
std::cerr << "Missing port. Default DNS port is 53\n";
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint16_t port{0};
|
||||||
|
try {
|
||||||
|
port = std::stoul(argv[1]);
|
||||||
|
} catch(std::exception& ex) {
|
||||||
|
std::cerr << "Failed to parse port\n";
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
std::string error{};
|
||||||
|
|
||||||
|
auto handler = std::make_shared<WebDNSHandler>();
|
||||||
|
DNSServer server{handler};
|
||||||
|
|
||||||
|
if(!server.start(bindings(port), error)) {
|
||||||
|
for(auto& binding : server.bindings())
|
||||||
|
std::cout << " - " << binding->error << "\n";
|
||||||
|
std::cerr << "Failed to start server: " << error << "\n";
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string line;
|
||||||
|
while(true) {
|
||||||
|
std::getline(std::cin, line);
|
||||||
|
if(line.empty())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if(line == "end" || line == "stop") {
|
||||||
|
std::cout << "Stopping server\n";
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
std::cerr << "Unknown command \"" << line << "\"\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
84
server/src/handler.cpp
Normal file
84
server/src/handler.cpp
Normal file
@ -0,0 +1,84 @@
|
|||||||
|
#include "./handler.h"
|
||||||
|
#include "./server.h"
|
||||||
|
#include "./net.h"
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
#include <teadns/parser.h>
|
||||||
|
#include <teadns/builder.h>
|
||||||
|
|
||||||
|
using namespace ts::dns;
|
||||||
|
using namespace ts::dns::builder;
|
||||||
|
|
||||||
|
void WebDNSHandler::handle_message(const std::shared_ptr<DNSServerBinding>& binding, const sockaddr_storage &address, void *buffer, size_t size) {
|
||||||
|
std::cout << "Received DNS request from " << net::to_string(address) << ":\n";
|
||||||
|
DNSParser parser{0, nullptr, buffer, size};
|
||||||
|
|
||||||
|
std::string error;
|
||||||
|
if(!parser.parse(error)) {
|
||||||
|
std::cout << " Failed to parse request: " << error << "\n";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::cout << " Query type: " << (uint32_t) parser.header().query_type() << "\n";
|
||||||
|
std::cout << " Queries (" << parser.queries().size() << "):\n";
|
||||||
|
for(auto& query : parser.queries())
|
||||||
|
std::cout << " " << query->qname() << " (" << rrclass::name(query->qclass()) << "::" << rrtype::name(query->qtype()) << ")\n";
|
||||||
|
std::cout << " Sending response.\n";
|
||||||
|
|
||||||
|
{
|
||||||
|
DNSBuilder response{};
|
||||||
|
|
||||||
|
response.header().id(*(uint16_t*) buffer);
|
||||||
|
response.header().set_answer(true);
|
||||||
|
response.header().set_response_code(rcode::NOERROR);
|
||||||
|
response.header().set_query_type(parser.header().query_type());
|
||||||
|
|
||||||
|
for(auto& query : parser.queries()) {
|
||||||
|
auto& q = response.push_query();
|
||||||
|
q.set_qclass(query->qclass());
|
||||||
|
q.set_qname(query->qname());
|
||||||
|
q.set_qtype(query->qtype());
|
||||||
|
|
||||||
|
if(query->qclass() == rrclass::IN && query->qtype() == rrtype::A) {
|
||||||
|
auto dn = query->qname();
|
||||||
|
uint8_t resp[4];
|
||||||
|
{
|
||||||
|
size_t index = 0;
|
||||||
|
size_t aindex = 0;
|
||||||
|
do {
|
||||||
|
auto found = dn.find('.', index);
|
||||||
|
auto length = index == -1 ? dn.length() - index : found - index;
|
||||||
|
|
||||||
|
try {
|
||||||
|
resp[aindex] = std::stoul(dn.substr(index, length));
|
||||||
|
} catch(std::exception& ex) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
aindex++;
|
||||||
|
index = found;
|
||||||
|
} while(index++ && aindex < 4);
|
||||||
|
if(aindex != 4)
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
std::cout << " Adding answer ";
|
||||||
|
for(size_t index = 0; index < 4; index++)
|
||||||
|
std::cout << (uint32_t) resp[index] << (index == 3 ? "\n" : ".");
|
||||||
|
|
||||||
|
auto& a = response.push_answer(query->qname());
|
||||||
|
a.set_class(query->qclass());
|
||||||
|
a.set_type(query->qtype());
|
||||||
|
a.set_ttl(120);
|
||||||
|
a.builder<rrbuilder::A>().set_address(resp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
char rbuffer[1024];
|
||||||
|
auto len = response.build(rbuffer, 1024, error);
|
||||||
|
if(!len) {
|
||||||
|
std::cout << " Failed to build response: " << error << "\n";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
binding->send(address, rbuffer, len);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
23
server/src/handler.h
Normal file
23
server/src/handler.h
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
|
||||||
|
namespace ts::dns {
|
||||||
|
class DNSServerBinding;
|
||||||
|
|
||||||
|
class DNSHandler {
|
||||||
|
public:
|
||||||
|
virtual void handle_message(const std::shared_ptr<DNSServerBinding>& /* binding */, const sockaddr_storage& /* address */, void* /* buffer */, size_t /* buffer size */) = 0;
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
class WebDNSHandler : public DNSHandler {
|
||||||
|
public:
|
||||||
|
void handle_message(const std::shared_ptr<DNSServerBinding>&, const sockaddr_storage &storage, void *pVoid, size_t size) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
};
|
||||||
|
}
|
261
server/src/net.h
Normal file
261
server/src/net.h
Normal file
@ -0,0 +1,261 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <cstring>
|
||||||
|
#include <deque>
|
||||||
|
#include <vector>
|
||||||
|
#include <tuple>
|
||||||
|
|
||||||
|
#ifdef WIN32
|
||||||
|
#include <WS2tcpip.h>
|
||||||
|
#include <WinSock2.h>
|
||||||
|
#include <Windows.h>
|
||||||
|
#include <in6addr.h>
|
||||||
|
#else
|
||||||
|
#include <arpa/inet.h>
|
||||||
|
#include <netinet/in.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#include <netdb.h>
|
||||||
|
#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<std::string> split(const std::string& message, char delimiter) {
|
||||||
|
std::deque<std::string> 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<std::tuple<std::string, sockaddr_storage, std::string>> resolve_bindings(const std::string& bindings, uint16_t port) {
|
||||||
|
auto binding_list = helpers::split(bindings, ',');
|
||||||
|
std::vector<std::tuple<std::string, sockaddr_storage, std::string>> 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;
|
||||||
|
}
|
||||||
|
}
|
321
server/src/server.cpp
Normal file
321
server/src/server.cpp
Normal file
@ -0,0 +1,321 @@
|
|||||||
|
#include "./server.h"
|
||||||
|
#include "./handler.h"
|
||||||
|
#include "./net.h"
|
||||||
|
|
||||||
|
#include <event.h>
|
||||||
|
#include <functional>
|
||||||
|
#include <cstring>
|
||||||
|
#include <iostream>
|
||||||
|
#include <cassert>
|
||||||
|
#include <zconf.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
|
||||||
|
using namespace ts::dns;
|
||||||
|
|
||||||
|
DNSServer::~DNSServer() {
|
||||||
|
this->stop();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DNSServer::start(const std::vector<sockaddr_storage> &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){
|
||||||
|
static_cast<DNSServer*>(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_shared<DNSServerBinding>();
|
||||||
|
binding->self = binding;
|
||||||
|
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(&DNSServer::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 DNSServer::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
|
||||||
|
|
||||||
|
|
||||||
|
for(auto& binding : this->bindings())
|
||||||
|
this->unbind(*binding);
|
||||||
|
|
||||||
|
event_base_loopexit(this->event_base, nullptr);
|
||||||
|
{
|
||||||
|
timeval timeout{0, 0};
|
||||||
|
event_add(this->event_base_ticker, &timeout);
|
||||||
|
}
|
||||||
|
|
||||||
|
lock.unlock();
|
||||||
|
this->event_base_executor.join();
|
||||||
|
lock.lock();
|
||||||
|
|
||||||
|
event_free(this->event_base_ticker);
|
||||||
|
this->event_base_ticker = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DNSServer::bind(DNSServerBinding &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, &DNSServer::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, &DNSServer::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 DNSServer::unbind(DNSServerBinding &binding) {
|
||||||
|
std::lock_guard lock{binding.io_lock};
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
auto head = binding.write_buffer_head;
|
||||||
|
while(head) {
|
||||||
|
auto tmp = head;
|
||||||
|
head = head->next;
|
||||||
|
free(tmp);
|
||||||
|
}
|
||||||
|
|
||||||
|
binding.write_buffer_head = nullptr;
|
||||||
|
binding.write_buffer_tail = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
binding.self = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
void DNSServerBinding::send(const sockaddr_storage &address, const void *payload, size_t size) {
|
||||||
|
std::lock_guard lock{this->io_lock};
|
||||||
|
if(!this->write_event)
|
||||||
|
return;
|
||||||
|
|
||||||
|
auto buffer = (char*) malloc(sizeof(BindingBuffer) + size);
|
||||||
|
auto bbuffer = (BindingBuffer*) buffer;
|
||||||
|
bbuffer->next = nullptr;
|
||||||
|
bbuffer->size = size;
|
||||||
|
memcpy(&bbuffer->target, &address, sizeof(address));
|
||||||
|
memcpy(buffer + sizeof(BindingBuffer), payload, size);
|
||||||
|
|
||||||
|
if(this->write_buffer_tail) {
|
||||||
|
assert(!this->write_buffer_tail->next);
|
||||||
|
this->write_buffer_tail->next = bbuffer;
|
||||||
|
this->write_buffer_tail = bbuffer;
|
||||||
|
} else {
|
||||||
|
assert(!this->write_buffer_head);
|
||||||
|
this->write_buffer_head = bbuffer;
|
||||||
|
this->write_buffer_tail = bbuffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
event_add(this->write_event, nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
void DNSServer::event_executor() {
|
||||||
|
do {
|
||||||
|
event_base_loop(this->event_base, EVLOOP_NO_EXIT_ON_EMPTY);
|
||||||
|
|
||||||
|
std::lock_guard lock{this->bind_lock};
|
||||||
|
if(!this->started)
|
||||||
|
return;
|
||||||
|
} while(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
void DNSServer::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 DNSServer::event_cb_read(evutil_socket_t fd, short, void *ptr_binding) {
|
||||||
|
auto binding = static_cast<DNSServerBinding*>(ptr_binding);
|
||||||
|
auto binding_ref = binding->self;
|
||||||
|
if(!binding_ref) return;
|
||||||
|
|
||||||
|
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 = binding->server->handler;
|
||||||
|
if(handler)
|
||||||
|
handler->handle_message(binding_ref, 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 DNSServer::event_cb_write(evutil_socket_t fd, short, void *ptr_binding) {
|
||||||
|
auto binding = static_cast<DNSServerBinding*>(ptr_binding);
|
||||||
|
auto binding_ref = binding->self;
|
||||||
|
if(!binding_ref) return;
|
||||||
|
|
||||||
|
ssize_t code;
|
||||||
|
DNSServerBinding::BindingBuffer* buffer{nullptr};
|
||||||
|
while(true) {
|
||||||
|
{
|
||||||
|
std::lock_guard lock{binding->io_lock};
|
||||||
|
buffer = binding->write_buffer_head;
|
||||||
|
if(!buffer)
|
||||||
|
break;
|
||||||
|
|
||||||
|
if(!(binding->write_buffer_head = binding->write_buffer_head->next))
|
||||||
|
binding->write_buffer_tail = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
code = sendto(fd, (char*) buffer + sizeof(DNSServerBinding::BindingBuffer), buffer->size, 0, (sockaddr*) &buffer->target, sizeof(buffer->target));
|
||||||
|
if(code <= 0)
|
||||||
|
std::cerr << "Failed to send DNS response to " << net::to_string(buffer->target, true) << ": " << errno << "/" << strerror(errno);
|
||||||
|
free(buffer);
|
||||||
|
}
|
||||||
|
}
|
73
server/src/server.h
Normal file
73
server/src/server.h
Normal file
@ -0,0 +1,73 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#include <utility>
|
||||||
|
#include <vector>
|
||||||
|
#include <string>
|
||||||
|
#include <mutex>
|
||||||
|
#include <thread>
|
||||||
|
#include <condition_variable>
|
||||||
|
#include <event.h>
|
||||||
|
|
||||||
|
namespace ts::dns {
|
||||||
|
class DNSServer;
|
||||||
|
class DNSServerBinding {
|
||||||
|
friend class DNSServer;
|
||||||
|
private:
|
||||||
|
struct BindingBuffer {
|
||||||
|
BindingBuffer* next{nullptr};
|
||||||
|
sockaddr_storage target{};
|
||||||
|
size_t size{0};
|
||||||
|
};
|
||||||
|
|
||||||
|
public:
|
||||||
|
DNSServer* server{nullptr};
|
||||||
|
std::shared_ptr<DNSServerBinding> self{nullptr};
|
||||||
|
|
||||||
|
sockaddr_storage address{};
|
||||||
|
int socket{0};
|
||||||
|
std::string error{};
|
||||||
|
|
||||||
|
inline bool active() const { return this->socket != 0; }
|
||||||
|
|
||||||
|
std::mutex io_lock{};
|
||||||
|
|
||||||
|
event* read_event{nullptr};
|
||||||
|
event* write_event{nullptr};
|
||||||
|
|
||||||
|
BindingBuffer* write_buffer_head{nullptr};
|
||||||
|
BindingBuffer* write_buffer_tail{nullptr};
|
||||||
|
|
||||||
|
void send(const sockaddr_storage& /* target */, const void* /* buffer */, size_t /* length */);
|
||||||
|
};
|
||||||
|
|
||||||
|
class DNSHandler;
|
||||||
|
class DNSServer {
|
||||||
|
public:
|
||||||
|
DNSServer(std::shared_ptr<DNSHandler> handler) : handler{std::move(handler)} {};
|
||||||
|
virtual ~DNSServer();
|
||||||
|
|
||||||
|
bool start(const std::vector<sockaddr_storage>& /* bindings */, std::string& /* error */);
|
||||||
|
void stop();
|
||||||
|
|
||||||
|
[[nodiscard]] const std::vector<std::shared_ptr<DNSServerBinding>>& bindings() const { return this->_bindings; }
|
||||||
|
private:
|
||||||
|
std::shared_ptr<DNSHandler> handler;
|
||||||
|
|
||||||
|
std::mutex bind_lock{};
|
||||||
|
std::vector<std::shared_ptr<DNSServerBinding>> _bindings;
|
||||||
|
bool started{false};
|
||||||
|
|
||||||
|
std::thread event_base_executor{};
|
||||||
|
struct event* event_base_ticker{nullptr};
|
||||||
|
struct event_base* event_base{nullptr};
|
||||||
|
|
||||||
|
bool bind(DNSServerBinding& /* binding */, std::string& /* error */);
|
||||||
|
void unbind(DNSServerBinding& /* 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();
|
||||||
|
};
|
||||||
|
}
|
5
util/CMakeLists.txt
Normal file
5
util/CMakeLists.txt
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
project(TeaDNS-Parser)
|
||||||
|
|
||||||
|
add_library(teadns__parser INTERFACE)
|
||||||
|
target_sources(teadns__parser INTERFACE src/parser.cpp src/types.cpp src/builder.cpp)
|
||||||
|
target_include_directories(teadns__parser INTERFACE include)
|
186
util/include/teadns/builder.h
Normal file
186
util/include/teadns/builder.h
Normal file
@ -0,0 +1,186 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
#include <netinet/in.h>
|
||||||
|
#include <vector>
|
||||||
|
#include <memory>
|
||||||
|
#include "types.h"
|
||||||
|
|
||||||
|
namespace ts::dns {
|
||||||
|
class DNSBuilder;
|
||||||
|
namespace builder {
|
||||||
|
namespace rrbuilder {
|
||||||
|
class base;
|
||||||
|
}
|
||||||
|
|
||||||
|
class DNSHeader {
|
||||||
|
friend class ts::dns::DNSBuilder;
|
||||||
|
|
||||||
|
template <size_t index>
|
||||||
|
constexpr static size_t be2leIndex() {
|
||||||
|
int result = index;
|
||||||
|
if(result >= 8)
|
||||||
|
result -= 8;
|
||||||
|
else
|
||||||
|
result += 8;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
public:
|
||||||
|
DNSHeader() {
|
||||||
|
this->_buffer = (uint16_t*) &this->internal_buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
explicit DNSHeader(uint16_t* buffer) : _buffer{buffer} { }
|
||||||
|
|
||||||
|
inline void id(uint16_t id) { this->_buffer[0] = id; }
|
||||||
|
|
||||||
|
inline void set_answer(bool flag) { this->set_option_bits<15>(flag, 1); }
|
||||||
|
|
||||||
|
inline void set_authoritative_answer(bool flag) { this->set_option_bits<10>(flag, 1); }
|
||||||
|
inline void set_truncation(bool flag) { this->set_option_bits<9>(flag, 1); }
|
||||||
|
inline void set_recursion_desired(bool flag) { this->set_option_bits<8>(flag, 1); }
|
||||||
|
inline void set_recursion_available(bool flag) { this->set_option_bits<7>(flag, 1); }
|
||||||
|
|
||||||
|
inline void set_query_type(uint8_t type) { this->set_option_bits<11>(type, 4); }
|
||||||
|
inline void set_response_code(uint8_t code) { this->set_option_bits<0>(code, 4); }
|
||||||
|
|
||||||
|
inline void set_query_count(uint16_t count) { this->_buffer[2] = htons(count); }
|
||||||
|
inline void set_answer_count(uint16_t count) { this->_buffer[3] = htons(count); }
|
||||||
|
inline void set_authority_count(uint16_t count) { this->_buffer[4] = htons(count); }
|
||||||
|
inline void set_additional_count(uint16_t count) { this->_buffer[5] = htons(count); }
|
||||||
|
|
||||||
|
[[nodiscard]] inline const void* buffer() const { return this->_buffer; }
|
||||||
|
[[nodiscard]] constexpr size_t buffer_size() const { return 12; }
|
||||||
|
private:
|
||||||
|
template <size_t index, size_t le_index = be2leIndex<index>()>
|
||||||
|
inline void set_option_bits(uint8_t value, uint8_t bit_count) {
|
||||||
|
auto clear = (1UL << bit_count) - 1;
|
||||||
|
this->_buffer[1] &= ~(clear << le_index);
|
||||||
|
this->_buffer[1] |= ((value & clear) << le_index);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint16_t* _buffer{};
|
||||||
|
uint16_t internal_buffer[6]{0,0,0,0,0,0};
|
||||||
|
};
|
||||||
|
|
||||||
|
class DNSQuery {
|
||||||
|
friend class ts::dns::DNSBuilder;
|
||||||
|
public:
|
||||||
|
inline void set_qname(const std::string& value) { this->name = value; }
|
||||||
|
inline void set_qtype(const rrtype::value& value) { this->type = value; }
|
||||||
|
inline void set_qclass(const rrclass::value& value) { this->klass = value; }
|
||||||
|
|
||||||
|
DNSQuery(std::string name, rrtype::value type, rrclass::value klass) : name{std::move(name)}, type{type}, klass{klass} {}
|
||||||
|
DNSQuery() = default;
|
||||||
|
private:
|
||||||
|
|
||||||
|
std::string name;
|
||||||
|
rrtype::value type{rrtype::A};
|
||||||
|
rrclass::value klass{rrclass::IN};
|
||||||
|
};
|
||||||
|
|
||||||
|
class DNSResourceRecords {
|
||||||
|
friend class ts::dns::DNSBuilder;
|
||||||
|
public:
|
||||||
|
[[nodiscard]] inline std::string get_name() { return this->name; }
|
||||||
|
|
||||||
|
inline void set_ttl(uint32_t value) { this->ttl = value; }
|
||||||
|
inline void set_type(rrtype::value value) { this->type = value; }
|
||||||
|
inline void set_class(rrclass::value value) { this->klass = value; }
|
||||||
|
|
||||||
|
bool build(char*& /* buffer */, size_t& /* max length */, std::string& /* error */);
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
[[nodiscard]] inline T& builder() {
|
||||||
|
if(T::type != this->type)
|
||||||
|
throw std::logic_error{"type mismatch"};
|
||||||
|
if(!this->payload_builder)
|
||||||
|
this->payload_builder.reset(new T{this});
|
||||||
|
auto entry = dynamic_cast<T*>(&*this->payload_builder);
|
||||||
|
if(!entry)
|
||||||
|
throw std::logic_error{"type mismatch to previous initialized"};
|
||||||
|
return *entry;
|
||||||
|
}
|
||||||
|
private:
|
||||||
|
uint32_t ttl{0};
|
||||||
|
|
||||||
|
std::string name;
|
||||||
|
rrtype::value type{rrtype::A};
|
||||||
|
rrclass::value klass{rrclass::IN};
|
||||||
|
|
||||||
|
std::unique_ptr<rrbuilder::base> payload_builder;
|
||||||
|
};
|
||||||
|
|
||||||
|
namespace rrbuilder {
|
||||||
|
class base {
|
||||||
|
public:
|
||||||
|
virtual bool build(char*& /* buffer */, size_t& /* max length */, std::string& /* error */) = 0;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
explicit base(DNSResourceRecords* handle) : handle{handle} {}
|
||||||
|
private:
|
||||||
|
DNSResourceRecords* handle;
|
||||||
|
};
|
||||||
|
|
||||||
|
#define define_builder(name, base, ...) \
|
||||||
|
struct name : public base { \
|
||||||
|
friend class builder::DNSResourceRecords; \
|
||||||
|
public: \
|
||||||
|
static constexpr auto type = rrtype::name; \
|
||||||
|
bool build(char*&, size_t&, std::string&) override; \
|
||||||
|
__VA_ARGS__ \
|
||||||
|
private: \
|
||||||
|
explicit name(DNSResourceRecords* handle) : base{handle} {} \
|
||||||
|
}
|
||||||
|
|
||||||
|
define_builder(A, base,
|
||||||
|
void set_address(uint32_t address) { this->address = address; }
|
||||||
|
inline void set_address(const uint8_t (&address)[4]) {
|
||||||
|
this->set_address((address[3] << 24UL) | (address[2] << 16UL) | (address[1] << 8UL) | address[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
uint32_t address{0};
|
||||||
|
);
|
||||||
|
|
||||||
|
#undef define_builder
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class DNSBuilder {
|
||||||
|
public:
|
||||||
|
builder::DNSHeader& header() { return this->_header; }
|
||||||
|
|
||||||
|
builder::DNSQuery& push_query() {
|
||||||
|
this->_queries.emplace_back();
|
||||||
|
return this->_queries.back();
|
||||||
|
}
|
||||||
|
|
||||||
|
builder::DNSQuery& query(size_t index) {
|
||||||
|
if(this->_queries.size() <= index)
|
||||||
|
std::__throw_out_of_range("index out of range");
|
||||||
|
|
||||||
|
return this->_queries[index];
|
||||||
|
}
|
||||||
|
|
||||||
|
builder::DNSResourceRecords& push_answer(const std::string& domain) {
|
||||||
|
auto& entry = this->_answers.emplace_back();
|
||||||
|
entry.name = domain;
|
||||||
|
return this->_answers.back();
|
||||||
|
}
|
||||||
|
|
||||||
|
builder::DNSResourceRecords& answer(size_t index) {
|
||||||
|
if(this->_answers.size() <= index)
|
||||||
|
std::__throw_out_of_range("index out of range");
|
||||||
|
|
||||||
|
return this->_answers[index];
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t build(char* /* buffer */, size_t /* max length */, std::string& /* error */);
|
||||||
|
private:
|
||||||
|
builder::DNSHeader _header{};
|
||||||
|
|
||||||
|
std::vector<builder::DNSQuery> _queries{};
|
||||||
|
std::vector<builder::DNSResourceRecords> _answers{};
|
||||||
|
};
|
||||||
|
}
|
257
util/include/teadns/parser.h
Normal file
257
util/include/teadns/parser.h
Normal file
@ -0,0 +1,257 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
#include <string>
|
||||||
|
#include <cstdint>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#ifdef WIN32
|
||||||
|
#include <WinSock2.h>
|
||||||
|
#include <Windows.h>
|
||||||
|
#include <WinDNS.h>
|
||||||
|
#else
|
||||||
|
#include <netinet/in.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "./types.h"
|
||||||
|
|
||||||
|
struct in6_addr;
|
||||||
|
namespace ts::dns {
|
||||||
|
namespace parser {
|
||||||
|
class DNSHeader;
|
||||||
|
class DNSQuery;
|
||||||
|
class DNSResourceRecords;
|
||||||
|
}
|
||||||
|
|
||||||
|
class DNSParser;
|
||||||
|
struct DNSParseData;
|
||||||
|
|
||||||
|
struct DNSParseData {
|
||||||
|
#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
|
||||||
|
|
||||||
|
~DNSParseData();
|
||||||
|
};
|
||||||
|
|
||||||
|
class DNSParser {
|
||||||
|
friend class Resolver;
|
||||||
|
public:
|
||||||
|
typedef std::vector<std::shared_ptr<parser::DNSResourceRecords>> rr_list_t;
|
||||||
|
typedef std::vector<std::shared_ptr<parser::DNSQuery>> q_list_t;
|
||||||
|
|
||||||
|
#ifdef WIN32
|
||||||
|
DNSResponse(std::shared_ptr<DNSResponseData>);
|
||||||
|
#else
|
||||||
|
DNSParser(uint8_t /* secure state */, const char* /* bogus */, void* /* packet */, size_t /* length */);
|
||||||
|
#endif
|
||||||
|
DNSParser(const DNSParser&) = delete;
|
||||||
|
DNSParser(DNSParser&&) = 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]] parser::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
|
||||||
|
std::shared_ptr<parser::DNSResourceRecords> parse_rr(std::string& /* error */, size_t& index, bool /* compression allowed dn */);
|
||||||
|
|
||||||
|
std::string bogus;
|
||||||
|
uint8_t secure_state{0};
|
||||||
|
#endif
|
||||||
|
std::shared_ptr<DNSParseData> 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 parser {
|
||||||
|
#ifndef WIN32
|
||||||
|
class DNSHeader {
|
||||||
|
friend class ts::dns::DNSParser;
|
||||||
|
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 DNSParser* response) : response{response} {}
|
||||||
|
const DNSParser* response{nullptr};
|
||||||
|
};
|
||||||
|
#endif
|
||||||
|
|
||||||
|
class DNSQuery {
|
||||||
|
friend class ts::dns::DNSParser;
|
||||||
|
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 ts::dns::DNSParser;
|
||||||
|
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<rrtype::value>(this->nrecord->wType);
|
||||||
|
#else
|
||||||
|
return this->type;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
[[nodiscard]] inline rrclass::value aclass() const {
|
||||||
|
#ifdef WIN32
|
||||||
|
return static_cast<rrclass::value>(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<DNSParseData> dns_data() const {
|
||||||
|
return this->data;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
[[nodiscard]] inline T parse() const {
|
||||||
|
if(T::type != this->atype())
|
||||||
|
throw std::logic_error{"util type mismatch"};
|
||||||
|
return T{this};
|
||||||
|
}
|
||||||
|
private:
|
||||||
|
std::shared_ptr<DNSParseData> data{nullptr};
|
||||||
|
|
||||||
|
#ifdef WIN32
|
||||||
|
DNSResourceRecords(std::shared_ptr<DNSResponseData>, PDNS_RECORDA);
|
||||||
|
|
||||||
|
PDNS_RECORDA nrecord{nullptr};
|
||||||
|
#else
|
||||||
|
DNSResourceRecords(std::shared_ptr<DNSParseData>, 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 parser::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);
|
||||||
|
|
||||||
|
#undef define_parser
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
168
util/include/teadns/types.h
Normal file
168
util/include/teadns/types.h
Normal file
@ -0,0 +1,168 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <map>
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
|
namespace ts::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 {
|
||||||
|
#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<value, const char*> names;
|
||||||
|
|
||||||
|
static const char* name(const value& value) {
|
||||||
|
if(names.count(value) > 0)
|
||||||
|
return names.at(value);
|
||||||
|
return "unknown";
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct rcode {
|
||||||
|
enum value : uint8_t {
|
||||||
|
NOERROR = 0,
|
||||||
|
FORMERR = 1,
|
||||||
|
SERVFAIL = 2,
|
||||||
|
NXDOMAIN = 3,
|
||||||
|
NOTIMP = 4,
|
||||||
|
REFUSED = 5,
|
||||||
|
YXDOMAIN = 6,
|
||||||
|
XRRSET = 7,
|
||||||
|
NOTAUTH = 8,
|
||||||
|
NOTZONE = 9
|
||||||
|
};
|
||||||
|
|
||||||
|
static std::map<value, const char*> names;
|
||||||
|
static std::map<value, const char*> descriptions;
|
||||||
|
|
||||||
|
static const char* name(const value& value) {
|
||||||
|
if(names.count(value) > 0)
|
||||||
|
return names.at(value);
|
||||||
|
return "unknown";
|
||||||
|
}
|
||||||
|
|
||||||
|
static const char* description(const value& value) {
|
||||||
|
if(descriptions.count(value) > 0)
|
||||||
|
return descriptions.at(value);
|
||||||
|
return "unknown";
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
122
util/src/builder.cpp
Normal file
122
util/src/builder.cpp
Normal file
@ -0,0 +1,122 @@
|
|||||||
|
#include <cstring>
|
||||||
|
#include "teadns/builder.h"
|
||||||
|
|
||||||
|
using namespace ts::dns;
|
||||||
|
using namespace ts::dns::builder;
|
||||||
|
|
||||||
|
inline bool write_dn(char *&buffer, size_t& max_size, std::string dn) {
|
||||||
|
size_t index{0};
|
||||||
|
size_t length;
|
||||||
|
do {
|
||||||
|
auto next = dn.find('.', index);
|
||||||
|
|
||||||
|
length = next == -1 ? dn.length() - index : next - index;
|
||||||
|
if(max_size + 1 < length)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
*buffer = length;
|
||||||
|
memcpy(buffer + 1, dn.data() + index, length);
|
||||||
|
buffer += 1 + length;
|
||||||
|
max_size -= 1 + length;
|
||||||
|
|
||||||
|
index = next;
|
||||||
|
} while(++index);
|
||||||
|
|
||||||
|
if(max_size < 1)
|
||||||
|
return false;
|
||||||
|
*buffer = 0;
|
||||||
|
buffer++;
|
||||||
|
max_size--;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t DNSBuilder::build(char *buffer, size_t max_size, std::string &error) {
|
||||||
|
size_t begin = max_size;
|
||||||
|
|
||||||
|
if(max_size < this->_header.buffer_size()) {
|
||||||
|
error = "buffer too short for header";
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
this->_header.set_query_count(this->_queries.size());
|
||||||
|
this->_header.set_answer_count(this->_answers.size());
|
||||||
|
this->_header.set_authority_count(0);
|
||||||
|
this->_header.set_additional_count(0);
|
||||||
|
|
||||||
|
memcpy(buffer, this->_header.buffer(), this->_header.buffer_size());
|
||||||
|
max_size -= this->_header.buffer_size();
|
||||||
|
buffer += this->_header.buffer_size();
|
||||||
|
|
||||||
|
for(auto& query : this->_queries) {
|
||||||
|
if(!write_dn(buffer, max_size, query.name)) {
|
||||||
|
error = "buffer too short for query dn";
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(max_size < 4) {
|
||||||
|
error = "buffer too short for query data";
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
*(uint16_t*) buffer = htons(query.type);
|
||||||
|
buffer += 2;
|
||||||
|
|
||||||
|
*(uint16_t*) buffer = htons(query.klass);
|
||||||
|
buffer += 2;
|
||||||
|
max_size -= 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
for(auto& answer : this->_answers) {
|
||||||
|
if(!answer.build(buffer, max_size, error)) {
|
||||||
|
error = "failed to build answer: " + error;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return begin - max_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DNSResourceRecords::build(char *&buffer, size_t &max_size, std::string &error) {
|
||||||
|
if(!write_dn(buffer, max_size, this->name)) {
|
||||||
|
error = "failed to write dn";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(max_size < 10) {
|
||||||
|
error = "buffer too small";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
*(uint16_t*) buffer = htons(this->type);
|
||||||
|
max_size -= 2; buffer += 2;
|
||||||
|
|
||||||
|
*(uint16_t*) buffer = htons(this->klass);
|
||||||
|
max_size -= 2; buffer += 2;
|
||||||
|
|
||||||
|
*(uint32_t*) buffer = htonl(this->ttl);
|
||||||
|
max_size -= 4; buffer += 4;
|
||||||
|
|
||||||
|
auto& length = *(uint16_t*) buffer;
|
||||||
|
max_size -= 2; buffer += 2;
|
||||||
|
if(this->payload_builder) {
|
||||||
|
auto start = max_size;
|
||||||
|
if(!this->payload_builder->build(buffer, max_size, error)) {
|
||||||
|
error = "failed to write payload: " + error;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
length = htons(start - max_size);
|
||||||
|
} else {
|
||||||
|
length = 0;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool rrbuilder::A::build(char *&buffer, size_t &max_size, std::string &error) {
|
||||||
|
if(max_size < 4) {
|
||||||
|
error = "buffer too small";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
memcpy(buffer, &this->address, 4);
|
||||||
|
buffer += 4;
|
||||||
|
max_size -= 4;
|
||||||
|
return true;
|
||||||
|
}
|
403
util/src/parser.cpp
Normal file
403
util/src/parser.cpp
Normal file
@ -0,0 +1,403 @@
|
|||||||
|
#include "teadns/parser.h"
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
#include <codecvt>
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
|
#ifdef WIN32
|
||||||
|
#include <WS2tcpip.h>
|
||||||
|
#include <in6addr.h>
|
||||||
|
#include <ip2string.h>
|
||||||
|
#include <inaddr.h>
|
||||||
|
#include <WinDNS.h>
|
||||||
|
#else
|
||||||
|
#include <arpa/inet.h>
|
||||||
|
#include <netinet/in.h>
|
||||||
|
#include <cstring>
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
using namespace ts::dns;
|
||||||
|
using namespace ts::dns::parser;
|
||||||
|
using namespace ts::dns::parser::rrparser;
|
||||||
|
|
||||||
|
thread_local std::vector<size_t> visited_links;
|
||||||
|
std::string DNSParseData::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;
|
||||||
|
}
|
||||||
|
|
||||||
|
DNSParseData::~DNSParseData() {
|
||||||
|
::free(this->buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
DNSParser::DNSParser(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<DNSParseData>();
|
||||||
|
this->data->buffer = (uint8_t*) malloc(size);
|
||||||
|
this->data->length = size;
|
||||||
|
|
||||||
|
memcpy(this->data->buffer, packet, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
parser::DNSHeader DNSParser::header() const {
|
||||||
|
return parser::DNSHeader{this};
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DNSParser::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 parser::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<parser::DNSResourceRecords> DNSParser::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<parser::DNSResourceRecords>(new parser::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<DNSParseData> 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<ts::dns::DNSResponseData> data, PDNS_RECORDA rdata) : nrecord{rdata}, data{std::move(data)} { }
|
||||||
|
bool DNSResourceRecords::is_wide_string() const {
|
||||||
|
return this->data->wide_string;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
bool A::is_valid() {
|
||||||
|
#ifdef WIN32
|
||||||
|
return true;
|
||||||
|
#else
|
||||||
|
return this->handle->payload_length() == 4;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
in_addr A::address() {
|
||||||
|
#ifdef WIN32
|
||||||
|
in_addr result{};
|
||||||
|
result.S_un.S_addr = this->handle->native_record()->Data.A.IpAddress;
|
||||||
|
return result;
|
||||||
|
#else
|
||||||
|
//TODO: Attention: Unaligned access
|
||||||
|
return {*(uint32_t*) this->handle->payload_data()};
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string A::address_string() {
|
||||||
|
#ifdef WIN32
|
||||||
|
struct in_addr address = this->address();
|
||||||
|
char buffer[17];
|
||||||
|
RtlIpv4AddressToStringA(&address, buffer);
|
||||||
|
return std::string{buffer};
|
||||||
|
#else
|
||||||
|
auto _1 = this->handle->payload_data()[0],
|
||||||
|
_2 = this->handle->payload_data()[1],
|
||||||
|
_3 = this->handle->payload_data()[2],
|
||||||
|
_4 = this->handle->payload_data()[3];
|
||||||
|
return std::to_string(_1) + "." + std::to_string(_2) + "." + std::to_string(_3) + "." + std::to_string(_4);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
//---------------- AAAA
|
||||||
|
#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
|
||||||
|
}
|
130
util/src/types.cpp
Normal file
130
util/src/types.cpp
Normal file
@ -0,0 +1,130 @@
|
|||||||
|
#include "teadns/types.h"
|
||||||
|
|
||||||
|
std::map<ts::dns::rrtype::value, const char*> ts::dns::rrtype::names{
|
||||||
|
{ts::dns::rrtype::A, "A"},
|
||||||
|
{ts::dns::rrtype::NS, "NS"},
|
||||||
|
{ts::dns::rrtype::MD, "MD"},
|
||||||
|
{ts::dns::rrtype::MF, "MF"},
|
||||||
|
{ts::dns::rrtype::CNAME, "CNAME"},
|
||||||
|
{ts::dns::rrtype::SOA, "SOA"},
|
||||||
|
{ts::dns::rrtype::MB, "MB"},
|
||||||
|
{ts::dns::rrtype::MG, "MG"},
|
||||||
|
{ts::dns::rrtype::MR, "MR"},
|
||||||
|
{ts::dns::rrtype::NULL_, "NULL_"},
|
||||||
|
{ts::dns::rrtype::WKS, "WKS"},
|
||||||
|
{ts::dns::rrtype::PTR, "PTR"},
|
||||||
|
{ts::dns::rrtype::HINFO, "HINFO"},
|
||||||
|
{ts::dns::rrtype::MINFO, "MINFO"},
|
||||||
|
{ts::dns::rrtype::MX, "MX"},
|
||||||
|
{ts::dns::rrtype::TXT, "TXT"},
|
||||||
|
{ts::dns::rrtype::RP, "RP"},
|
||||||
|
{ts::dns::rrtype::AFSDB, "AFSDB"},
|
||||||
|
{ts::dns::rrtype::X25, "X25"},
|
||||||
|
{ts::dns::rrtype::ISDN, "ISDN"},
|
||||||
|
{ts::dns::rrtype::RT, "RT"},
|
||||||
|
{ts::dns::rrtype::NSAP, "NSAP"},
|
||||||
|
{ts::dns::rrtype::NSAP_PTR, "NSAP_PTR"},
|
||||||
|
{ts::dns::rrtype::SIG, "SIG"},
|
||||||
|
{ts::dns::rrtype::KEY, "KEY"},
|
||||||
|
{ts::dns::rrtype::PX, "PX"},
|
||||||
|
{ts::dns::rrtype::GPOS, "GPOS"},
|
||||||
|
{ts::dns::rrtype::AAAA, "AAAA"},
|
||||||
|
{ts::dns::rrtype::LOC, "LOC"},
|
||||||
|
{ts::dns::rrtype::NXT, "NXT"},
|
||||||
|
{ts::dns::rrtype::EID, "EID"},
|
||||||
|
{ts::dns::rrtype::NIMLOC, "NIMLOC"},
|
||||||
|
{ts::dns::rrtype::SRV, "SRV"},
|
||||||
|
{ts::dns::rrtype::ATMA, "ATMA"},
|
||||||
|
{ts::dns::rrtype::NAPTR, "NAPTR"},
|
||||||
|
{ts::dns::rrtype::KX, "KX"},
|
||||||
|
{ts::dns::rrtype::CERT, "CERT"},
|
||||||
|
{ts::dns::rrtype::A6, "A6"},
|
||||||
|
{ts::dns::rrtype::DNAME, "DNAME"},
|
||||||
|
{ts::dns::rrtype::SINK, "SINK"},
|
||||||
|
{ts::dns::rrtype::OPT, "OPT"},
|
||||||
|
{ts::dns::rrtype::APL, "APL"},
|
||||||
|
{ts::dns::rrtype::DS, "DS"},
|
||||||
|
{ts::dns::rrtype::SSHFP, "SSHFP"},
|
||||||
|
{ts::dns::rrtype::IPSECKEY, "IPSECKEY"},
|
||||||
|
{ts::dns::rrtype::RRSIG, "RRSIG"},
|
||||||
|
{ts::dns::rrtype::NSEC, "NSEC"},
|
||||||
|
{ts::dns::rrtype::DNSKEY, "DNSKEY"},
|
||||||
|
{ts::dns::rrtype::DHCID, "DHCID"},
|
||||||
|
{ts::dns::rrtype::NSEC3, "NSEC3"},
|
||||||
|
{ts::dns::rrtype::NSEC3PARAM, "NSEC3PARAM"},
|
||||||
|
{ts::dns::rrtype::TLSA, "TLSA"},
|
||||||
|
{ts::dns::rrtype::SMIMEA, "SMIMEA"},
|
||||||
|
{ts::dns::rrtype::Unassigned, "Unassigned"},
|
||||||
|
{ts::dns::rrtype::HIP, "HIP"},
|
||||||
|
{ts::dns::rrtype::NINFO, "NINFO"},
|
||||||
|
{ts::dns::rrtype::RKEY, "RKEY"},
|
||||||
|
{ts::dns::rrtype::TALINK, "TALINK"},
|
||||||
|
{ts::dns::rrtype::CDS, "CDS"},
|
||||||
|
{ts::dns::rrtype::CDNSKEY, "CDNSKEY"},
|
||||||
|
{ts::dns::rrtype::OPENPGPKEY, "OPENPGPKEY"},
|
||||||
|
{ts::dns::rrtype::CSYNC, "CSYNC"},
|
||||||
|
{ts::dns::rrtype::ZONEMD, "ZONEMD"},
|
||||||
|
{ts::dns::rrtype::SPF, "SPF"},
|
||||||
|
{ts::dns::rrtype::UINFO, "UINFO"},
|
||||||
|
{ts::dns::rrtype::UID, "UID"},
|
||||||
|
{ts::dns::rrtype::GID, "GID"},
|
||||||
|
{ts::dns::rrtype::UNSPEC, "UNSPEC"},
|
||||||
|
{ts::dns::rrtype::NID, "NID"},
|
||||||
|
{ts::dns::rrtype::L32, "L32"},
|
||||||
|
{ts::dns::rrtype::L64, "L64"},
|
||||||
|
{ts::dns::rrtype::LP, "LP"},
|
||||||
|
{ts::dns::rrtype::EUI48, "EUI48"},
|
||||||
|
{ts::dns::rrtype::EUI64, "EUI64"},
|
||||||
|
{ts::dns::rrtype::TKEY, "TKEY"},
|
||||||
|
{ts::dns::rrtype::TSIG, "TSIG"},
|
||||||
|
{ts::dns::rrtype::IXFR, "IXFR"},
|
||||||
|
{ts::dns::rrtype::AXFR, "AXFR"},
|
||||||
|
{ts::dns::rrtype::MAILB, "MAILB"},
|
||||||
|
{ts::dns::rrtype::MAILA, "MAILA"},
|
||||||
|
{ts::dns::rrtype::ANY, "ANY"},
|
||||||
|
{ts::dns::rrtype::URI, "URI"},
|
||||||
|
{ts::dns::rrtype::CAA, "CAA"},
|
||||||
|
{ts::dns::rrtype::AVC, "AVC"},
|
||||||
|
{ts::dns::rrtype::DOA, "DOA"},
|
||||||
|
{ts::dns::rrtype::AMTRELAY, "AMTRELAY"},
|
||||||
|
{ts::dns::rrtype::TA, "TA"},
|
||||||
|
{ts::dns::rrtype::DLV, "DLV"},
|
||||||
|
{ts::dns::rrtype::Reserved, "Reserved"},
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
std::map<ts::dns::rrclass::value, const char*> ts::dns::rrclass::names{
|
||||||
|
{ts::dns::rrclass::IN, "IN"},
|
||||||
|
{ts::dns::rrclass::CH, "CH"},
|
||||||
|
{ts::dns::rrclass::HS, "HS"},
|
||||||
|
{ts::dns::rrclass::QCLASS_ANY, "QCLASS_ANY"},
|
||||||
|
{ts::dns::rrclass::QCLASS_NONE, "QCLASS_NONE"},
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
std::map<ts::dns::rcode::value, const char*> ts::dns::rcode::names{
|
||||||
|
{ts::dns::rcode::NOERROR, "NOERROR"},
|
||||||
|
{ts::dns::rcode::FORMERR, "FORMERR"},
|
||||||
|
{ts::dns::rcode::SERVFAIL, "SERVFAIL"},
|
||||||
|
{ts::dns::rcode::NXDOMAIN, "NXDOMAIN"},
|
||||||
|
{ts::dns::rcode::NOTIMP, "NOTIMP"},
|
||||||
|
{ts::dns::rcode::REFUSED, "REFUSED"},
|
||||||
|
{ts::dns::rcode::YXDOMAIN, "YXDOMAIN"},
|
||||||
|
{ts::dns::rcode::XRRSET, "XRRSET"},
|
||||||
|
{ts::dns::rcode::NOTAUTH, "NOTAUTH"},
|
||||||
|
{ts::dns::rcode::NOTZONE, "NOTZONE"},
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
std::map<ts::dns::rcode::value, const char*> ts::dns::rcode::descriptions{
|
||||||
|
{ts::dns::rcode::NOERROR, "DNS Query completed successfully"},
|
||||||
|
{ts::dns::rcode::FORMERR, "DNS Query Format Error"},
|
||||||
|
{ts::dns::rcode::SERVFAIL, "Server failed to complete the DNS request"},
|
||||||
|
{ts::dns::rcode::NXDOMAIN, "Domain name does not exist. "},
|
||||||
|
{ts::dns::rcode::NOTIMP, "Function not implemented"},
|
||||||
|
{ts::dns::rcode::REFUSED, "The server refused to answer for the query"},
|
||||||
|
{ts::dns::rcode::YXDOMAIN, "Name that should not exist, does exist"},
|
||||||
|
{ts::dns::rcode::XRRSET, "RRset that should not exist, does exist"},
|
||||||
|
{ts::dns::rcode::NOTAUTH, "Server not authoritative for the zone"},
|
||||||
|
{ts::dns::rcode::NOTZONE, "Name not in zone"},
|
||||||
|
};
|
Loading…
Reference in New Issue
Block a user