diff --git a/src/cache.c b/src/cache.c index 91e60cb..24bd450 100644 --- a/src/cache.c +++ b/src/cache.c @@ -488,8 +488,6 @@ struct crec *cache_insert(char *name, union all_addr *addr, unsigned short class else #endif { - /* Don't log DNSSEC records here, done elsewhere */ - log_query(flags | F_UPSTREAM, name, addr, NULL); if (daemon->max_cache_ttl != 0 && daemon->max_cache_ttl < ttl) ttl = daemon->max_cache_ttl; if (daemon->min_cache_ttl != 0 && daemon->min_cache_ttl > ttl) @@ -1897,17 +1895,24 @@ static char *edestr(int ede) void log_query(unsigned int flags, char *name, union all_addr *addr, char *arg) { - char *source, *dest = daemon->addrbuff; + char *source, *dest = arg; char *verb = "is"; char *extra = ""; if (!option_bool(OPT_LOG)) return; + +#ifdef HAVE_DNSSEC + if ((flags & F_DNSSECOK) && option_bool(OPT_EXTRALOG)) + extra = " (DNSSEC signed)"; +#endif name = sanitise(name); if (addr) { + dest = daemon->addrbuff; + if (flags & F_KEYTAG) sprintf(daemon->addrbuff, arg, addr->log.keytag, addr->log.algo, addr->log.digest); else if (flags & F_RCODE) @@ -1929,13 +1934,12 @@ void log_query(unsigned int flags, char *name, union all_addr *addr, char *arg) sprintf(extra, " (EDE: %s)", edestr(addr->log.ede)); } } - else + else if (flags & (F_IPV4 | F_IPV6)) inet_ntop(flags & F_IPV4 ? AF_INET : AF_INET6, addr, daemon->addrbuff, ADDRSTRLEN); - + else + dest = arg; } - else - dest = arg; if (flags & F_REVERSE) { diff --git a/src/rfc1035.c b/src/rfc1035.c index 43d1060..bf8c163 100644 --- a/src/rfc1035.c +++ b/src/rfc1035.c @@ -394,18 +394,13 @@ static int private_net6(struct in6_addr *a, int ban_localhost) ((u32 *)a)[0] == htonl(0x20010db8); /* RFC 6303 4.6 */ } -static unsigned char *do_doctor(unsigned char *p, int count, struct dns_header *header, size_t qlen, char *name, int *doctored) +static unsigned char *do_doctor(unsigned char *p, int count, struct dns_header *header, size_t qlen, int *doctored) { int i, qtype, qclass, rdlen; for (i = count; i != 0; i--) { - if (name && option_bool(OPT_LOG)) - { - if (!extract_name(header, qlen, &p, name, 1, 10)) - return 0; - } - else if (!(p = skip_name(p, header, qlen, 10))) + if (!(p = skip_name(p, header, qlen, 10))) return 0; /* bad packet */ GETSHORT(qtype, p); @@ -444,34 +439,6 @@ static unsigned char *do_doctor(unsigned char *p, int count, struct dns_header * break; } } - else if (qtype == T_TXT && name && option_bool(OPT_LOG)) - { - unsigned char *p1 = p; - if (!CHECK_LEN(header, p1, qlen, rdlen)) - return 0; - while ((p1 - p) < rdlen) - { - unsigned int i, len = *p1; - unsigned char *p2 = p1; - if ((p1 + len - p) >= rdlen) - return 0; /* bad packet */ - /* make counted string zero-term and sanitise */ - for (i = 0; i < len; i++) - { - if (!isprint((int)*(p2+1))) - break; - - *p2 = *(p2+1); - p2++; - } - *p2 = 0; - my_syslog(LOG_INFO, "reply %s is %s", name, p1); - /* restore */ - memmove(p1 + 1, p1, i); - *p1 = len; - p1 += len+1; - } - } if (!ADD_RDLEN(header, p, qlen, rdlen)) return 0; /* bad packet */ @@ -480,7 +447,7 @@ static unsigned char *do_doctor(unsigned char *p, int count, struct dns_header * return p; } -static int find_soa(struct dns_header *header, size_t qlen, char *name, int *doctored) +static int find_soa(struct dns_header *header, size_t qlen, int *doctored) { unsigned char *p; int qtype, qclass, rdlen; @@ -489,7 +456,7 @@ static int find_soa(struct dns_header *header, size_t qlen, char *name, int *doc /* first move to NS section and find TTL from any SOA section */ if (!(p = skip_questions(header, qlen)) || - !(p = do_doctor(p, ntohs(header->ancount), header, qlen, name, doctored))) + !(p = do_doctor(p, ntohs(header->ancount), header, qlen, doctored))) return 0; /* bad packet */ for (i = ntohs(header->nscount); i != 0; i--) @@ -525,7 +492,7 @@ static int find_soa(struct dns_header *header, size_t qlen, char *name, int *doc } /* rewrite addresses in additional section too */ - if (!do_doctor(p, ntohs(header->arcount), header, qlen, NULL, doctored)) + if (!do_doctor(p, ntohs(header->arcount), header, qlen, doctored)) return 0; if (!found_soa) @@ -534,6 +501,40 @@ static int find_soa(struct dns_header *header, size_t qlen, char *name, int *doc return minttl; } +/* 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) +{ + unsigned char *p1 = p; + if (!CHECK_LEN(header, p1, qlen, ardlen)) + return 0; + /* Loop over TXT payload */ + while ((p1 - p) < ardlen) + { + unsigned int i, len = *p1; + unsigned char *p3 = p1; + if ((p1 + len - p) >= ardlen) + return 0; /* bad packet */ + + /* make counted string zero-term and sanitise */ + for (i = 0; i < len; i++) + { + if (!isprint((int)*(p3+1))) + break; + *p3 = *(p3+1); + p3++; + } + + *p3 = 0; + log_query(F_FORWARD | F_UPSTREAM, name, NULL, (char*)p1); + /* restore */ + memmove(p1 + 1, p1, i); + *p1 = len; + p1 += len+1; + } + return 1; +} + /* Note that the following code can create CNAME chains that don't point to a real record, either because of lack of memory, or lack of SOA records. These are treated by the cache code as expired and cleaned out that way. @@ -551,15 +552,21 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t #else (void)ipsets; /* unused */ #endif - + int found = 0, cname_count = CNAME_CHAIN; + struct crec *cpp = NULL; + int flags = RCODE(header) == NXDOMAIN ? F_NXDOMAIN : 0; +#ifdef HAVE_DNSSEC + int cname_short = 0; +#endif + unsigned long cttl = ULONG_MAX, attl; cache_start_insert(); - /* find_soa is needed for dns_doctor and logging side-effects, so don't call it lazily if there are any. */ - if (daemon->doctors || option_bool(OPT_LOG) || option_bool(OPT_DNSSEC_VALID)) + /* find_soa is needed for dns_doctor side effects, so don't call it lazily if there are any. */ + if (daemon->doctors || option_bool(OPT_DNSSEC_VALID)) { searched_soa = 1; - ttl = find_soa(header, qlen, name, doctored); + ttl = find_soa(header, qlen, doctored); if (*doctored) { @@ -574,152 +581,39 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t } } - /* go through the questions. */ - p = (unsigned char *)(header+1); + namep = p = (unsigned char *)(header+1); - for (i = ntohs(header->qdcount); i != 0; i--) - { - int found = 0, cname_count = CNAME_CHAIN; - struct crec *cpp = NULL; - int flags = RCODE(header) == NXDOMAIN ? F_NXDOMAIN : 0; -#ifdef HAVE_DNSSEC - int cname_short = 0; -#endif - unsigned long cttl = ULONG_MAX, attl; - - namep = p; - if (!extract_name(header, qlen, &p, name, 1, 4)) - return 0; /* bad packet */ - - GETSHORT(qtype, p); - GETSHORT(qclass, p); + if (ntohs(header->qdcount) != 1 || !extract_name(header, qlen, &p, name, 1, 4)) + return 0; /* bad packet */ + + GETSHORT(qtype, p); + GETSHORT(qclass, p); + + if (qclass != C_IN) + return 0; + + /* PTRs: we chase CNAMEs here, since we have no way to + represent them in the cache. */ + if (qtype == T_PTR) + { + int insert = 1, name_encoding = in_arpa_name_2_addr(name, &addr); - if (qclass != C_IN) - continue; - - /* PTRs: we chase CNAMEs here, since we have no way to - represent them in the cache. */ - if (qtype == T_PTR) - { - int name_encoding = in_arpa_name_2_addr(name, &addr); - - if (!name_encoding) - continue; - - if (!(flags & F_NXDOMAIN)) - { - cname_loop: - if (!(p1 = skip_questions(header, qlen))) - return 0; - - for (j = 0; j < ntohs(header->ancount); j++) - { - int secflag = 0; - unsigned char *tmp = namep; - /* the loop body overwrites the original name, so get it back here. */ - if (!extract_name(header, qlen, &tmp, name, 1, 0) || - !(res = extract_name(header, qlen, &p1, name, 0, 10))) - return 0; /* bad packet */ - - GETSHORT(aqtype, p1); - GETSHORT(aqclass, p1); - GETLONG(attl, p1); - if ((daemon->max_ttl != 0) && (attl > daemon->max_ttl) && !is_sign) - { - (p1) -= 4; - PUTLONG(daemon->max_ttl, p1); - } - GETSHORT(ardlen, p1); - endrr = p1+ardlen; - - /* TTL of record is minimum of CNAMES and PTR */ - if (attl < cttl) - cttl = attl; - - if (aqclass == C_IN && res != 2 && (aqtype == T_CNAME || aqtype == T_PTR)) - { - if (!extract_name(header, qlen, &p1, name, 1, 0)) - return 0; -#ifdef HAVE_DNSSEC - if (option_bool(OPT_DNSSEC_VALID) && daemon->rr_status[j] != 0) - { - /* validated RR anywhere in CNAME chain, don't cache. */ - if (cname_short || aqtype == T_CNAME) - return 0; - - secflag = F_DNSSECOK; - /* limit TTL based on signature. */ - if (daemon->rr_status[j] < cttl) - cttl = daemon->rr_status[j]; - } -#endif - - if (aqtype == T_CNAME) - { - if (!cname_count--) - return 0; /* looped CNAMES, we can't cache. */ -#ifdef HAVE_DNSSEC - cname_short = 1; -#endif - goto cname_loop; - } - - cache_insert(name, &addr, C_IN, now, cttl, name_encoding | secflag | F_REVERSE); - found = 1; - } - - p1 = endrr; - if (!CHECK_LEN(header, p1, qlen, 0)) - return 0; /* bad packet */ - } - } - - if (!found && !option_bool(OPT_NO_NEG)) - { - if (!searched_soa) - { - searched_soa = 1; - ttl = find_soa(header, qlen, NULL, doctored); - } - if (ttl) - cache_insert(NULL, &addr, C_IN, now, ttl, name_encoding | F_REVERSE | F_NEG | flags | (secure ? F_DNSSECOK : 0)); - } - } - else + if (!(flags & F_NXDOMAIN)) { - /* everything other than PTR */ - struct crec *newc; - int addrlen = 0; - - if (qtype == T_A) - { - addrlen = INADDRSZ; - flags |= F_IPV4; - } - else if (qtype == T_AAAA) - { - addrlen = IN6ADDRSZ; - flags |= F_IPV6; - } - else if (qtype == T_SRV) - flags |= F_SRV; - else - continue; - - cname_loop1: + cname_loop: if (!(p1 = skip_questions(header, qlen))) return 0; for (j = 0; j < ntohs(header->ancount); j++) { int secflag = 0; - if (!(res = extract_name(header, qlen, &p1, name, 0, 10))) return 0; /* bad packet */ GETSHORT(aqtype, p1); GETSHORT(aqclass, p1); GETLONG(attl, p1); + if ((daemon->max_ttl != 0) && (attl > daemon->max_ttl) && !is_sign) { (p1) -= 4; @@ -728,128 +622,281 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t GETSHORT(ardlen, p1); endrr = p1+ardlen; - if (aqclass == C_IN && res != 2 && (aqtype == T_CNAME || aqtype == qtype)) + /* TTL of record is minimum of CNAMES and PTR */ + if (attl < cttl) + cttl = attl; + + if (aqclass == C_IN && res != 2 && (aqtype == T_CNAME || aqtype == T_PTR)) { #ifdef HAVE_DNSSEC if (option_bool(OPT_DNSSEC_VALID) && daemon->rr_status[j] != 0) { + /* validated RR anywhere in CNAME chain, don't cache. */ + if (cname_short || aqtype == T_CNAME) + insert = 0; + secflag = F_DNSSECOK; - - /* limit TTl based on sig. */ - if (daemon->rr_status[j] < attl) - attl = daemon->rr_status[j]; + /* limit TTL based on signature. */ + if (daemon->rr_status[j] < cttl) + cttl = daemon->rr_status[j]; } -#endif +#endif + + if (aqtype == T_CNAME) + log_query(secflag | F_CNAME | F_FORWARD | F_UPSTREAM, name, NULL, NULL); + + if (!extract_name(header, qlen, &p1, name, 1, 0)) + return 0; + if (aqtype == T_CNAME) { if (!cname_count--) - return 0; /* looped CNAMES */ - - if ((newc = cache_insert(name, NULL, C_IN, now, attl, F_CNAME | F_FORWARD | secflag))) - { - newc->addr.cname.target.cache = NULL; - newc->addr.cname.is_name_ptr = 0; - if (cpp) - { - next_uid(newc); - cpp->addr.cname.target.cache = newc; - cpp->addr.cname.uid = newc->uid; - } - } - - cpp = newc; - if (attl < cttl) - cttl = attl; - - namep = p1; - if (!extract_name(header, qlen, &p1, name, 1, 0)) - return 0; - - goto cname_loop1; - } - else if (!(flags & F_NXDOMAIN)) - { - found = 1; - - if (flags & F_SRV) - { - unsigned char *tmp = namep; - - if (!CHECK_LEN(header, p1, qlen, 6)) - return 0; /* bad packet */ - GETSHORT(addr.srv.priority, p1); - GETSHORT(addr.srv.weight, p1); - GETSHORT(addr.srv.srvport, p1); - if (!extract_name(header, qlen, &p1, name, 1, 0)) - return 0; - addr.srv.targetlen = strlen(name) + 1; /* include terminating zero */ - if (!(addr.srv.target = blockdata_alloc(name, addr.srv.targetlen))) - return 0; - - /* we overwrote the original name, so get it back here. */ - if (!extract_name(header, qlen, &tmp, name, 1, 0)) - return 0; - } - else - { - /* copy address into aligned storage */ - if (!CHECK_LEN(header, p1, qlen, addrlen)) - return 0; /* bad packet */ - memcpy(&addr, p1, addrlen); - - /* check for returned address in private space */ - if (check_rebind) - { - if ((flags & F_IPV4) && - private_net(addr.addr4, !option_bool(OPT_LOCAL_REBIND))) - return 1; - - if ((flags & F_IPV6) && - private_net6(&addr.addr6, !option_bool(OPT_LOCAL_REBIND))) - return 1; - } - -#ifdef HAVE_IPSET - if (ipsets && (flags & (F_IPV4 | F_IPV6))) - { - ipsets_cur = ipsets; - while (*ipsets_cur) - { - log_query((flags & (F_IPV4 | F_IPV6)) | F_IPSET, name, &addr, *ipsets_cur); - add_to_ipset(*ipsets_cur++, &addr, flags, 0); - } - } + return 0; /* looped CNAMES, we can't cache. */ +#ifdef HAVE_DNSSEC + cname_short = 1; #endif - } - - newc = cache_insert(name, &addr, C_IN, now, attl, flags | F_FORWARD | secflag); - if (newc && cpp) + goto cname_loop; + } + + found = 1; + + if (!name_encoding) + log_query(secflag | F_FORWARD | F_UPSTREAM, name, NULL, querystr(NULL, aqtype)); + else + { + log_query(name_encoding | secflag | F_REVERSE | F_UPSTREAM, name, &addr, NULL); + if (insert) + cache_insert(name, &addr, C_IN, now, cttl, name_encoding | secflag | F_REVERSE); + } + } + + p1 = endrr; + if (!CHECK_LEN(header, p1, qlen, 0)) + return 0; /* bad packet */ + } + } + + if (!found && !option_bool(OPT_NO_NEG)) + { + if (!searched_soa) + { + searched_soa = 1; + ttl = find_soa(header, qlen, doctored); + } + + flags |= F_NEG | (secure ? F_DNSSECOK : 0); + if (name_encoding && ttl) + { + flags |= F_REVERSE | name_encoding; + cache_insert(NULL, &addr, C_IN, now, ttl, flags); + } + + log_query(flags | F_UPSTREAM, name, &addr, NULL); + } + } + else + { + /* everything other than PTR */ + struct crec *newc; + int addrlen = 0, insert = 1; + + if (qtype == T_A) + { + addrlen = INADDRSZ; + flags |= F_IPV4; + } + else if (qtype == T_AAAA) + { + addrlen = IN6ADDRSZ; + flags |= F_IPV6; + } + else if (qtype == T_SRV) + flags |= F_SRV; + else + insert = 0; /* NOTE: do not cache data from CNAME queries. */ + + cname_loop1: + if (!(p1 = skip_questions(header, qlen))) + return 0; + + for (j = 0; j < ntohs(header->ancount); j++) + { + int secflag = 0; + + if (!(res = extract_name(header, qlen, &p1, name, 0, 10))) + return 0; /* bad packet */ + + GETSHORT(aqtype, p1); + GETSHORT(aqclass, p1); + GETLONG(attl, p1); + if ((daemon->max_ttl != 0) && (attl > daemon->max_ttl) && !is_sign) + { + (p1) -= 4; + PUTLONG(daemon->max_ttl, p1); + } + GETSHORT(ardlen, p1); + endrr = p1+ardlen; + + /* Not what we're looking for? */ + if (aqclass != C_IN || res == 2) + { + p1 = endrr; + if (!CHECK_LEN(header, p1, qlen, 0)) + return 0; /* bad packet */ + continue; + } + +#ifdef HAVE_DNSSEC + if (option_bool(OPT_DNSSEC_VALID) && daemon->rr_status[j] != 0) + { + secflag = F_DNSSECOK; + + /* limit TTl based on sig. */ + if (daemon->rr_status[j] < attl) + attl = daemon->rr_status[j]; + } +#endif + + if (aqtype == T_CNAME) + { + if (!cname_count--) + return 0; /* looped CNAMES */ + + log_query(secflag | F_CNAME | F_FORWARD | F_UPSTREAM, name, NULL, NULL); + + if (insert) + { + if ((newc = cache_insert(name, NULL, C_IN, now, attl, F_CNAME | F_FORWARD | secflag))) + { + newc->addr.cname.target.cache = NULL; + newc->addr.cname.is_name_ptr = 0; + if (cpp) { next_uid(newc); cpp->addr.cname.target.cache = newc; cpp->addr.cname.uid = newc->uid; } - cpp = NULL; } + + cpp = newc; + if (attl < cttl) + cttl = attl; } - p1 = endrr; - if (!CHECK_LEN(header, p1, qlen, 0)) - return 0; /* bad packet */ + namep = p1; + if (!extract_name(header, qlen, &p1, name, 1, 0)) + return 0; + + goto cname_loop1; + } + else if (aqtype != qtype) + { +#ifdef HAVE_DNSSEC + if (!option_bool(OPT_DNSSEC_VALID) || aqtype != T_RRSIG) +#endif + log_query(secflag | F_FORWARD | F_UPSTREAM, name, NULL, querystr(NULL, aqtype)); + } + else if (!(flags & F_NXDOMAIN)) + { + found = 1; + + if (flags & F_SRV) + { + unsigned char *tmp = namep; + + if (!CHECK_LEN(header, p1, qlen, 6)) + return 0; /* bad packet */ + GETSHORT(addr.srv.priority, p1); + GETSHORT(addr.srv.weight, p1); + GETSHORT(addr.srv.srvport, p1); + if (!extract_name(header, qlen, &p1, name, 1, 0)) + return 0; + addr.srv.targetlen = strlen(name) + 1; /* include terminating zero */ + if (!(addr.srv.target = blockdata_alloc(name, addr.srv.targetlen))) + return 0; + + /* we overwrote the original name, so get it back here. */ + if (!extract_name(header, qlen, &tmp, name, 1, 0)) + return 0; + } + else if (flags & (F_IPV4 | F_IPV6)) + { + /* copy address into aligned storage */ + if (!CHECK_LEN(header, p1, qlen, addrlen)) + return 0; /* bad packet */ + memcpy(&addr, p1, addrlen); + + /* check for returned address in private space */ + if (check_rebind) + { + if ((flags & F_IPV4) && + private_net(addr.addr4, !option_bool(OPT_LOCAL_REBIND))) + return 1; + + if ((flags & F_IPV6) && + private_net6(&addr.addr6, !option_bool(OPT_LOCAL_REBIND))) + return 1; + } + +#ifdef HAVE_IPSET + if (ipsets && (flags & (F_IPV4 | F_IPV6))) + { + ipsets_cur = ipsets; + while (*ipsets_cur) + { + log_query((flags & (F_IPV4 | F_IPV6)) | F_IPSET, name, &addr, *ipsets_cur); + add_to_ipset(*ipsets_cur++, &addr, flags, 0); + } + } +#endif + } + + if (insert) + { + newc = cache_insert(name, &addr, C_IN, now, attl, flags | F_FORWARD | secflag); + if (newc && cpp) + { + next_uid(newc); + cpp->addr.cname.target.cache = newc; + cpp->addr.cname.uid = newc->uid; + } + cpp = NULL; + } + + if (aqtype == T_TXT) + { + if (!print_txt(header, qlen, name, p1, ardlen)) + return 0; + } + else + log_query(flags | F_FORWARD | secflag | F_UPSTREAM, name, &addr, querystr(NULL, aqtype)); } - if (!found && !option_bool(OPT_NO_NEG)) + p1 = endrr; + if (!CHECK_LEN(header, p1, qlen, 0)) + return 0; /* bad packet */ + } + + if (!found && !option_bool(OPT_NO_NEG)) + { + if (!searched_soa) { - if (!searched_soa) + searched_soa = 1; + ttl = find_soa(header, qlen, doctored); + } + + /* If there's no SOA to get the TTL from, but there is a CNAME + pointing at this, inherit its TTL */ + if (ttl || cpp) + { + if (ttl == 0) + ttl = cttl; + + log_query(F_UPSTREAM | F_FORWARD | F_NEG | flags | (secure ? F_DNSSECOK : 0), name, NULL, NULL); + + if (insert) { - searched_soa = 1; - ttl = find_soa(header, qlen, NULL, doctored); - } - /* If there's no SOA to get the TTL from, but there is a CNAME - pointing at this, inherit its TTL */ - if (ttl || cpp) - { - newc = cache_insert(name, NULL, C_IN, now, ttl ? ttl : cttl, F_FORWARD | F_NEG | flags | (secure ? F_DNSSECOK : 0)); + newc = cache_insert(name, NULL, C_IN, now, ttl, F_FORWARD | F_NEG | flags | (secure ? F_DNSSECOK : 0)); if (newc && cpp) { next_uid(newc);