mirror of
https://github.com/systemd/systemd
synced 2024-07-23 03:04:57 +00:00
test: introduce a dummy DNS test server
Introduce a _very_ simple DNS server using our internal DNS-related code, that responds to queries with specifically crafted packets, to cover scenarios that are difficult to reproduce with well-behaving DNS servers. Also, hide the test DNS server behind Knot using the dnsproxy module, so we don't have to switch DNS servers during tests.
This commit is contained in:
parent
ed6c51781f
commit
f1caa5d6e7
|
@ -196,6 +196,20 @@ executables += [
|
|||
],
|
||||
'include_directories' : resolve_includes,
|
||||
},
|
||||
test_template + {
|
||||
'sources' : [
|
||||
files('test-resolved-dummy-server.c'),
|
||||
basic_dns_sources,
|
||||
systemd_resolved_sources,
|
||||
],
|
||||
'dependencies' : [
|
||||
lib_openssl_or_gcrypt,
|
||||
libm,
|
||||
systemd_resolved_dependencies,
|
||||
],
|
||||
'include_directories' : resolve_includes,
|
||||
'type' : 'manual',
|
||||
},
|
||||
resolve_fuzz_template + {
|
||||
'sources' : files('fuzz-dns-packet.c'),
|
||||
},
|
||||
|
|
428
src/resolve/test-resolved-dummy-server.c
Normal file
428
src/resolve/test-resolved-dummy-server.c
Normal file
|
@ -0,0 +1,428 @@
|
|||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
|
||||
#include "sd-daemon.h"
|
||||
|
||||
#include "fd-util.h"
|
||||
#include "iovec-util.h"
|
||||
#include "log.h"
|
||||
#include "resolved-dns-packet.h"
|
||||
#include "resolved-manager.h"
|
||||
#include "socket-netlink.h"
|
||||
#include "socket-util.h"
|
||||
|
||||
/* Taken from resolved-dns-stub.c */
|
||||
#define ADVERTISE_DATAGRAM_SIZE_MAX (65536U-14U-20U-8U)
|
||||
|
||||
/* This is more or less verbatim manager_recv() from resolved-manager.c, sans the manager stuff */
|
||||
static int server_recv(int fd, DnsPacket **ret) {
|
||||
_cleanup_(dns_packet_unrefp) DnsPacket *p = NULL;
|
||||
CMSG_BUFFER_TYPE(CMSG_SPACE(MAXSIZE(struct in_pktinfo, struct in6_pktinfo))
|
||||
+ CMSG_SPACE(int) /* ttl/hoplimit */
|
||||
+ EXTRA_CMSG_SPACE /* kernel appears to require extra buffer space */) control;
|
||||
union sockaddr_union sa;
|
||||
struct iovec iov;
|
||||
struct msghdr mh = {
|
||||
.msg_name = &sa.sa,
|
||||
.msg_namelen = sizeof(sa),
|
||||
.msg_iov = &iov,
|
||||
.msg_iovlen = 1,
|
||||
.msg_control = &control,
|
||||
.msg_controllen = sizeof(control),
|
||||
};
|
||||
struct cmsghdr *cmsg;
|
||||
ssize_t ms, l;
|
||||
int r;
|
||||
|
||||
assert(fd >= 0);
|
||||
assert(ret);
|
||||
|
||||
ms = next_datagram_size_fd(fd);
|
||||
if (ms < 0)
|
||||
return ms;
|
||||
|
||||
r = dns_packet_new(&p, DNS_PROTOCOL_DNS, ms, DNS_PACKET_SIZE_MAX);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
iov = IOVEC_MAKE(DNS_PACKET_DATA(p), p->allocated);
|
||||
|
||||
l = recvmsg_safe(fd, &mh, 0);
|
||||
if (ERRNO_IS_NEG_TRANSIENT(l))
|
||||
return 0;
|
||||
if (l <= 0)
|
||||
return l;
|
||||
|
||||
assert(!(mh.msg_flags & MSG_TRUNC));
|
||||
|
||||
p->size = (size_t) l;
|
||||
|
||||
p->family = sa.sa.sa_family;
|
||||
p->ipproto = IPPROTO_UDP;
|
||||
if (p->family == AF_INET) {
|
||||
p->sender.in = sa.in.sin_addr;
|
||||
p->sender_port = be16toh(sa.in.sin_port);
|
||||
} else if (p->family == AF_INET6) {
|
||||
p->sender.in6 = sa.in6.sin6_addr;
|
||||
p->sender_port = be16toh(sa.in6.sin6_port);
|
||||
p->ifindex = sa.in6.sin6_scope_id;
|
||||
} else
|
||||
return -EAFNOSUPPORT;
|
||||
|
||||
p->timestamp = now(CLOCK_BOOTTIME);
|
||||
|
||||
CMSG_FOREACH(cmsg, &mh) {
|
||||
|
||||
if (cmsg->cmsg_level == IPPROTO_IPV6) {
|
||||
assert(p->family == AF_INET6);
|
||||
|
||||
switch (cmsg->cmsg_type) {
|
||||
|
||||
case IPV6_PKTINFO: {
|
||||
struct in6_pktinfo *i = CMSG_TYPED_DATA(cmsg, struct in6_pktinfo);
|
||||
|
||||
if (p->ifindex <= 0)
|
||||
p->ifindex = i->ipi6_ifindex;
|
||||
|
||||
p->destination.in6 = i->ipi6_addr;
|
||||
break;
|
||||
}
|
||||
|
||||
case IPV6_HOPLIMIT:
|
||||
p->ttl = *CMSG_TYPED_DATA(cmsg, int);
|
||||
break;
|
||||
|
||||
case IPV6_RECVFRAGSIZE:
|
||||
p->fragsize = *CMSG_TYPED_DATA(cmsg, int);
|
||||
break;
|
||||
}
|
||||
} else if (cmsg->cmsg_level == IPPROTO_IP) {
|
||||
assert(p->family == AF_INET);
|
||||
|
||||
switch (cmsg->cmsg_type) {
|
||||
|
||||
case IP_PKTINFO: {
|
||||
struct in_pktinfo *i = CMSG_TYPED_DATA(cmsg, struct in_pktinfo);
|
||||
|
||||
if (p->ifindex <= 0)
|
||||
p->ifindex = i->ipi_ifindex;
|
||||
|
||||
p->destination.in = i->ipi_addr;
|
||||
break;
|
||||
}
|
||||
|
||||
case IP_TTL:
|
||||
p->ttl = *CMSG_TYPED_DATA(cmsg, int);
|
||||
break;
|
||||
|
||||
case IP_RECVFRAGSIZE:
|
||||
p->fragsize = *CMSG_TYPED_DATA(cmsg, int);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* The Linux kernel sets the interface index to the loopback
|
||||
* device if the packet came from the local host since it
|
||||
* avoids the routing table in such a case. Let's unset the
|
||||
* interface index in such a case. */
|
||||
if (p->ifindex == LOOPBACK_IFINDEX)
|
||||
p->ifindex = 0;
|
||||
|
||||
log_debug("Received DNS UDP packet of size %zu, ifindex=%i, ttl=%u, fragsize=%zu, sender=%s, destination=%s",
|
||||
p->size, p->ifindex, p->ttl, p->fragsize,
|
||||
IN_ADDR_TO_STRING(p->family, &p->sender),
|
||||
IN_ADDR_TO_STRING(p->family, &p->destination));
|
||||
|
||||
*ret = TAKE_PTR(p);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Same as above, see manager_ipv4_send() in resolved-manager.c */
|
||||
static int server_ipv4_send(
|
||||
int fd,
|
||||
const struct in_addr *destination,
|
||||
uint16_t port,
|
||||
const struct in_addr *source,
|
||||
DnsPacket *packet) {
|
||||
|
||||
union sockaddr_union sa;
|
||||
struct iovec iov;
|
||||
struct msghdr mh = {
|
||||
.msg_iov = &iov,
|
||||
.msg_iovlen = 1,
|
||||
.msg_name = &sa.sa,
|
||||
.msg_namelen = sizeof(sa.in),
|
||||
};
|
||||
|
||||
assert(fd >= 0);
|
||||
assert(destination);
|
||||
assert(port > 0);
|
||||
assert(packet);
|
||||
|
||||
iov = IOVEC_MAKE(DNS_PACKET_DATA(packet), packet->size);
|
||||
|
||||
sa = (union sockaddr_union) {
|
||||
.in.sin_family = AF_INET,
|
||||
.in.sin_addr = *destination,
|
||||
.in.sin_port = htobe16(port),
|
||||
};
|
||||
|
||||
return sendmsg_loop(fd, &mh, 0);
|
||||
}
|
||||
|
||||
static int make_reply_packet(DnsPacket *packet, DnsPacket **ret) {
|
||||
_cleanup_(dns_packet_unrefp) DnsPacket *p = NULL;
|
||||
int r;
|
||||
|
||||
assert(packet);
|
||||
assert(ret);
|
||||
|
||||
r = dns_packet_new(&p, DNS_PROTOCOL_DNS, 0, DNS_PACKET_PAYLOAD_SIZE_MAX(packet));
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = dns_packet_append_question(p, packet->question);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
DNS_PACKET_HEADER(p)->id = DNS_PACKET_ID(packet);
|
||||
DNS_PACKET_HEADER(p)->qdcount = htobe16(dns_question_size(packet->question));
|
||||
|
||||
*ret = TAKE_PTR(p);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int reply_append_edns(DnsPacket *packet, DnsPacket *reply, const char *extra_text, size_t rcode, uint16_t ede_code) {
|
||||
size_t saved_size;
|
||||
int r;
|
||||
|
||||
assert(packet);
|
||||
assert(reply);
|
||||
|
||||
/* Append EDNS0 stuff (inspired by dns_packet_append_opt() from resolved-dns-packet.c).
|
||||
*
|
||||
* Relevant headers from RFC 6891:
|
||||
*
|
||||
* +------------+--------------+------------------------------+
|
||||
* | Field Name | Field Type | Description |
|
||||
* +------------+--------------+------------------------------+
|
||||
* | NAME | domain name | MUST be 0 (root domain) |
|
||||
* | TYPE | u_int16_t | OPT (41) |
|
||||
* | CLASS | u_int16_t | requestor's UDP payload size |
|
||||
* | TTL | u_int32_t | extended RCODE and flags |
|
||||
* | RDLEN | u_int16_t | length of all RDATA |
|
||||
* | RDATA | octet stream | {attribute,value} pairs |
|
||||
* +------------+--------------+------------------------------+
|
||||
*
|
||||
* +0 (MSB) +1 (LSB)
|
||||
* +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
|
||||
* 0: | OPTION-CODE |
|
||||
* +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
|
||||
* 2: | OPTION-LENGTH |
|
||||
* +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
|
||||
* 4: | |
|
||||
* / OPTION-DATA /
|
||||
* / /
|
||||
* +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
|
||||
*
|
||||
* And from RFC 8914:
|
||||
*
|
||||
* 1 1 1 1 1 1
|
||||
* 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
|
||||
* +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
|
||||
* 0: | OPTION-CODE |
|
||||
* +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
|
||||
* 2: | OPTION-LENGTH |
|
||||
* +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
|
||||
* 4: | INFO-CODE |
|
||||
* +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
|
||||
* 6: / EXTRA-TEXT ... /
|
||||
* +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
|
||||
*/
|
||||
|
||||
saved_size = reply->size;
|
||||
|
||||
/* empty name */
|
||||
r = dns_packet_append_uint8(reply, 0, NULL);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
/* type */
|
||||
r = dns_packet_append_uint16(reply, DNS_TYPE_OPT, NULL);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
/* class: maximum udp packet that can be received */
|
||||
r = dns_packet_append_uint16(reply, ADVERTISE_DATAGRAM_SIZE_MAX, NULL);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
/* extended RCODE and VERSION */
|
||||
r = dns_packet_append_uint16(reply, ((uint16_t) rcode & 0x0FF0) << 4, NULL);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
/* flags: DNSSEC OK (DO), see RFC3225 */
|
||||
r = dns_packet_append_uint16(reply, 0, NULL);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
/* RDATA */
|
||||
|
||||
size_t extra_text_len = isempty(extra_text) ? 0 : strlen(extra_text);
|
||||
/* RDLENGTH (OPTION CODE + OPTION LENGTH + INFO-CODE + EXTRA-TEXT) */
|
||||
r = dns_packet_append_uint16(reply, 2 + 2 + 2 + extra_text_len, NULL);
|
||||
if (r < 0)
|
||||
return 0;
|
||||
|
||||
/* OPTION-CODE: 15 for EDE */
|
||||
r = dns_packet_append_uint16(reply, 15, NULL);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
/* OPTION-LENGTH: INFO-CODE + EXTRA-TEXT */
|
||||
r = dns_packet_append_uint16(reply, 2 + extra_text_len, NULL);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
/* INFO-CODE: EDE code */
|
||||
r = dns_packet_append_uint16(reply, ede_code, NULL);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
/* EXTRA-TEXT */
|
||||
if (extra_text_len > 0) {
|
||||
/* From RFC 8914:
|
||||
* EDE text may be null terminated but MUST NOT be assumed to be; the length MUST be derived
|
||||
* from the OPTION-LENGTH field
|
||||
*
|
||||
* Let's exercise our code on the receiving side and not NUL-terminate the EXTRA-TEXT field
|
||||
*/
|
||||
r = dns_packet_append_blob(reply, extra_text, extra_text_len, NULL);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
DNS_PACKET_HEADER(reply)->arcount = htobe16(DNS_PACKET_ARCOUNT(reply) + 1);
|
||||
reply->opt_start = saved_size;
|
||||
reply->opt_size = reply->size - saved_size;
|
||||
|
||||
/* Order: qr, opcode, aa, tc, rd, ra, ad, cd, rcode */
|
||||
DNS_PACKET_HEADER(reply)->flags = htobe16(DNS_PACKET_MAKE_FLAGS(
|
||||
1, 0, 0, 0, DNS_PACKET_RD(packet), 1, 0, 1, rcode));
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void server_fail(DnsPacket *packet, DnsPacket *reply, int rcode) {
|
||||
assert(reply);
|
||||
|
||||
/* Order: qr, opcode, aa, tc, rd, ra, ad, cd, rcode */
|
||||
DNS_PACKET_HEADER(reply)->flags = htobe16(DNS_PACKET_MAKE_FLAGS(
|
||||
1, 0, 0, 0, DNS_PACKET_RD(packet), 1, 0, 1, rcode));
|
||||
}
|
||||
|
||||
static int server_handle_edns_bogus_dnssec(DnsPacket *packet, DnsPacket *reply) {
|
||||
assert(packet);
|
||||
assert(reply);
|
||||
|
||||
return reply_append_edns(packet, reply, NULL, DNS_RCODE_SERVFAIL, DNS_EDE_RCODE_DNSSEC_BOGUS);
|
||||
}
|
||||
|
||||
static int server_handle_edns_extra_text(DnsPacket *packet, DnsPacket *reply) {
|
||||
assert(packet);
|
||||
assert(reply);
|
||||
|
||||
return reply_append_edns(packet, reply, "Nothing to see here!", DNS_RCODE_SERVFAIL, DNS_EDE_RCODE_CENSORED);
|
||||
}
|
||||
|
||||
static int server_handle_edns_invalid_code(DnsPacket *packet, DnsPacket *reply, const char *extra_text) {
|
||||
assert(packet);
|
||||
assert(reply);
|
||||
assert_cc(_DNS_EDE_RCODE_MAX_DEFINED < UINT16_MAX);
|
||||
|
||||
return reply_append_edns(packet, reply, extra_text, DNS_RCODE_SERVFAIL, _DNS_EDE_RCODE_MAX_DEFINED + 1);
|
||||
}
|
||||
|
||||
static int server_handle_edns_code_zero(DnsPacket *packet, DnsPacket *reply) {
|
||||
assert(packet);
|
||||
assert(reply);
|
||||
assert_cc(DNS_EDE_RCODE_OTHER == 0);
|
||||
|
||||
return reply_append_edns(packet, reply, "\xF0\x9F\x90\xB1", DNS_RCODE_SERVFAIL, DNS_EDE_RCODE_OTHER);
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
_cleanup_close_ int fd = -EBADF;
|
||||
int r;
|
||||
|
||||
log_parse_environment();
|
||||
log_open();
|
||||
|
||||
if (argc != 2)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
|
||||
"This program takes one argument in format ip_address:port");
|
||||
|
||||
fd = make_socket_fd(LOG_DEBUG, argv[1], SOCK_DGRAM, SOCK_CLOEXEC);
|
||||
if (fd < 0)
|
||||
return log_error_errno(fd, "Failed to listen on address '%s': %m", argv[1]);
|
||||
|
||||
(void) sd_notify(/* unset_environment=false */ false, "READY=1");
|
||||
|
||||
for (;;) {
|
||||
_cleanup_(dns_packet_unrefp) DnsPacket *packet = NULL;
|
||||
_cleanup_(dns_packet_unrefp) DnsPacket *reply = NULL;
|
||||
const char *name;
|
||||
|
||||
r = server_recv(fd, &packet);
|
||||
if (r < 0) {
|
||||
log_debug_errno(r, "Failed to receive packet, ignoring: %m");
|
||||
continue;
|
||||
}
|
||||
|
||||
r = dns_packet_validate_query(packet);
|
||||
if (r < 0) {
|
||||
log_debug_errno(r, "Invalid DNS UDP packet, ignoring.");
|
||||
continue;
|
||||
}
|
||||
|
||||
r = dns_packet_extract(packet);
|
||||
if (r < 0) {
|
||||
log_debug_errno(r, "Failed to extract DNS packet, ignoring: %m");
|
||||
continue;
|
||||
}
|
||||
|
||||
name = dns_question_first_name(packet->question);
|
||||
log_info("Processing question for name '%s'", name);
|
||||
|
||||
(void) dns_question_dump(packet->question, stdout);
|
||||
|
||||
r = make_reply_packet(packet, &reply);
|
||||
if (r < 0) {
|
||||
log_debug_errno(r, "Failed to make reply packet: %m");
|
||||
break;
|
||||
}
|
||||
|
||||
if (streq_ptr(name, "edns-bogus-dnssec.forwarded.test"))
|
||||
r = server_handle_edns_bogus_dnssec(packet, reply);
|
||||
else if (streq_ptr(name, "edns-extra-text.forwarded.test"))
|
||||
r = server_handle_edns_extra_text(packet, reply);
|
||||
else if (streq_ptr(name, "edns-invalid-code.forwarded.test"))
|
||||
r = server_handle_edns_invalid_code(packet, reply, NULL);
|
||||
else if (streq_ptr(name, "edns-invalid-code-with-extra-text.forwarded.test"))
|
||||
r = server_handle_edns_invalid_code(packet, reply, "Hello [#]$%~ World");
|
||||
else if (streq_ptr(name, "edns-code-zero.forwarded.test"))
|
||||
r = server_handle_edns_code_zero(packet, reply);
|
||||
else
|
||||
r = log_debug_errno(SYNTHETIC_ERRNO(EFAULT), "Unhandled name '%s', ignoring.", name);
|
||||
|
||||
if (r < 0)
|
||||
server_fail(packet, reply, DNS_RCODE_NXDOMAIN);
|
||||
|
||||
r = server_ipv4_send(fd, &packet->sender.in, packet->sender_port, &packet->destination.in, reply);
|
||||
if (r < 0)
|
||||
log_debug_errno(r, "Failed to send reply: %m");
|
||||
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -29,6 +29,9 @@ remote:
|
|||
address: 10.0.0.1@53
|
||||
address: fd00:dead:beef:cafe::1@53
|
||||
|
||||
- id: forwarded
|
||||
address: 10.99.0.1@53
|
||||
|
||||
submission:
|
||||
- id: parent_zone_sbm
|
||||
check-interval: 2s
|
||||
|
@ -69,6 +72,11 @@ policy:
|
|||
- id: manual
|
||||
manual: on
|
||||
|
||||
mod-dnsproxy:
|
||||
- id: forwarded
|
||||
remote: forwarded
|
||||
fallback: off
|
||||
|
||||
template:
|
||||
# Sign everything by default and propagate the respective DS records to the parent
|
||||
- id: default
|
||||
|
@ -86,6 +94,11 @@ template:
|
|||
semantic-checks: on
|
||||
storage: "/var/lib/knot/zones"
|
||||
|
||||
- id: forwarded
|
||||
dnssec-signing: off
|
||||
module: mod-dnsproxy/forwarded
|
||||
zonefile-load: none
|
||||
|
||||
zone:
|
||||
# Create our own DNSSEC-aware root zone, so we can test the whole chain of
|
||||
# trust. This needs a ZSK/KSK keypair to be generated before running knot +
|
||||
|
@ -119,3 +132,7 @@ zone:
|
|||
# An unsigned zone
|
||||
- domain: unsigned.test
|
||||
template: unsigned
|
||||
|
||||
# Forward all queries for this zone to our dummy test server
|
||||
- domain: forwarded.test
|
||||
template: forwarded
|
||||
|
|
|
@ -197,6 +197,25 @@ DNSSEC=allow-downgrade
|
|||
DNS=10.0.0.1
|
||||
DNS=fd00:dead:beef:cafe::1
|
||||
EOF
|
||||
cat >/etc/systemd/network/10-dns1.netdev <<EOF
|
||||
[NetDev]
|
||||
Name=dns1
|
||||
Kind=dummy
|
||||
EOF
|
||||
cat >/etc/systemd/network/10-dns1.network <<EOF
|
||||
[Match]
|
||||
Name=dns1
|
||||
|
||||
[Network]
|
||||
Address=10.99.0.1/24
|
||||
DNSSEC=no
|
||||
EOF
|
||||
systemctl edit --stdin --full --runtime --force "resolved-dummy-server.service" <<EOF
|
||||
[Service]
|
||||
Type=notify
|
||||
Environment=SYSTEMD_LOG_LEVEL=debug
|
||||
ExecStart=/usr/lib/systemd/tests/unit-tests/manual/test-resolved-dummy-server 10.99.0.1:53
|
||||
EOF
|
||||
|
||||
DNS_ADDRESSES=(
|
||||
"10.0.0.1"
|
||||
|
@ -236,6 +255,7 @@ ln -svf /etc/bind.keys /etc/bind/bind.keys
|
|||
systemctl unmask systemd-networkd
|
||||
systemctl start systemd-networkd
|
||||
restart_resolved
|
||||
systemctl start resolved-dummy-server
|
||||
# Create knot's runtime dir, since from certain version it's provided only by
|
||||
# the package and not created by tmpfiles/systemd
|
||||
if [[ ! -d /run/knot ]]; then
|
||||
|
@ -246,6 +266,7 @@ systemctl start knot
|
|||
# Wait a bit for the keys to propagate
|
||||
sleep 4
|
||||
|
||||
systemctl status resolved-dummy-server
|
||||
networkctl status
|
||||
resolvectl status
|
||||
resolvectl log-level debug
|
||||
|
@ -254,7 +275,14 @@ resolvectl log-level debug
|
|||
systemd-run -u resolvectl-monitor.service -p Type=notify resolvectl monitor
|
||||
systemd-run -u resolvectl-monitor-json.service -p Type=notify resolvectl monitor --json=short
|
||||
|
||||
knotc --force zone-check
|
||||
# FIXME: knot, unfortunately, incorrectly complains about missing zone files for zones
|
||||
# that are forwarded using the `dnsproxy` module. Until the issue is resolved,
|
||||
# let's fall back to pre-processing the `zone-check` output a bit before checking it
|
||||
#
|
||||
# See: https://gitlab.nic.cz/knot/knot-dns/-/issues/913
|
||||
run knotc zone-check || :
|
||||
sed -i '/forwarded.test./d' "$RUN_OUT"
|
||||
[[ ! -s "$RUN_OUT" ]]
|
||||
# We need to manually propagate the DS records of onlinesign.test. to the parent
|
||||
# zone, since they're generated online
|
||||
knotc zone-begin test.
|
||||
|
@ -552,6 +580,61 @@ grep -qF "fd00:dead:beef:cafe::123" "$RUN_OUT"
|
|||
#run dig +dnssec this.does.not.exist.untrusted.test
|
||||
#grep -qF "status: NXDOMAIN" "$RUN_OUT"
|
||||
|
||||
: "--- ZONE: forwarded.test (queries forwarded to our dummy test server) ---"
|
||||
JOURNAL_CURSOR="$(mktemp)"
|
||||
journalctl -n0 -q --cursor-file="$JOURNAL_CURSOR"
|
||||
|
||||
# See "test-resolved-dummy-server.c" for the server part
|
||||
(! run resolvectl query nope.forwarded.test)
|
||||
grep -qF "nope.forwarded.test" "$RUN_OUT"
|
||||
grep -qF "not found" "$RUN_OUT"
|
||||
|
||||
# SERVFAIL + EDE code 6: DNSSEC Bogus
|
||||
(! run resolvectl query edns-bogus-dnssec.forwarded.test)
|
||||
grep -qE "^edns-bogus-dnssec.forwarded.test:.+: upstream-failure \(DNSSEC Bogus\)" "$RUN_OUT"
|
||||
# Same thing, but over Varlink
|
||||
(! run varlinkctl call /run/systemd/resolve/io.systemd.Resolve io.systemd.Resolve.ResolveHostname '{"name" : "edns-bogus-dnssec.forwarded.test"}')
|
||||
grep -qF "io.systemd.Resolve.DNSSECValidationFailed" "$RUN_OUT"
|
||||
grep -qF '{"result":"upstream-failure","extendedDNSErrorCode":6}' "$RUN_OUT"
|
||||
journalctl --sync
|
||||
journalctl -u systemd-resolved.service --cursor-file="$JOURNAL_CURSOR" --grep "Server returned error: SERVFAIL \(DNSSEC Bogus\). Lookup failed."
|
||||
|
||||
# SERVFAIL + EDE code 16: Censored + extra text
|
||||
(! run resolvectl query edns-extra-text.forwarded.test)
|
||||
grep -qE "^edns-extra-text.forwarded.test.+: SERVFAIL \(Censored: Nothing to see here!\)" "$RUN_OUT"
|
||||
(! run varlinkctl call /run/systemd/resolve/io.systemd.Resolve io.systemd.Resolve.ResolveHostname '{"name" : "edns-extra-text.forwarded.test"}')
|
||||
grep -qF "io.systemd.Resolve.DNSError" "$RUN_OUT"
|
||||
grep -qF '{"rcode":2,"extendedDNSErrorCode":16,"extendedDNSErrorMessage":"Nothing to see here!"}' "$RUN_OUT"
|
||||
journalctl --sync
|
||||
journalctl -u systemd-resolved.service --cursor-file="$JOURNAL_CURSOR" --grep "Server returned error: SERVFAIL \(Censored: Nothing to see here!\)"
|
||||
|
||||
# SERVFAIL + EDE code 0: Other + extra text
|
||||
(! run resolvectl query edns-code-zero.forwarded.test)
|
||||
grep -qE "^edns-code-zero.forwarded.test:.+: SERVFAIL \(Other: 🐱\)" "$RUN_OUT"
|
||||
(! run varlinkctl call /run/systemd/resolve/io.systemd.Resolve io.systemd.Resolve.ResolveHostname '{"name" : "edns-code-zero.forwarded.test"}')
|
||||
grep -qF "io.systemd.Resolve.DNSError" "$RUN_OUT"
|
||||
grep -qF '{"rcode":2,"extendedDNSErrorCode":0,"extendedDNSErrorMessage":"🐱"}' "$RUN_OUT"
|
||||
journalctl --sync
|
||||
journalctl -u systemd-resolved.service --cursor-file="$JOURNAL_CURSOR" --grep "Server returned error: SERVFAIL \(Other: 🐱\)"
|
||||
|
||||
# SERVFAIL + invalid EDE code
|
||||
(! run resolvectl query edns-invalid-code.forwarded.test)
|
||||
grep -qE "^edns-invalid-code.forwarded.test:.+: SERVFAIL \([0-9]+\)" "$RUN_OUT"
|
||||
(! run varlinkctl call /run/systemd/resolve/io.systemd.Resolve io.systemd.Resolve.ResolveHostname '{"name" : "edns-invalid-code.forwarded.test"}')
|
||||
grep -qF "io.systemd.Resolve.DNSError" "$RUN_OUT"
|
||||
grep -qE '{"rcode":2,"extendedDNSErrorCode":[0-9]+}' "$RUN_OUT"
|
||||
journalctl --sync
|
||||
journalctl -u systemd-resolved.service --cursor-file="$JOURNAL_CURSOR" --grep "Server returned error: SERVFAIL \(\d+\)"
|
||||
|
||||
# SERVFAIL + invalid EDE code + extra text
|
||||
(! run resolvectl query edns-invalid-code-with-extra-text.forwarded.test)
|
||||
grep -qE '^edns-invalid-code-with-extra-text.forwarded.test:.+: SERVFAIL \([0-9]+: Hello \[#\]\$%~ World\)' "$RUN_OUT"
|
||||
(! run varlinkctl call /run/systemd/resolve/io.systemd.Resolve io.systemd.Resolve.ResolveHostname '{"name" : "edns-invalid-code-with-extra-text.forwarded.test"}')
|
||||
grep -qF "io.systemd.Resolve.DNSError" "$RUN_OUT"
|
||||
grep -qE '{"rcode":2,"extendedDNSErrorCode":[0-9]+,"extendedDNSErrorMessage":"Hello \[#\]\$%~ World"}' "$RUN_OUT"
|
||||
journalctl --sync
|
||||
journalctl -u systemd-resolved.service --cursor-file="$JOURNAL_CURSOR" --grep "Server returned error: SERVFAIL \(\d+: Hello \[\#\]\\$%~ World\)"
|
||||
|
||||
### Test resolvectl show-cache
|
||||
run resolvectl show-cache
|
||||
run resolvectl show-cache --json=short
|
||||
|
|
Loading…
Reference in a new issue