From 22b0dcf8a19aaeb1e6f32ad9f0aad95ab26b8a61 Mon Sep 17 00:00:00 2001 From: Brandon Black Date: Thu, 11 Jul 2013 14:37:57 -0500 Subject: [PATCH] Fix auth section of ANY-query on CNAME Queries with QTYPE=ANY for a name which has a CNAME RR should be treated as if QTYPE=CNAME. Prior to this fix, they were being treated more like QTYPE=A. Given it's QTYPE=ANY and the effects seem to be limited to the auth section, I doubt this is a production concern for anyone, but it's good to be correct. Fixes Issue #51 (thanks Timo!) --- gdnsd/dnspacket.c | 11 ++- t/012cname/001cname.t | 157 +++++++++++++++++++++++++++++++++++++++++++ t/012cname/gdnsd.conf | 11 +++ t/012cname/zones/example.com | 24 +++++++ 4 files changed, 201 insertions(+), 2 deletions(-) create mode 100644 t/012cname/001cname.t create mode 100644 t/012cname/gdnsd.conf create mode 100644 t/012cname/zones/example.com diff --git a/gdnsd/dnspacket.c b/gdnsd/dnspacket.c index 3c26d83..db7e26a 100644 --- a/gdnsd/dnspacket.c +++ b/gdnsd/dnspacket.c @@ -1190,6 +1190,9 @@ static unsigned int encode_rrs_any(dnspacket_context_t* c, unsigned int offset, case DNS_TYPE_SOA: offset = encode_rr_soa(c, offset, (const void*)rrset, true); break; + case DNS_TYPE_CNAME: + offset = encode_rr_cname(c, offset, (const void*)rrset, true); + break; case DNS_TYPE_NS: offset = encode_rrs_ns(c, offset, (const void*)rrset, true); break; @@ -1659,8 +1662,12 @@ static unsigned int answer_from_db(dnspacket_context_t* c, const uint8_t* qname, // for the normal response handling code below. The explicit check of the first // rrsets entry works because if CNAME exists at all, by definition it is the only // type of rrset at this node. - while(resdom && resdom->rrsets - && resdom->rrsets->gen.type == DNS_TYPE_CNAME && c->qtype != DNS_TYPE_CNAME) { + while(resdom + && resdom->rrsets + && resdom->rrsets->gen.type == DNS_TYPE_CNAME + && c->qtype != DNS_TYPE_CNAME + && c->qtype != DNS_TYPE_ANY) { + dmn_assert(status == DNAME_AUTH); res_hdr->flags1 |= 4; // AA bit diff --git a/t/012cname/001cname.t b/t/012cname/001cname.t new file mode 100644 index 0000000..6e96335 --- /dev/null +++ b/t/012cname/001cname.t @@ -0,0 +1,157 @@ + +# CNAME test, with include_optional_ns to get the auth section right... +# this is basically going through A, CNAME, and ANY queries against +# five different classes of CNAME targets (local nonexistent, +# local existent, delegation, delegation glue record, and external). +# CNAME and ANY responses should be identical (this was the bug that +# triggered writing these testcases - ANY was being treated more like A). + +use _GDT (); +use FindBin (); +use File::Spec (); +use Test::More tests => 17; + +my $standard_soa = 'example.com 21600 SOA ns1.example.com hmaster.example.net 1 7200 1800 259200 900'; + +my $pid = _GDT->test_spawn_daemon(File::Spec->catfile($FindBin::Bin, 'gdnsd.conf')); + +_GDT->test_dns( + qname => 'cn-nx.example.com', qtype => 'A', + header => { rcode => 'NXDOMAIN' }, + answer => 'cn-nx.example.com 21600 CNAME nx.example.com', + auth => $standard_soa, + stats => [qw/udp_reqs nxdomain/], +); + +foreach my $qt (qw/CNAME ANY/) { + _GDT->test_dns( + qname => 'cn-nx.example.com', qtype => $qt, + answer => 'cn-nx.example.com 21600 CNAME nx.example.com', + auth => [ + 'example.com 21600 NS ns1.example.com', + 'example.com 21600 NS ns2.example.com', + ], + addtl => [ + 'ns1.example.com 21600 A 192.0.2.1', + 'ns2.example.com 21600 A 192.0.2.2', + ], + ); +} + +_GDT->test_dns( + qname => 'cn-local.example.com', qtype => 'A', + answer => [ + 'cn-local.example.com 21600 CNAME ns1.example.com', + 'ns1.example.com 21600 A 192.0.2.1', + ], + auth => [ + 'example.com 21600 NS ns1.example.com', + 'example.com 21600 NS ns2.example.com', + ], + addtl => [ + 'ns2.example.com 21600 A 192.0.2.2', + ], +); + +foreach my $qt (qw/CNAME ANY/) { + _GDT->test_dns( + qname => 'cn-local.example.com', qtype => $qt, + answer => [ + 'cn-local.example.com 21600 CNAME ns1.example.com' + ], + auth => [ + 'example.com 21600 NS ns1.example.com', + 'example.com 21600 NS ns2.example.com', + ], + addtl => [ + 'ns1.example.com 21600 A 192.0.2.1', + 'ns2.example.com 21600 A 192.0.2.2', + ], + ); +} + +_GDT->test_dns( + qname => 'cn-deleg.example.com', qtype => 'A', + answer => [ + 'cn-deleg.example.com 21600 CNAME foo.subz.example.com', + ], + auth => [ + 'subz.example.com 21600 NS ns1.subz.example.com', + 'subz.example.com 21600 NS ns2.subz.example.com', + ], + addtl => [ + 'ns1.subz.example.com 21600 A 192.0.2.10', + 'ns2.subz.example.com 21600 A 192.0.2.20', + ], +); + +foreach my $qt (qw/CNAME ANY/) { + _GDT->test_dns( + qname => 'cn-deleg.example.com', qtype => $qt, + answer => [ + 'cn-deleg.example.com 21600 CNAME foo.subz.example.com', + ], + auth => [ + 'example.com 21600 NS ns1.example.com', + 'example.com 21600 NS ns2.example.com', + ], + addtl => [ + 'ns1.example.com 21600 A 192.0.2.1', + 'ns2.example.com 21600 A 192.0.2.2', + ], + ); +} + +_GDT->test_dns( + qname => 'cn-deleg-glue.example.com', qtype => 'A', + answer => [ + 'cn-deleg-glue.example.com 21600 CNAME ns1.subz.example.com', + ], + auth => [ + 'subz.example.com 21600 NS ns1.subz.example.com', + 'subz.example.com 21600 NS ns2.subz.example.com', + ], + addtl => [ + 'ns1.subz.example.com 21600 A 192.0.2.10', + 'ns2.subz.example.com 21600 A 192.0.2.20', + ], +); + +foreach my $qt (qw/CNAME ANY/) { + _GDT->test_dns( + qname => 'cn-deleg-glue.example.com', qtype => $qt, + answer => [ + 'cn-deleg-glue.example.com 21600 CNAME ns1.subz.example.com', + ], + auth => [ + 'example.com 21600 NS ns1.example.com', + 'example.com 21600 NS ns2.example.com', + ], + addtl => [ + 'ns1.example.com 21600 A 192.0.2.1', + 'ns2.example.com 21600 A 192.0.2.2', + ], + ); +} + +_GDT->test_dns( + qname => 'cn-ext.example.com', qtype => 'A', + answer => 'cn-ext.example.com 21600 CNAME www.example.net', +); + +foreach my $qt (qw/CNAME ANY/) { + _GDT->test_dns( + qname => 'cn-ext.example.com', qtype => $qt, + answer => 'cn-ext.example.com 21600 CNAME www.example.net', + auth => [ + 'example.com 21600 NS ns1.example.com', + 'example.com 21600 NS ns2.example.com', + ], + addtl => [ + 'ns1.example.com 21600 A 192.0.2.1', + 'ns2.example.com 21600 A 192.0.2.2', + ], + ); +} + +_GDT->test_kill_daemon($pid); diff --git a/t/012cname/gdnsd.conf b/t/012cname/gdnsd.conf new file mode 100644 index 0000000..2bc6c92 --- /dev/null +++ b/t/012cname/gdnsd.conf @@ -0,0 +1,11 @@ +options => { + listen => @dns_lspec@ + http_listen => @http_lspec@ + dns_port => @dns_port@ + http_port => @http_port@ + zones_default_ttl = 21600 + realtime_stats = true + max_response = 62464 + chaos_response = "some random string" + include_optional_ns = true +} diff --git a/t/012cname/zones/example.com b/t/012cname/zones/example.com new file mode 100644 index 0000000..94a452f --- /dev/null +++ b/t/012cname/zones/example.com @@ -0,0 +1,24 @@ + +@ SOA ns1 hmaster.example.net. ( + 1 ; serial + 7200 ; refresh + 1800 ; retry + 259200 ; expire + 900 ; ncache +) + +@ NS ns1 +@ NS ns2 +ns1 A 192.0.2.1 +ns2 A 192.0.2.2 + +subz NS ns1.subz +subz NS ns2.subz +ns1.subz A 192.0.2.10 +ns2.subz A 192.0.2.20 + +cn-nx CNAME nx +cn-local CNAME ns1 +cn-deleg CNAME foo.subz +cn-deleg-glue CNAME ns1.subz +cn-ext CNAME www.example.net. -- 1.8.3.2