Fix negative responses

Previously if the resolver was asked for a record it didn't have, it would
return a response with a NS record in the authority section.  This is
incorrect, as the lack of answer indicates to the resolver that it should try
that NS record as the next step, resulting in a loop:

    $ dig @8.8.8.8 rbf-seed.btc.petertodd.org TXT +trace

    <snip>

    rbf-seed.btc.petertodd.org. 300 IN      NS      rbf-seed-ns1.btc.petertodd.org.
    rbf-seed.btc.petertodd.org. 300 IN      NS      rbf-seed-ns2.btc.petertodd.org.
    ;; Received 141 bytes from 205.251.193.174#53(ns-430.awsdns-53.com) in 426 ms

    rbf-seed.btc.petertodd.org. 40000 IN    NS      rbf-seed-ns2.btc.petertodd.org.
    ;; BAD (HORIZONTAL) REFERRAL
    ;; Received 88 bytes from 185.52.1.173#53(rbf-seed-ns2.btc.petertodd.org) in 108 ms

    rbf-seed.btc.petertodd.org. 40000 IN    NS      rbf-seed-ns2.btc.petertodd.org.
    ;; BAD (HORIZONTAL) REFERRAL
    ;; Received 88 bytes from 185.52.1.173#53(rbf-seed-ns2.btc.petertodd.org) in 108 ms

    <snip>

    rbf-seed.btc.petertodd.org. 40000 IN    NS      rbf-seed-ns2.btc.petertodd.org.
    ;; BAD (HORIZONTAL) REFERRAL
    dig: too many lookups

The correct response in the authority section of a negative response is a SOA
record, which indicates that the answer is authoritative and the resolver can
consider the record missing and stop looking for it:

    $ dig @8.8.8.8 rbf-seed.btc.petertodd.org TXT +trace

    <snip>

    rbf-seed.btc.petertodd.org. 300 IN      NS      rbf-seed-ns1.btc.petertodd.org.
    rbf-seed.btc.petertodd.org. 300 IN      NS      rbf-seed-ns2.btc.petertodd.org.
    ;; Received 141 bytes from 205.251.196.185#53(ns-1209.awsdns-23.org) in 740 ms

    rbf-seed.btc.petertodd.org. 40000 IN    SOA     rbf-seed-ns1.btc.petertodd.org. pete.petertodd.org. 1435846201 604800 86400 2592000 604800
    ;; Received 128 bytes from 104.236.95.174#53(rbf-seed-ns1.btc.petertodd.org) in 31 ms

There have been a few reports of problems resolving seed domains on some
ISPs - hopefully this was the root cause.
This commit is contained in:
Peter Todd 2015-07-02 09:58:14 -04:00
parent 85fb114ed5
commit 5eca8e269a
No known key found for this signature in database
GPG key ID: 2481403DA5F091FB

40
dns.c
View file

@ -287,18 +287,23 @@ ssize_t static dnshandle(dns_opt_t *opt, const unsigned char *inbuf, size_t insi
unsigned char *outpos = outbuf+(inpos-inbuf);
unsigned char *outend = outbuf + BUFLEN;
// printf("DNS: Request host='%s' type=%i class=%i\n", name, typ, cls);
// printf("DNS: Request host='%s' type=%i class=%i\n", name, typ, cls);
// calculate size of authority section
// calculate max size of authority section
int auth_size = 0;
int max_auth_size = 0;
if (!((typ == TYPE_NS || typ == QTYPE_ANY) && (cls == CLASS_IN || cls == QCLASS_ANY))) {
// authority section will be necessary
unsigned char *oldpos = outpos;
write_record_ns(&oldpos, outend, "", offset, CLASS_IN, 0, opt->ns);
auth_size = oldpos - outpos;
// printf("Authority section will claim %i bytes\n", auth_size);
// authority section will be necessary, either NS or SOA
unsigned char *newpos = outpos;
write_record_ns(&newpos, outend, "", offset, CLASS_IN, 0, opt->ns);
max_auth_size = newpos - outpos;
newpos = outpos;
write_record_soa(&newpos, outend, "", offset, CLASS_IN, opt->nsttl, opt->ns, opt->mbox, time(NULL), 604800, 86400, 2592000, 604800);
if (max_auth_size < newpos - outpos)
max_auth_size = newpos - outpos;
// printf("Authority section will claim %i bytes max\n", max_auth_size);
}
// Answer section
@ -307,14 +312,14 @@ ssize_t static dnshandle(dns_opt_t *opt, const unsigned char *inbuf, size_t insi
// NS records
if ((typ == TYPE_NS || typ == QTYPE_ANY) && (cls == CLASS_IN || cls == QCLASS_ANY)) {
int ret2 = write_record_ns(&outpos, outend - auth_size, "", offset, CLASS_IN, opt->nsttl, opt->ns);
int ret2 = write_record_ns(&outpos, outend - max_auth_size, "", offset, CLASS_IN, opt->nsttl, opt->ns);
// printf("wrote NS record: %i\n", ret2);
if (!ret2) { outbuf[7]++; have_ns++; }
}
// SOA records
if ((typ == TYPE_SOA || typ == QTYPE_ANY) && (cls == CLASS_IN || cls == QCLASS_ANY) && opt->mbox) {
int ret2 = write_record_soa(&outpos, outend - auth_size, "", offset, CLASS_IN, opt->nsttl, opt->ns, opt->mbox, time(NULL), 604800, 86400, 2592000, 604800);
int ret2 = write_record_soa(&outpos, outend - max_auth_size, "", offset, CLASS_IN, opt->nsttl, opt->ns, opt->mbox, time(NULL), 604800, 86400, 2592000, 604800);
// printf("wrote SOA record: %i\n", ret2);
if (!ret2) { outbuf[7]++; }
}
@ -327,9 +332,9 @@ ssize_t static dnshandle(dns_opt_t *opt, const unsigned char *inbuf, size_t insi
while (n < naddr) {
int ret = 1;
if (addr[n].v == 4)
ret = write_record_a(&outpos, outend - auth_size, "", offset, CLASS_IN, opt->datattl, &addr[n]);
ret = write_record_a(&outpos, outend - max_auth_size, "", offset, CLASS_IN, opt->datattl, &addr[n]);
else if (addr[n].v == 6)
ret = write_record_aaaa(&outpos, outend - auth_size, "", offset, CLASS_IN, opt->datattl, &addr[n]);
ret = write_record_aaaa(&outpos, outend - max_auth_size, "", offset, CLASS_IN, opt->datattl, &addr[n]);
// printf("wrote A record: %i\n", ret);
if (!ret) {
n++;
@ -340,13 +345,22 @@ ssize_t static dnshandle(dns_opt_t *opt, const unsigned char *inbuf, size_t insi
}
// Authority section
if (!have_ns) {
if (!have_ns && outbuf[7]) {
int ret2 = write_record_ns(&outpos, outend, "", offset, CLASS_IN, opt->nsttl, opt->ns);
// printf("wrote NS record: %i\n", ret2);
if (!ret2) {
outbuf[9]++;
}
}
else if (!outbuf[7]) {
// Didn't include any answers, so reply with SOA as this is a negative
// response. If we replied with NS above we'd create a bad horizontal
// referral loop, as the NS response indicates where the resolver should
// try next.
int ret2 = write_record_soa(&outpos, outend, "", offset, CLASS_IN, opt->nsttl, opt->ns, opt->mbox, time(NULL), 604800, 86400, 2592000, 604800);
// printf("wrote SOA record: %i\n", ret2);
if (!ret2) { outbuf[9]++; }
}
// set AA
outbuf[2] |= 4;