Forráskód Böngészése

Add MulticastDnsClient

JDierkse 6 éve
szülő
commit
36d6b47f78

+ 129 - 0
Network/Dns/Data.cpp

@@ -0,0 +1,129 @@
+#include "Data.h"
+#include <arpa/inet.h>
+
+
+namespace Network {
+namespace Dns {
+
+Data::Data()
+{
+}
+
+Data::Data(const std::vector<uint8_t>& 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<unsigned>(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<uint8_t>((value >> 8) & ((1 << 8) - 1)));
+	m_data.emplace_back(static_cast<uint8_t>((value >> 0) & ((1 << 8) - 1)));
+}
+
+void Data::append(uint32_t value)
+{
+	m_data.emplace_back(static_cast<uint8_t>((value >> 24) & ((1 << 8) - 1)));
+	m_data.emplace_back(static_cast<uint8_t>((value >> 16) & ((1 << 8) - 1)));
+	m_data.emplace_back(static_cast<uint8_t>((value >>  8) & ((1 << 8) - 1)));
+	m_data.emplace_back(static_cast<uint8_t>((value >>  0) & ((1 << 8) - 1)));
+}
+
+void Data::append(uint64_t value)
+{
+	m_data.emplace_back(static_cast<uint8_t>((value >> 56) & ((1 << 8) - 1)));
+	m_data.emplace_back(static_cast<uint8_t>((value >> 48) & ((1 << 8) - 1)));
+	m_data.emplace_back(static_cast<uint8_t>((value >> 40) & ((1 << 8) - 1)));
+	m_data.emplace_back(static_cast<uint8_t>((value >> 32) & ((1 << 8) - 1)));
+	m_data.emplace_back(static_cast<uint8_t>((value >> 24) & ((1 << 8) - 1)));
+	m_data.emplace_back(static_cast<uint8_t>((value >> 16) & ((1 << 8) - 1)));
+	m_data.emplace_back(static_cast<uint8_t>((value >>  8) & ((1 << 8) - 1)));
+	m_data.emplace_back(static_cast<uint8_t>((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<int>(ptr[i]);
+	}
+	stream << ")";
+	return stream;
+}
+
+} // namespace Dns
+} // namespace Network

+ 55 - 0
Network/Dns/Data.h

@@ -0,0 +1,55 @@
+#ifndef NETWORK_DNS_DATA_H
+#define NETWORK_DNS_DATA_H
+
+#include <vector>
+#include <cstdint>
+#include <iostream>
+
+namespace Network {
+namespace Dns {
+
+class Data
+{
+public:
+	Data();
+	Data(const std::vector<uint8_t>& 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<typename Arg1, typename ...Args>
+	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<uint8_t> m_data;
+};
+
+} // namespace Dns
+} // namespace Network
+
+#endif // NETWORK_DNS_DATA_H

+ 113 - 0
Network/Dns/DataReader.cpp

@@ -0,0 +1,113 @@
+#include "DataReader.h"
+#include <arpa/inet.h>
+
+
+namespace Network {
+namespace Dns {
+
+DataReader::DataReader(const uint8_t* data, unsigned length) :
+	m_data(data),
+	m_length(length)
+{
+}
+
+DataReader::DataReader(const std::vector<uint8_t>& 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<uint8_t>((*this)[pos + 0]) << 0);
+	pos++;
+	return value;
+}
+
+uint16_t DataReader::Read16(unsigned& pos) const
+{
+	uint16_t value =
+		(static_cast<uint16_t>((*this)[pos + 0]) << 8) |
+		(static_cast<uint16_t>((*this)[pos + 1]) << 0);
+	pos += 2;
+	return value;
+}
+
+uint32_t DataReader::Read32(unsigned& pos) const
+{
+	uint32_t value =
+		(static_cast<uint32_t>((*this)[pos + 0]) << 24) |
+		(static_cast<uint32_t>((*this)[pos + 1]) << 16) |
+		(static_cast<uint32_t>((*this)[pos + 2]) <<  8) |
+		(static_cast<uint32_t>((*this)[pos + 3]) <<  0);
+	pos += 4;
+	return value;
+}
+
+uint64_t DataReader::Read64(unsigned& pos) const
+{
+	uint64_t value =
+		(static_cast<uint64_t>((*this)[pos + 0]) << 56) |
+		(static_cast<uint64_t>((*this)[pos + 1]) << 48) |
+		(static_cast<uint64_t>((*this)[pos + 2]) << 40) |
+		(static_cast<uint64_t>((*this)[pos + 3]) << 32) |
+		(static_cast<uint64_t>((*this)[pos + 4]) << 24) |
+		(static_cast<uint64_t>((*this)[pos + 5]) << 16) |
+		(static_cast<uint64_t>((*this)[pos + 6]) <<  8) |
+		(static_cast<uint64_t>((*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<int>(reader.m_data[i]);
+	}
+	stream << "}";
+	return stream;
+}
+
+} // namespace Dns
+} // namespace Network

+ 46 - 0
Network/Dns/DataReader.h

@@ -0,0 +1,46 @@
+#ifndef NETWORK_DNS_DATAREADER_H
+#define NETWORK_DNS_DATAREADER_H
+
+#include <exception>
+#include <iostream>
+#include <vector>
+#include <cstdint>
+
+
+namespace Network {
+namespace Dns {
+
+class DataReader
+{
+public:
+	DataReader(const uint8_t* data, unsigned length);
+	DataReader(const std::vector<uint8_t>& 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

+ 1 - 0
Network/Dns/Makefile

@@ -0,0 +1 @@
+../Makefile

+ 17 - 0
Network/Dns/MalformedPacket.h

@@ -0,0 +1,17 @@
+#ifndef NETWORK_DNS_MALFORMEDPACKET_H
+#define NETWORK_DNS_MALFORMEDPACKET_H
+
+#include <exception>
+
+
+namespace Network {
+namespace Dns {
+
+class MalformedPacket : public std::exception
+{
+};
+
+} // namespace Dns
+} // namespace Network
+
+#endif // NETWORK_DNS_MALFORMEDPACKET_H

+ 32 - 0
Network/Dns/MulticastDnsClient.cpp

@@ -0,0 +1,32 @@
+#include "Dns/MulticastDnsClient.h"
+#include "MulticastDnsClientImpl.h"
+
+
+namespace Network {
+namespace Dns {
+
+MulticastDnsClient::MulticastDnsClient(std::function<void(const std::vector<ResourceRecord>&)> 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<ResourceRecordImpl>& answers)
+{
+	std::vector<ResourceRecord> ans;
+
+	for (auto& answer : answers)
+		ans.push_back(ResourceRecord(std::make_shared<ResourceRecordImpl>(answer)));
+
+	m_callback(ans);
+}
+
+} // namespace Dns
+} // namespace Network

+ 146 - 0
Network/Dns/MulticastDnsClientImpl.cpp

@@ -0,0 +1,146 @@
+#include "MulticastDnsClientImpl.h"
+#include "MalformedPacket.h"
+#include "Packet.h"
+#include <Logging.h>
+
+
+namespace Network {
+namespace Dns {
+
+MulticastDnsClientImpl::MulticastDnsClientImpl(std::function<void(const std::vector<ResourceRecordImpl>&)> 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<Data>(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> 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

+ 52 - 0
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 <asio.hpp>
+#include <functional>
+#include <memory>
+#include <string>
+#include <vector>
+
+
+namespace Network {
+namespace Dns {
+
+class MulticastDnsClientImpl
+{
+public:
+	MulticastDnsClientImpl(std::function<void(const std::vector<ResourceRecordImpl>&)> 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> 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<uint8_t> 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<void(const std::vector<ResourceRecordImpl>&)> m_callback;
+};
+
+} // namespace Dns
+} // namespace Network
+
+#endif // NETWORK_DNS_MULTICASTDNSCLIENTIMPL_H

+ 79 - 0
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<NameImpl> pNameImpl) :
+	m_pNameImpl(pNameImpl)
+{
+}
+
+Name::Name(const std::vector<std::string>& 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

+ 146 - 0
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<std::string>& 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<uint8_t>(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<std::string> 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<unsigned>(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<char>(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<int>(name.m_labels.size()); ++i)
+	{
+		if (i)
+			stream << ".";
+		stream << "\"" << name.m_labels[i] << "\"";
+	}
+	return stream;
+}
+
+} // namespace Dns
+} // namespace Network

+ 52 - 0
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 <string>
+#include <vector>
+
+
+namespace Network {
+namespace Dns {
+
+class NameImpl
+{
+public:
+	NameImpl();
+
+	template<typename ...Args>
+	NameImpl(const std::string& label, const Args& ...args) : NameImpl(args...)
+	{
+		m_labels.insert(m_labels.begin(), label);
+	}
+
+	NameImpl(const std::vector<std::string>& 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<std::string> m_labels;
+};
+
+} // namespace Dns
+} // namespace Network
+
+#endif // NETWORK_DNS_NAMEIMPL_H

+ 22 - 0
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

+ 284 - 0
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<OperationCode::type>(((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<ResponseCode::type>((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<uint16_t>(m_identifier));
+
+	uint8_t flags =
+		(static_cast<uint8_t>(m_query) << 7) |
+		(static_cast<uint8_t>(m_operationCode) << 3) |
+		(static_cast<uint8_t>(m_authorativeAnswer) << 2) |
+		(static_cast<uint8_t>(m_truncation) << 1) |
+		(static_cast<uint8_t>(m_recursionDesired) << 0);
+	data.append(flags);
+
+	flags =
+		(static_cast<uint8_t>(m_recursionAvailable) << 7) |
+		(static_cast<uint8_t>(m_zero) << 4) |
+		(static_cast<uint8_t>(m_responseCode) << 0);
+	data.append(flags);
+
+	data.append(static_cast<uint16_t>(GetQueryCount()));
+	data.append(static_cast<uint16_t>(GetAnswerCount()));
+	data.append(static_cast<uint16_t>(GetAuthorityCount()));
+	data.append(static_cast<uint16_t>(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<QueryImpl> Packet::GetQueries() const
+{
+	return m_queries;
+}
+
+std::vector<ResourceRecordImpl> Packet::GetAnswers() const
+{
+	return m_answers;
+}
+
+std::vector<ResourceRecordImpl> Packet::GetAuthorities() const
+{
+	return m_authorities;
+}
+
+std::vector<ResourceRecordImpl> Packet::GetAdditionalRecords() const
+{
+	return m_additionalRecords;
+}
+
+unsigned Packet::GetQueryCount() const
+{
+	return static_cast<unsigned>(m_queries.size());
+}
+
+unsigned Packet::GetAnswerCount() const
+{
+	return static_cast<unsigned>(m_answers.size());
+}
+
+unsigned Packet::GetAuthorityCount() const
+{
+	return static_cast<unsigned>(m_authorities.size());
+}
+
+unsigned Packet::GetAdditionalRecordsCount() const
+{
+	return static_cast<unsigned>(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<int>(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<int>(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

+ 79 - 0
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<QueryImpl> GetQueries() const;
+	std::vector<ResourceRecordImpl> GetAnswers() const;
+	std::vector<ResourceRecordImpl> GetAuthorities() const;
+	std::vector<ResourceRecordImpl> 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<QueryImpl> m_queries;
+	std::vector<ResourceRecordImpl> m_answers;
+	std::vector<ResourceRecordImpl> m_authorities;
+	std::vector<ResourceRecordImpl> m_additionalRecords;
+};
+
+} // namespace Dns
+} // namespace Network
+
+#endif // NETWORK_DNS_PACKET_H

+ 59 - 0
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

+ 102 - 0
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<uint16_t>(m_type));
+	uint16_t classField = static_cast<uint16_t>(m_class);
+	if (m_unicast)
+		classField |= (static_cast<uint16_t>(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<QueryType::type>(reader.Read16(pos));
+		bool newUnicast = false;
+
+		uint16_t classField = reader.Read16(pos);
+		if (classField & (static_cast<uint16_t>(1) << 15))
+		{
+			classField ^= static_cast<uint16_t>(1) << 15;
+			newUnicast = true;
+		}
+		QueryClass::type newClass = static_cast<QueryClass::type>(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<int>(query.m_type)
+		<< ", class = " << static_cast<int>(query.m_class)
+		<< ", " << (query.m_unicast ? "Unicast" : "Multicast") << ")";
+}
+
+} // namespace Dns
+} // namespace Network

+ 45 - 0
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

+ 82 - 0
Network/Dns/ResourceRecord.cpp

@@ -0,0 +1,82 @@
+#include "Dns/ResourceRecord.h"
+#include "ResourceRecordImpl.h"
+
+
+namespace Network {
+namespace Dns {
+
+ResourceRecord::ResourceRecord(std::shared_ptr<ResourceRecordImpl> 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<uint8_t> ResourceRecord::GetResourceData() const
+{
+	Data data = m_pResourceRecordImpl->GetResourceData();
+
+	const uint8_t* ptr = data.GetPtr();
+	int length = data.GetLength();
+
+	std::vector<uint8_t> returnValue(length);
+	for (int i = 0; i < length; i++)
+		returnValue[i] = ptr[i];
+
+	return returnValue;
+}
+
+std::vector<uint8_t> ResourceRecord::GetData() const
+{
+	Data data = m_pResourceRecordImpl->GetData();
+
+	const uint8_t* ptr = data.GetPtr();
+	int length = data.GetLength();
+
+	std::vector<uint8_t> 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

+ 121 - 0
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<uint16_t>(m_type));
+	data.append(static_cast<uint16_t>(m_class));
+	data.append(static_cast<uint32_t>(m_ttl));
+	data.append(static_cast<uint16_t>(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<Type::type>(reader.Read16(pos));
+		Class::type newClass = static_cast<Class::type>(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<int>(record.m_type)
+		<< ", class = " << static_cast<int>(record.m_class)
+		<< ", ttl = " << record.m_ttl
+		<< ", rdata = " << record.m_resourceData << ")";
+}
+
+} // namespace Dns
+} // namespace Network

+ 48 - 0
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

+ 25 - 0
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

+ 24 - 0
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

+ 36 - 0
include/Dns/MulticastDnsClient.h

@@ -0,0 +1,36 @@
+#ifndef MULTICASTDNSCLIENT_H
+#define MULTICASTDNSCLIENT_H
+
+#include "Query.h"
+#include "ResourceRecord.h"
+#include <functional>
+#include <memory>
+#include <string>
+
+
+namespace Network {
+namespace Dns {
+
+class MulticastDnsClientImpl;
+
+class MulticastDnsClient
+{
+public:
+	MulticastDnsClient(std::function<void(const std::vector<ResourceRecord>&)> callback);
+	~MulticastDnsClient();
+
+public:
+	void Query(const Dns::Query& query);
+
+private:
+	void Callback(const std::vector<ResourceRecordImpl>& data);
+
+private:
+	std::unique_ptr<MulticastDnsClientImpl> m_pMulticastDnsClientImpl;
+	std::function<void(const std::vector<ResourceRecord>&)> m_callback;
+};
+
+} // namespace Dns
+} // namespace Network
+
+#endif // MULTICASTDNSCLIENT_H

+ 73 - 0
include/Dns/Name.h

@@ -0,0 +1,73 @@
+#ifndef NAME_H
+#define NAME_H
+
+#include <memory>
+#include <sstream>
+#include <string>
+#include <vector>
+
+
+namespace Network {
+namespace Dns {
+
+class NameImpl;
+
+class Name
+{
+public:
+	Name();
+
+	template<typename ...Args>
+	Name(const Args& ...args) :
+		Name(toStringVector(args...))
+	{
+	}
+
+	Name(const std::vector<std::string>& 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<NameImpl> pNameImpl);
+	NameImpl* PImpl() const;
+
+private:
+	template <class T>
+	std::string toString(T&& t)
+	{
+		std::stringstream s;
+		s << std::forward<T>(t);
+		return s.str();
+	}
+
+	template <class... T>
+	std::vector<std::string> toStringVector(T&&... args)
+	{
+		std::vector<std::string> res { toString(std::forward<T>(args))... };
+		return res;
+	}
+
+private:
+	friend class Query;
+	friend class ResourceRecord;
+
+private:
+	std::shared_ptr<NameImpl> m_pNameImpl;
+};
+
+} // namespace Dns
+} // namespace Network
+
+#endif // NAME_H

+ 43 - 0
include/Dns/Query.h

@@ -0,0 +1,43 @@
+#ifndef QUERY_H
+#define QUERY_H
+
+#include "Name.h"
+#include "QueryClass.h"
+#include "QueryType.h"
+#include <memory>
+
+
+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<QueryImpl> m_pQueryImpl;
+};
+
+} // namespace Dns
+} // namespace Network
+
+#endif // QUERY_H

+ 25 - 0
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

+ 40 - 0
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

+ 44 - 0
include/Dns/ResourceRecord.h

@@ -0,0 +1,44 @@
+#ifndef RESOURCERECORD_H
+#define RESOURCERECORD_H
+
+#include "Class.h"
+#include "Name.h"
+#include "Type.h"
+#include <memory>
+
+
+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<uint8_t> GetResourceData() const;
+	std::vector<uint8_t> 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<ResourceRecordImpl> pResourceRecordImpl);
+
+private:
+	friend class MulticastDnsClient;
+
+private:
+	std::shared_ptr<ResourceRecordImpl> m_pResourceRecordImpl;
+};
+
+} // namespace Dns
+} // namespace Network
+
+#endif // RESOURCERECORD_H

+ 36 - 0
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