os/include/net: add support for the DNS protocol

Issue genodelabs/genode#5003
This commit is contained in:
Alice Domage 2023-09-18 18:55:54 +02:00 committed by Christian Helmuth
parent 58a420511c
commit 132e027c69
4 changed files with 839 additions and 0 deletions

534
repos/os/include/net/dns.h Normal file
View File

@ -0,0 +1,534 @@
/*
* \brief DNS Request class and related
* \author Alice Domage <alice.domage@gapfruit.com>
* \date 2023-09-13
*/
/*
* Copyright (C) 2023 Genode Labs GmbH
* Copyright (C) 2023 gapfruit ag
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU Affero General Public License version 3.
*/
#ifndef _NET__DNS_H_
#define _NET__DNS_H_
/* Genode */
#include <util/string.h>
#include <util/construct_at.h>
#include <util/endian.h>
#include <net/size_guard.h>
#include <net/ipv4.h>
namespace Net {
using namespace Genode;
class Domain_name;
class Dns_packet;
static inline size_t ascii_to(char const *, Domain_name&);
}
/*
* Domain Name format following RFC 1035
*
* Various objects and parameters in the DNS have size limits. They are
* listed below. Some could be easily changed, others are more
* fundamental.
*
* labels 63 octets or less
*
* names 255 octets or less
*
* QNAME a domain name represented as a sequence of labels, where
* each label consists of a length octet followed by that
* number of octets. The domain name terminates with the
* zero length octet for the null label of the root. Note
* that this field may be an odd number of octets; no
* padding is used.*
*/
class Net::Domain_name
{
public:
static constexpr const size_t ZERO_LENGTH_OCTET = 1;
static constexpr const size_t NAME_MAX_LEN = 255;
static constexpr const size_t LABEL_MAX_LEN = 63;
static constexpr const size_t MIN_ROOT_LABEL = 3;
static constexpr const size_t MAX_ROOT_LABEL = 6;
private:
String<NAME_MAX_LEN> _name { };
static char const *_next_label(char const *label)
{
size_t length = static_cast<size_t>(*label);
++label;
return label + length;
}
public:
Domain_name() = default;
Domain_name(char const *name) { ascii_to(name, *this); }
bool operator==(Domain_name const &other) const {
return _name == other._name; }
size_t length() const { return _name.length(); }
void copy(void *dest) const {
memcpy(dest, _name.string(), _name.length()); }
void label(size_t label_length, char const *label)
{
if (label_length + _name.length() > NAME_MAX_LEN) {
return; }
if (label_length > LABEL_MAX_LEN) {
return; }
/*
* copy label into a separate buffer, first to easily avoid
* label_length to be converted to ascii. Also all the labels,
* including the bytes further than label_length would be copied
* into the string.
*/
char buff[LABEL_MAX_LEN + 1] { };
buff[0] = static_cast<char>(label_length);
memcpy(buff + 1, label, label_length);
_name = String<NAME_MAX_LEN> { _name, String<LABEL_MAX_LEN>(buff) };
}
size_t label_count() const
{
char const *label = _name.string();
size_t count { 0 };
if (*label == 0) {
return 0; }
while(*label) {
++count;
label = _next_label(label); }
return count;
}
void print(Output &output) const
{
char const *label = _name.string();
if (*label == 0) {
return; }
size_t label_length = static_cast<size_t>(*label);
for (size_t idx = 0; idx < label_length; ++idx) {
output.out_char(*(label + 1 + idx)); }
label = _next_label(label);
while (*label) {
label_length = static_cast<size_t>(*label);
output.out_char('.');
for (size_t idx = 0; idx < label_length; ++idx) {
output.out_char(static_cast<char>(*(label + 1 + idx))); }
label = _next_label(label);
}
}
};
/**
* Data layout of this class conforms to a DNS Request layout (RFC 1035)
*
* DNS request header:
*
* +---------------------+
* | Header | the request header
* +---------------------+
* | Question | the question for the name server
* +---------------------+
* | Answer | RRs answering the question
* +---------------------+
* | Authority | RRs pointing toward an authority
* +---------------------+
* | Additional | RRs holding additional information
* +---------------------+*
*/
class Net::Dns_packet
{
public:
enum class Type: uint16_t {
A = 1, /* a host address */
NS = 2, /* an authoritative name server*/
MD = 3, /* a mail destination (Obsolete - use MX) */
MF = 4, /* a mail forwarder (Obsolete - use MX) */
CNA = 5, /* the canonical name for an alias */
SOA = 6, /* marks the start of a zone of authority */
MB = 7, /* a mailbox domain name (EXPERIMENTAL) */
MG = 8, /* a mail group member (EXPERIMENTAL) */
MR = 9, /* a mail rename domain name (EXPERIMENTAL) */
NUL = 10, /* a null RR (EXPERIMENTAL) */
WKS = 11, /* a well known service description */
PTR = 12, /* a domain name pointer */
HIN = 13, /* host information */
MIN = 14, /* mailbox or mail list information */
MX = 15, /* mail exchange */
TXT = 16, /* text strings */
AXFR = 252, /* A request for a transfer of an entire zone */
MAILB = 253, /* A request for mailbox-related records (MB, MG or MR) */
MAILA = 254, /* A request for mail agent RRs (Obsolete - see MX) */
WILDCARD = 255, /* A request for all records */
};
enum class Class: uint16_t {
IN = 1, /* the Internet */
CS = 2, /* the CSNET class (Obsolete - used only for examples in some obsolete RFCs) */
CH = 3, /* the CHAOS class */
HS = 4, /* Hesiod [Dyer 87] */
WILDCARD = 255, /* any class */
};
struct Dns_entry
{
Domain_name name { };
Type net_type { };
Class net_class { };
uint32_t ttl { };
Ipv4_address addr { };
};
private:
/**
* DNS Request Header
*
* 1 1 1 1 1 1
* 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
* +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
* | ID |
* +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
* |QR| Opcode |AA|TC|RD|RA| Z | RCODE |
* +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
* | QDCOUNT |
* +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
* | ANCOUNT |
* +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
* | NSCOUNT |
* +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
* | ARCOUNT |
* +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+*
*/
struct Header_datagram {
uint16_t id;
uint16_t flags;
uint16_t qdcount;
uint16_t ancount;
uint16_t nscount;
uint16_t arcount;
} __attribute__((packed));
/*
* Flags struct to access Header::flags
*/
struct Flags: Register<16> {
struct Rcode : Register<16>::Bitfield<0, 4> { };
struct Recursion_available : Register<16>::Bitfield<7, 1> { };
struct Recursion_desired : Register<16>::Bitfield<8, 1> { };
struct Truncation : Register<16>::Bitfield<9, 1> { };
struct Authoritative_answer : Register<16>::Bitfield<10, 1> { };
struct Opcode : Register<16>::Bitfield<11, 4> { };
struct Querry : Register<16>::Bitfield<15, 1> { };
};
/*
* Querry layout for dns request (RFC 1035)
*
* 1 1 1 1 1 1
* 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
* +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
* | |
* / QNAME /
* / /
* +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
* | QTYPE |
* +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
* | QCLASS |
* +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
*/
struct Question_datagram {
uint16_t qtype;
uint16_t qclass;
} __attribute__((packed));
/*
* Response layout for dns request (RFC 1035)
*
* 1 1 1 1 1 1
* 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
* +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
* | |
* / /
* / NAME /
* | |
* +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
* | TYPE |
* +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
* | CLASS |
* +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
* | TTL |
* | |
* +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
* | RDLENGTH |
* +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--|
* / RDATA /
* / /
* +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
*
*/
struct Response_datagram {
uint16_t rtype;
uint16_t rclass;
uint32_t rttl;
uint16_t rdlength;
uint8_t rdata[0];
} __attribute__((packed));
Header_datagram _header;
uint16_t _data[0];
uint16_t *_next_question_entry(void *curr_field)
{
uint8_t *label = reinterpret_cast<uint8_t*>(curr_field);
while(*label) ++label;
++label;
label += sizeof(Question_datagram);
return reinterpret_cast<uint16_t*>(label);
}
void _check_size_guard(Size_guard size_guard, void *ptr)
{
if (reinterpret_cast<size_t>(ptr) > reinterpret_cast<size_t>(_data)) {
if (reinterpret_cast<size_t>(ptr) - reinterpret_cast<size_t>(_data) > size_guard.unconsumed()) {
throw Size_guard::Exceeded(); } }
}
public:
enum { UDP_PORT = 53, };
void id(uint16_t id) { _header.id = host_to_big_endian(id); }
uint16_t id() const { return big_endian_to_host(_header.id); }
bool truncated() const {
return Flags::Truncation::get(big_endian_to_host(_header.flags)); }
bool response() const {
return Flags::Querry::get(big_endian_to_host(_header.flags)); }
static inline size_t sizeof_question(Domain_name const &dn) {
return sizeof(Question_datagram) + dn.length(); }
uint16_t qdcount() const { return big_endian_to_host(_header.qdcount); }
uint16_t ancount() const { return big_endian_to_host(_header.ancount); }
void recursion_desired(bool value)
{
uint16_t flags = big_endian_to_host(_header.flags);
Flags::Recursion_desired::set(flags, value);
_header.flags = host_to_big_endian(flags);
}
void question(Size_guard &size_guard,
Domain_name const &dn,
Type qtype = Type::A,
Class qclass = Class::IN)
{
/* only populate questions, when the message is a querry */
if (response()) return;
void *qslot = _data;
/* skip existing question entries */
for (size_t count = 0; count < big_endian_to_host(_header.qdcount); ++count) {
qslot = _next_question_entry(qslot); }
size_guard.consume_head(dn.length() + sizeof(Question_datagram));
dn.copy(qslot);
auto *q = construct_at<Question_datagram>((uint8_t*)qslot + dn.length());
q->qtype = host_to_big_endian(static_cast<uint16_t>(qtype));
q->qclass = host_to_big_endian(static_cast<uint16_t>(qclass));
/* adjust header's question count */
_header.qdcount = host_to_big_endian(
static_cast<uint16_t>(big_endian_to_host(_header.qdcount) + 1));
}
template<typename FN = void(*)(Dns_entry const &)>
void for_each_entry(Size_guard &size_guard, FN fn)
{
/* only read responses, when the message is a response */
if (!response()) return;
/* by-pass questions entries */
void *rslot = _data;
Domain_name name;
/* skip question entries */
for (size_t idx = 0; idx < big_endian_to_host(_header.qdcount); ++idx) {
_check_size_guard(size_guard, rslot);
rslot = _next_question_entry(rslot); }
/* for each answer entries */
for (size_t idx = 0; idx < big_endian_to_host(_header.ancount); ++idx) {
_check_size_guard(size_guard, rslot);
Dns_entry entry { };
/* read domain name, adjusting methods if compression bits are set */
uint16_t compression_octets =
big_endian_to_host(*reinterpret_cast<uint16_t*>(rslot));
char *label = reinterpret_cast<char*>(rslot);
/* if compression bits are set, adjust label pointer */
if (compression_octets & 0xC000) {
size_t offset = compression_octets & 0x3FFF;
label = reinterpret_cast<char*>(&_header) + offset;
_check_size_guard(size_guard, label);
}
/* read domain name labels */
while(*label) {
size_t size = *label;
char *str = label + 1;
entry.name.label(size, str);
label = str + size;
_check_size_guard(size_guard, label); }
/* if compression bits are set, adjust rslot pointer */
if (compression_octets & 0xC000) {
rslot = reinterpret_cast<uint16_t*>(rslot) + 1;
} else {
/* if no compersion bits are set, rslot continue after the domain name */
rslot = label + 1;
}
Response_datagram *rd = reinterpret_cast<Response_datagram*>(rslot);
_check_size_guard(size_guard, rd->rdata);
entry.net_class = static_cast<Class>(big_endian_to_host(rd->rclass));
entry.net_type = static_cast<Type>(big_endian_to_host(rd->rtype));
entry.ttl = big_endian_to_host(rd->rttl);
/* currently only support IPV4 formated response data */
if (big_endian_to_host(rd->rdlength) != IPV4_ADDR_LEN) {
log("Dns address data length is unsupported, ignoring entry for ", entry.name);
continue; }
entry.addr = Ipv4_address { rd->rdata };
fn(entry);
rslot = rd->rdata + big_endian_to_host(rd->rdlength);
}
}
} __attribute__((packed));
/*
* Domain Name Grammar (RFC 1035)
*
* <domain> ::= <subdomain> | " "
*
* <subdomain> ::= <label> | <subdomain> "." <label>
*
* <label> ::= <letter> [ [ <ldh-str> ] <let-dig> ]
*
* <ldh-str> ::= <let-dig-hyp> | <let-dig-hyp> <ldh-str>
*
* <let-dig-hyp> ::= <let-dig> | "-"
*
* <let-dig> ::= <letter> | <digit>
*
* <letter> ::= any one of the 52 alphabetic characters A through Z in upper
* case and a through z in lower case
*
* <digit> ::= any one of the ten digits 0 through 9
*
* Note that while upper and lower case letters are allowed in domain
* names, no significance is attached to the case. That is, two names with
* the same spelling but different case are to be treated as if identical.
*
* The labels must follow the rules for ARPANET host names. They must
* start with a letter, end with a letter or digit, and have as interior
* characters only letters, digits, and hyphen. There are also some
* restrictions on the length. Labels must be 63 characters or less.
*/
Genode::size_t Net::ascii_to(char const *str, Net::Domain_name &result)
{
char const *label = str;
size_t label_length = 0;
Domain_name domain_name;
for (;;) {
/* label must start by <letter> */
if (label_length == 0 && !is_letter(*label)) {
return 0; }
/* last label character is <let-dig> */
if (*(str + 1) == '.' || *(str + 1) == '\"' || *(str + 1) == '\0') {
if (!is_letter(*str) && !is_digit(*str)) {
return 0; } }
/* label is <subdomain> */
if (*str == '.') {
if (label_length == 0) return 0;
if (label_length > 63) return 0;
domain_name.label(label_length, label);
label_length = 0;
++str;
label = str; }
/* label is <domain> */
if (*str == '\"' || *str == '\0') {
if (label_length < Domain_name::MIN_ROOT_LABEL ||
label_length > Domain_name::MAX_ROOT_LABEL) {
return 0; }
if (domain_name.length() + label_length > Domain_name::NAME_MAX_LEN) {
return 0; }
domain_name.label(label_length, label);
if (domain_name.label_count() < 2) return 0;
result = domain_name;
return result.length(); }
/* label character is <let-dig-hyp> */
if (!is_letter(*str) && !is_digit(*str) && *str != '-') {
return 0; }
++label_length;
++str;
}
return 0;
}
#endif /* _NET__DNS_H_ */

41
repos/os/run/dns.run Normal file
View File

@ -0,0 +1,41 @@
create_boot_directory
import_from_depot [depot_user]/src/[base_src] \
[depot_user]/src/init
build { test/dns }
append config {
<config>
<parent-provides>
<service name="ROM"/>
<service name="PD"/>
<service name="RM"/>
<service name="CPU"/>
<service name="LOG"/>
</parent-provides>
<default-route>
<any-service> <parent/> <any-child/> </any-service>
</default-route>
<default caps="100"/>
<start name="dns_test">
<resource name="RAM" quantum="1M"/>
<config>
</config>
</start>
</config>}
install_config $config
build_boot_image { dns_test }
append qemu_args " -nographic "
append_qemu_nic_args
run_genode_until ".*child \"dns_test\" exited with exit value 0" 30

View File

@ -0,0 +1,256 @@
/*
* \brief Dns protocol test
* \author Alice Domage <alice.domage@gapfruit.com>
* \date 2023-09-20
*/
/*
* Copyright (C) 2023 Genode Labs GmbH
* Copyright (C) 2023 gapfruit ag
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU Affero General Public License version 3.
*/
/* Genode includes */
#include <base/attached_rom_dataspace.h>
#include <base/heap.h>
#include <base/component.h>
#include <base/exception.h>
#include <base/env.h>
#include <net/dns.h>
namespace Dns_test {
using namespace Genode;
struct Test_failure: Exception { };
void test_domain_name_format();
void test_dns_request();
void test_dns_response();
void test_dns_malformated_response();
}
void Dns_test::test_domain_name_format()
{
log("----- ", __FUNCTION__, " -----");
Net::Domain_name dn;
Net::ascii_to("pool.ntp.org", dn);
if (dn == Net::Domain_name()) {
throw Test_failure();
} else {
log("Valide domain name: ", dn); }
Net::ascii_to("genode-is.cool.org", dn);
if (dn == Net::Domain_name()) {
throw Test_failure();
} else {
log("Valide domain name: ", dn); }
Net::ascii_to("genode-is42.cool.org", dn);
if (dn == Net::Domain_name()) {
throw Test_failure();
} else {
log("Valide domain name: ", dn); }
/* clear dn var before proceeding with invalide domains tests */
dn = Net::Domain_name();
Net::ascii_to("wrong", dn);
if (dn != Net::Domain_name()) {
throw Test_failure();
} else {
log("wrong: is an invalide domain name"); }
Net::ascii_to("-.wrong", dn);
if (dn != Net::Domain_name()) {
throw Test_failure();
} else {
log("-.wrong: is an invalide domain name"); }
Net::ascii_to("-abc.wrong", dn);
if (dn != Net::Domain_name()) {
throw Test_failure();
} else {
log("-abc.wrong: is an invalide domain name"); }
Net::ascii_to("abc-.wrong", dn);
if (dn != Net::Domain_name()) {
throw Test_failure();
} else {
log("abc-.wrong: is an invalide domain name"); }
Net::ascii_to("6abc.wrong", dn);
if (dn != Net::Domain_name()) {
throw Test_failure(); }
else {
log("6abc.wrong: is an invalide domain name"); }
Net::ascii_to("test..wrong", dn);
if (dn != Net::Domain_name()) {
throw Test_failure();
} else {
log("test..wrong: is an invalide domain name"); }
Net::ascii_to("tooshort.a", dn);
if (dn != Net::Domain_name()) {
throw Test_failure();
} else {
log("tooshort.a: is an invalide domain name"); }
Net::ascii_to("toolong.abcdefghijglmn", dn);
if (dn != Net::Domain_name()) {
throw Test_failure();
} else {
log("toolong.abcdefghijglmn: is an invalide domain name"); }
}
void Dns_test::test_dns_request()
{
log("----- ", __FUNCTION__, " -----");
const uint8_t CAPTURED_DATAGRAM[] = {
0xE4, 0x45, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x04, 0x70, 0x6f, 0x6f, 0x6C, 0x03, 0x6e, 0x74, 0x70, 0x03,
0x6f, 0x72, 0x67, 0x00, 0x00, 0x01, 0x00, 0x01
};
uint8_t DATAGRAM[sizeof(CAPTURED_DATAGRAM)] = {};
Net::Size_guard size_guard { sizeof(DATAGRAM) };
Net::Domain_name dn { "pool.ntp.org" };
if (sizeof(Net::Dns_packet) + Net::Dns_packet::sizeof_question(dn) != sizeof(CAPTURED_DATAGRAM)) {
error("Datagram size for the given DNS request is incorrect!");
throw Test_failure(); }
auto *dns = Genode::construct_at<Net::Dns_packet>(DATAGRAM);
size_guard.consume_tail(sizeof(Net::Dns_packet));
dns->id(0xe445);
dns->recursion_desired(true);
dns->question(size_guard, dn);
if (memcmp(CAPTURED_DATAGRAM, DATAGRAM, sizeof(DATAGRAM)) != 0) {
throw Test_failure();
}
log("DNS request successfully created");
}
void Dns_test::test_dns_response()
{
log("----- ", __FUNCTION__, " -----");
uint8_t CAPTURED_DATAGRAM[] = {
0xe4, 0x45, 0x81, 0x80, 0x00, 0x01, 0x00, 0x04,
0x00, 0x00, 0x00, 0x00, 0x04, 0x70, 0x6f, 0x6f,
0x6c, 0x03, 0x6e, 0x74, 0x70, 0x03, 0x6f, 0x72,
0x67, 0x00, 0x00, 0x01, 0x00, 0x01, 0xc0, 0x0c,
0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x24,
0x00, 0x04, 0xc0, 0x21, 0xd6, 0x2f, 0xc0, 0x0c,
0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x24,
0x00, 0x04, 0xd4, 0x33, 0x90, 0x2e, 0xc0, 0x0c,
0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x24,
0x00, 0x04, 0xd4, 0xf3, 0x60, 0x4c, 0xc0, 0x0c,
0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x24,
0x00, 0x04, 0xc1, 0x21, 0x1e, 0x27
};
uint8_t DATAGRAM[sizeof(CAPTURED_DATAGRAM)] = {};
Net::Size_guard size_guard { sizeof(DATAGRAM) };
auto *dns = Genode::construct_at<Net::Dns_packet>(DATAGRAM);
memcpy(DATAGRAM, CAPTURED_DATAGRAM, sizeof(CAPTURED_DATAGRAM));
if (dns->id() != 0xe445) {
error("Could not extract response ID properly");
throw Test_failure(); }
if (!dns->response()) {
error("Querry bit is not interpreted correctly");
throw Test_failure(); }
if (dns->truncated()) {
error("Truncated bit is not interpreted correctly");
throw Test_failure(); }
/* expected address from the CAPTURED_DATAGRAM */
uint8_t addr1[4] { 192, 33, 214, 47 };
uint8_t addr2[4] { 212, 51, 144, 46 };
uint8_t addr3[4] { 212, 243, 96, 76 };
uint8_t addr4[4] { 193, 33, 30, 39 };
Net::Ipv4_address addrs[4] {
addr1,
addr2,
addr3,
addr4
};
size_t idx = 0;
dns->for_each_entry(size_guard, [&addrs, &idx] (Net::Dns_packet::Dns_entry const &entry) {
log(entry.name, " resolved to ", entry.addr);
if (entry.addr != addrs[idx]) {
throw Test_failure(); }
++idx; });
log("DNS response successfully parsed");
}
void Dns_test::test_dns_malformated_response()
{
log("----- ", __FUNCTION__, " -----");
/* the byte in uppercase (0xFF) is a malicious offset */
uint8_t MALICIOUS_DATAGRAM[] = {
0xe4, 0x45, 0x81, 0x80, 0x00, 0x01, 0x00, 0x04,
0x00, 0x00, 0x00, 0x00, 0x04, 0x70, 0x6f, 0x6f,
0x6c, 0x03, 0x6e, 0x74, 0x70, 0x03, 0x6f, 0x72,
0x67, 0x00, 0x00, 0x01, 0x00, 0x01, 0xc0, 0x0c,
0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x24,
0x00, 0x04, 0xc0, 0x21, 0xd6, 0x2f, 0xc0, 0x0c,
0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x24,
0x00, 0x04, 0xd4, 0x33, 0x90, 0x2e, 0xc0, 0x0c,
0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x24,
0x00, 0x04, 0xd4, 0xf3, 0x60, 0x4c, 0xFF, 0x0c,
0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x24,
0x00, 0x04, 0xc1, 0x21, 0x1e, 0x27
};
uint8_t DATAGRAM[sizeof(MALICIOUS_DATAGRAM)] = {};
Net::Size_guard size_guard { sizeof(DATAGRAM) };
auto *dns = Genode::construct_at<Net::Dns_packet>(DATAGRAM);
memcpy(DATAGRAM, MALICIOUS_DATAGRAM, sizeof(MALICIOUS_DATAGRAM));
size_t idx { 0 };
try {
dns->for_each_entry(size_guard, [&idx] (Net::Dns_packet::Dns_entry const &entry) {
log(entry.name, " resolved to ", entry.addr);
++idx; });
} catch(...) { }
if (idx != 3) {
error("out of bound access not detected");
throw Test_failure(); }
log("DNS malformated response successfully parsed");
}
void Component::construct(Genode::Env &env)
{
Dns_test::test_domain_name_format();
Dns_test::test_dns_request();
Dns_test::test_dns_response();
Dns_test::test_dns_malformated_response();
env.parent().exit(0);
}

View File

@ -0,0 +1,8 @@
TARGET := dns_test
LIBS += base net
SRC_CC += main.cc
INC_DIR += $(PRG_DIR)