mirror of
https://github.com/RGBCube/serenity
synced 2025-07-27 15:07:45 +00:00
LookupServer: Move DNS related code into new LibDNS library
This allows other code to use the DNSPacket class, e.g. when sent over IPC.
This commit is contained in:
parent
0a92dbd390
commit
be4a4144f2
17 changed files with 51 additions and 40 deletions
|
@ -15,6 +15,7 @@ add_subdirectory(LibDesktop)
|
|||
add_subdirectory(LibDeviceTree)
|
||||
add_subdirectory(LibDiff)
|
||||
add_subdirectory(LibDl)
|
||||
add_subdirectory(LibDNS)
|
||||
add_subdirectory(LibDSP)
|
||||
add_subdirectory(LibEDID)
|
||||
add_subdirectory(LibELF)
|
||||
|
|
8
Userland/Libraries/LibDNS/CMakeLists.txt
Normal file
8
Userland/Libraries/LibDNS/CMakeLists.txt
Normal file
|
@ -0,0 +1,8 @@
|
|||
set(SOURCES
|
||||
DNSAnswer.cpp
|
||||
DNSName.cpp
|
||||
DNSPacket.cpp
|
||||
)
|
||||
|
||||
serenity_lib(LibDNS dns)
|
||||
target_link_libraries(LibDNS LibC)
|
69
Userland/Libraries/LibDNS/DNSAnswer.cpp
Normal file
69
Userland/Libraries/LibDNS/DNSAnswer.cpp
Normal file
|
@ -0,0 +1,69 @@
|
|||
/*
|
||||
* Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include "DNSAnswer.h"
|
||||
#include <AK/Stream.h>
|
||||
#include <time.h>
|
||||
|
||||
namespace DNS {
|
||||
|
||||
DNSAnswer::DNSAnswer(DNSName const& name, DNSRecordType type, DNSRecordClass class_code, u32 ttl, String const& record_data, bool mdns_cache_flush)
|
||||
: m_name(name)
|
||||
, m_type(type)
|
||||
, m_class_code(class_code)
|
||||
, m_ttl(ttl)
|
||||
, m_record_data(record_data)
|
||||
, m_mdns_cache_flush(mdns_cache_flush)
|
||||
{
|
||||
time(&m_received_time);
|
||||
}
|
||||
|
||||
bool DNSAnswer::has_expired() const
|
||||
{
|
||||
return time(nullptr) >= m_received_time + m_ttl;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
ErrorOr<void> AK::Formatter<DNS::DNSRecordType>::format(AK::FormatBuilder& builder, DNS::DNSRecordType value)
|
||||
{
|
||||
switch (value) {
|
||||
case DNS::DNSRecordType::A:
|
||||
return builder.put_string("A");
|
||||
case DNS::DNSRecordType::NS:
|
||||
return builder.put_string("NS");
|
||||
case DNS::DNSRecordType::CNAME:
|
||||
return builder.put_string("CNAME");
|
||||
case DNS::DNSRecordType::SOA:
|
||||
return builder.put_string("SOA");
|
||||
case DNS::DNSRecordType::PTR:
|
||||
return builder.put_string("PTR");
|
||||
case DNS::DNSRecordType::MX:
|
||||
return builder.put_string("MX");
|
||||
case DNS::DNSRecordType::TXT:
|
||||
return builder.put_string("TXT");
|
||||
case DNS::DNSRecordType::AAAA:
|
||||
return builder.put_string("AAAA");
|
||||
case DNS::DNSRecordType::SRV:
|
||||
return builder.put_string("SRV");
|
||||
}
|
||||
|
||||
TRY(builder.put_string("DNS record type "));
|
||||
TRY(builder.put_u64((u16)value));
|
||||
return {};
|
||||
}
|
||||
|
||||
ErrorOr<void> AK::Formatter<DNS::DNSRecordClass>::format(AK::FormatBuilder& builder, DNS::DNSRecordClass value)
|
||||
{
|
||||
switch (value) {
|
||||
case DNS::DNSRecordClass::IN:
|
||||
return builder.put_string("IN");
|
||||
}
|
||||
|
||||
TRY(builder.put_string("DNS record class "));
|
||||
TRY(builder.put_u64((u16)value));
|
||||
return {};
|
||||
}
|
80
Userland/Libraries/LibDNS/DNSAnswer.h
Normal file
80
Userland/Libraries/LibDNS/DNSAnswer.h
Normal file
|
@ -0,0 +1,80 @@
|
|||
/*
|
||||
* Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "DNSName.h"
|
||||
#include <AK/Format.h>
|
||||
#include <AK/String.h>
|
||||
#include <AK/Types.h>
|
||||
|
||||
namespace DNS {
|
||||
|
||||
enum class DNSRecordType : u16 {
|
||||
A = 1,
|
||||
NS = 2,
|
||||
CNAME = 5,
|
||||
SOA = 6,
|
||||
PTR = 12,
|
||||
MX = 15,
|
||||
TXT = 16,
|
||||
AAAA = 28,
|
||||
SRV = 33,
|
||||
};
|
||||
|
||||
enum class DNSRecordClass : u16 {
|
||||
IN = 1
|
||||
};
|
||||
|
||||
#define MDNS_CACHE_FLUSH 0x8000
|
||||
|
||||
class DNSAnswer {
|
||||
public:
|
||||
DNSAnswer(DNSName const& name, DNSRecordType type, DNSRecordClass class_code, u32 ttl, String const& record_data, bool mdns_cache_flush);
|
||||
|
||||
DNSName const& name() const { return m_name; }
|
||||
DNSRecordType type() const { return m_type; }
|
||||
DNSRecordClass class_code() const { return m_class_code; }
|
||||
u16 raw_class_code() const { return (u16)m_class_code | (m_mdns_cache_flush ? MDNS_CACHE_FLUSH : 0); }
|
||||
u32 ttl() const { return m_ttl; }
|
||||
time_t received_time() const { return m_received_time; }
|
||||
String const& record_data() const { return m_record_data; }
|
||||
bool mdns_cache_flush() const { return m_mdns_cache_flush; }
|
||||
|
||||
bool has_expired() const;
|
||||
|
||||
private:
|
||||
DNSName m_name;
|
||||
DNSRecordType m_type { 0 };
|
||||
DNSRecordClass m_class_code { 0 };
|
||||
u32 m_ttl { 0 };
|
||||
time_t m_received_time { 0 };
|
||||
String m_record_data;
|
||||
bool m_mdns_cache_flush { false };
|
||||
};
|
||||
|
||||
}
|
||||
template<>
|
||||
struct AK::Formatter<DNS::DNSRecordType> : StandardFormatter {
|
||||
Formatter() = default;
|
||||
explicit Formatter(StandardFormatter formatter)
|
||||
: StandardFormatter(formatter)
|
||||
{
|
||||
}
|
||||
|
||||
ErrorOr<void> format(AK::FormatBuilder&, DNS::DNSRecordType);
|
||||
};
|
||||
|
||||
template<>
|
||||
struct AK::Formatter<DNS::DNSRecordClass> : StandardFormatter {
|
||||
Formatter() = default;
|
||||
explicit Formatter(StandardFormatter formatter)
|
||||
: StandardFormatter(formatter)
|
||||
{
|
||||
}
|
||||
|
||||
ErrorOr<void> format(AK::FormatBuilder&, DNS::DNSRecordClass);
|
||||
};
|
99
Userland/Libraries/LibDNS/DNSName.cpp
Normal file
99
Userland/Libraries/LibDNS/DNSName.cpp
Normal file
|
@ -0,0 +1,99 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
|
||||
* Copyright (c) 2021, Sergey Bugaev <bugaevc@serenityos.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include "DNSName.h"
|
||||
#include <AK/Random.h>
|
||||
#include <AK/Vector.h>
|
||||
#include <ctype.h>
|
||||
|
||||
namespace DNS {
|
||||
|
||||
DNSName::DNSName(String const& name)
|
||||
{
|
||||
if (name.ends_with('.'))
|
||||
m_name = name.substring(0, name.length() - 1);
|
||||
else
|
||||
m_name = name;
|
||||
}
|
||||
|
||||
DNSName DNSName::parse(u8 const* data, size_t& offset, size_t max_offset, size_t recursion_level)
|
||||
{
|
||||
if (recursion_level > 4)
|
||||
return DNSName({});
|
||||
|
||||
StringBuilder builder;
|
||||
while (true) {
|
||||
if (offset >= max_offset)
|
||||
return DNSName({});
|
||||
u8 b = data[offset++];
|
||||
if (b == '\0') {
|
||||
// This terminates the name.
|
||||
return builder.to_string();
|
||||
} else if ((b & 0xc0) == 0xc0) {
|
||||
// The two bytes tell us the offset when to continue from.
|
||||
if (offset >= max_offset)
|
||||
return DNSName({});
|
||||
size_t dummy = (b & 0x3f) << 8 | data[offset++];
|
||||
auto rest_of_name = parse(data, dummy, max_offset, recursion_level + 1);
|
||||
builder.append(rest_of_name.as_string());
|
||||
return builder.to_string();
|
||||
} else {
|
||||
// This is the length of a part.
|
||||
if (offset + b >= max_offset)
|
||||
return DNSName({});
|
||||
builder.append((char const*)&data[offset], (size_t)b);
|
||||
builder.append('.');
|
||||
offset += b;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
size_t DNSName::serialized_size() const
|
||||
{
|
||||
if (m_name.is_empty())
|
||||
return 1;
|
||||
return m_name.length() + 2;
|
||||
}
|
||||
|
||||
void DNSName::randomize_case()
|
||||
{
|
||||
StringBuilder builder;
|
||||
for (char c : m_name) {
|
||||
// Randomize the 0x20 bit in every ASCII character.
|
||||
if (isalpha(c)) {
|
||||
if (get_random_uniform(2))
|
||||
c |= 0x20;
|
||||
else
|
||||
c &= ~0x20;
|
||||
}
|
||||
builder.append(c);
|
||||
}
|
||||
m_name = builder.to_string();
|
||||
}
|
||||
|
||||
OutputStream& operator<<(OutputStream& stream, DNSName const& name)
|
||||
{
|
||||
auto parts = name.as_string().split_view('.');
|
||||
for (auto& part : parts) {
|
||||
stream << (u8)part.length();
|
||||
stream << part.bytes();
|
||||
}
|
||||
stream << '\0';
|
||||
return stream;
|
||||
}
|
||||
|
||||
unsigned DNSName::Traits::hash(DNSName const& name)
|
||||
{
|
||||
return CaseInsensitiveStringTraits::hash(name.as_string());
|
||||
}
|
||||
|
||||
bool DNSName::Traits::equals(DNSName const& a, DNSName const& b)
|
||||
{
|
||||
return CaseInsensitiveStringTraits::equals(a.as_string(), b.as_string());
|
||||
}
|
||||
|
||||
}
|
48
Userland/Libraries/LibDNS/DNSName.h
Normal file
48
Userland/Libraries/LibDNS/DNSName.h
Normal file
|
@ -0,0 +1,48 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
|
||||
* Copyright (c) 2021, Sergey Bugaev <bugaevc@serenityos.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <AK/Forward.h>
|
||||
#include <AK/String.h>
|
||||
|
||||
namespace DNS {
|
||||
|
||||
class DNSName {
|
||||
public:
|
||||
DNSName(String const&);
|
||||
|
||||
static DNSName parse(u8 const* data, size_t& offset, size_t max_offset, size_t recursion_level = 0);
|
||||
|
||||
size_t serialized_size() const;
|
||||
String const& as_string() const { return m_name; }
|
||||
|
||||
void randomize_case();
|
||||
|
||||
bool operator==(DNSName const& other) const { return Traits::equals(*this, other); }
|
||||
|
||||
class Traits : public AK::Traits<DNSName> {
|
||||
public:
|
||||
static unsigned hash(DNSName const& name);
|
||||
static bool equals(DNSName const&, DNSName const&);
|
||||
};
|
||||
|
||||
private:
|
||||
String m_name;
|
||||
};
|
||||
|
||||
OutputStream& operator<<(OutputStream& stream, DNSName const&);
|
||||
|
||||
}
|
||||
|
||||
template<>
|
||||
struct AK::Formatter<DNS::DNSName> : Formatter<StringView> {
|
||||
ErrorOr<void> format(FormatBuilder& builder, DNS::DNSName const& value)
|
||||
{
|
||||
return Formatter<StringView>::format(builder, value.as_string());
|
||||
}
|
||||
};
|
180
Userland/Libraries/LibDNS/DNSPacket.cpp
Normal file
180
Userland/Libraries/LibDNS/DNSPacket.cpp
Normal file
|
@ -0,0 +1,180 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
|
||||
* Copyright (c) 2021, Sergey Bugaev <bugaevc@serenityos.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include "DNSPacket.h"
|
||||
#include "DNSName.h"
|
||||
#include "DNSPacketHeader.h"
|
||||
#include <AK/Debug.h>
|
||||
#include <AK/MemoryStream.h>
|
||||
#include <AK/StringBuilder.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
namespace DNS {
|
||||
|
||||
void DNSPacket::add_question(DNSQuestion const& question)
|
||||
{
|
||||
m_questions.empend(question);
|
||||
|
||||
VERIFY(m_questions.size() <= UINT16_MAX);
|
||||
}
|
||||
|
||||
void DNSPacket::add_answer(DNSAnswer const& answer)
|
||||
{
|
||||
m_answers.empend(answer);
|
||||
|
||||
VERIFY(m_answers.size() <= UINT16_MAX);
|
||||
}
|
||||
|
||||
ByteBuffer DNSPacket::to_byte_buffer() const
|
||||
{
|
||||
DNSPacketHeader header;
|
||||
header.set_id(m_id);
|
||||
if (is_query())
|
||||
header.set_is_query();
|
||||
else
|
||||
header.set_is_response();
|
||||
header.set_authoritative_answer(m_authoritative_answer);
|
||||
// FIXME: What should this be?
|
||||
header.set_opcode(0);
|
||||
header.set_response_code(m_code);
|
||||
header.set_truncated(false); // hopefully...
|
||||
header.set_recursion_desired(m_recursion_desired);
|
||||
// FIXME: what should the be for requests?
|
||||
header.set_recursion_available(m_recursion_available);
|
||||
header.set_question_count(m_questions.size());
|
||||
header.set_answer_count(m_answers.size());
|
||||
|
||||
DuplexMemoryStream stream;
|
||||
|
||||
stream << ReadonlyBytes { &header, sizeof(header) };
|
||||
for (auto& question : m_questions) {
|
||||
stream << question.name();
|
||||
stream << htons((u16)question.record_type());
|
||||
stream << htons(question.raw_class_code());
|
||||
}
|
||||
for (auto& answer : m_answers) {
|
||||
stream << answer.name();
|
||||
stream << htons((u16)answer.type());
|
||||
stream << htons(answer.raw_class_code());
|
||||
stream << htonl(answer.ttl());
|
||||
if (answer.type() == DNSRecordType::PTR) {
|
||||
DNSName name { answer.record_data() };
|
||||
stream << htons(name.serialized_size());
|
||||
stream << name;
|
||||
} else {
|
||||
stream << htons(answer.record_data().length());
|
||||
stream << answer.record_data().bytes();
|
||||
}
|
||||
}
|
||||
|
||||
return stream.copy_into_contiguous_buffer();
|
||||
}
|
||||
|
||||
class [[gnu::packed]] DNSRecordWithoutName {
|
||||
public:
|
||||
DNSRecordWithoutName() = default;
|
||||
|
||||
u16 type() const { return m_type; }
|
||||
u16 record_class() const { return m_class; }
|
||||
u32 ttl() const { return m_ttl; }
|
||||
u16 data_length() const { return m_data_length; }
|
||||
|
||||
void* data() { return this + 1; }
|
||||
void const* data() const { return this + 1; }
|
||||
|
||||
private:
|
||||
NetworkOrdered<u16> m_type;
|
||||
NetworkOrdered<u16> m_class;
|
||||
NetworkOrdered<u32> m_ttl;
|
||||
NetworkOrdered<u16> m_data_length;
|
||||
};
|
||||
|
||||
static_assert(sizeof(DNSRecordWithoutName) == 10);
|
||||
|
||||
Optional<DNSPacket> DNSPacket::from_raw_packet(u8 const* raw_data, size_t raw_size)
|
||||
{
|
||||
if (raw_size < sizeof(DNSPacketHeader)) {
|
||||
dbgln("DNS response not large enough ({} out of {}) to be a DNS packet.", raw_size, sizeof(DNSPacketHeader));
|
||||
return {};
|
||||
}
|
||||
|
||||
auto& header = *(DNSPacketHeader const*)(raw_data);
|
||||
dbgln_if(LOOKUPSERVER_DEBUG, "Got packet (ID: {})", header.id());
|
||||
dbgln_if(LOOKUPSERVER_DEBUG, " Question count: {}", header.question_count());
|
||||
dbgln_if(LOOKUPSERVER_DEBUG, " Answer count: {}", header.answer_count());
|
||||
dbgln_if(LOOKUPSERVER_DEBUG, " Authority count: {}", header.authority_count());
|
||||
dbgln_if(LOOKUPSERVER_DEBUG, "Additional count: {}", header.additional_count());
|
||||
|
||||
DNSPacket packet;
|
||||
packet.m_id = header.id();
|
||||
packet.m_query_or_response = header.is_response();
|
||||
packet.m_code = header.response_code();
|
||||
|
||||
// FIXME: Should we parse further in this case?
|
||||
if (packet.code() != Code::NOERROR)
|
||||
return packet;
|
||||
|
||||
size_t offset = sizeof(DNSPacketHeader);
|
||||
|
||||
for (u16 i = 0; i < header.question_count(); i++) {
|
||||
auto name = DNSName::parse(raw_data, offset, raw_size);
|
||||
struct RawDNSAnswerQuestion {
|
||||
NetworkOrdered<u16> record_type;
|
||||
NetworkOrdered<u16> class_code;
|
||||
};
|
||||
auto& record_and_class = *(RawDNSAnswerQuestion const*)&raw_data[offset];
|
||||
u16 class_code = record_and_class.class_code & ~MDNS_WANTS_UNICAST_RESPONSE;
|
||||
bool mdns_wants_unicast_response = record_and_class.class_code & MDNS_WANTS_UNICAST_RESPONSE;
|
||||
packet.m_questions.empend(name, (DNSRecordType)(u16)record_and_class.record_type, (DNSRecordClass)class_code, mdns_wants_unicast_response);
|
||||
offset += 4;
|
||||
auto& question = packet.m_questions.last();
|
||||
dbgln_if(LOOKUPSERVER_DEBUG, "Question #{}: name=_{}_, type={}, class={}", i, question.name(), question.record_type(), question.class_code());
|
||||
}
|
||||
|
||||
for (u16 i = 0; i < header.answer_count(); ++i) {
|
||||
auto name = DNSName::parse(raw_data, offset, raw_size);
|
||||
|
||||
auto& record = *(DNSRecordWithoutName const*)(&raw_data[offset]);
|
||||
|
||||
String data;
|
||||
|
||||
offset += sizeof(DNSRecordWithoutName);
|
||||
|
||||
switch ((DNSRecordType)record.type()) {
|
||||
case DNSRecordType::PTR: {
|
||||
size_t dummy_offset = offset;
|
||||
data = DNSName::parse(raw_data, dummy_offset, raw_size).as_string();
|
||||
break;
|
||||
}
|
||||
case DNSRecordType::CNAME:
|
||||
// Fall through
|
||||
case DNSRecordType::A:
|
||||
// Fall through
|
||||
case DNSRecordType::TXT:
|
||||
// Fall through
|
||||
case DNSRecordType::AAAA:
|
||||
// Fall through
|
||||
case DNSRecordType::SRV:
|
||||
data = { record.data(), record.data_length() };
|
||||
break;
|
||||
default:
|
||||
// FIXME: Parse some other record types perhaps?
|
||||
dbgln("data=(unimplemented record type {})", (u16)record.type());
|
||||
}
|
||||
|
||||
dbgln_if(LOOKUPSERVER_DEBUG, "Answer #{}: name=_{}_, type={}, ttl={}, length={}, data=_{}_", i, name, record.type(), record.ttl(), record.data_length(), data);
|
||||
u16 class_code = record.record_class() & ~MDNS_CACHE_FLUSH;
|
||||
bool mdns_cache_flush = record.record_class() & MDNS_CACHE_FLUSH;
|
||||
packet.m_answers.empend(name, (DNSRecordType)record.type(), (DNSRecordClass)class_code, record.ttl(), data, mdns_cache_flush);
|
||||
offset += record.data_length();
|
||||
}
|
||||
|
||||
return packet;
|
||||
}
|
||||
|
||||
}
|
89
Userland/Libraries/LibDNS/DNSPacket.h
Normal file
89
Userland/Libraries/LibDNS/DNSPacket.h
Normal file
|
@ -0,0 +1,89 @@
|
|||
/*
|
||||
* Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
|
||||
* Copyright (c) 2021, Sergey Bugaev <bugaevc@serenityos.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "DNSAnswer.h"
|
||||
#include "DNSQuestion.h"
|
||||
#include <AK/Optional.h>
|
||||
#include <AK/Types.h>
|
||||
#include <AK/Vector.h>
|
||||
|
||||
namespace DNS {
|
||||
|
||||
enum class ShouldRandomizeCase {
|
||||
No = 0,
|
||||
Yes
|
||||
};
|
||||
|
||||
class DNSPacket {
|
||||
public:
|
||||
DNSPacket() = default;
|
||||
|
||||
static Optional<DNSPacket> from_raw_packet(u8 const*, size_t);
|
||||
ByteBuffer to_byte_buffer() const;
|
||||
|
||||
bool is_query() const { return !m_query_or_response; }
|
||||
bool is_response() const { return m_query_or_response; }
|
||||
bool is_authoritative_answer() const { return m_authoritative_answer; }
|
||||
bool recursion_desired() const { return m_recursion_desired; }
|
||||
bool recursion_available() const { return m_recursion_available; }
|
||||
void set_is_query() { m_query_or_response = false; }
|
||||
void set_is_response() { m_query_or_response = true; }
|
||||
void set_authoritative_answer(bool authoritative_answer) { m_authoritative_answer = authoritative_answer; }
|
||||
void set_recursion_desired(bool recursion_desired) { m_recursion_desired = recursion_desired; }
|
||||
void set_recursion_available(bool recursion_available) { m_recursion_available = recursion_available; }
|
||||
|
||||
u16 id() const { return m_id; }
|
||||
void set_id(u16 id) { m_id = id; }
|
||||
|
||||
Vector<DNSQuestion> const& questions() const { return m_questions; }
|
||||
Vector<DNSAnswer> const& answers() const { return m_answers; }
|
||||
|
||||
u16 question_count() const
|
||||
{
|
||||
VERIFY(m_questions.size() <= UINT16_MAX);
|
||||
return m_questions.size();
|
||||
}
|
||||
|
||||
u16 answer_count() const
|
||||
{
|
||||
VERIFY(m_answers.size() <= UINT16_MAX);
|
||||
return m_answers.size();
|
||||
}
|
||||
|
||||
void add_question(DNSQuestion const&);
|
||||
void add_answer(DNSAnswer const&);
|
||||
|
||||
enum class Code : u8 {
|
||||
NOERROR = 0,
|
||||
FORMERR = 1,
|
||||
SERVFAIL = 2,
|
||||
NXDOMAIN = 3,
|
||||
NOTIMP = 4,
|
||||
REFUSED = 5,
|
||||
YXDOMAIN = 6,
|
||||
XRRSET = 7,
|
||||
NOTAUTH = 8,
|
||||
NOTZONE = 9,
|
||||
};
|
||||
|
||||
Code code() const { return (Code)m_code; }
|
||||
void set_code(Code code) { m_code = (u8)code; }
|
||||
|
||||
private:
|
||||
u16 m_id { 0 };
|
||||
u8 m_code { 0 };
|
||||
bool m_authoritative_answer { false };
|
||||
bool m_query_or_response { false };
|
||||
bool m_recursion_desired { true };
|
||||
bool m_recursion_available { true };
|
||||
Vector<DNSQuestion> m_questions;
|
||||
Vector<DNSAnswer> m_answers;
|
||||
};
|
||||
|
||||
}
|
99
Userland/Libraries/LibDNS/DNSPacketHeader.h
Normal file
99
Userland/Libraries/LibDNS/DNSPacketHeader.h
Normal file
|
@ -0,0 +1,99 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <AK/Endian.h>
|
||||
#include <AK/Types.h>
|
||||
|
||||
namespace DNS {
|
||||
|
||||
class [[gnu::packed]] DNSPacketHeader {
|
||||
public:
|
||||
DNSPacketHeader()
|
||||
: m_recursion_desired(false)
|
||||
, m_truncated(false)
|
||||
, m_authoritative_answer(false)
|
||||
, m_opcode(0)
|
||||
, m_query_or_response(false)
|
||||
, m_response_code(0)
|
||||
, m_checking_disabled(false)
|
||||
, m_authenticated_data(false)
|
||||
, m_zero(false)
|
||||
, m_recursion_available(false)
|
||||
{
|
||||
}
|
||||
|
||||
u16 id() const { return m_id; }
|
||||
void set_id(u16 w) { m_id = w; }
|
||||
|
||||
bool recursion_desired() const { return m_recursion_desired; }
|
||||
void set_recursion_desired(bool b) { m_recursion_desired = b; }
|
||||
|
||||
bool is_truncated() const { return m_truncated; }
|
||||
void set_truncated(bool b) { m_truncated = b; }
|
||||
|
||||
bool is_authoritative_answer() const { return m_authoritative_answer; }
|
||||
void set_authoritative_answer(bool b) { m_authoritative_answer = b; }
|
||||
|
||||
u8 opcode() const { return m_opcode; }
|
||||
void set_opcode(u8 b) { m_opcode = b; }
|
||||
|
||||
bool is_query() const { return !m_query_or_response; }
|
||||
bool is_response() const { return m_query_or_response; }
|
||||
void set_is_query() { m_query_or_response = false; }
|
||||
void set_is_response() { m_query_or_response = true; }
|
||||
|
||||
u8 response_code() const { return m_response_code; }
|
||||
void set_response_code(u8 b) { m_response_code = b; }
|
||||
|
||||
bool checking_disabled() const { return m_checking_disabled; }
|
||||
void set_checking_disabled(bool b) { m_checking_disabled = b; }
|
||||
|
||||
bool is_authenticated_data() const { return m_authenticated_data; }
|
||||
void set_authenticated_data(bool b) { m_authenticated_data = b; }
|
||||
|
||||
bool is_recursion_available() const { return m_recursion_available; }
|
||||
void set_recursion_available(bool b) { m_recursion_available = b; }
|
||||
|
||||
u16 question_count() const { return m_question_count; }
|
||||
void set_question_count(u16 w) { m_question_count = w; }
|
||||
|
||||
u16 answer_count() const { return m_answer_count; }
|
||||
void set_answer_count(u16 w) { m_answer_count = w; }
|
||||
|
||||
u16 authority_count() const { return m_authority_count; }
|
||||
void set_authority_count(u16 w) { m_authority_count = w; }
|
||||
|
||||
u16 additional_count() const { return m_additional_count; }
|
||||
void set_additional_count(u16 w) { m_additional_count = w; }
|
||||
|
||||
void* payload() { return this + 1; }
|
||||
void const* payload() const { return this + 1; }
|
||||
|
||||
private:
|
||||
NetworkOrdered<u16> m_id;
|
||||
|
||||
bool m_recursion_desired : 1;
|
||||
bool m_truncated : 1;
|
||||
bool m_authoritative_answer : 1;
|
||||
u8 m_opcode : 4;
|
||||
bool m_query_or_response : 1;
|
||||
u8 m_response_code : 4;
|
||||
bool m_checking_disabled : 1;
|
||||
bool m_authenticated_data : 1;
|
||||
bool m_zero : 1;
|
||||
bool m_recursion_available : 1;
|
||||
|
||||
NetworkOrdered<u16> m_question_count;
|
||||
NetworkOrdered<u16> m_answer_count;
|
||||
NetworkOrdered<u16> m_authority_count;
|
||||
NetworkOrdered<u16> m_additional_count;
|
||||
};
|
||||
|
||||
static_assert(sizeof(DNSPacketHeader) == 12);
|
||||
|
||||
}
|
39
Userland/Libraries/LibDNS/DNSQuestion.h
Normal file
39
Userland/Libraries/LibDNS/DNSQuestion.h
Normal file
|
@ -0,0 +1,39 @@
|
|||
/*
|
||||
* Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "DNSName.h"
|
||||
#include <AK/Types.h>
|
||||
|
||||
namespace DNS {
|
||||
|
||||
#define MDNS_WANTS_UNICAST_RESPONSE 0x8000
|
||||
|
||||
class DNSQuestion {
|
||||
public:
|
||||
DNSQuestion(DNSName const& name, DNSRecordType record_type, DNSRecordClass class_code, bool mdns_wants_unicast_response)
|
||||
: m_name(name)
|
||||
, m_record_type(record_type)
|
||||
, m_class_code(class_code)
|
||||
, m_mdns_wants_unicast_response(mdns_wants_unicast_response)
|
||||
{
|
||||
}
|
||||
|
||||
DNSRecordType record_type() const { return m_record_type; }
|
||||
DNSRecordClass class_code() const { return m_class_code; }
|
||||
u16 raw_class_code() const { return (u16)m_class_code | (m_mdns_wants_unicast_response ? MDNS_WANTS_UNICAST_RESPONSE : 0); }
|
||||
DNSName const& name() const { return m_name; }
|
||||
bool mdns_wants_unicast_response() const { return m_mdns_wants_unicast_response; }
|
||||
|
||||
private:
|
||||
DNSName m_name;
|
||||
DNSRecordType m_record_type { 0 };
|
||||
DNSRecordClass m_class_code { 0 };
|
||||
bool m_mdns_wants_unicast_response { false };
|
||||
};
|
||||
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue