From 36d6b47f7879ba55bd680bad6a34feccae9622c3 Mon Sep 17 00:00:00 2001 From: JDierkse Date: Mon, 24 Feb 2020 20:46:47 +0100 Subject: [PATCH] Add MulticastDnsClient --- Network/Dns/Data.cpp | 129 +++++++++++ Network/Dns/Data.h | 55 +++++ Network/Dns/DataReader.cpp | 113 ++++++++++ Network/Dns/DataReader.h | 46 ++++ Network/Dns/Makefile | 1 + Network/Dns/MalformedPacket.h | 17 ++ Network/Dns/MulticastDnsClient.cpp | 32 +++ Network/Dns/MulticastDnsClientImpl.cpp | 146 +++++++++++++ Network/Dns/MulticastDnsClientImpl.h | 52 +++++ Network/Dns/Name.cpp | 79 +++++++ Network/Dns/NameImpl.cpp | 146 +++++++++++++ Network/Dns/NameImpl.h | 52 +++++ Network/Dns/OperationCode.h | 22 ++ Network/Dns/Packet.cpp | 284 +++++++++++++++++++++++++ Network/Dns/Packet.h | 79 +++++++ Network/Dns/Query.cpp | 59 +++++ Network/Dns/QueryImpl.cpp | 102 +++++++++ Network/Dns/QueryImpl.h | 45 ++++ Network/Dns/ResourceRecord.cpp | 82 +++++++ Network/Dns/ResourceRecordImpl.cpp | 121 +++++++++++ Network/Dns/ResourceRecordImpl.h | 48 +++++ Network/Dns/ResponseCode.h | 25 +++ include/Dns/Class.h | 24 +++ include/Dns/MulticastDnsClient.h | 36 ++++ include/Dns/Name.h | 73 +++++++ include/Dns/Query.h | 43 ++++ include/Dns/QueryClass.h | 25 +++ include/Dns/QueryType.h | 40 ++++ include/Dns/ResourceRecord.h | 44 ++++ include/Dns/Type.h | 36 ++++ 30 files changed, 2056 insertions(+) create mode 100644 Network/Dns/Data.cpp create mode 100644 Network/Dns/Data.h create mode 100644 Network/Dns/DataReader.cpp create mode 100644 Network/Dns/DataReader.h create mode 120000 Network/Dns/Makefile create mode 100644 Network/Dns/MalformedPacket.h create mode 100644 Network/Dns/MulticastDnsClient.cpp create mode 100644 Network/Dns/MulticastDnsClientImpl.cpp create mode 100644 Network/Dns/MulticastDnsClientImpl.h create mode 100644 Network/Dns/Name.cpp create mode 100644 Network/Dns/NameImpl.cpp create mode 100644 Network/Dns/NameImpl.h create mode 100644 Network/Dns/OperationCode.h create mode 100644 Network/Dns/Packet.cpp create mode 100644 Network/Dns/Packet.h create mode 100644 Network/Dns/Query.cpp create mode 100644 Network/Dns/QueryImpl.cpp create mode 100644 Network/Dns/QueryImpl.h create mode 100644 Network/Dns/ResourceRecord.cpp create mode 100644 Network/Dns/ResourceRecordImpl.cpp create mode 100644 Network/Dns/ResourceRecordImpl.h create mode 100644 Network/Dns/ResponseCode.h create mode 100644 include/Dns/Class.h create mode 100644 include/Dns/MulticastDnsClient.h create mode 100644 include/Dns/Name.h create mode 100644 include/Dns/Query.h create mode 100644 include/Dns/QueryClass.h create mode 100644 include/Dns/QueryType.h create mode 100644 include/Dns/ResourceRecord.h create mode 100644 include/Dns/Type.h diff --git a/Network/Dns/Data.cpp b/Network/Dns/Data.cpp new file mode 100644 index 0000000..9a747f8 --- /dev/null +++ b/Network/Dns/Data.cpp @@ -0,0 +1,129 @@ +#include "Data.h" +#include + + +namespace Network { +namespace Dns { + +Data::Data() +{ +} + +Data::Data(const std::vector& data) : + m_data(data) +{ +} + +Data::Data(const uint8_t* msg, unsigned length) : + m_data(msg, msg + length) +{ +} + +Data::Data(const Data& data) : + m_data(data.m_data) +{ +} + +uint8_t& Data::GetAt(unsigned pos) +{ + return m_data[pos]; +} + +const uint8_t& Data::GetAt(unsigned pos) const +{ + return m_data[pos]; +} + +uint16_t& Data::GetAt16(unsigned pos) +{ + return *((uint16_t*)(GetPtr() + pos)); +} + +const uint16_t& Data::GetAt16(unsigned pos) const +{ + return *((const uint16_t*)(GetPtr() + pos)); +} + +uint32_t& Data::GetAt32(unsigned pos) +{ + return *((uint32_t*)(GetPtr() + pos)); +} + +const uint32_t& Data::GetAt32(unsigned pos) const +{ + return *((const uint32_t*)(GetPtr() + pos)); +} + +const uint8_t* Data::GetPtr() const +{ + return m_data.data(); +} + +unsigned Data::GetLength() const +{ + return static_cast(m_data.size()); +} + +void Data::append(const Data& data) +{ + m_data.insert(m_data.end(), data.m_data.begin(), data.m_data.end()); +} + +void Data::append(uint8_t value) +{ + m_data.emplace_back(value); +} + +void Data::append(uint16_t value) +{ + m_data.emplace_back(static_cast((value >> 8) & ((1 << 8) - 1))); + m_data.emplace_back(static_cast((value >> 0) & ((1 << 8) - 1))); +} + +void Data::append(uint32_t value) +{ + m_data.emplace_back(static_cast((value >> 24) & ((1 << 8) - 1))); + m_data.emplace_back(static_cast((value >> 16) & ((1 << 8) - 1))); + m_data.emplace_back(static_cast((value >> 8) & ((1 << 8) - 1))); + m_data.emplace_back(static_cast((value >> 0) & ((1 << 8) - 1))); +} + +void Data::append(uint64_t value) +{ + m_data.emplace_back(static_cast((value >> 56) & ((1 << 8) - 1))); + m_data.emplace_back(static_cast((value >> 48) & ((1 << 8) - 1))); + m_data.emplace_back(static_cast((value >> 40) & ((1 << 8) - 1))); + m_data.emplace_back(static_cast((value >> 32) & ((1 << 8) - 1))); + m_data.emplace_back(static_cast((value >> 24) & ((1 << 8) - 1))); + m_data.emplace_back(static_cast((value >> 16) & ((1 << 8) - 1))); + m_data.emplace_back(static_cast((value >> 8) & ((1 << 8) - 1))); + m_data.emplace_back(static_cast((value >> 0) & ((1 << 8) - 1))); +} + +void Data::prepend(const Data& data) +{ + m_data.insert(m_data.begin(), data.m_data.begin(), data.m_data.end()); +} + +void Data::swap(Data& x) noexcept +{ + m_data.swap(x.m_data); +} + +std::ostream& operator<<(std::ostream& stream, const Data& data) +{ + const uint8_t* ptr = data.GetPtr(); + int length = data.GetLength(); + stream << "Data[" << length << "]("; + for (int i = 0; i < length; ++i) + { + if (i) + stream << ", "; + stream << static_cast(ptr[i]); + } + stream << ")"; + return stream; +} + +} // namespace Dns +} // namespace Network diff --git a/Network/Dns/Data.h b/Network/Dns/Data.h new file mode 100644 index 0000000..9641bc4 --- /dev/null +++ b/Network/Dns/Data.h @@ -0,0 +1,55 @@ +#ifndef NETWORK_DNS_DATA_H +#define NETWORK_DNS_DATA_H + +#include +#include +#include + +namespace Network { +namespace Dns { + +class Data +{ +public: + Data(); + Data(const std::vector& data); + Data(const uint8_t* msg, unsigned length); + Data(const Data& data); + + uint8_t& GetAt(unsigned pos); + const uint8_t& GetAt(unsigned pos) const; + uint16_t& GetAt16(unsigned pos); + const uint16_t& GetAt16(unsigned pos) const; + uint32_t& GetAt32(unsigned pos); + const uint32_t& GetAt32(unsigned pos) const; + + const uint8_t* GetPtr() const; + unsigned GetLength() const; + + void append(const Data& data); + void append(uint8_t value); + void append(uint16_t value); + void append(uint32_t value); + void append(uint64_t value); + + template + void append(Arg1 value, Args ...args) + { + append(value); + append(args...); + } + + void prepend(const Data& data); + + void swap(Data& x) noexcept; + + friend std::ostream& operator<<(std::ostream& stream, const Data& data); + +private: + std::vector m_data; +}; + +} // namespace Dns +} // namespace Network + +#endif // NETWORK_DNS_DATA_H diff --git a/Network/Dns/DataReader.cpp b/Network/Dns/DataReader.cpp new file mode 100644 index 0000000..1155522 --- /dev/null +++ b/Network/Dns/DataReader.cpp @@ -0,0 +1,113 @@ +#include "DataReader.h" +#include + + +namespace Network { +namespace Dns { + +DataReader::DataReader(const uint8_t* data, unsigned length) : + m_data(data), + m_length(length) +{ +} + +DataReader::DataReader(const std::vector& data) : + DataReader(data.data(), data.size()) +{ +} + +const uint8_t* DataReader::GetPtr() const +{ + return m_data; +} + +unsigned DataReader::GetLength() const +{ + return m_length; +} + +uint8_t DataReader::operator[](unsigned index) const +{ + if (index >= m_length) + throw DataReaderOutOfBounds(); + return m_data[index]; +} + +DataReader& DataReader::operator+=(unsigned index) +{ + if (index > m_length) + throw DataReaderOutOfBounds(); + m_data += index; + m_length -= index; + return *this; +} + +const DataReader DataReader::operator+(unsigned index) const +{ + DataReader reader(*this); + return reader += index; +} + +uint8_t DataReader::Read8(unsigned& pos) const +{ + uint8_t value = + (static_cast((*this)[pos + 0]) << 0); + pos++; + return value; +} + +uint16_t DataReader::Read16(unsigned& pos) const +{ + uint16_t value = + (static_cast((*this)[pos + 0]) << 8) | + (static_cast((*this)[pos + 1]) << 0); + pos += 2; + return value; +} + +uint32_t DataReader::Read32(unsigned& pos) const +{ + uint32_t value = + (static_cast((*this)[pos + 0]) << 24) | + (static_cast((*this)[pos + 1]) << 16) | + (static_cast((*this)[pos + 2]) << 8) | + (static_cast((*this)[pos + 3]) << 0); + pos += 4; + return value; +} + +uint64_t DataReader::Read64(unsigned& pos) const +{ + uint64_t value = + (static_cast((*this)[pos + 0]) << 56) | + (static_cast((*this)[pos + 1]) << 48) | + (static_cast((*this)[pos + 2]) << 40) | + (static_cast((*this)[pos + 3]) << 32) | + (static_cast((*this)[pos + 4]) << 24) | + (static_cast((*this)[pos + 5]) << 16) | + (static_cast((*this)[pos + 6]) << 8) | + (static_cast((*this)[pos + 7]) << 0); + pos += 8; + return value; +} + +std::ostream& operator<<(std::ostream& stream, const DataReader& reader) +{ + stream << "DataReader[" << reader.m_length << "] = {"; + for (unsigned i = 0; i < reader.m_length; ++i) + { + if (i) + stream << ", "; + for(int j = 7; j >= 0; j--) + if (reader.m_data[i] & (1 << j)) + stream << "1"; + else + stream << "0"; + stream << " " << static_cast(reader.m_data[i]); + } + stream << "}"; + return stream; +} + +} // namespace Dns +} // namespace Network diff --git a/Network/Dns/DataReader.h b/Network/Dns/DataReader.h new file mode 100644 index 0000000..3a16478 --- /dev/null +++ b/Network/Dns/DataReader.h @@ -0,0 +1,46 @@ +#ifndef NETWORK_DNS_DATAREADER_H +#define NETWORK_DNS_DATAREADER_H + +#include +#include +#include +#include + + +namespace Network { +namespace Dns { + +class DataReader +{ +public: + DataReader(const uint8_t* data, unsigned length); + DataReader(const std::vector& data); + + const uint8_t* GetPtr() const; + unsigned GetLength() const; + + uint8_t operator[](unsigned index) const; + + DataReader& operator+=(unsigned index); + const DataReader operator+(unsigned index) const; + + uint8_t Read8(unsigned& pos) const; + uint16_t Read16(unsigned& pos) const; + uint32_t Read32(unsigned& pos) const; + uint64_t Read64(unsigned& pos) const; + + friend std::ostream& operator<<(std::ostream& strem, const DataReader& reader); + +private: + const uint8_t* m_data; + unsigned m_length; +}; + +class DataReaderOutOfBounds : public std::exception +{ +}; + +} // namespace Dns +} // namespace Network + +#endif // NETWORK_DNS_DATAREADER_H diff --git a/Network/Dns/Makefile b/Network/Dns/Makefile new file mode 120000 index 0000000..d0b0e8e --- /dev/null +++ b/Network/Dns/Makefile @@ -0,0 +1 @@ +../Makefile \ No newline at end of file diff --git a/Network/Dns/MalformedPacket.h b/Network/Dns/MalformedPacket.h new file mode 100644 index 0000000..d0b2296 --- /dev/null +++ b/Network/Dns/MalformedPacket.h @@ -0,0 +1,17 @@ +#ifndef NETWORK_DNS_MALFORMEDPACKET_H +#define NETWORK_DNS_MALFORMEDPACKET_H + +#include + + +namespace Network { +namespace Dns { + +class MalformedPacket : public std::exception +{ +}; + +} // namespace Dns +} // namespace Network + +#endif // NETWORK_DNS_MALFORMEDPACKET_H diff --git a/Network/Dns/MulticastDnsClient.cpp b/Network/Dns/MulticastDnsClient.cpp new file mode 100644 index 0000000..efde6ee --- /dev/null +++ b/Network/Dns/MulticastDnsClient.cpp @@ -0,0 +1,32 @@ +#include "Dns/MulticastDnsClient.h" +#include "MulticastDnsClientImpl.h" + + +namespace Network { +namespace Dns { + +MulticastDnsClient::MulticastDnsClient(std::function&)> callback) : + m_pMulticastDnsClientImpl(new MulticastDnsClientImpl(std::bind(&MulticastDnsClient::Callback, this, std::placeholders::_1))), + m_callback(callback) +{ +} + +MulticastDnsClient::~MulticastDnsClient() = default; + +void MulticastDnsClient::Query(const Dns::Query& query) +{ + m_pMulticastDnsClientImpl->Query(*query.PImpl()); +} + +void MulticastDnsClient::Callback(const std::vector& answers) +{ + std::vector ans; + + for (auto& answer : answers) + ans.push_back(ResourceRecord(std::make_shared(answer))); + + m_callback(ans); +} + +} // namespace Dns +} // namespace Network diff --git a/Network/Dns/MulticastDnsClientImpl.cpp b/Network/Dns/MulticastDnsClientImpl.cpp new file mode 100644 index 0000000..5ff3fba --- /dev/null +++ b/Network/Dns/MulticastDnsClientImpl.cpp @@ -0,0 +1,146 @@ +#include "MulticastDnsClientImpl.h" +#include "MalformedPacket.h" +#include "Packet.h" +#include + + +namespace Network { +namespace Dns { + +MulticastDnsClientImpl::MulticastDnsClientImpl(std::function&)> callback) : + m_timer(m_ioService), + m_responseBuffer(1024), + m_socket(m_ioService, asio::ip::udp::v4()), + m_multicastEndpoint(asio::ip::address::from_string("224.0.0.251"), 5353), + m_callback(callback) +{ + m_socket.set_option(asio::socket_base::reuse_address(true)); + m_socket.set_option(asio::ip::multicast::join_group(m_multicastEndpoint.address())); + m_socket.set_option(asio::ip::multicast::hops(255)); + m_socket.bind(asio::ip::udp::endpoint(asio::ip::udp::v4(), 5353)); + + m_thread = std::thread([&] { m_ioService.run(); }); +} + +MulticastDnsClientImpl::~MulticastDnsClientImpl() +{ + m_ioService.stop(); + m_thread.join(); +} + +void MulticastDnsClientImpl::Query(const QueryImpl& query) +{ + m_query = query; + m_iterations = 3; + SendQuery(); +} + +void MulticastDnsClientImpl::SetTimer(int milliseconds) +{ + m_timer.cancel(); + m_timer.expires_from_now(std::chrono::milliseconds(milliseconds)); +} + +void MulticastDnsClientImpl::SendQuery() +{ + if (m_iterations == 0) + return; + m_iterations--; + + Packet packet; + packet.AddQuery(m_query); + + auto data = std::make_shared(packet.GetData()); + + m_socket.async_send_to( + asio::buffer(data->GetPtr(), data->GetLength()), + m_multicastEndpoint, + std::bind(&MulticastDnsClientImpl::HandleQuerySent, this, + std::placeholders::_1, + data + ) + ); +} + +void MulticastDnsClientImpl::HandleQuerySent(const asio::error_code & error, std::shared_ptr data) +{ + if (error == asio::error::operation_aborted) + { + SendQuery(); + } + else if (error) + { + std::stringstream ss; + ss << "MulticastDnsClientImpl::HandleQuerySent: " << error.message(); + Logging::Log(Logging::Severity::Error, ss.str()); + } + else + { + SetTimer(1000); + m_timer.async_wait(std::bind( + &MulticastDnsClientImpl::HandleQueryTimeout, this, + std::placeholders::_1 + )); + ReceiveQuery(); + } +} + +void MulticastDnsClientImpl::ReceiveQuery() +{ + m_socket.async_receive_from( + asio::buffer(m_responseBuffer), + m_receiveEndpoint, + std::bind(&MulticastDnsClientImpl::HandleQueryResponse, this, + std::placeholders::_1, + std::placeholders::_2 + ) + ); +} + +void MulticastDnsClientImpl::HandleQueryResponse(const asio::error_code & error, std::size_t size) +{ + if (error == asio::error::operation_aborted) + { + SendQuery(); + } + else if (error) + { + std::stringstream ss; + ss << "MulticastDnsClientImpl::HandleQueryResponse: " << error.message(); + Logging::Log(Logging::Severity::Error, ss.str()); + } + else + { + try + { + Packet packet(DataReader(m_responseBuffer.data(), size), 0); + if (packet.GetQuery()) + m_callback(packet.GetAnswers()); + } + catch(MalformedPacket& mp) + { + } + + ReceiveQuery(); + } +} + +void MulticastDnsClientImpl::HandleQueryTimeout(const asio::error_code & error) +{ + if (error == asio::error::operation_aborted) + { + } + else if (error) + { + std::stringstream ss; + ss << "MulticastDnsClientImpl::HandleQueryTimeout: " << error.message(); + Logging::Log(Logging::Severity::Error, ss.str()); + } + else + { + m_socket.cancel(); + } +} + +} // namespace Dns +} // namespace Network diff --git a/Network/Dns/MulticastDnsClientImpl.h b/Network/Dns/MulticastDnsClientImpl.h new file mode 100644 index 0000000..4bc09df --- /dev/null +++ b/Network/Dns/MulticastDnsClientImpl.h @@ -0,0 +1,52 @@ +#ifndef NETWORK_DNS_MULTICASTDNSCLIENTIMPL_H +#define NETWORK_DNS_MULTICASTDNSCLIENTIMPL_H + +#include "QueryImpl.h" +#include "ResourceRecordImpl.h" +#include +#include +#include +#include +#include + + +namespace Network { +namespace Dns { + +class MulticastDnsClientImpl +{ +public: + MulticastDnsClientImpl(std::function&)> callback); + ~MulticastDnsClientImpl(); + +public: + void Query(const QueryImpl& query); + +private: + void SetTimer(int milliseconds); + void SendQuery(); + void HandleQuerySent(const asio::error_code & error, std::shared_ptr data); + void ReceiveQuery(); + void HandleQueryResponse(const asio::error_code & error, std::size_t size); + void HandleQueryTimeout(const asio::error_code & error); + +private: + std::thread m_thread; + asio::io_service m_ioService; + asio::system_timer m_timer; + + std::vector m_responseBuffer; + + asio::ip::udp::socket m_socket; + asio::ip::udp::endpoint m_multicastEndpoint; + asio::ip::udp::endpoint m_receiveEndpoint; + + int m_iterations; + QueryImpl m_query; + std::function&)> m_callback; +}; + +} // namespace Dns +} // namespace Network + +#endif // NETWORK_DNS_MULTICASTDNSCLIENTIMPL_H diff --git a/Network/Dns/Name.cpp b/Network/Dns/Name.cpp new file mode 100644 index 0000000..0c970cc --- /dev/null +++ b/Network/Dns/Name.cpp @@ -0,0 +1,79 @@ +#include "Dns/Name.h" +#include "NameImpl.h" + + +namespace Network { +namespace Dns { + +Name::Name() : + m_pNameImpl(new NameImpl()) +{ +} + +Name::Name(std::shared_ptr pNameImpl) : + m_pNameImpl(pNameImpl) +{ +} + +Name::Name(const std::vector& labels) : + m_pNameImpl(new NameImpl(labels)) +{ +} + +std::string Name::GetFirstLabel() const +{ + return m_pNameImpl->GetFirstLabel(); +} + +std::string Name::GetName() const +{ + return m_pNameImpl->GetName(); +} + +bool Name::empty() const +{ + return m_pNameImpl->empty(); +} + +bool Name::endsWith(const Name& suffix) const +{ + return m_pNameImpl->endsWith(*suffix.PImpl()); +} + +void Name::append(const std::string& label) +{ + m_pNameImpl->append(label); +} + +void Name::prepend(const std::string& label) +{ + m_pNameImpl->prepend(label); +} + +void Name::swap(Name& x) noexcept +{ + m_pNameImpl->swap(*x.PImpl()); +} + +bool Name::operator==(const Name& name) const +{ + return m_pNameImpl->operator==(*name.PImpl()); +} + +bool Name::operator!=(const Name& name) const +{ + return m_pNameImpl->operator!=(*name.PImpl()); +} + +bool Name::operator<(const Name& name) const +{ + return m_pNameImpl->operator<(*name.PImpl()); +} + +NameImpl* Name::PImpl() const +{ + return m_pNameImpl.get(); +} + +} // namespace Dns +} // namespace Network diff --git a/Network/Dns/NameImpl.cpp b/Network/Dns/NameImpl.cpp new file mode 100644 index 0000000..9d7bdcb --- /dev/null +++ b/Network/Dns/NameImpl.cpp @@ -0,0 +1,146 @@ +#include "NameImpl.h" +#include "MalformedPacket.h" + + +namespace Network { +namespace Dns { + +NameImpl::NameImpl() +{ +} + +NameImpl::NameImpl(const std::vector& labels) : + m_labels(labels) +{ +} + +std::string NameImpl::GetFirstLabel() const +{ + if (m_labels.empty()) + return std::string(); + return m_labels[0]; +} + +std::string NameImpl::GetName() const +{ + std::string name; + for (const std::string& s : m_labels) + { + if(name != "") + name += '.'; + name += s; + } + return name; +} + +bool NameImpl::empty() const +{ + return m_labels.empty(); +} + +bool NameImpl::endsWith(const NameImpl& name) const +{ + unsigned size_l = m_labels.size(); + unsigned name_size_l = name.m_labels.size(); + if (name_size_l > size_l) + return false; + for (size_t i = 0; i < name_size_l; i++) + if (m_labels[size_l - 1 - i] != name.m_labels[name_size_l - 1 - i]) + return false; + return true; +} + +Data NameImpl::GetData() const +{ + Data data; + for (const std::string& s : m_labels) + { + data.append(static_cast(s.length())); + for (char c : s) + data.append((uint8_t) c); + } + data.append((uint8_t) 0); + return data; +} + +void NameImpl::append(const std::string& label) +{ + m_labels.push_back(label); +} + +void NameImpl::prepend(const std::string& label) +{ + m_labels.insert(m_labels.begin(), label); +} + +unsigned NameImpl::ReadFromData(DataReader reader, unsigned pos) +{ + try + { + std::vector newLabels; + const uint8_t twoHighBits = 3 << 6; // = 11000000 + unsigned maxPos = pos; + unsigned start = pos; + while (reader[pos] != 0) + { + if ((reader[pos] & twoHighBits) == twoHighBits) + { + unsigned newPos = ((static_cast(reader[pos] ^ twoHighBits)) << 8) | reader[pos + 1]; + if(newPos >= start) + throw MalformedPacket(); + maxPos = std::max(maxPos, pos + 2); + start = pos = newPos; + } + else + { + unsigned length = reader[pos]; + std::string label; + for (unsigned i = 1; i <= length; i++) + label += static_cast(reader[pos + i]); + pos += length + 1; + newLabels.emplace_back(label); + maxPos = std::max(maxPos, pos); + } + } + m_labels.swap(newLabels); + return std::max(maxPos, pos + 1); + } + catch(const DataReaderOutOfBounds& droob) + { + throw MalformedPacket(); + } +} + +void NameImpl::swap(NameImpl& x) noexcept +{ + std::swap(m_labels, x.m_labels); +} + +bool NameImpl::operator==(const NameImpl& name) const +{ + return m_labels == name.m_labels; +} + +bool NameImpl::operator!=(const NameImpl& name) const +{ + return !(*this == name); +} + +bool NameImpl::operator<(const NameImpl& name) const +{ + return m_labels < name.m_labels; +} + +std::ostream& operator<<(std::ostream& stream, const NameImpl& name) +{ + for (int i = 0; i < static_cast(name.m_labels.size()); ++i) + { + if (i) + stream << "."; + stream << "\"" << name.m_labels[i] << "\""; + } + return stream; +} + +} // namespace Dns +} // namespace Network diff --git a/Network/Dns/NameImpl.h b/Network/Dns/NameImpl.h new file mode 100644 index 0000000..0ae1777 --- /dev/null +++ b/Network/Dns/NameImpl.h @@ -0,0 +1,52 @@ +#ifndef NETWORK_DNS_NAMEIMPL_H +#define NETWORK_DNS_NAMEIMPL_H + +#include "Data.h" +#include "DataReader.h" +#include +#include + + +namespace Network { +namespace Dns { + +class NameImpl +{ +public: + NameImpl(); + + template + NameImpl(const std::string& label, const Args& ...args) : NameImpl(args...) + { + m_labels.insert(m_labels.begin(), label); + } + + NameImpl(const std::vector& labels); + + std::string GetFirstLabel() const; + std::string GetName() const; + bool empty() const; + bool endsWith(const NameImpl& suffix) const; + Data GetData() const; + + void append(const std::string& label); + void prepend(const std::string& label); + + unsigned ReadFromData(DataReader reader, unsigned pos); + + void swap(NameImpl& x) noexcept; + + bool operator==(const NameImpl& name) const; + bool operator!=(const NameImpl& name) const; + bool operator<(const NameImpl& name) const; + + friend std::ostream& operator<<(std::ostream& stream, const NameImpl& name); + +private: + std::vector m_labels; +}; + +} // namespace Dns +} // namespace Network + +#endif // NETWORK_DNS_NAMEIMPL_H diff --git a/Network/Dns/OperationCode.h b/Network/Dns/OperationCode.h new file mode 100644 index 0000000..d3b899f --- /dev/null +++ b/Network/Dns/OperationCode.h @@ -0,0 +1,22 @@ +#ifndef NETWORK_DNS_OPERATIONCODE_H +#define NETWORK_DNS_OPERATIONCODE_H + + +namespace Network { +namespace Dns { + +class OperationCode +{ +public: + enum type + { + QUERY = 0, + IQUERY = 1, + STATUS = 2 + }; +}; + +} // namespace Dns +} // namespace Network + +#endif // NETWORK_DNS_OPERATIONCODE_H diff --git a/Network/Dns/Packet.cpp b/Network/Dns/Packet.cpp new file mode 100644 index 0000000..476b4ed --- /dev/null +++ b/Network/Dns/Packet.cpp @@ -0,0 +1,284 @@ +#include "Packet.h" +#include "MalformedPacket.h" + + +namespace Network { +namespace Dns { + +Packet::Packet() : + m_identifier(0), + m_query(0), + m_authorativeAnswer(0), + m_truncation(0), + m_recursionDesired(0), + m_recursionAvailable(0), + m_zero(0), + m_operationCode(OperationCode::QUERY), + m_responseCode(ResponseCode::NO_ERROR) +{ +} + +Packet::Packet(DataReader reader, unsigned pos) +{ + try + { + m_identifier = reader.Read16(pos); + + uint8_t flags = reader.Read8(pos); + m_query = (flags & (1 << 7)) >> 7; // 10000000 + m_operationCode = static_cast(((flags & (((1 << 4) - 1) << 3)) >> 3)); // 01111000 + m_authorativeAnswer = (flags & (1 << 2)) >> 2; // 00000100 + m_truncation = (flags & (1 << 1)) >> 1; // 00000010 + m_recursionDesired = (flags & (1 << 0)) >> 0; // 00000001 + + flags = reader.Read8(pos); + m_recursionAvailable = (flags & (1 << 7)) >> 7; // 10000000 + m_zero = (flags & (((1 << 3) - 1) << 4)) >> 4; // 01110000 + m_responseCode = static_cast((flags & ((1 << 4) - 1))); // 00001111 + + unsigned queryCount = reader.Read16(pos); + unsigned answerCount = reader.Read16(pos); + unsigned authorityCount = reader.Read16(pos); + unsigned additionalRecordsCount = reader.Read16(pos); + + for (size_t i = 0; i < queryCount; i++) + { + m_queries.emplace_back(); + pos = m_queries.back().ReadFromData(reader, pos); + } + + for (size_t i = 0; i < answerCount; i++) + { + m_answers.emplace_back(); + pos = m_answers.back().ReadFromData(reader, pos); + } + + for (size_t i = 0; i < authorityCount; i++) + { + m_authorities.emplace_back(); + pos = m_authorities.back().ReadFromData(reader, pos); + } + + for (size_t i = 0; i < additionalRecordsCount; i++) + { + m_additionalRecords.emplace_back(); + pos = m_additionalRecords.back().ReadFromData(reader, pos); + } + } + catch (const DataReaderOutOfBounds& droob) + { + throw MalformedPacket(); + } +} + +Data Packet::GetData() const +{ + Data data; + data.append(static_cast(m_identifier)); + + uint8_t flags = + (static_cast(m_query) << 7) | + (static_cast(m_operationCode) << 3) | + (static_cast(m_authorativeAnswer) << 2) | + (static_cast(m_truncation) << 1) | + (static_cast(m_recursionDesired) << 0); + data.append(flags); + + flags = + (static_cast(m_recursionAvailable) << 7) | + (static_cast(m_zero) << 4) | + (static_cast(m_responseCode) << 0); + data.append(flags); + + data.append(static_cast(GetQueryCount())); + data.append(static_cast(GetAnswerCount())); + data.append(static_cast(GetAuthorityCount())); + data.append(static_cast(GetAdditionalRecordsCount())); + + for (const QueryImpl& q : m_queries) + data.append(q.GetData()); + for (const ResourceRecordImpl& r : m_answers) + data.append(r.GetData()); + for (const ResourceRecordImpl& r : m_authorities) + data.append(r.GetData()); + for (const ResourceRecordImpl& r : m_additionalRecords) + data.append(r.GetData()); + return data; +} + +uint16_t Packet::GetIdentifier() const +{ + return m_identifier; +} + +bool Packet::GetQuery() const +{ + return m_query; +} + +OperationCode::type Packet::GetOperationCode() const +{ + return m_operationCode; +} + +bool Packet::GetAuthorativeAnswer() const +{ + return m_authorativeAnswer; +} + +bool Packet::GetTruncation() const +{ + return m_truncation; +} + +bool Packet::GetRecursionDesired() const +{ + return m_recursionDesired; +} + +bool Packet::GetRecursionAvailable() const +{ + return m_recursionAvailable; +} + +uint8_t Packet::GetZero() const +{ + return m_zero; +} + +ResponseCode::type Packet::GetResponseCode() const +{ + return m_responseCode; +} + +std::vector Packet::GetQueries() const +{ + return m_queries; +} + +std::vector Packet::GetAnswers() const +{ + return m_answers; +} + +std::vector Packet::GetAuthorities() const +{ + return m_authorities; +} + +std::vector Packet::GetAdditionalRecords() const +{ + return m_additionalRecords; +} + +unsigned Packet::GetQueryCount() const +{ + return static_cast(m_queries.size()); +} + +unsigned Packet::GetAnswerCount() const +{ + return static_cast(m_answers.size()); +} + +unsigned Packet::GetAuthorityCount() const +{ + return static_cast(m_authorities.size()); +} + +unsigned Packet::GetAdditionalRecordsCount() const +{ + return static_cast(m_additionalRecords.size()); +} + +void Packet::SetIdentifier(uint16_t identifier) +{ + m_identifier = identifier; +} + +void Packet::SetQuery(bool query) +{ + m_query = query; +} + +void Packet::SetOperationCode(OperationCode::type operationCode) +{ + m_operationCode = operationCode; +} + +void Packet::SetAuthorativeAnswer(bool authorativeAnswer) +{ + m_authorativeAnswer = authorativeAnswer; +} + +void Packet::SetTruncation(bool truncation) +{ + m_truncation = truncation; +} + +void Packet::SetRecursionDesired(bool recursionDesired) +{ + m_recursionDesired = recursionDesired; +} + +void Packet::SetRecursionAvailable(bool recursionAvailable) +{ + m_recursionAvailable = recursionAvailable; +} + +void Packet::SetZero(uint8_t zero) +{ + m_zero = zero; +} + +void Packet::SetResponseCode(ResponseCode::type responseCode) +{ + m_responseCode = responseCode; +} + +void Packet::AddQuery(const QueryImpl& query) +{ + m_queries.emplace_back(query); +} + +void Packet::AddAnswer(const ResourceRecordImpl& record) +{ + m_answers.emplace_back(record); +} + +void Packet::AddAuthority(const ResourceRecordImpl& record) +{ + m_authorities.emplace_back(record); +} + +void Packet::AddAdditionalRecords(const ResourceRecordImpl& record) +{ + m_additionalRecords.emplace_back(record); +} + +std::ostream& operator<<(std::ostream& stream, const Packet& packet) +{ + stream << "Packet (\n"; + stream << " identifier = " << packet.m_identifier + << ", query = " << packet.m_query + << ", operationCode = " << static_cast(packet.m_operationCode) + << ", authorativeAnswer = " << packet.m_authorativeAnswer + << ", truncation = " << packet.m_truncation + << ", recursionDesired = " << packet.m_recursionDesired << "\n"; + stream << " recursionAvailable = " << packet.m_recursionAvailable + << ", zero = " << packet.m_zero + << ", responseCode = " << static_cast(packet.m_responseCode) << "\n"; + for (const QueryImpl& q : packet.m_queries) + stream << " Query: " << q << "\n"; + for (const ResourceRecordImpl& r : packet.m_answers) + stream << " Answer: " << r << "\n"; + for (const ResourceRecordImpl& r : packet.m_authorities) + stream << " Authority: " << r << "\n"; + for (const ResourceRecordImpl& r : packet.m_additionalRecords) + stream << " AdditionalRecord: " << r << "\n"; + stream << ")\n"; + return stream; +} + +} // namespace Dns +} // namespace Network diff --git a/Network/Dns/Packet.h b/Network/Dns/Packet.h new file mode 100644 index 0000000..cf0d0ba --- /dev/null +++ b/Network/Dns/Packet.h @@ -0,0 +1,79 @@ +#ifndef NETWORK_DNS_PACKET_H +#define NETWORK_DNS_PACKET_H + +#include "Data.h" +#include "DataReader.h" +#include "OperationCode.h" +#include "QueryImpl.h" +#include "ResourceRecordImpl.h" +#include "ResponseCode.h" + + +namespace Network { +namespace Dns { + +class Packet +{ +public: + Packet(); + Packet(DataReader reader, unsigned pos); + + Data GetData() const; + + uint16_t GetIdentifier() const; + bool GetQuery() const; + OperationCode::type GetOperationCode() const; + bool GetAuthorativeAnswer() const; + bool GetTruncation() const; + bool GetRecursionDesired() const; + bool GetRecursionAvailable() const; + uint8_t GetZero() const; + ResponseCode::type GetResponseCode() const; + + std::vector GetQueries() const; + std::vector GetAnswers() const; + std::vector GetAuthorities() const; + std::vector GetAdditionalRecords() const; + + unsigned GetQueryCount() const; + unsigned GetAnswerCount() const; + unsigned GetAuthorityCount() const; + unsigned GetAdditionalRecordsCount() const; + + void SetIdentifier(uint16_t identifier); + void SetQuery(bool query); + void SetOperationCode(OperationCode::type oerationCode); + void SetAuthorativeAnswer(bool authorativeAnswer); + void SetTruncation(bool truncation); + void SetRecursionDesired(bool recursionDesired); + void SetRecursionAvailable(bool recursionAvailable); + void SetZero(uint8_t zero); + void SetResponseCode(ResponseCode::type responseCode); + + void AddQuery(const QueryImpl& query); + void AddAnswer(const ResourceRecordImpl& record); + void AddAuthority(const ResourceRecordImpl& record); + void AddAdditionalRecords(const ResourceRecordImpl& record); + + friend std::ostream& operator<<(std::ostream& stream, const Packet& packet); + +private: + unsigned m_identifier : 16; + unsigned m_query : 1; + unsigned m_authorativeAnswer : 1; + unsigned m_truncation : 1; + unsigned m_recursionDesired : 1; + unsigned m_recursionAvailable : 1; + unsigned m_zero : 3; + OperationCode::type m_operationCode; + ResponseCode::type m_responseCode; + std::vector m_queries; + std::vector m_answers; + std::vector m_authorities; + std::vector m_additionalRecords; +}; + +} // namespace Dns +} // namespace Network + +#endif // NETWORK_DNS_PACKET_H diff --git a/Network/Dns/Query.cpp b/Network/Dns/Query.cpp new file mode 100644 index 0000000..ba6c6f5 --- /dev/null +++ b/Network/Dns/Query.cpp @@ -0,0 +1,59 @@ +#include "Dns/Query.h" +#include "QueryImpl.h" + + +namespace Network { +namespace Dns { + +Query::Query() : + m_pQueryImpl(new QueryImpl()) +{ +} + +bool Query::GetUnicast() const +{ + return m_pQueryImpl->GetUnicast(); +} + +QueryType::type Query::GetType() const +{ + return m_pQueryImpl->GetType(); +} + +QueryClass::type Query::GetClass() const +{ + return m_pQueryImpl->GetClass(); +} + +Name Query::GetName() const +{ + return Name(m_pQueryImpl->GetName()); +} + +void Query::SetUnicast(bool unicast) +{ + m_pQueryImpl->SetUnicast(unicast); +} + +void Query::SetType(QueryType::type type) +{ + m_pQueryImpl->SetType(type); +} + +void Query::SetClass(QueryClass::type class_) +{ + m_pQueryImpl->SetClass(class_); +} + +void Query::SetName(const Name& name) +{ + m_pQueryImpl->SetName(*name.PImpl()); +} + +QueryImpl* Query::PImpl() const +{ + return m_pQueryImpl.get(); +} + +} // namespace Dns +} // namespace Network diff --git a/Network/Dns/QueryImpl.cpp b/Network/Dns/QueryImpl.cpp new file mode 100644 index 0000000..785fc53 --- /dev/null +++ b/Network/Dns/QueryImpl.cpp @@ -0,0 +1,102 @@ +#include "QueryImpl.h" +#include "MalformedPacket.h" + + +namespace Network { +namespace Dns { + +QueryImpl::QueryImpl() : + m_unicast(false) +{ +} + +bool QueryImpl::GetUnicast() const +{ + return m_unicast; +} + +QueryType::type QueryImpl::GetType() const +{ + return m_type; +} + +QueryClass::type QueryImpl::GetClass() const +{ + return m_class; +} + +NameImpl QueryImpl::GetName() const +{ + return m_name; +} + +void QueryImpl::SetUnicast(bool unicast) +{ + m_unicast = unicast; +} + +void QueryImpl::SetType(QueryType::type type) +{ + m_type = type; +} + +void QueryImpl::SetClass(QueryClass::type class_) +{ + m_class = class_; +} + +void QueryImpl::SetName(const NameImpl& name) +{ + m_name = name; +} + +Data QueryImpl::GetData() const +{ + Data data = m_name.GetData(); + data.append(static_cast(m_type)); + uint16_t classField = static_cast(m_class); + if (m_unicast) + classField |= (static_cast(1) << 15); + data.append(classField); + return data; +} + +unsigned QueryImpl::ReadFromData(DataReader reader, unsigned pos) +{ + try + { + NameImpl newName; + pos = newName.ReadFromData(reader, pos); + QueryType::type newType = static_cast(reader.Read16(pos)); + bool newUnicast = false; + + uint16_t classField = reader.Read16(pos); + if (classField & (static_cast(1) << 15)) + { + classField ^= static_cast(1) << 15; + newUnicast = true; + } + QueryClass::type newClass = static_cast(classField); + + std::swap(m_name, newName); + std::swap(m_type, newType); + std::swap(m_class, newClass); + std::swap(m_unicast, newUnicast); + return pos; + } + catch (const DataReaderOutOfBounds& droob) + { + throw MalformedPacket(); + } +} + +std::ostream& operator<<(std::ostream& stream, const QueryImpl& query) +{ + return stream << "Query(" << query.m_name + << ", type = " << static_cast(query.m_type) + << ", class = " << static_cast(query.m_class) + << ", " << (query.m_unicast ? "Unicast" : "Multicast") << ")"; +} + +} // namespace Dns +} // namespace Network diff --git a/Network/Dns/QueryImpl.h b/Network/Dns/QueryImpl.h new file mode 100644 index 0000000..7db5225 --- /dev/null +++ b/Network/Dns/QueryImpl.h @@ -0,0 +1,45 @@ +#ifndef NETWORK_DNS_QUERYIMPL_H +#define NETWORK_DNS_QUERYIMPL_H + +#include "Data.h" +#include "DataReader.h" +#include "NameImpl.h" +#include "Dns/QueryClass.h" +#include "Dns/QueryType.h" + + +namespace Network { +namespace Dns { + +class QueryImpl +{ +public: + QueryImpl(); + + bool GetUnicast() const; + QueryType::type GetType() const; + QueryClass::type GetClass() const; + NameImpl GetName() const; + + void SetUnicast(bool unicast); + void SetType(QueryType::type type); + void SetClass(QueryClass::type class_); + void SetName(const NameImpl& name); + + Data GetData() const; + + unsigned ReadFromData(DataReader reader, unsigned pos); + + friend std::ostream& operator<<(std::ostream & stream, const QueryImpl& query); + +private: + bool m_unicast; + NameImpl m_name; + QueryType::type m_type; + QueryClass::type m_class; +}; + +} // namespace Dns +} // namespace Network + +#endif // NETWORK_DNS_QUERYIMPL_H diff --git a/Network/Dns/ResourceRecord.cpp b/Network/Dns/ResourceRecord.cpp new file mode 100644 index 0000000..be2938b --- /dev/null +++ b/Network/Dns/ResourceRecord.cpp @@ -0,0 +1,82 @@ +#include "Dns/ResourceRecord.h" +#include "ResourceRecordImpl.h" + + +namespace Network { +namespace Dns { + +ResourceRecord::ResourceRecord(std::shared_ptr pResourceRecordImpl) : + m_pResourceRecordImpl(pResourceRecordImpl) +{ +} + +Name ResourceRecord::GetName() const +{ + return Name(m_pResourceRecordImpl->GetName()); +} + +Type::type ResourceRecord::GetType() const +{ + return m_pResourceRecordImpl->GetType(); +} + +Class::type ResourceRecord::GetClass() const +{ + return m_pResourceRecordImpl->GetClass(); +} + +uint32_t ResourceRecord::GetTTL() const +{ + return m_pResourceRecordImpl->GetTTL(); +} + +std::vector ResourceRecord::GetResourceData() const +{ + Data data = m_pResourceRecordImpl->GetResourceData(); + + const uint8_t* ptr = data.GetPtr(); + int length = data.GetLength(); + + std::vector returnValue(length); + for (int i = 0; i < length; i++) + returnValue[i] = ptr[i]; + + return returnValue; +} + +std::vector ResourceRecord::GetData() const +{ + Data data = m_pResourceRecordImpl->GetData(); + + const uint8_t* ptr = data.GetPtr(); + int length = data.GetLength(); + + std::vector returnValue(length); + for (int i = 0; i < length; i++) + returnValue[i] = ptr[i]; + + return returnValue; +} + +void ResourceRecord::SetName(const Name& name) +{ + return m_pResourceRecordImpl->SetName(*name.PImpl()); +} + +void ResourceRecord::SetType(Type::type type) +{ + return m_pResourceRecordImpl->SetType(type); +} + +void ResourceRecord::SetClass(Class::type class_) +{ + return m_pResourceRecordImpl->SetClass(class_); +} + +void ResourceRecord::SetTTL(uint32_t ttl) +{ + return m_pResourceRecordImpl->SetTTL(ttl); +} + +} // namespace Dns +} // namespace Network diff --git a/Network/Dns/ResourceRecordImpl.cpp b/Network/Dns/ResourceRecordImpl.cpp new file mode 100644 index 0000000..2935a44 --- /dev/null +++ b/Network/Dns/ResourceRecordImpl.cpp @@ -0,0 +1,121 @@ +#include "ResourceRecordImpl.h" +#include "MalformedPacket.h" + + +namespace Network { +namespace Dns { + +NameImpl ResourceRecordImpl::GetName() const +{ + return m_name; +} + +Type::type ResourceRecordImpl::GetType() const +{ + return m_type; +} + +Class::type ResourceRecordImpl::GetClass() const +{ + return m_class; +} + +uint32_t ResourceRecordImpl::GetTTL() const +{ + return m_ttl; +} + +Data ResourceRecordImpl::GetResourceData() const +{ + return m_resourceData; +} + +NameImpl ResourceRecordImpl::GetResourceDataName() const +{ + return m_resourceDataName; +} + +Data ResourceRecordImpl::GetData() const +{ + Data data = m_name.GetData(); + data.append(static_cast(m_type)); + data.append(static_cast(m_class)); + data.append(static_cast(m_ttl)); + data.append(static_cast(m_resourceData.GetLength())); + data.append(m_resourceData); + return data; +} + +void ResourceRecordImpl::SetName(const NameImpl& name) +{ + m_name = name; +} + +void ResourceRecordImpl::SetType(Type::type type) +{ + m_type = type; +} + +void ResourceRecordImpl::SetClass(Class::type dnsClass) +{ + m_class = dnsClass; +} + +void ResourceRecordImpl::SetTTL(uint32_t ttl) +{ + m_ttl = ttl; +} + +void ResourceRecordImpl::SetData(const Data& resourceData) +{ + m_resourceData = resourceData; +} + +unsigned ResourceRecordImpl::ReadFromData(DataReader reader, unsigned pos) +{ + try + { + NameImpl newName; + unsigned oldpos = pos; + pos = newName.ReadFromData(reader, pos); + Type::type newType = static_cast(reader.Read16(pos)); + Class::type newClass = static_cast(reader.Read16(pos)); + uint32_t newTTL = reader.Read32(pos); + Data newRData; + unsigned rdlength = reader.Read16(pos); + for (size_t i = 0; i < rdlength; i++) + newRData.append(reader.Read8(pos)); + std::swap(m_name, newName); + std::swap(m_type, newType); + std::swap(m_class, newClass); + std::swap(m_ttl, newTTL); + std::swap(m_resourceData, newRData); + + try + { + m_resourceDataName.ReadFromData(reader, pos - m_resourceData.GetLength()); + } + catch (MalformedPacket& mp) + { + m_resourceDataName = NameImpl(); + } + + return pos; + } + catch (const DataReaderOutOfBounds& droob) + { + throw MalformedPacket(); + } +} + +std::ostream& operator<<(std::ostream& stream, const ResourceRecordImpl& record) +{ + return stream << "ResourceRecord(" << record.m_name + << ", type = " << static_cast(record.m_type) + << ", class = " << static_cast(record.m_class) + << ", ttl = " << record.m_ttl + << ", rdata = " << record.m_resourceData << ")"; +} + +} // namespace Dns +} // namespace Network diff --git a/Network/Dns/ResourceRecordImpl.h b/Network/Dns/ResourceRecordImpl.h new file mode 100644 index 0000000..c437625 --- /dev/null +++ b/Network/Dns/ResourceRecordImpl.h @@ -0,0 +1,48 @@ +#ifndef NETWORK_DNS_RESOURCERECORD_H +#define NETWORK_DNS_RESOURCERECORD_H + +#include "Data.h" +#include "DataReader.h" +#include "NameImpl.h" +#include "Dns/Class.h" +#include "Dns/Type.h" + + +namespace Network { +namespace Dns { + +class ResourceRecordImpl +{ +public: + NameImpl GetName() const; + Type::type GetType() const; + Class::type GetClass() const; + uint32_t GetTTL() const; + Data GetResourceData() const; + NameImpl GetResourceDataName() const; + + Data GetData() const; + + void SetName(const NameImpl& name); + void SetType(Type::type type); + void SetClass(Class::type dnsClass); + void SetTTL(uint32_t ttl); + void SetData(const Data& resourceData); + + unsigned ReadFromData(DataReader reader, unsigned pos); + + friend std::ostream& operator<<(std::ostream& stream, const ResourceRecordImpl& record); + +private: + NameImpl m_name; + Type::type m_type; + Class::type m_class; + uint32_t m_ttl; + Data m_resourceData; + NameImpl m_resourceDataName; +}; + +} // namespace Dns +} // namespace Network + +#endif // NETWORK_DNS_RESOURCERECORD_H diff --git a/Network/Dns/ResponseCode.h b/Network/Dns/ResponseCode.h new file mode 100644 index 0000000..4e6930b --- /dev/null +++ b/Network/Dns/ResponseCode.h @@ -0,0 +1,25 @@ +#ifndef NETWORK_DNS_RESPONSECODE_H +#define NETWORK_DNS_RESPONSECODE_H + + +namespace Network { +namespace Dns { + +class ResponseCode +{ +public: + enum type + { + NO_ERROR = 0, + FORMAT_ERROR = 1, + SERVER_FAILURE = 2, + NAME_ERROR = 3, + NOT_IMPLEMENTED = 4, + REFUSED = 5 + }; +}; + +} // namespace Dns +} // namespace Network + +#endif // NETWORK_DNS_RESPONSECODE_H diff --git a/include/Dns/Class.h b/include/Dns/Class.h new file mode 100644 index 0000000..b73942a --- /dev/null +++ b/include/Dns/Class.h @@ -0,0 +1,24 @@ +#ifndef CLASS_H +#define CLASS_H + + +namespace Network { +namespace Dns { + +class Class +{ +public: + enum type + { + NONE = 0, + IN = 1, + CS = 2, + CH = 3, + HS = 4 + }; +}; + +} // namespace Dns +} // namespace Network + +#endif // CLASS_H diff --git a/include/Dns/MulticastDnsClient.h b/include/Dns/MulticastDnsClient.h new file mode 100644 index 0000000..b1c5918 --- /dev/null +++ b/include/Dns/MulticastDnsClient.h @@ -0,0 +1,36 @@ +#ifndef MULTICASTDNSCLIENT_H +#define MULTICASTDNSCLIENT_H + +#include "Query.h" +#include "ResourceRecord.h" +#include +#include +#include + + +namespace Network { +namespace Dns { + +class MulticastDnsClientImpl; + +class MulticastDnsClient +{ +public: + MulticastDnsClient(std::function&)> callback); + ~MulticastDnsClient(); + +public: + void Query(const Dns::Query& query); + +private: + void Callback(const std::vector& data); + +private: + std::unique_ptr m_pMulticastDnsClientImpl; + std::function&)> m_callback; +}; + +} // namespace Dns +} // namespace Network + +#endif // MULTICASTDNSCLIENT_H diff --git a/include/Dns/Name.h b/include/Dns/Name.h new file mode 100644 index 0000000..8ca5ae3 --- /dev/null +++ b/include/Dns/Name.h @@ -0,0 +1,73 @@ +#ifndef NAME_H +#define NAME_H + +#include +#include +#include +#include + + +namespace Network { +namespace Dns { + +class NameImpl; + +class Name +{ +public: + Name(); + + template + Name(const Args& ...args) : + Name(toStringVector(args...)) + { + } + + Name(const std::vector& labels); + + std::string GetFirstLabel() const; + std::string GetName() const; + bool empty() const; + bool endsWith(const Name& suffix) const; + + void append(const std::string& label); + void prepend(const std::string& label); + + void swap(Name& x) noexcept; + + bool operator==(const Name& name) const; + bool operator!=(const Name& name) const; + bool operator<(const Name& name) const; + +private: + Name(std::shared_ptr pNameImpl); + NameImpl* PImpl() const; + +private: + template + std::string toString(T&& t) + { + std::stringstream s; + s << std::forward(t); + return s.str(); + } + + template + std::vector toStringVector(T&&... args) + { + std::vector res { toString(std::forward(args))... }; + return res; + } + +private: + friend class Query; + friend class ResourceRecord; + +private: + std::shared_ptr m_pNameImpl; +}; + +} // namespace Dns +} // namespace Network + +#endif // NAME_H diff --git a/include/Dns/Query.h b/include/Dns/Query.h new file mode 100644 index 0000000..d99ed54 --- /dev/null +++ b/include/Dns/Query.h @@ -0,0 +1,43 @@ +#ifndef QUERY_H +#define QUERY_H + +#include "Name.h" +#include "QueryClass.h" +#include "QueryType.h" +#include + + +namespace Network { +namespace Dns { + +class QueryImpl; + +class Query +{ +public: + Query(); + + bool GetUnicast() const; + QueryType::type GetType() const; + QueryClass::type GetClass() const; + Name GetName() const; + + void SetUnicast(bool unicast); + void SetType(QueryType::type type); + void SetClass(QueryClass::type class_); + void SetName(const Name& name); + +private: + QueryImpl* PImpl() const; + +private: + friend class MulticastDnsClient; + +private: + std::shared_ptr m_pQueryImpl; +}; + +} // namespace Dns +} // namespace Network + +#endif // QUERY_H diff --git a/include/Dns/QueryClass.h b/include/Dns/QueryClass.h new file mode 100644 index 0000000..02aaefd --- /dev/null +++ b/include/Dns/QueryClass.h @@ -0,0 +1,25 @@ +#ifndef QUERYCLASS_H +#define QUERYCLASS_H + + +namespace Network { +namespace Dns { + +class QueryClass +{ +public: + enum type + { + NONE = 0, + IN = 1, + CS = 2, + CH = 3, + HS = 4, + ANY = 255 + }; +}; + +} // namespace Dns +} // namespace Network + +#endif // QUERYCLASS_H diff --git a/include/Dns/QueryType.h b/include/Dns/QueryType.h new file mode 100644 index 0000000..91f3a96 --- /dev/null +++ b/include/Dns/QueryType.h @@ -0,0 +1,40 @@ +#ifndef QUERYTYPE_H +#define QUERYTYPE_H + + +namespace Network { +namespace Dns { + +class QueryType +{ +public: + enum type + { + NONE = 0, + A = 1, + NC = 2, + MD = 3, + MF = 4, + CNAME = 5, + SOA = 6, + MB = 7, + MG = 8, + MR = 9, + NULL_ = 10, + WKS = 11, + PTR = 12, + HINFO = 13, + MINFO = 14, + MX = 15, + TXT = 16, + AXFR = 252, + MAILB = 253, + MAILA = 254, + ANY = 255 + }; +}; + +} // namespace Dns +} // namespace Network + +#endif // QUERYTYPE_H diff --git a/include/Dns/ResourceRecord.h b/include/Dns/ResourceRecord.h new file mode 100644 index 0000000..c07a1a4 --- /dev/null +++ b/include/Dns/ResourceRecord.h @@ -0,0 +1,44 @@ +#ifndef RESOURCERECORD_H +#define RESOURCERECORD_H + +#include "Class.h" +#include "Name.h" +#include "Type.h" +#include + + +namespace Network { +namespace Dns { + +class ResourceRecordImpl; + +class ResourceRecord +{ +public: + Name GetName() const; + Type::type GetType() const; + Class::type GetClass() const; + uint32_t GetTTL() const; + + std::vector GetResourceData() const; + std::vector GetData() const; + + void SetName(const Name& name); + void SetType(Type::type type); + void SetClass(Class::type class_); + void SetTTL(uint32_t ttl); + +private: + ResourceRecord(std::shared_ptr pResourceRecordImpl); + +private: + friend class MulticastDnsClient; + +private: + std::shared_ptr m_pResourceRecordImpl; +}; + +} // namespace Dns +} // namespace Network + +#endif // RESOURCERECORD_H diff --git a/include/Dns/Type.h b/include/Dns/Type.h new file mode 100644 index 0000000..971843b --- /dev/null +++ b/include/Dns/Type.h @@ -0,0 +1,36 @@ +#ifndef TYPE_H +#define TYPE_H + + +namespace Network { +namespace Dns { + +class Type +{ +public: + enum type + { + NONE = 0, + A = 1, + NC = 2, + MD = 3, + MF = 4, + CNAME = 5, + SOA = 6, + MB = 7, + MG = 8, + MR = 9, + NULL_ = 10, + WKS = 11, + PTR = 12, + HINFO = 13, + MINFO = 14, + MX = 15, + TXT = 16 + }; +}; + +} // namespace Dns +} // namespace Network + +#endif // TYPE_H