Logging unknown query ID patch. written by Kazunori Fujiwara . This patch logs responses which query IDs are unknown. It also logs duplicate responses because the first response is used for regular resolving process. (It includes duplicate packets over network equipments.) This patch cannot distinguish duplicated responses from attack responses. Comparing this unknown logs and BIND query logs may distinguish this difference. It logs to BIND logging system. (security section, warning level) This patch targets BIND 9.3.2 and 9.3.3, 9.4.0rc. This patch is tested on FreeBSD 6.2, NetBSD 2.0 only. --- bind-9.3.2/lib/dns/dispatch.c.orig 2004-09-01 13:27:41.000000000 +0900 +++ bind-9.3.2/lib/dns/dispatch.c 2006-09-27 13:57:58.000000000 +0900 @@ -498,6 +498,69 @@ return (ev); } +#include + +static void warn_dupresponse(u_char *msg, int msglen, dns_dispatch_t *disp) +{ + HEADER *hp = (HEADER *)msg; + u_char *cp, *rdatap, *eom = msg + msglen; + int n, qdcount, ancount, nscount, arcount; + u_int qtype, qclass, id; + u_int rrtype, rrclass, rrttl, rdlength; + char qname[MAXDNAME], rrname[MAXDNAME]; + + /* + * Here we handle high level formatting problems by parsing the header. + */ + qdcount = ntohs(hp->qdcount); + ancount = ntohs(hp->ancount); + nscount = ntohs(hp->nscount); + arcount = ntohs(hp->arcount); + id = ntohs(hp->id); + if (hp->rcode != NOERROR || hp->opcode != QUERY || hp->aa == 0 || hp->qr == 0) + return; + if (ancount + nscount + arcount == 0 || qdcount != 1) + goto formerr; + cp = msg + HFIXEDSZ; + n = dn_expand(msg, eom, cp, qname, sizeof(qname)); + if (n <= 0) goto formerr; + cp += n; + if (cp + 2 * INT16SZ > eom) goto formerr; + GETSHORT(qtype, cp); + GETSHORT(qclass, cp); + if (cp > eom) goto formerr; + /* cp now points after the query section. */ + + /* only log first RR */ + n = dn_expand(msg, eom, cp, rrname, sizeof rrname); + if (n < 0) + goto formerr; + cp += n; /* name */ + if (cp + 3 * INT16SZ + INT32SZ > eom) + goto formerr; + GETSHORT(rrtype, cp); /* type */ + GETSHORT(rrclass, cp); /* class */ + GETLONG(rrttl, cp); /* ttl */ + GETSHORT(rdlength, cp); /* dlen */ + rdatap = cp; /* start of rdata */ + if (cp + rdlength > eom) + goto formerr; + /* we got + query section: qname, qtype, qclass + answer section: name, rrtype, rrclass, rrttl, rdlength, rdatap + */ + if (strcasecmp(qname, rrname) || qtype != rrtype || qclass != qtype || qclass != C_IN) + goto formerr; + if (rrtype == T_A) { + if (rdlength != 4) goto formerr; + dispatch_log(disp, ISC_LOG_WARNING, "UnknownID: %u %d/%d/%d %s %u IN A %d.%d.%d.%d", id, ancount, nscount, arcount, rrname, rrttl, rdatap[0], rdatap[1], rdatap[2], rdatap[3]); + } else { + dispatch_log(disp, ISC_LOG_WARNING, "UnknownID: %u %d/%d/%d %s %u IN TYPE%d", id, ancount, nscount, arcount, rrname, rrttl, rrtype); + } +formerr: + return; +} + /* * General flow: * @@ -638,6 +701,7 @@ bucket, (resp == NULL ? "not found" : "found")); if (resp == NULL) { + warn_dupresponse(ev->region.base, ev->region.length, disp); free_buffer(disp, ev->region.base, ev->region.length); goto unlock; }