From 8b0bbc6691950041a40fc924b106c63c8c3d2dbc Mon Sep 17 00:00:00 2001 From: Wouter Wijngaards Date: Wed, 25 Jul 2007 12:31:04 +0000 Subject: [PATCH] Tests can handle multiple hosts. Tests for chaos and iterator. git-svn-id: file:///svn/unbound/trunk@450 be551aaa-1e26-0410-a405-d3ace91eadb9 --- doc/Changelog | 5 + testcode/fake_event.c | 46 ++++++++- testcode/ldns-testpkts.c | 2 + testcode/replay.c | 36 +++++++ testcode/replay.h | 6 +- testdata/iter_recurse.rpl | 178 +++++++++++++++++++++++++++++++++ testdata/iter_resolve.rpl | 93 +++++++++++++++++ testdata/version_bind.rpl | 74 ++++++++++++++ testdata/version_bind_hide.rpl | 67 +++++++++++++ 9 files changed, 501 insertions(+), 6 deletions(-) create mode 100644 testdata/iter_recurse.rpl create mode 100644 testdata/iter_resolve.rpl create mode 100644 testdata/version_bind.rpl create mode 100644 testdata/version_bind_hide.rpl diff --git a/doc/Changelog b/doc/Changelog index 943b5bbd8..88c2bb5d4 100644 --- a/doc/Changelog +++ b/doc/Changelog @@ -1,3 +1,8 @@ +25 July 2007: Wouter + - testbound read ADDRESS and check it. + - test for version.bind and friends. + - test for iterator chaining through several referrals. + 24 July 2007: Wouter - Example section in config manual. - Addr stored for range and moment in replay. diff --git a/testcode/fake_event.c b/testcode/fake_event.c index 83c477edf..341328d2d 100644 --- a/testcode/fake_event.c +++ b/testcode/fake_event.c @@ -143,6 +143,10 @@ pending_matches_current(struct replay_runtime* runtime, return 0; /* see if any of the pending queries matches */ for(p = runtime->pending_list; p; p = p->next) { + if(runtime->now->addrlen != 0 && + sockaddr_cmp(&p->addr, p->addrlen, &runtime->now->addr, + runtime->now->addrlen) != 0) + continue; if((e=find_match(runtime->now->match, p->pkt, p->transport))) { *entry = e; *pend = p; @@ -167,10 +171,14 @@ pending_find_match(struct replay_runtime* runtime, struct entry** entry, struct replay_range* p = runtime->scenario->range_list; while(p) { if(p->start_step <= timenow && timenow <= p->end_step && + (p->addrlen == 0 || sockaddr_cmp(&p->addr, p->addrlen, + &pend->addr, pend->addrlen) == 0) && (*entry = find_match(p->match, pend->pkt, pend->transport))) { log_info("matched query time %d in range [%d, %d] " "with entry line %d", timenow, p->start_step, p->end_step, (*entry)->lineno); + if(p->addrlen != 0) + log_addr("matched ip", &p->addr, p->addrlen); return 1; } p = p->next_range; @@ -192,6 +200,7 @@ pending_matches_range(struct replay_runtime* runtime, struct fake_pending* p = runtime->pending_list; /* slow, O(N*N), but it works as advertised with weird matching */ while(p) { + log_info("check of pending"); if(pending_find_match(runtime, entry, p)) { *pend = p; return 1; @@ -266,6 +275,9 @@ answer_callback_from_entry(struct replay_runtime* runtime, { struct comm_point c; struct comm_reply repinfo; + void* cb_arg = pend->cb_arg; + comm_point_callback_t* cb = pend->callback; + memset(&c, 0, sizeof(c)); c.fd = -1; c.buffer = ldns_buffer_new(runtime->bufsize); @@ -276,11 +288,12 @@ answer_callback_from_entry(struct replay_runtime* runtime, repinfo.c = &c; repinfo.addrlen = pend->addrlen; memcpy(&repinfo.addr, &pend->addr, pend->addrlen); - if((*pend->callback)(&c, pend->cb_arg, NETEVENT_NOERROR, &repinfo)) { + if(!pend->serviced) + pending_list_delete(runtime, pend); + if((*cb)(&c, cb_arg, NETEVENT_NOERROR, &repinfo)) { fatal_exit("testbound: unexpected: callback returned 1"); } ldns_buffer_free(c.buffer); - pending_list_delete(runtime, pend); } /** Check the now moment answer check event */ @@ -295,7 +308,10 @@ answer_check_it(struct replay_runtime* runtime) enum transport_type tr = transport_tcp; if(ans->repinfo.c->type == comm_udp) tr = transport_udp; - if(find_match(runtime->now->match, ans->pkt, tr)) { + if((runtime->now->addrlen == 0 || sockaddr_cmp( + &runtime->now->addr, runtime->now->addrlen, + &ans->repinfo.addr, ans->repinfo.addrlen) == 0) && + find_match(runtime->now->match, ans->pkt, tr)) { struct replay_answer *n = ans->next; log_info("testbound matched event entry from line %d", runtime->now->match->lineno); @@ -330,6 +346,10 @@ fake_front_query(struct replay_runtime* runtime, struct replay_moment *todo) memset(&repinfo, 0, sizeof(repinfo)); repinfo.c = (struct comm_point*)calloc(1, sizeof(struct comm_point)); repinfo.addrlen = (socklen_t)sizeof(struct sockaddr_in); + if(todo->addrlen != 0) { + repinfo.addrlen = todo->addrlen; + memcpy(&repinfo.addr, &todo->addr, todo->addrlen); + } repinfo.c->fd = -1; repinfo.c->ev = (struct internal_event*)runtime; repinfo.c->buffer = ldns_buffer_new(runtime->bufsize); @@ -484,6 +504,10 @@ run_scenario(struct replay_runtime* runtime) } while(runtime->now); if(runtime->pending_list) { + struct fake_pending* p; + log_err("testbound: there are still messages pending."); + for(p = runtime->pending_list; p; p=p->next) + log_pkt("pending msg", p->pkt); fatal_exit("testbound: there are still messages pending."); } if(runtime->answer_list) { @@ -698,6 +722,7 @@ pending_udp_query(struct outside_network* outnet, ldns_buffer* packet, pend->timeout = timeout/1000; pend->transport = transport_udp; pend->pkt = NULL; + pend->serviced = 0; pend->runtime = runtime; status = ldns_buffer2pkt_wire(&pend->pkt, packet); if(status != LDNS_STATUS_OK) { @@ -709,6 +734,9 @@ pending_udp_query(struct outside_network* outnet, ldns_buffer* packet, /* see if it matches the current moment */ if(runtime->now && runtime->now->evt_type == repevt_back_query && + (runtime->now->addrlen == 0 || sockaddr_cmp( + &runtime->now->addr, runtime->now->addrlen, + &pend->addr, pend->addrlen) == 0) && find_match(runtime->now->match, pend->pkt, pend->transport)) { log_info("testbound: matched pending to event. " "advance time between events."); @@ -748,6 +776,7 @@ pending_tcp_query(struct outside_network* outnet, ldns_buffer* packet, pend->transport = transport_tcp; pend->pkt = NULL; pend->runtime = runtime; + pend->serviced = 0; status = ldns_buffer2pkt_wire(&pend->pkt, packet); if(status != LDNS_STATUS_OK) { log_err("ldns error parsing tcp output packet: %s", @@ -758,6 +787,9 @@ pending_tcp_query(struct outside_network* outnet, ldns_buffer* packet, /* see if it matches the current moment */ if(runtime->now && runtime->now->evt_type == repevt_back_query && + (runtime->now->addrlen == 0 || sockaddr_cmp( + &runtime->now->addr, runtime->now->addrlen, + &pend->addr, pend->addrlen) == 0) && find_match(runtime->now->match, pend->pkt, pend->transport)) { log_info("testbound: matched pending to event. " "advance time between events."); @@ -820,6 +852,7 @@ struct serviced_query* outnet_serviced_query(struct outside_network* outnet, pend->transport = transport_udp; /* pretend UDP */ pend->pkt = NULL; pend->runtime = runtime; + pend->serviced = 1; status = ldns_buffer2pkt_wire(&pend->pkt, pend->buffer); if(status != LDNS_STATUS_OK) { log_err("ldns error parsing serviced output packet: %s", @@ -830,6 +863,9 @@ struct serviced_query* outnet_serviced_query(struct outside_network* outnet, /* see if it matches the current moment */ if(runtime->now && runtime->now->evt_type == repevt_back_query && + (runtime->now->addrlen == 0 || sockaddr_cmp( + &runtime->now->addr, runtime->now->addrlen, + &pend->addr, pend->addrlen) == 0) && find_match(runtime->now->match, pend->pkt, pend->transport)) { log_info("testbound: matched pending to event. " "advance time between events."); @@ -854,10 +890,10 @@ void outnet_serviced_query_stop(struct serviced_query* sq, void* cb_arg) while(p) { if(p == pend) { log_assert(p->cb_arg == cb_arg); + log_info("serviced pending delete"); if(prev) prev->next = p->next; else runtime->pending_list = p->next; - log_pkt("deleted pending serviced query.", p->pkt); ldns_buffer_free(p->buffer); ldns_pkt_free(p->pkt); free(p); @@ -866,7 +902,7 @@ void outnet_serviced_query_stop(struct serviced_query* sq, void* cb_arg) prev = p; p = p->next; } - log_pkt("double delet of pending serviced query", p->pkt); + log_info("double delete of pending serviced query"); } struct listen_port* listening_ports_open(struct config_file* ATTR_UNUSED(cfg)) diff --git a/testcode/ldns-testpkts.c b/testcode/ldns-testpkts.c index 39a183769..ce5216049 100644 --- a/testcode/ldns-testpkts.c +++ b/testcode/ldns-testpkts.c @@ -157,6 +157,8 @@ static void replyline(const char* line, ldns_pkt *reply) ldns_pkt_set_rcode(reply, LDNS_RCODE_NXDOMAIN); } else if(str_keyword(&parse, "NOTIMPL")) { ldns_pkt_set_rcode(reply, LDNS_RCODE_NOTIMPL); + } else if(str_keyword(&parse, "REFUSED")) { + ldns_pkt_set_rcode(reply, LDNS_RCODE_REFUSED); } else if(str_keyword(&parse, "YXDOMAIN")) { ldns_pkt_set_rcode(reply, LDNS_RCODE_YXDOMAIN); } else if(str_keyword(&parse, "YXRRSET")) { diff --git a/testcode/replay.c b/testcode/replay.c index 4ba20dd39..34eb9208d 100644 --- a/testcode/replay.c +++ b/testcode/replay.c @@ -41,6 +41,7 @@ #include "config.h" #include "util/log.h" +#include "util/net_help.h" #include "testcode/replay.h" #include "testcode/ldns-testpkts.h" @@ -85,6 +86,18 @@ replay_range_delete(struct replay_range* rng) free(rng); } +/** strip whitespace from end of string */ +static void +strip_end_white(char* p) +{ + size_t i; + for(i = strlen(p); i > 0; i--) { + if(isspace(p[i-1])) + p[i-1] = 0; + else return; + } +} + /** * Read a range from file. * @param remain: Rest of line (after RANGE keyword). @@ -124,6 +137,19 @@ replay_range_read(char* remain, FILE* in, const char* name, int* lineno, parse++; if(!*parse || *parse == ';') continue; + if(parse_keyword(&parse, "ADDRESS")) { + while(isspace(*parse)) + parse++; + strip_end_white(parse); + if(!extstrtoaddr(parse, &rng->addr, &rng->addrlen)) { + log_err("Line %d: could not read ADDRESS: %s", + *lineno, parse); + free(rng); + return NULL; + } + pos = ftello(in); + continue; + } if(parse_keyword(&parse, "RANGE_END")) { return rng; } @@ -198,6 +224,16 @@ replay_moment_read(char* remain, FILE* in, const char* name, int* lineno, free(mom); return NULL; } + while(isspace(*remain)) + remain++; + if(parse_keyword(&remain, "ADDRESS")) { + if(!extstrtoaddr(remain, &mom->addr, &mom->addrlen)) { + log_err("line %d: could not parse ADDRESS: %s", + *lineno, remain); + free(mom); + return NULL; + } + } if(readentry) { mom->match = read_entry(in, name, lineno, ttl, or, prev); diff --git a/testcode/replay.h b/testcode/replay.h index 62f31cfb1..ca68c885f 100644 --- a/testcode/replay.h +++ b/testcode/replay.h @@ -44,11 +44,13 @@ * ; comment line. * SCENARIO_BEGIN name_of_scenario * RANGE_BEGIN start_time end_time + * ; give ip of the virtual server, it matches any ip if not present. + * ADDRESS ip_address * match_entries * RANGE_END * ; more RANGE items. * ; go to the next moment - * STEP time_step event_type + * STEP time_step event_type [ADDRESS ip_address] * ; event_type can be: * o NOTHING - nothing * o QUERY - followed by entry @@ -254,6 +256,8 @@ struct fake_pending { ldns_pkt* pkt; /** by what transport was the query sent out */ enum transport_type transport; + /** if this is a serviced query */ + int serviced; /** the runtime structure this is part of */ struct replay_runtime* runtime; }; diff --git a/testdata/iter_recurse.rpl b/testdata/iter_recurse.rpl new file mode 100644 index 000000000..a822b413e --- /dev/null +++ b/testdata/iter_recurse.rpl @@ -0,0 +1,178 @@ +; config options +stub-zone: + name: "." + stub-addr: 193.0.14.129 # K.ROOT-SERVERS.NET. +CONFIG_END + +SCENARIO_BEGIN Test resolution with recursion for NS target. + +; K.ROOT-SERVERS.NET. +RANGE_BEGIN 0 100 + ADDRESS 193.0.14.129 +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR NOERROR +SECTION QUESTION +. IN NS +SECTION ANSWER +. IN NS K.ROOT-SERVERS.NET. +SECTION ADDITIONAL +K.ROOT-SERVERS.NET. IN A 193.0.14.129 +ENTRY_END + +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR NOERROR +SECTION QUESTION +www.example.com. IN A +SECTION AUTHORITY +com. IN NS a.gtld-servers.net. +SECTION ADDITIONAL +a.gtld-servers.net. IN A 192.5.6.30 +ENTRY_END + +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR NOERROR +SECTION QUESTION +ns.example.net. IN A +SECTION AUTHORITY +net. IN NS e.gtld-servers.net. +SECTION ADDITIONAL +e.gtld-servers.net. IN A 192.12.94.30 +ENTRY_END + +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR NOERROR +SECTION QUESTION +ns.example.net. IN AAAA +SECTION AUTHORITY +net. IN NS e.gtld-servers.net. +SECTION ADDITIONAL +e.gtld-servers.net. IN A 192.12.94.30 +ENTRY_END +RANGE_END + +; a.gtld-servers.net. +RANGE_BEGIN 0 100 + ADDRESS 192.5.6.30 +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR NOERROR +SECTION QUESTION +www.example.com. IN A +SECTION AUTHORITY +example.com. IN NS ns.example.net. +ENTRY_END +RANGE_END + +; e.gtld-servers.net. +RANGE_BEGIN 0 100 + ADDRESS 192.12.94.30 +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR NOERROR +SECTION QUESTION +ns.example.net. IN A +SECTION AUTHORITY +example.net. IN NS ns.example.net. +SECTION ADDITIONAL +ns.example.net. IN A 1.2.3.44 +ENTRY_END + +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR NOERROR +SECTION QUESTION +ns.example.net. IN AAAA +SECTION AUTHORITY +example.net. IN NS ns.example.net. +SECTION ADDITIONAL +ns.example.net. IN A 1.2.3.44 +ENTRY_END +RANGE_END + +; ns.example.net. +RANGE_BEGIN 0 100 + ADDRESS 1.2.3.44 +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR NOERROR +SECTION QUESTION +ns.example.net. IN A +SECTION ANSWER +ns.example.net. IN A 1.2.3.44 +SECTION AUTHORITY +example.net. IN NS ns.example.net. +ENTRY_END + +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR NOERROR +SECTION QUESTION +ns.example.net. IN AAAA +SECTION AUTHORITY +example.net. IN NS ns.example.net. +SECTION ADDITIONAL +www.example.net. IN A 1.2.3.44 +ENTRY_END + +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR NOERROR +SECTION QUESTION +www.example.com. IN A +SECTION ANSWER +www.example.com. IN A 10.20.30.40 +SECTION AUTHORITY +example.com. IN NS ns.example.net. +SECTION ADDITIONAL +ns.example.net IN A 1.2.3.44 +ENTRY_END +RANGE_END + +STEP 1 QUERY +ENTRY_BEGIN +REPLY RD +SECTION QUESTION +www.example.com. IN A +ENTRY_END + +; recursion happens here. +STEP 20 CHECK_ANSWER +ENTRY_BEGIN +MATCH all +REPLY QR RD RA NOERROR +SECTION QUESTION +www.example.com. IN A +SECTION ANSWER +www.example.com. IN A 10.20.30.40 +SECTION AUTHORITY +example.com. IN NS ns.example.net. +; scrubbed off +;SECTION ADDITIONAL +;ns.example.net IN A 1.2.3.44 +ENTRY_END + +; due to ordering of answer packets, this is still outstanding, remove it +STEP 21 CHECK_OUT_QUERY +ENTRY_BEGIN +ADJUST copy_id +MATCH qname qtype +REPLY QR +SECTION QUESTION +ns.example.net IN AAAA +ENTRY_END + +SCENARIO_END diff --git a/testdata/iter_resolve.rpl b/testdata/iter_resolve.rpl new file mode 100644 index 000000000..7dfe84868 --- /dev/null +++ b/testdata/iter_resolve.rpl @@ -0,0 +1,93 @@ +; config options +stub-zone: + name: "." + stub-addr: 193.0.14.129 # K.ROOT-SERVERS.NET. +CONFIG_END + +SCENARIO_BEGIN Test basic iterative resolve of www.example.com. + +; K.ROOT-SERVERS.NET. +RANGE_BEGIN 0 100 + ADDRESS 193.0.14.129 +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR NOERROR +SECTION QUESTION +. IN NS +SECTION ANSWER +. IN NS K.ROOT-SERVERS.NET. +SECTION ADDITIONAL +K.ROOT-SERVERS.NET. IN A 193.0.14.129 +ENTRY_END + +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR NOERROR +SECTION QUESTION +www.example.com. IN A +SECTION AUTHORITY +com. IN NS a.gtld-servers.net. +SECTION ADDITIONAL +a.gtld-servers.net. IN A 192.5.6.30 +ENTRY_END +RANGE_END + +; a.gtld-servers.net. +RANGE_BEGIN 0 100 + ADDRESS 192.5.6.30 +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR NOERROR +SECTION QUESTION +www.example.com. IN A +SECTION AUTHORITY +example.com. IN NS ns.example.com. +SECTION ADDITIONAL +ns.example.com. IN A 1.2.3.4 +ENTRY_END +RANGE_END + +; ns.example.com. +RANGE_BEGIN 0 100 + ADDRESS 1.2.3.4 +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR NOERROR +SECTION QUESTION +www.example.com. IN A +SECTION ANSWER +www.example.com. IN A 10.20.30.40 +SECTION AUTHORITY +example.com. IN NS ns.example.com. +SECTION ADDITIONAL +ns.example.com. IN A 1.2.3.4 +ENTRY_END +RANGE_END + +STEP 1 QUERY +ENTRY_BEGIN +REPLY RD +SECTION QUESTION +www.example.com. IN A +ENTRY_END + +; recursion happens here. +STEP 10 CHECK_ANSWER +ENTRY_BEGIN +MATCH all +REPLY QR RD RA NOERROR +SECTION QUESTION +www.example.com. IN A +SECTION ANSWER +www.example.com. IN A 10.20.30.40 +SECTION AUTHORITY +example.com. IN NS ns.example.com. +SECTION ADDITIONAL +ns.example.com. IN A 1.2.3.4 +ENTRY_END + +SCENARIO_END diff --git a/testdata/version_bind.rpl b/testdata/version_bind.rpl new file mode 100644 index 000000000..1c5b914c1 --- /dev/null +++ b/testdata/version_bind.rpl @@ -0,0 +1,74 @@ +; config options +server: + hide-identity: no + hide-version: no + identity: "test-identity" + version: "test-version" +CONFIG_END +SCENARIO_BEGIN Test version.bind identity and version queries + +; version.bind. +STEP 1 QUERY +ENTRY_BEGIN +SECTION QUESTION +version.bind. CH TXT +ENTRY_END +STEP 2 CHECK_ANSWER +ENTRY_BEGIN +MATCH all +REPLY QR RA +SECTION QUESTION +version.bind. CH TXT +SECTION ANSWER +version.bind. 0 CH TXT "test-version" +ENTRY_END + +; version.server. +STEP 3 QUERY +ENTRY_BEGIN +SECTION QUESTION +version.server. CH TXT +ENTRY_END +STEP 4 CHECK_ANSWER +ENTRY_BEGIN +MATCH all +REPLY QR RA +SECTION QUESTION +version.server. CH TXT +SECTION ANSWER +version.server. 0 CH TXT "test-version" +ENTRY_END + +; hostname.bind. +STEP 5 QUERY +ENTRY_BEGIN +SECTION QUESTION +hostname.bind. CH TXT +ENTRY_END +STEP 6 CHECK_ANSWER +ENTRY_BEGIN +MATCH all +REPLY QR RA +SECTION QUESTION +hostname.bind. CH TXT +SECTION ANSWER +hostname.bind. 0 CH TXT "test-identity" +ENTRY_END + +; id.server. +STEP 7 QUERY +ENTRY_BEGIN +SECTION QUESTION +id.server. CH TXT +ENTRY_END +STEP 8 CHECK_ANSWER +ENTRY_BEGIN +MATCH all +REPLY QR RA +SECTION QUESTION +id.server. CH TXT +SECTION ANSWER +id.server. 0 CH TXT "test-identity" +ENTRY_END + +SCENARIO_END diff --git a/testdata/version_bind_hide.rpl b/testdata/version_bind_hide.rpl new file mode 100644 index 000000000..08ba86a92 --- /dev/null +++ b/testdata/version_bind_hide.rpl @@ -0,0 +1,67 @@ +; config options +server: + hide-identity: yes + hide-version: yes + identity: "test-identity" + version: "test-version" +; we rely on the fact that there are no builtin stubs for class CH. +CONFIG_END +SCENARIO_BEGIN Test config hide options for identity and version queries + +; version.bind. +STEP 1 QUERY +ENTRY_BEGIN +SECTION QUESTION +version.bind. CH TXT +ENTRY_END +STEP 2 CHECK_ANSWER +ENTRY_BEGIN +MATCH all +REPLY QR RA REFUSED +SECTION QUESTION +version.bind. CH TXT +ENTRY_END + +; version.server. +STEP 3 QUERY +ENTRY_BEGIN +SECTION QUESTION +version.server. CH TXT +ENTRY_END +STEP 4 CHECK_ANSWER +ENTRY_BEGIN +MATCH all +REPLY QR RA REFUSED +SECTION QUESTION +version.server. CH TXT +ENTRY_END + +; hostname.bind. +STEP 5 QUERY +ENTRY_BEGIN +SECTION QUESTION +hostname.bind. CH TXT +ENTRY_END +STEP 6 CHECK_ANSWER +ENTRY_BEGIN +MATCH all +REPLY QR RA REFUSED +SECTION QUESTION +hostname.bind. CH TXT +ENTRY_END + +; id.server. +STEP 7 QUERY +ENTRY_BEGIN +SECTION QUESTION +id.server. CH TXT +ENTRY_END +STEP 8 CHECK_ANSWER +ENTRY_BEGIN +MATCH all +REPLY QR RA REFUSED +SECTION QUESTION +id.server. CH TXT +ENTRY_END + +SCENARIO_END -- 2.47.2