+14 January 2018: Wouter
+ - streamtcp option -a send queries consecutively and prints answers
+ as they arrive.
+ - Fix for out of order processing administration quit cleanup.
+ - unit test for tcp out of order processing.
+
11 January 2018: Wouter
- Initial commit for out-of-order processing for TCP and TLS.
/** number of queued TCP connections for listen() */
#define TCP_BACKLOG 256
+/** number of simultaneous requests a client can have */
+#define TCP_MAX_REQ_SIMULTANEOUS 32
+
/**
* Debug print of the getaddrinfo returned address.
* @param addr: the address returned.
}
}
-/** number of simultaneous requests a client can have */
-#define TCP_MAX_REQ_SIMULTANEOUS 10
-
/** setup listening for read or write */
static void
tcp_req_info_setup_listen(struct tcp_req_info* req)
tcp_req_info_handle_read_close(struct tcp_req_info* req)
{
verbose(VERB_ALGO, "tcp channel read side closed %d", req->cp->fd);
+ /* if we still have results to write, pick up next and write it */
if(req->num_done_req != 0) {
tcp_req_pickup_next_result(req);
tcp_req_info_setup_listen(req);
return 1;
}
+ /* if nothing to do, this closes the connection */
if(req->num_open_req == 0 && req->num_done_req == 0)
return 0;
+ /* otherwise, we must be waiting for dns resolve, wait with timeout */
req->read_is_closed = 1;
tcp_req_info_setup_listen(req);
return 1;
mesh = mstate->s.env->mesh;
/* drop unsent replies */
if(!mstate->replies_sent) {
- struct mesh_reply* rep;
+ struct mesh_reply* rep = mstate->reply_list;
struct mesh_cb* cb;
- for(rep=mstate->reply_list; rep; rep=rep->next) {
+ /* in tcp_req_info, the mstates linked are removed, but
+ * the reply_list is now NULL, so the remove-from-empty-list
+ * takes no time and also it does not do the mesh accounting */
+ mstate->reply_list = NULL;
+ for(; rep; rep=rep->next) {
comm_point_drop_reply(&rep->query_reply);
mesh->num_reply_addrs--;
}
.B \-n
Do not wait for the answer.
.TP
+.B \-a
+Print answers on arrival. This mean queries are sent in sequence without
+waiting for answer but if answers arrive in this time they are printed out.
+After sending queries the program waits and prints the remainder.
+.TP
.B \-s
Use SSL.
.TP
printf("-f server what ipaddr@portnr to send the queries to\n");
printf("-u use UDP. No retries are attempted.\n");
printf("-n do not wait for an answer.\n");
+ printf("-a print answers as they arrive.\n");
printf("-d secs delay after connection before sending query\n");
printf("-s use ssl\n");
printf("-h this help text\n");
uint16_t len;
if(!udp) {
if(ssl) {
- if(SSL_read(ssl, (void*)&len, (int)sizeof(len)) <= 0) {
+ int sr = SSL_read(ssl, (void*)&len, (int)sizeof(len));
+ if(sr == 0) {
+ printf("ssl: stream closed\n");
+ exit(1);
+ }
+ if(sr < 0) {
log_crypto_err("could not SSL_read");
exit(1);
}
} else {
- if(recv(fd, (void*)&len, sizeof(len), 0) <
- (ssize_t)sizeof(len)) {
+ ssize_t r = recv(fd, (void*)&len, sizeof(len), 0);
+ if(r == 0) {
+ printf("recv: stream closed\n");
+ exit(1);
+ }
+ if(r < (ssize_t)sizeof(len)) {
#ifndef USE_WINSOCK
perror("read() len failed");
#else
free(pktstr);
}
+/** see if we can receive any results */
+static void
+print_any_answers(int fd, int udp, SSL* ssl, sldns_buffer* buf,
+ int* num_answers, int wait_all)
+{
+ /* see if the fd can read, if so, print one answer, repeat */
+ int ret;
+ struct timeval tv, *waittv;
+ fd_set rfd;
+ while(*num_answers > 0) {
+ memset(&rfd, 0, sizeof(rfd));
+ memset(&tv, 0, sizeof(tv));
+ FD_ZERO(&rfd);
+ FD_SET(fd, &rfd);
+ if(wait_all) waittv = NULL;
+ else waittv = &tv;
+ ret = select(fd+1, &rfd, NULL, NULL, waittv);
+ if(ret < 0) {
+ if(errno == EINTR || errno == EAGAIN) continue;
+ perror("select() failed");
+ exit(1);
+ }
+ if(ret == 0) {
+ if(wait_all) continue;
+ return;
+ }
+ (*num_answers) -= 1;
+ recv_one(fd, udp, ssl, buf);
+ }
+}
+
static int get_random(void)
{
int r;
/** send the TCP queries and print answers */
static void
-send_em(const char* svr, int udp, int usessl, int noanswer, int delay,
- int num, char** qs)
+send_em(const char* svr, int udp, int usessl, int noanswer, int onarrival,
+ int delay, int num, char** qs)
{
sldns_buffer* buf = sldns_buffer_new(65553);
int fd = open_svr(svr, udp);
- int i;
+ int i, wait_results = 0;
SSL_CTX* ctx = NULL;
SSL* ssl = NULL;
if(!buf) fatal_exit("out of memory");
write_q(fd, udp, ssl, buf, (uint16_t)get_random(), qs[i],
qs[i+1], qs[i+2]);
/* print at least one result */
- if(!noanswer)
+ if(onarrival) {
+ wait_results += 1; /* one more answer to fetch */
+ print_any_answers(fd, udp, ssl, buf, &wait_results, 0);
+ } else if(!noanswer) {
recv_one(fd, udp, ssl, buf);
+ }
}
+ if(onarrival)
+ print_any_answers(fd, udp, ssl, buf, &wait_results, 1);
if(usessl) {
SSL_shutdown(ssl);
const char* svr = "127.0.0.1";
int udp = 0;
int noanswer = 0;
+ int onarrival = 0;
int usessl = 0;
int delay = 0;
if(argc == 1) {
usage(argv);
}
- while( (c=getopt(argc, argv, "f:hnsud:")) != -1) {
+ while( (c=getopt(argc, argv, "af:hnsud:")) != -1) {
switch(c) {
case 'f':
svr = optarg;
break;
+ case 'a':
+ onarrival = 1;
+ break;
case 'n':
noanswer = 1;
break;
(void)OPENSSL_init_ssl(OPENSSL_INIT_LOAD_SSL_STRINGS, NULL);
#endif
}
- send_em(svr, udp, usessl, noanswer, delay, argc, argv);
+ send_em(svr, udp, usessl, noanswer, onarrival, delay, argc, argv);
checklock_stop();
#ifdef USE_WINSOCK
WSACleanup();
--- /dev/null
+server:
+ verbosity: 2
+ # num-threads: 1
+ interface: 127.0.0.1
+ port: @PORT@
+ use-syslog: no
+ directory: .
+ pidfile: "unbound.pid"
+ chroot: ""
+ username: ""
+ do-not-query-localhost: no
+
+ local-zone: "example.net" static
+ local-data: "www1.example.net. IN A 1.2.3.1"
+ local-data: "www2.example.net. IN A 1.2.3.2"
+ local-data: "www3.example.net. IN A 1.2.3.3"
+ tcp-upstream: yes
+ local-zone: "drop.net" deny
+
+forward-zone:
+ name: "."
+ forward-addr: "127.0.0.1@@TOPORT@"
--- /dev/null
+BaseName: tcp_req_order
+Version: 1.0
+Description: Test tcp request order processing.
+CreationDate: Mon Jan 14 13:34:00 CET 2018
+Maintainer: Wouter Wijngaards
+Category:
+Component:
+CmdDepends:
+Depends:
+Help:
+Pre: tcp_req_order.pre
+Post: tcp_req_order.post
+Test: tcp_req_order.test
+AuxFiles:
+Passed:
+Failure:
--- /dev/null
+# #-- tcp_req_order.post --#
+# source the master var file when it's there
+[ -f ../.tpkg.var.master ] && source ../.tpkg.var.master
+# source the test var file when it's there
+[ -f .tpkg.var.test ] && source .tpkg.var.test
+#
+# do your teardown here
+. ../common.sh
+kill_pid $FWD_PID
+kill_pid $UNBOUND_PID
+cat unbound.log
--- /dev/null
+# #-- tcp_req_order.pre--#
+# source the master var file when it's there
+[ -f ../.tpkg.var.master ] && source ../.tpkg.var.master
+# use .tpkg.var.test for in test variable passing
+[ -f .tpkg.var.test ] && source .tpkg.var.test
+
+. ../common.sh
+get_random_port 2
+UNBOUND_PORT=$RND_PORT
+FWD_PORT=$(($RND_PORT + 1))
+echo "UNBOUND_PORT=$UNBOUND_PORT" >> .tpkg.var.test
+echo "FWD_PORT=$FWD_PORT" >> .tpkg.var.test
+
+# start forwarder
+get_ldns_testns
+$LDNS_TESTNS -p $FWD_PORT tcp_req_order.testns >fwd.log 2>&1 &
+FWD_PID=$!
+echo "FWD_PID=$FWD_PID" >> .tpkg.var.test
+
+# make config file
+sed -e 's/@PORT\@/'$UNBOUND_PORT'/' -e 's/@TOPORT\@/'$FWD_PORT'/' < tcp_req_order.conf > ub.conf
+# start unbound in the background
+PRE="../.."
+$PRE/unbound -vvvv -d -c ub.conf >unbound.log 2>&1 &
+UNBOUND_PID=$!
+echo "UNBOUND_PID=$UNBOUND_PID" >> .tpkg.var.test
+
+cat .tpkg.var.test
+wait_ldns_testns_up fwd.log
+wait_unbound_up unbound.log
+
--- /dev/null
+# #-- tcp_req_order.test --#
+# source the master var file when it's there
+[ -f ../.tpkg.var.master ] && source ../.tpkg.var.master
+# use .tpkg.var.test for in test variable passing
+[ -f .tpkg.var.test ] && source .tpkg.var.test
+
+PRE="../.."
+. ../common.sh
+get_make
+(cd $PRE; $MAKE streamtcp)
+
+# this test query should just work (server is up)
+echo "> query www1.example.net."
+$PRE/streamtcp -f 127.0.0.1@$UNBOUND_PORT www1.example.net. A IN >outfile 2>&1
+cat outfile
+if test "$?" -ne 0; then
+ echo "exit status not OK"
+ echo "> cat logfiles"
+ cat outfile
+ cat fwd.log
+ cat unbound.log
+ echo "Not OK"
+ exit 1
+fi
+if grep "www1.example.net" outfile | grep "1.2.3.1"; then
+ echo "content OK"
+else
+ echo "result contents not OK"
+ echo "> cat logfiles"
+ cat outfile
+ cat fwd.log
+ cat unbound.log
+ echo "result contents not OK"
+ exit 1
+fi
+echo "OK"
+
+# multiple requests (from localdata)
+echo "> query www1.example.net. www2.example.net. www3.example.net."
+$PRE/streamtcp -f 127.0.0.1@$UNBOUND_PORT www1.example.net. A IN www2.example.net A IN www3.example.net A IN >outfile 2>&1
+cat outfile
+if test "$?" -ne 0; then
+ echo "exit status not OK"
+ echo "> cat logfiles"
+ cat outfile
+ cat fwd.log
+ cat unbound.log
+ echo "Not OK"
+ exit 1
+fi
+if grep "www1.example.net" outfile | grep "1.2.3.1"; then
+ echo "content OK"
+else
+ echo "result contents not OK"
+ echo "> cat logfiles"
+ cat outfile
+ cat fwd.log
+ cat unbound.log
+ echo "result contents not OK"
+ exit 1
+fi
+if grep "www2.example.net" outfile | grep "1.2.3.2"; then
+ echo "content OK"
+else
+ echo "result contents not OK"
+ echo "> cat logfiles"
+ cat outfile
+ cat fwd.log
+ cat unbound.log
+ echo "result contents not OK"
+ exit 1
+fi
+if grep "www3.example.net" outfile | grep "1.2.3.3"; then
+ echo "content OK"
+else
+ echo "result contents not OK"
+ echo "> cat logfiles"
+ cat outfile
+ cat fwd.log
+ cat unbound.log
+ echo "result contents not OK"
+ exit 1
+fi
+
+# out of order requests, the example.com elements take 2 seconds to wait.
+echo ""
+echo "> query www1.example.net. www.example.com. www2.example.net. www2.example.com. www3.example.net."
+$PRE/streamtcp -a -f 127.0.0.1@$UNBOUND_PORT www1.example.net. A IN www.example.com. A IN www2.example.net A IN www2.example.com. A IN www3.example.net A IN >outfile 2>&1
+cat outfile
+if test "$?" -ne 0; then
+ echo "exit status not OK"
+ echo "> cat logfiles"
+ cat outfile
+ cat fwd.log
+ cat unbound.log
+ echo "Not OK"
+ exit 1
+fi
+if grep "www1.example.net" outfile | grep "1.2.3.1"; then
+ echo "content OK"
+else
+ echo "result contents not OK"
+ echo "> cat logfiles"
+ cat outfile
+ cat fwd.log
+ cat unbound.log
+ echo "result contents not OK"
+ exit 1
+fi
+if grep "www2.example.net" outfile | grep "1.2.3.2"; then
+ echo "content OK"
+else
+ echo "result contents not OK"
+ echo "> cat logfiles"
+ cat outfile
+ cat fwd.log
+ cat unbound.log
+ echo "result contents not OK"
+ exit 1
+fi
+if grep "www3.example.net" outfile | grep "1.2.3.3"; then
+ echo "content OK"
+else
+ echo "result contents not OK"
+ echo "> cat logfiles"
+ cat outfile
+ cat fwd.log
+ cat unbound.log
+ echo "result contents not OK"
+ exit 1
+fi
+if grep "www.example.com" outfile | grep "10.20.30.40"; then
+ echo "content OK"
+else
+ echo "result contents not OK"
+ echo "> cat logfiles"
+ cat outfile
+ cat fwd.log
+ cat unbound.log
+ echo "result contents not OK"
+ exit 1
+fi
+if grep "www2.example.com" outfile | grep "10.20.30.42"; then
+ echo "content OK"
+else
+ echo "result contents not OK"
+ echo "> cat logfiles"
+ cat outfile
+ cat fwd.log
+ cat unbound.log
+ echo "result contents not OK"
+ exit 1
+fi
+
+# out of order requests, the example.com elements take 2 seconds to wait.
+# www.example.com present twice, answered twice.
+echo ""
+echo "> query www1.example.net. www.example.com. www2.example.net. www.example.com. www3.example.net."
+$PRE/streamtcp -a -f 127.0.0.1@$UNBOUND_PORT www1.example.net. A IN www.example.com. A IN www2.example.net A IN www.example.com. A IN www3.example.net A IN >outfile 2>&1
+cat outfile
+if test "$?" -ne 0; then
+ echo "exit status not OK"
+ echo "> cat logfiles"
+ cat outfile
+ cat fwd.log
+ cat unbound.log
+ echo "Not OK"
+ exit 1
+fi
+if grep "www1.example.net" outfile | grep "1.2.3.1"; then
+ echo "content OK"
+else
+ echo "result contents not OK"
+ echo "> cat logfiles"
+ cat outfile
+ cat fwd.log
+ cat unbound.log
+ echo "result contents not OK"
+ exit 1
+fi
+if grep "www2.example.net" outfile | grep "1.2.3.2"; then
+ echo "content OK"
+else
+ echo "result contents not OK"
+ echo "> cat logfiles"
+ cat outfile
+ cat fwd.log
+ cat unbound.log
+ echo "result contents not OK"
+ exit 1
+fi
+if grep "www3.example.net" outfile | grep "1.2.3.3"; then
+ echo "content OK"
+else
+ echo "result contents not OK"
+ echo "> cat logfiles"
+ cat outfile
+ cat fwd.log
+ cat unbound.log
+ echo "result contents not OK"
+ exit 1
+fi
+if grep "www.example.com" outfile | grep "10.20.30.40"; then
+ echo "content OK"
+else
+ echo "result contents not OK"
+ echo "> cat logfiles"
+ cat outfile
+ cat fwd.log
+ cat unbound.log
+ echo "result contents not OK"
+ exit 1
+fi
+
+# out of order requests, the example.com elements take 2 seconds to wait.
+# www3.example.com present twice, answered twice.
+echo ""
+echo "> query www1.example.net. www3.example.com. www2.example.net. www3.example.com. www3.example.net."
+$PRE/streamtcp -a -f 127.0.0.1@$UNBOUND_PORT www1.example.net. A IN www3.example.com. A IN www2.example.net A IN www3.example.com. A IN www3.example.net A IN >outfile 2>&1
+cat outfile
+if test "$?" -ne 0; then
+ echo "exit status not OK"
+ echo "> cat logfiles"
+ cat outfile
+ cat fwd.log
+ cat unbound.log
+ echo "Not OK"
+ exit 1
+fi
+if grep "www1.example.net" outfile | grep "1.2.3.1"; then
+ echo "content OK"
+else
+ echo "result contents not OK"
+ echo "> cat logfiles"
+ cat outfile
+ cat fwd.log
+ cat unbound.log
+ echo "result contents not OK"
+ exit 1
+fi
+if grep "www2.example.net" outfile | grep "1.2.3.2"; then
+ echo "content OK"
+else
+ echo "result contents not OK"
+ echo "> cat logfiles"
+ cat outfile
+ cat fwd.log
+ cat unbound.log
+ echo "result contents not OK"
+ exit 1
+fi
+if grep "www3.example.net" outfile | grep "1.2.3.3"; then
+ echo "content OK"
+else
+ echo "result contents not OK"
+ echo "> cat logfiles"
+ cat outfile
+ cat fwd.log
+ cat unbound.log
+ echo "result contents not OK"
+ exit 1
+fi
+if grep "www3.example.com" outfile | grep "10.20.30.43"; then
+ echo "content OK"
+else
+ echo "result contents not OK"
+ echo "> cat logfiles"
+ cat outfile
+ cat fwd.log
+ cat unbound.log
+ echo "result contents not OK"
+ exit 1
+fi
+
+echo ""
+echo "> query www4.example.com. www3.example.net."
+$PRE/streamtcp -a -f 127.0.0.1@$UNBOUND_PORT www4.example.com. A IN www3.example.net A IN >outfile 2>&1
+cat outfile
+if test "$?" -ne 0; then
+ echo "exit status not OK"
+ echo "> cat logfiles"
+ cat outfile
+ cat fwd.log
+ cat unbound.log
+ echo "Not OK"
+ exit 1
+fi
+if grep "www3.example.net" outfile | grep "1.2.3.3"; then
+ echo "content OK"
+else
+ echo "result contents not OK"
+ echo "> cat logfiles"
+ cat outfile
+ cat fwd.log
+ cat unbound.log
+ echo "result contents not OK"
+ exit 1
+fi
+if grep "www4.example.com" outfile | grep "10.20.30.44"; then
+ echo "content OK"
+else
+ echo "result contents not OK"
+ echo "> cat logfiles"
+ cat outfile
+ cat fwd.log
+ cat unbound.log
+ echo "result contents not OK"
+ exit 1
+fi
+
+echo ""
+echo "> query a1.example.com. - a100.example.com."
+$PRE/streamtcp -a -f 127.0.0.1@$UNBOUND_PORT www6.example.com. A IN a1.a.example.com. A IN a2.a.example.com. A IN a3.a.example.com. A IN a4.a.example.com. A IN a5.a.example.com. A IN a6.a.example.com. A IN a7.a.example.com. A IN a8.a.example.com. A IN a9.a.example.com. A IN a10.a.example.com. A IN a11.a.example.com. A IN a12.a.example.com. A IN a13.a.example.com. A IN a14.a.example.com. A IN a15.a.example.com. A IN a16.a.example.com. A IN a17.a.example.com. A IN a18.a.example.com. A IN a19.a.example.com. A IN a20.a.example.com. A IN a21.a.example.com. A IN a22.a.example.com. A IN a23.a.example.com. A IN a24.a.example.com. A IN a25.a.example.com. A IN a26.a.example.com. A IN a27.a.example.com. A IN a28.a.example.com. A IN a29.a.example.com. A IN a30.a.example.com. A IN a31.a.example.com. A IN a32.a.example.com. A IN a33.a.example.com. A IN a34.a.example.com. A IN a35.a.example.com. A IN a36.a.example.com. A IN a37.a.example.com. A IN a38.a.example.com. A IN a39.a.example.com. A IN a40.a.example.com. A IN a41.a.example.com. A IN a42.a.example.com. A IN a43.a.example.com. A IN a44.a.example.com. A IN a45.a.example.com. A IN a46.a.example.com. A IN a47.a.example.com. A IN a48.a.example.com. A IN a49.a.example.com. A IN a50.a.example.com. A IN a51.a.example.com. A IN a52.a.example.com. A IN a53.a.example.com. A IN a54.a.example.com. A IN a55.a.example.com. A IN a56.a.example.com. A IN a57.a.example.com. A IN a58.a.example.com. A IN a59.a.example.com. A IN a60.a.example.com. A IN a61.a.example.com. A IN a62.a.example.com. A IN a63.a.example.com. A IN a64.a.example.com. A IN a65.a.example.com. A IN a66.a.example.com. A IN a67.a.example.com. A IN a68.a.example.com. A IN a69.a.example.com. A IN a70.a.example.com. A IN a71.a.example.com. A IN a72.a.example.com. A IN a73.a.example.com. A IN a74.a.example.com. A IN a75.a.example.com. A IN a76.a.example.com. A IN a77.a.example.com. A IN a78.a.example.com. A IN a79.a.example.com. A IN a80.a.example.com. A IN a81.a.example.com. A IN a82.a.example.com. A IN a83.a.example.com. A IN a84.a.example.com. A IN a85.a.example.com. A IN a86.a.example.com. A IN a87.a.example.com. A IN a88.a.example.com. A IN a89.a.example.com. A IN a90.a.example.com. A IN a91.a.example.com. A IN a92.a.example.com. A IN a93.a.example.com. A IN a94.a.example.com. A IN a95.a.example.com. A IN a96.a.example.com. A IN a97.a.example.com. A IN a98.a.example.com. A IN a99.a.example.com. A IN a100.a.example.com. A IN >outfile 2>&1
+cat outfile
+if test "$?" -ne 0; then
+ echo "exit status not OK"
+ echo "> cat logfiles"
+ cat outfile
+ cat fwd.log
+ cat unbound.log
+ echo "Not OK"
+ exit 1
+fi
+grep "a.example.com. IN A" outfile
+
+echo ""
+echo "> query www5.example.net. www3.example.net. www.drop.net."
+$PRE/streamtcp -a -f 127.0.0.1@$UNBOUND_PORT www5.example.com. A IN www3.example.net A IN www.drop.net A IN >outfile 2>&1
+cat outfile
+if test "$?" -ne 0; then
+ echo "exit status not OK"
+ echo "> cat logfiles"
+ cat outfile
+ cat fwd.log
+ cat unbound.log
+ echo "Not OK"
+ exit 1
+fi
+
+echo "OK"
+exit 0
--- /dev/null
+; nameserver test file
+$ORIGIN example.com.
+$TTL 3600
+
+ENTRY_BEGIN
+MATCH opcode qtype qname
+REPLY QR AA NOERROR
+ADJUST copy_id sleep=2
+SECTION QUESTION
+www IN A
+SECTION ANSWER
+www IN A 10.20.30.40
+ENTRY_END
+
+ENTRY_BEGIN
+MATCH opcode qtype qname
+REPLY QR AA NOERROR
+ADJUST copy_id
+SECTION QUESTION
+www2 IN A
+SECTION ANSWER
+www2 IN A 10.20.30.42
+ENTRY_END
+
+ENTRY_BEGIN
+MATCH opcode qtype qname
+REPLY QR AA NOERROR
+ADJUST copy_id
+SECTION QUESTION
+www3 IN A
+SECTION ANSWER
+www3 IN A 10.20.30.43
+ENTRY_END
+
+ENTRY_BEGIN
+MATCH opcode qtype qname
+REPLY QR AA NOERROR
+ADJUST copy_id sleep=2
+SECTION QUESTION
+www4 IN A
+SECTION ANSWER
+www4 IN A 10.20.30.44
+ENTRY_END
+
+ENTRY_BEGIN
+MATCH opcode qtype qname
+REPLY QR AA NOERROR
+ADJUST copy_id sleep=2
+SECTION QUESTION
+www5 IN A
+SECTION ANSWER
+www5 IN A 10.20.30.45
+ENTRY_END
+
+ENTRY_BEGIN
+MATCH opcode qtype qname
+REPLY QR AA NOERROR
+ADJUST copy_id sleep=2
+SECTION QUESTION
+www6 IN A
+SECTION ANSWER
+www6 IN A 10.20.30.46
+ENTRY_END
+
+; lots of noerror/nodata answers for other queries (a.. queries)
+ENTRY_BEGIN
+MATCH opcode qtype subdomain
+REPLY QR AA NOERROR
+ADJUST copy_id copy_query
+SECTION QUESTION
+a.example.com. IN A
+SECTION AUTHORITY
+example.com. IN SOA ns hostmaster 2019 28800 7200 604800 3600
+ENTRY_END