diff --git a/src/dns/dns_cli.cpp b/src/dns/dns_cli.cpp new file mode 100644 index 0000000..fc31b7b --- /dev/null +++ b/src/dns/dns_cli.cpp @@ -0,0 +1,34 @@ +#include "dns_cli.hpp" + +// helper for default values +constexpr auto withDefault = [](auto value) { + using T = decltype(value); + return boost::program_options::value()->default_value(value); +}; + +DnsCli::DnsCli() + : CliBase("DNS Server Options") + , port_(5300) + , ip_("127.0.0.1") +{ +} + +void DnsCli::setupOptions() +{ + // clang-format off + desc_.add_options() + ("help,h", "Show help message") + ("port,p", withDefault(5300), "UDP port to listen on") + ("ip,i", withDefault(std::string("127.0.0.1")), "IP address to return in A record"); + // clang-format on +} + +int DnsCli::getPort() const +{ + return vm_["port"].as(); +} + +std::string DnsCli::getIp() const +{ + return vm_["ip"].as(); +} diff --git a/src/dns/dns_cli.hpp b/src/dns/dns_cli.hpp new file mode 100644 index 0000000..d0c0ca6 --- /dev/null +++ b/src/dns/dns_cli.hpp @@ -0,0 +1,20 @@ +#pragma once + +#include "cli_base.hpp" +#include + +class DnsCli : public CliBase +{ + public: + DnsCli(); + + int getPort() const; + std::string getIp() const; + + protected: + void setupOptions() override; + + private: + int port_; + std::string ip_; +}; diff --git a/src/dns/dns_server.cpp b/src/dns/dns_server.cpp new file mode 100644 index 0000000..cbfdd27 --- /dev/null +++ b/src/dns/dns_server.cpp @@ -0,0 +1,57 @@ +#include "dns_server.hpp" +#include "logger.hpp" + +DnsServer::DnsServer(IoContext &io, unsigned short port, std::string answerIp) + : UdpServerBase(io, port) + , answerIp_(std::move(answerIp)) +{ +} + +void DnsServer::handleRequest( + const std::string_view data, const UdpEndpoint &sender +) +{ + if (data.size() < 12) { + Logger::warn("Invalid DNS packet"); + return; + } + + // response buffer + std::string resp; + + // copy ID + resp.append(data.substr(0, 2)); + + // flags: QR=1 (response), AA=1 + resp.push_back('\x81'); + resp.push_back('\x80'); + + // QDCOUNT=1, ANCOUNT=1, NSCOUNT=0, ARCOUNT=0 + resp.append("\0\1\0\1\0\0\0\0", 8); + + // copy question (from offset 12 till end of query) + resp.append(data.substr(12)); + + // answer section + resp.append("\xc0\x0c"); // name pointer to question + resp.append("\0\1"); // TYPE A + resp.append("\0\1"); // CLASS IN + resp.append("\0\0\0\x3c"); // TTL = 60 + resp.append("\0\x04"); // RDLENGTH = 4 + + // answer IP (from CLI or default 127.0.0.1) + unsigned int b1, b2, b3, b4; + if (sscanf(answerIp_.c_str(), "%u.%u.%u.%u", &b1, &b2, &b3, &b4) != 4) { + b1 = 127; + b2 = 0; + b3 = 0; + b4 = 1; + } + resp.push_back(static_cast(b1)); + resp.push_back(static_cast(b2)); + resp.push_back(static_cast(b3)); + resp.push_back(static_cast(b4)); + + Logger::info("DNS query answered with A " + answerIp_); + doSend(resp, sender); +} diff --git a/src/dns/dns_server.hpp b/src/dns/dns_server.hpp new file mode 100644 index 0000000..24fcd5c --- /dev/null +++ b/src/dns/dns_server.hpp @@ -0,0 +1,18 @@ +#pragma once + +#include "udp_server_base.hpp" +#include + +class DnsServer : public UdpServerBase +{ + public: + DnsServer(IoContext &io, unsigned short port, std::string answerIp); + + protected: + void handleRequest( + const std::string_view data, const UdpEndpoint &sender + ) override; + + private: + std::string answerIp_; +}; diff --git a/src/main/main_dns.cpp b/src/main/main_dns.cpp new file mode 100644 index 0000000..3d27811 --- /dev/null +++ b/src/main/main_dns.cpp @@ -0,0 +1,26 @@ +#include "dns_cli.hpp" +#include "dns_server.hpp" +#include "logger.hpp" + +int main(int argc, char *argv[]) +{ + DnsCli cli; + cli.parse(argc, argv); + + if (cli.isHelp()) { + cli.printHelp(); + return 0; + } + + Logger::init(true, "dns_server.log"); + Logger::info( + "DNS server starting on port " + std::to_string(cli.getPort()) + + ", answering with IP " + cli.getIp() + ); + + IoContext io; + DnsServer server(io, cli.getPort(), cli.getIp()); + server.start(); + + io.run(); +}