227 lines
7.5 KiB
Diff
227 lines
7.5 KiB
Diff
--- a/query.c
|
|
+++ b/query.c
|
|
@@ -91,6 +91,21 @@ static void cleanup(struct query *z)
|
|
}
|
|
}
|
|
|
|
+static int move_name_to_alias(struct query *z,uint32 ttl)
|
|
+{
|
|
+ int j ;
|
|
+
|
|
+ if (z->alias[QUERY_MAXALIAS - 1]) return 0 ;
|
|
+ for (j = QUERY_MAXALIAS - 1;j > 0;--j)
|
|
+ z->alias[j] = z->alias[j - 1];
|
|
+ for (j = QUERY_MAXALIAS - 1;j > 0;--j)
|
|
+ z->aliasttl[j] = z->aliasttl[j - 1];
|
|
+ z->alias[0] = z->name[0];
|
|
+ z->aliasttl[0] = ttl;
|
|
+ z->name[0] = 0;
|
|
+ return 1 ;
|
|
+}
|
|
+
|
|
static int rqa(struct query *z)
|
|
{
|
|
int i;
|
|
@@ -123,7 +138,6 @@ static int globalip(char *d,char ip[4])
|
|
static char *t1 = 0;
|
|
static char *t2 = 0;
|
|
static char *t3 = 0;
|
|
-static char *cname = 0;
|
|
static char *referral = 0;
|
|
static unsigned int *records = 0;
|
|
|
|
@@ -179,15 +193,14 @@ static int doit(struct query *z,int stat
|
|
uint16 datalen;
|
|
char *control;
|
|
char *d;
|
|
+ char *owner_name = 0 ;
|
|
const char *dtype;
|
|
unsigned int dlen;
|
|
int flagout;
|
|
- int flagcname;
|
|
int flagreferral;
|
|
int flagsoa;
|
|
uint32 ttl;
|
|
uint32 soattl;
|
|
- uint32 cnamettl;
|
|
int i;
|
|
int j;
|
|
int k;
|
|
@@ -253,7 +266,10 @@ static int doit(struct query *z,int stat
|
|
|
|
byte_copy(key,2,DNS_T_CNAME);
|
|
cached = cache_get(key,dlen + 2,&cachedlen,&ttl);
|
|
- if (cached) {
|
|
+ /* A previous explicit query might have caused an empty RRSet to have been
|
|
+ ** cached. Take care to ignore such a thing.
|
|
+ */
|
|
+ if (cached && cachedlen) {
|
|
if (typematch(DNS_T_CNAME,dtype)) {
|
|
log_cachedanswer(d,DNS_T_CNAME);
|
|
if (!rqa(z)) goto DIE;
|
|
@@ -262,8 +278,11 @@ static int doit(struct query *z,int stat
|
|
return 1;
|
|
}
|
|
log_cachedcname(d,cached);
|
|
- if (!dns_domain_copy(&cname,cached)) goto DIE;
|
|
- goto CNAME;
|
|
+ if (!z->level) {
|
|
+ if (!move_name_to_alias(z,ttl)) goto DIE ;
|
|
+ }
|
|
+ if (!dns_domain_copy(&z->name[z->level],cached)) goto DIE;
|
|
+ goto NEWNAME;
|
|
}
|
|
|
|
if (typematch(DNS_T_NS,dtype)) {
|
|
@@ -352,7 +371,7 @@ static int doit(struct query *z,int stat
|
|
}
|
|
}
|
|
|
|
- if (!typematch(DNS_T_ANY,dtype) && !typematch(DNS_T_AXFR,dtype) && !typematch(DNS_T_CNAME,dtype) && !typematch(DNS_T_NS,dtype) && !typematch(DNS_T_PTR,dtype) && !typematch(DNS_T_A,dtype) && !typematch(DNS_T_MX,dtype)) {
|
|
+ if (!typematch(DNS_T_ANY,dtype) && !typematch(DNS_T_AXFR,dtype) && !typematch(DNS_T_NS,dtype) && !typematch(DNS_T_PTR,dtype) && !typematch(DNS_T_A,dtype) && !typematch(DNS_T_MX,dtype)) {
|
|
byte_copy(key,2,dtype);
|
|
cached = cache_get(key,dlen + 2,&cachedlen,&ttl);
|
|
if (cached && (cachedlen || byte_diff(dtype,2,DNS_T_ANY))) {
|
|
@@ -473,29 +492,31 @@ static int doit(struct query *z,int stat
|
|
|
|
cachettl = 0;
|
|
flagout = 0;
|
|
- flagcname = 0;
|
|
flagreferral = 0;
|
|
flagsoa = 0;
|
|
soattl = 0;
|
|
- cnamettl = 0;
|
|
+ if (!dns_domain_copy(&owner_name,d)) goto DIE;
|
|
+ /* This code assumes that the CNAME chain is presented in the correct
|
|
+ ** order. The example algorithm in RFC 1034 will actually result in this
|
|
+ ** being the case, but the words do not require it to be so.
|
|
+ */
|
|
for (j = 0;j < numanswers;++j) {
|
|
pos = dns_packet_getname(buf,len,pos,&t1); if (!pos) goto DIE;
|
|
pos = dns_packet_copy(buf,len,pos,header,10); if (!pos) goto DIE;
|
|
|
|
- if (dns_domain_equal(t1,d))
|
|
+ if (dns_domain_equal(t1,owner_name))
|
|
if (byte_equal(header + 2,2,DNS_C_IN)) { /* should always be true */
|
|
if (typematch(header,dtype))
|
|
flagout = 1;
|
|
else if (typematch(header,DNS_T_CNAME)) {
|
|
- if (!dns_packet_getname(buf,len,pos,&cname)) goto DIE;
|
|
- flagcname = 1;
|
|
- cnamettl = ttlget(header + 4);
|
|
+ if (!dns_packet_getname(buf,len,pos,&owner_name)) goto DIE;
|
|
}
|
|
}
|
|
|
|
uint16_unpack_big(header + 8,&datalen);
|
|
pos += datalen;
|
|
}
|
|
+ dns_domain_free(&owner_name) ;
|
|
posauthority = pos;
|
|
|
|
for (j = 0;j < numauthority;++j) {
|
|
@@ -522,15 +543,6 @@ static int doit(struct query *z,int stat
|
|
}
|
|
posglue = pos;
|
|
|
|
-
|
|
- if (!flagcname && !rcode && !flagout && flagreferral && !flagsoa)
|
|
- if (dns_domain_equal(referral,control) || !dns_domain_suffix(referral,control)) {
|
|
- log_lame(whichserver,control,referral);
|
|
- byte_zero(whichserver,4);
|
|
- goto HAVENS;
|
|
- }
|
|
-
|
|
-
|
|
if (records) { alloc_free(records); records = 0; }
|
|
|
|
k = numanswers + numauthority + numglue;
|
|
@@ -677,24 +689,36 @@ static int doit(struct query *z,int stat
|
|
|
|
alloc_free(records); records = 0;
|
|
|
|
+ if (byte_diff(DNS_T_CNAME,2,dtype)) {
|
|
+ /* This code assumes that the CNAME chain is presented in the correct
|
|
+ ** order. The example algorithm in RFC 1034 will actually result in this
|
|
+ ** being the case, but the words do not require it to be so.
|
|
+ */
|
|
+ pos = posanswers;
|
|
+ for (j = 0;j < numanswers;++j) {
|
|
+ pos = dns_packet_getname(buf,len,pos,&t1); if (!pos) goto DIE;
|
|
+ pos = dns_packet_copy(buf,len,pos,header,10); if (!pos) goto DIE;
|
|
+
|
|
+ if (dns_domain_equal(t1,d))
|
|
+ if (byte_equal(header + 2,2,DNS_C_IN)) { /* should always be true */
|
|
+ if (typematch(header,DNS_T_CNAME)) {
|
|
+ ttl = ttlget(header + 4);
|
|
+ if (z->level == 0) {
|
|
+ if (!move_name_to_alias(z,ttl)) goto DIE ;
|
|
+ }
|
|
+ if (!dns_packet_getname(buf,len,pos,&z->name[z->level])) goto DIE;
|
|
+ d = z->name[z->level];
|
|
+ if (!dns_domain_suffix(d,control) || !roots_same(d,control))
|
|
+ goto NEWNAME ; /* Cannot trust the chain further - restart using current name */
|
|
+ }
|
|
+ }
|
|
|
|
- if (flagcname) {
|
|
- ttl = cnamettl;
|
|
- CNAME:
|
|
- if (!z->level) {
|
|
- if (z->alias[QUERY_MAXALIAS - 1]) goto DIE;
|
|
- for (j = QUERY_MAXALIAS - 1;j > 0;--j)
|
|
- z->alias[j] = z->alias[j - 1];
|
|
- for (j = QUERY_MAXALIAS - 1;j > 0;--j)
|
|
- z->aliasttl[j] = z->aliasttl[j - 1];
|
|
- z->alias[0] = z->name[0];
|
|
- z->aliasttl[0] = ttl;
|
|
- z->name[0] = 0;
|
|
+ uint16_unpack_big(header + 8,&datalen);
|
|
+ pos += datalen;
|
|
}
|
|
- if (!dns_domain_copy(&z->name[z->level],cname)) goto DIE;
|
|
- goto NEWNAME;
|
|
}
|
|
|
|
+ /* A "no such name" error applies to the end of any CNAME chain, not to the start. */
|
|
if (rcode == 3) {
|
|
log_nxdomain(whichserver,d,cachettl);
|
|
cachegeneric(DNS_T_ANY,d,"",0,cachettl);
|
|
@@ -707,10 +731,26 @@ static int doit(struct query *z,int stat
|
|
return 1;
|
|
}
|
|
|
|
+ /* We check for a lame server _after_ we have cached any records that it
|
|
+ ** might have returned to us. This copes better with the incorrect
|
|
+ ** behaviour of one content DNS server software that doesn't return
|
|
+ ** complete CNAME chains but instead returns only the first link in a
|
|
+ ** chain followed by a lame delegation to the same server.
|
|
+ ** Also: We check for a lame server _after_ following the CNAME chain. The
|
|
+ ** delegation in a referral answer applies to the _end_ of the chain, not
|
|
+ ** to the beginning.
|
|
+ */
|
|
+ if (!rcode && !flagout && flagreferral && !flagsoa)
|
|
+ if (dns_domain_equal(referral,control) || !dns_domain_suffix(referral,control)) {
|
|
+ log_lame(whichserver,control,referral);
|
|
+ byte_zero(whichserver,4);
|
|
+ goto HAVENS;
|
|
+ }
|
|
+
|
|
if (!flagout && flagsoa)
|
|
+ /* Don't save empty RRSets for those types that we use as special markers. */
|
|
if (byte_diff(DNS_T_ANY,2,dtype))
|
|
- if (byte_diff(DNS_T_AXFR,2,dtype))
|
|
- if (byte_diff(DNS_T_CNAME,2,dtype)) {
|
|
+ if (byte_diff(DNS_T_AXFR,2,dtype)) {
|
|
save_start();
|
|
save_finish(dtype,d,soattl);
|
|
log_nodata(whichserver,d,dtype,soattl);
|
|
@@ -822,6 +862,7 @@ static int doit(struct query *z,int stat
|
|
DIE:
|
|
cleanup(z);
|
|
if (records) { alloc_free(records); records = 0; }
|
|
+ dns_domain_free(&owner_name) ;
|
|
return -1;
|
|
}
|
|
|