diff options
Diffstat (limited to 'src/rfc1035.c')
-rw-r--r-- | src/rfc1035.c | 160 |
1 files changed, 131 insertions, 29 deletions
diff --git a/src/rfc1035.c b/src/rfc1035.c index ea21ffa..28579c6 100644 --- a/src/rfc1035.c +++ b/src/rfc1035.c @@ -89,23 +89,14 @@ int extract_name(struct dns_header *header, size_t plen, unsigned char **pp, if (isExtract) { unsigned char c = *p; -#ifdef HAVE_DNSSEC - if (option_bool(OPT_DNSSEC_VALID)) + + if (c == 0 || c == '.' || c == NAME_ESCAPE) { - if (c == 0 || c == '.' || c == NAME_ESCAPE) - { - *cp++ = NAME_ESCAPE; - *cp++ = c+1; - } - else - *cp++ = c; + *cp++ = NAME_ESCAPE; + *cp++ = c+1; } else -#endif - if (c != 0 && c != '.') - *cp++ = c; - else - return 0; + *cp++ = c; } else { @@ -118,10 +109,9 @@ int extract_name(struct dns_header *header, size_t plen, unsigned char **pp, cp++; if (c1 >= 'A' && c1 <= 'Z') c1 += 'a' - 'A'; -#ifdef HAVE_DNSSEC - if (option_bool(OPT_DNSSEC_VALID) && c1 == NAME_ESCAPE) + + if (c1 == NAME_ESCAPE) c1 = (*cp++)-1; -#endif if (c2 >= 'A' && c2 <= 'Z') c2 += 'a' - 'A'; @@ -502,12 +492,10 @@ static int find_soa(struct dns_header *header, size_t qlen, int *doctored) } /* Print TXT reply to log */ -static int print_txt(struct dns_header *header, const size_t qlen, char *name, - unsigned char *p, const int ardlen, int secflag) +static int log_txt(char *name, unsigned char *p, const int ardlen, int secflag) { unsigned char *p1 = p; - if (!CHECK_LEN(header, p1, qlen, ardlen)) - return 0; + /* Loop over TXT payload */ while ((p1 - p) < ardlen) { @@ -526,7 +514,7 @@ static int print_txt(struct dns_header *header, const size_t qlen, char *name, } *p3 = 0; - log_query(secflag | F_FORWARD | F_UPSTREAM, name, NULL, (char*)p1, 0); + log_query(secflag | F_FORWARD, name, NULL, (char*)p1, 0); /* restore */ memmove(p1 + 1, p1, i); *p1 = len; @@ -719,6 +707,8 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t } else if (qtype == T_SRV) flags |= F_SRV; + else if (qtype != T_CNAME && rr_on_list(daemon->cache_rr, qtype)) + flags |= F_RR; else insert = 0; /* NOTE: do not cache data from CNAME queries. */ @@ -804,7 +794,7 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t #ifdef HAVE_DNSSEC if (!option_bool(OPT_DNSSEC_VALID) || aqtype != T_RRSIG) #endif - log_query(secflag | F_FORWARD | F_UPSTREAM, name, NULL, NULL, aqtype); + log_query(secflag | F_FORWARD | F_UPSTREAM | F_RRNAME, name, NULL, NULL, aqtype); } else if (!(flags & F_NXDOMAIN)) { @@ -829,6 +819,64 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t if (!extract_name(header, qlen, &tmp, name, 1, 0)) return 2; } + else if (flags & F_RR) + { + short desc, *rrdesc = rrfilter_desc(aqtype); + unsigned char *tmp = namep; + + if (!CHECK_LEN(header, p1, qlen, ardlen)) + return 2; /* bad packet */ + addr.rr.rrtype = aqtype; + addr.rr.datalen = 0; + + /* The RR data may include names, and those names may include + compression, which will be rendered meaningless when + copied into another packet. + Here we go through a description of the packet type to + find the names, and extract them to a c-string and then + re-encode them to standalone DNS format without compression. */ + if (!(addr.rr.rrdata = blockdata_alloc(NULL, 0))) + return 0; + do + { + desc = *rrdesc++; + + if (desc == -1) + { + /* Copy the rest of the RR and end. */ + if (!blockdata_expand(addr.rr.rrdata, addr.rr.datalen, (char *)p1, endrr - p1)) + return 0; + addr.rr.datalen += endrr - p1; + } + else if (desc == 0) + { + /* Name, extract it then re-encode. */ + int len; + + if (!extract_name(header, qlen, &p1, name, 1, 0)) + return 2; + + len = to_wire(name); + if (!blockdata_expand(addr.rr.rrdata, addr.rr.datalen, name, len)) + return 0; + addr.rr.datalen += len; + } + else + { + /* desc is length of a block of data to be used as-is */ + if (desc > endrr - p1) + desc = endrr - p1; + if (!blockdata_expand(addr.rr.rrdata, addr.rr.datalen, (char *)p1, desc)) + return 0; + addr.rr.datalen += desc; + p1 += desc; + } + } while (desc != -1); + + /* we overwrote the original name, so get it back here. */ + if (!extract_name(header, qlen, &tmp, name, 1, 0)) + return 2; + } else if (flags & (F_IPV4 | F_IPV6)) { /* copy address into aligned storage */ @@ -876,8 +924,10 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t if (aqtype == T_TXT) { - if (!print_txt(header, qlen, name, p1, ardlen, secflag)) - return 2; + if (!CHECK_LEN(header, p1, qlen, ardlen)) + return 2; + + log_txt(name, p1, ardlen, secflag | F_UPSTREAM); } else { @@ -903,7 +953,7 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t { if (flags & F_NXDOMAIN) { - flags &= ~(F_IPV4 | F_IPV6 | F_SRV); + flags &= ~(F_IPV4 | F_IPV6 | F_SRV | F_RR); /* Can store NXDOMAIN reply for any qtype. */ insert = 1; @@ -924,7 +974,10 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t if (ttl == 0) ttl = cttl; - newc = cache_insert(name, NULL, C_IN, now, ttl, F_FORWARD | F_NEG | flags | (secure ? F_DNSSECOK : 0)); + if (flags & F_RR) + addr.rr.rrtype = qtype; + + newc = cache_insert(name, &addr, C_IN, now, ttl, F_FORWARD | F_NEG | flags | (secure ? F_DNSSECOK : 0)); if (newc && cpp) { next_uid(newc); @@ -2044,7 +2097,7 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen, if (!found) { if ((crecp = cache_find_by_name(NULL, name, now, F_SRV | F_NXDOMAIN | (dryrun ? F_NO_RR : 0))) && - rd_bit && (!do_bit || (option_bool(OPT_DNSSEC_VALID) && !(crecp->flags & F_DNSSECOK)))) + rd_bit && (!do_bit || cache_validated(crecp))) do { int stale_flag = 0; @@ -2125,8 +2178,57 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen, if (!dryrun) log_query(F_CONFIG | F_NEG, name, &addr, NULL, 0); } - } + if (!ans && qtype != T_ANY) + { + if ((crecp = cache_find_by_name(NULL, name, now, F_RR | F_NXDOMAIN | (dryrun ? F_NO_RR : 0))) && + rd_bit && (!do_bit || cache_validated(crecp))) + do + { + int stale_flag = 0; + + if (crecp->addr.rr.rrtype == qtype) + { + if (crec_isstale(crecp, now)) + { + if (stale) + *stale = 1; + + stale_flag = F_STALE; + } + + if (!(crecp->flags & F_DNSSECOK)) + sec_data = 0; + + auth = 0; + ans = 1; + + if (!dryrun) + { + char *rrdata = NULL; + + if (!(crecp->flags & F_NEG)) + { + rrdata = blockdata_retrieve(crecp->addr.rr.rrdata, crecp->addr.rr.datalen, NULL); + + if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, + crec_ttl(crecp, now), NULL, qtype, C_IN, "t", + crecp->addr.rr.datalen, rrdata)) + anscount++; + } + + /* log after cache insertion as log_txt mangles rrdata */ + if (qtype == T_TXT && !(crecp->flags & F_NEG)) + log_txt(name, (unsigned char *)rrdata, crecp->addr.rr.datalen, crecp->flags & F_DNSSECOK); + else + log_query(stale_flag | crecp->flags, name, &crecp->addr, NULL, 0); + } + } + } while ((crecp = cache_find_by_name(crecp, name, now, F_RR))); + } + } + + if (!ans) { /* We may know that the domain doesn't exist for any RRtype. */ |