/* * BLT Memory Util for parsing DNS packets / BLT + ASIO simple packet sender / BLT Type Name Demangler. * This software is unlikely to become part of BLT main but is provided under the BLT license (GPL 3). * Copyright (C) 2023 Brett Terpstra, Et al * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef INSANE_DNS_UTIL_H #define INSANE_DNS_UTIL_H #include #include #include #include #include #include #include #ifdef __GNUC__ #include #endif using asio::ip::udp; using asio::ip::tcp; namespace blt { /** * Basic parser for processing strings of bytes received from UDP sockets. This class provides a simple interface for efficiently copying the * big endian bytes from the network into the little endian bytes required by x86 processors. The class also provides basic safety guarantees * in the sense that the program will halt execution if reading the bytes would overflow the internal buffer. */ class byte_reader { private: unsigned char* _data; const size_t _size; mutable size_t _current_byte; public: explicit byte_reader(unsigned char* data, size_t size, size_t start_offset): _data(data), _size(size), _current_byte(start_offset) {} /** * Read the next byte in the data stream then increment the internal counter by 1 * @return the next byte in the array */ inline unsigned char& next() const { BLT_ASSERT(_current_byte < _size); return _data[_current_byte++]; } /** * Reads data into the provided template parameter, reordering the byte into the correct little endian format * @tparam T type to read (deduction is automatic) * @param t variable reference to read into */ template inline void to(T& t) const { BLT_ASSERT(_current_byte + sizeof(T) <= _size); // I hate little endian. I hate dealing with converting between endianness // it is very easy to do it very slowly // So I made BLT provide a simple interface to convert between bytes dependent on the platform // for small integral types (16, 32, and 64) it will compile to 3 instructions using compiler intrinsics. // Larger POD types are supported but will use std::reverse and therefore be slow. blt::mem::fromBytes(&_data[_current_byte], t); skip(sizeof(T)); } /** * Copy from the internal data buffer, advancing the internal stream, into the provided unsigned char array. *No reordering is done* * @param out character array to write to * @param size number of bytes to copy */ inline void copy(unsigned char*& out, size_t size) const { BLT_ASSERT(_current_byte + size < _size); std::memcpy(out, &_data[_current_byte], size); skip(size); } /** * Skip bytes in the data stream, useful for skipping sections of the DNS packet we may not care about. * @param s number of bytes to skip. */ inline void skip(size_t s = 1) const { _current_byte += s; } /** * @return 1 past the last byte we wrote to, otherwise known as the next byte we will be writing to. */ inline size_t last() const { return _current_byte; } /** * @return a pointer to a location in the internal buffer starting at last() */ inline unsigned char* from() { BLT_ASSERT(_current_byte < _size); return &_data[_current_byte]; } }; namespace network { /** * Sends a simple UDP message to the provided host on the DNS port and outputs the response into the out buffer * @param host host to send the message to * @param in buffer to send * @param in_size size of buffer * @param out buffer to write to * @param out_size size of data that was written. Make sure `out` has enough room for the packet. */ void sendUDPMessage(const std::string& host, const unsigned char* const& in, size_t in_size, blt::scoped_buffer& out, size_t& out_size) { BLT_DEBUG("Sending UDP DNS request to '%s' of size %d", host.c_str(), in_size); asio::io_context io_context; udp::endpoint receiver_endpoint(asio::ip::address::from_string(host), 53); udp::socket socket(io_context); socket.open(udp::v4()); socket.send_to(asio::const_buffers_1(in, in_size), receiver_endpoint); udp::endpoint sender_endpoint; out_size = socket.receive_from(asio::buffer(out.data(), out.size()), sender_endpoint); BLT_DEBUG("Received UDP DNS response from '%s' of size %d", host.c_str(), out_size); } /** * Sends a simple TCP message to the provided host on the DNS port and outputs the response into the out buffer * @param host host to send the message to * @param in buffer to send * @param in_size size of buffer * @param out buffer to write to * @param out_size size of data that was written. Make sure `out` has enough room for the packet. */ void sendTCPMessage(const std::string& host, const unsigned char* const& in, size_t in_size, blt::scoped_buffer& out, size_t& out_size) { BLT_DEBUG("Sending TCP DNS request to '%s' of size %d", host.c_str(), in_size); asio::io_context io_context; tcp::resolver resolver(io_context); tcp::resolver::results_type endpoints = resolver.resolve(host, "53"); tcp::socket socket(io_context); asio::connect(socket, endpoints); asio::write(socket, asio::buffer(in, in_size)); out_size = socket.read_some(asio::buffer(out.data(), out.size())); BLT_DEBUG("Received TCP dns response from '%s' of size %d", host.c_str(), out_size); } } #ifdef __GNUC__ /** * Attempts to demangle the type name of the provided template type * @tparam T * @return */ template static BLT_CPP20_CONSTEXPR inline std::string type_name() { int status; std::string return_name = typeid(T).name(); // only defined for GNU C++11? char *demangled_name = abi::__cxa_demangle(return_name.c_str(), nullptr, nullptr, &status); if (demangled_name == nullptr) return return_name; return_name = demangled_name; std::free(demangled_name); return return_name; } template static BLT_CPP20_CONSTEXPR inline std::string is_UDP_or_TCP(){ std::string type = "UDP"; if (blt::string::contains(blt::type_name(), "TCP")) type = "TCP"; return type; } #endif } #endif //INSANE_DNS_UTIL_H // these are no longer used, but I am keeping them around because they are useful and were not added to git // template // struct is_byte_ptr_type : std::integral_constant || std::is_same_v || // std::is_same_v || std::is_same_v || std::is_same_v> // { // }; // // template // inline constexpr bool is_byte_ptr_type_v = is_byte_ptr_type::value; // // template // using messageFunction = std::function;