diff --git a/src/dnsmasq.h b/src/dnsmasq.h index a6bba97..61885c7 100644 --- a/src/dnsmasq.h +++ b/src/dnsmasq.h @@ -1373,7 +1373,7 @@ int is_name_synthetic(int flags, char *name, union all_addr *addr); int is_rev_synth(int flag, union all_addr *addr, char *name); /* rfc1035.c */ -int do_doctor(struct dns_header *header, size_t qlen); +int do_doctor(struct dns_header *header, size_t qlen, char *namebuff); int extract_name(struct dns_header *header, size_t plen, unsigned char **pp, char *name, int isExtract, int extrabytes); unsigned char *skip_name(unsigned char *ansp, struct dns_header *header, size_t plen, int extrabytes); diff --git a/src/forward.c b/src/forward.c index 45542d7..d7f9eb6 100644 --- a/src/forward.c +++ b/src/forward.c @@ -687,14 +687,13 @@ static size_t process_reply(struct dns_header *header, time_t now, struct server { unsigned char *pheader, *sizep; struct ipsets *ipsets = NULL, *nftsets = NULL; - int munged = 0, is_sign; + int is_sign; unsigned int rcode = RCODE(header); size_t plen; (void)ad_reqd; (void)do_bit; - (void)bogusanswer; - + #ifdef HAVE_IPSET if (daemon->ipsets && extract_request(header, n, daemon->namebuff, NULL)) ipsets = domain_find_sets(daemon->ipsets, daemon->namebuff); @@ -785,64 +784,64 @@ static size_t process_reply(struct dns_header *header, time_t now, struct server if (header->hb3 & HB3_TC) { log_query(F_UPSTREAM, NULL, NULL, "truncated", 0); - munged = 1; + header->ancount = htons(0); + header->nscount = htons(0); + header->arcount = htons(0); } - else if (daemon->bogus_addr && rcode != NXDOMAIN && - check_for_bogus_wildcard(header, n, daemon->namebuff, now)) + + if (!(header->hb3 & HB3_TC) && (!bogusanswer || (header->hb4 & HB4_CD))) { - munged = 1; - SET_RCODE(header, NXDOMAIN); - header->hb3 &= ~HB3_AA; - cache_secure = 0; - ede = EDE_BLOCKED; - } - else - { - if (rcode == NXDOMAIN && - extract_request(header, n, daemon->namebuff, NULL)) + if (rcode == NXDOMAIN && extract_request(header, n, daemon->namebuff, NULL) && + (check_for_local_domain(daemon->namebuff, now) || lookup_domain(daemon->namebuff, F_CONFIG, NULL, NULL))) { - if (check_for_local_domain(daemon->namebuff, now) || - lookup_domain(daemon->namebuff, F_CONFIG, NULL, NULL)) - { - /* if we forwarded a query for a locally known name (because it was for - an unknown type) and the answer is NXDOMAIN, convert that to NODATA, - since we know that the domain exists, even if upstream doesn't */ - munged = 1; - header->hb3 |= HB3_AA; - SET_RCODE(header, NOERROR); - cache_secure = 0; - } + /* if we forwarded a query for a locally known name (because it was for + an unknown type) and the answer is NXDOMAIN, convert that to NODATA, + since we know that the domain exists, even if upstream doesn't */ + header->hb3 |= HB3_AA; + SET_RCODE(header, NOERROR); + cache_secure = 0; } - if (!bogusanswer) + if (daemon->doctors && do_doctor(header, n, daemon->namebuff)) + cache_secure = 0; + + /* check_for_bogus_wildcard() does it's own caching, so + don't call extract_addresses() if it triggers. */ + if (daemon->bogus_addr && rcode != NXDOMAIN && + check_for_bogus_wildcard(header, n, daemon->namebuff, now)) { - if (daemon->doctors && !do_doctor(header, n)) + header->ancount = htons(0); + header->nscount = htons(0); + header->arcount = htons(0); + SET_RCODE(header, NXDOMAIN); + header->hb3 &= ~HB3_AA; + cache_secure = 0; + ede = EDE_BLOCKED; + } + else + { + int rc = extract_addresses(header, n, daemon->namebuff, now, ipsets, nftsets, is_sign, check_rebind, no_cache, cache_secure); + + if (rc != 0) { - /* do_doctors found malformed answer. */ - munged = 1; - SET_RCODE(header, SERVFAIL); + header->ancount = htons(0); + header->nscount = htons(0); + header->arcount = htons(0); cache_secure = 0; - ede = EDE_OTHER; } - if (RCODE(header) != SERVFAIL) - switch (extract_addresses(header, n, daemon->namebuff, now, ipsets, nftsets, is_sign, check_rebind, no_cache, cache_secure)) - { - case 1: - my_syslog(LOG_WARNING, _("possible DNS-rebind attack detected: %s"), daemon->namebuff); - munged = 1; - cache_secure = 0; - ede = EDE_BLOCKED; - break; - - /* extract_addresses() found a malformed answer. */ - case 2: - munged = 1; - SET_RCODE(header, SERVFAIL); - cache_secure = 0; - ede = EDE_OTHER; - break; - } + if (rc == 1) + { + my_syslog(LOG_WARNING, _("possible DNS-rebind attack detected: %s"), daemon->namebuff); + ede = EDE_BLOCKED; + } + + if (rc == 2) + { + /* extract_addresses() found a malformed answer. */ + SET_RCODE(header, SERVFAIL); + ede = EDE_OTHER; + } } if (RCODE(header) == NOERROR && rrfilter(header, &n, RRFILTER_CONF) > 0) @@ -850,18 +849,21 @@ static size_t process_reply(struct dns_header *header, time_t now, struct server } #ifdef HAVE_DNSSEC - if (bogusanswer && !(header->hb4 & HB4_CD) && !option_bool(OPT_DNSSEC_DEBUG)) - { - /* Bogus reply, turn into SERVFAIL */ - SET_RCODE(header, SERVFAIL); - munged = 1; - } - if (option_bool(OPT_DNSSEC_VALID)) { - header->hb4 &= ~HB4_AD; - - if (!(header->hb4 & HB4_CD) && ad_reqd && cache_secure) + if (bogusanswer) + { + if (!(header->hb4 & HB4_CD) && !option_bool(OPT_DNSSEC_DEBUG)) + { + /* Bogus reply, turn into SERVFAIL */ + SET_RCODE(header, SERVFAIL); + header->ancount = htons(0); + header->nscount = htons(0); + header->arcount = htons(0); + ede = EDE_DNSSEC_BOGUS; + } + } + else if (!(header->hb4 & HB4_CD) && ad_reqd && cache_secure) header->hb4 |= HB4_AD; /* If the requestor didn't set the DO bit, don't return DNSSEC info. */ @@ -869,19 +871,9 @@ static size_t process_reply(struct dns_header *header, time_t now, struct server rrfilter(header, &n, RRFILTER_DNSSEC); } #endif - - /* do this after extract_addresses. Ensure NODATA reply and remove - nameserver info. */ - if (munged) - { - header->ancount = htons(0); - header->nscount = htons(0); - header->arcount = htons(0); - } - /* the bogus-nxdomain stuff, doctor and NXDOMAIN->NODATA munging can all elide - sections of the packet. Find the new length here and put back pseudoheader - if it was removed. */ + /* the code above can elide sections of the packet. Find the new length here + and put back pseudoheader if it was removed. */ n = resize_packet(header, n, pheader, plen); if (pheader && ede != EDE_UNSET) diff --git a/src/rfc1035.c b/src/rfc1035.c index a83d9c1..fcad97d 100644 --- a/src/rfc1035.c +++ b/src/rfc1035.c @@ -384,22 +384,23 @@ static int private_net6(struct in6_addr *a, int ban_localhost) ((u32 *)a)[0] == htonl(0x20010db8); /* RFC 6303 4.6 */ } -int do_doctor(struct dns_header *header, size_t qlen) +int do_doctor(struct dns_header *header, size_t qlen, char *namebuff) { unsigned char *p; int i, qtype, qclass, rdlen; - + int done = 0; + if (!(p = skip_questions(header, qlen))) - return 0; + return done; for (i = 0; i < ntohs(header->ancount) + ntohs(header->arcount); i++) { /* Skip over auth section */ if (i == ntohs(header->ancount) && !(p = skip_section(p, ntohs(header->nscount), header, qlen))) - return 0; + return done; - if (!extract_name(header, qlen, &p, daemon->workspacename, 1, 10)) - return 0; /* bad packet */ + if (!extract_name(header, qlen, &p, namebuff, 1, 10)) + return done; /* bad packet */ GETSHORT(qtype, p); GETSHORT(qclass, p); @@ -412,7 +413,7 @@ int do_doctor(struct dns_header *header, size_t qlen) union all_addr addr; if (!CHECK_LEN(header, p, qlen, INADDRSZ)) - return 0; + return done; /* alignment */ memcpy(&addr.addr4, p, INADDRSZ); @@ -437,17 +438,18 @@ int do_doctor(struct dns_header *header, size_t qlen) if (option_bool(OPT_DNSSEC_VALID) && i < ntohs(header->ancount)) daemon->rr_status[i] = 0; #endif + done = 1; memcpy(p, &addr.addr4, INADDRSZ); - log_query(F_FORWARD | F_CONFIG | F_IPV4, daemon->workspacename, &addr, NULL, 0); + log_query(F_FORWARD | F_CONFIG | F_IPV4, namebuff, &addr, NULL, 0); break; } } if (!ADD_RDLEN(header, p, qlen, rdlen)) - return 0; /* bad packet */ + return done; /* bad packet */ } - return 1; + return done; } /* Find SOA RR in auth section to get TTL for negative caching of name. @@ -1046,8 +1048,6 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t if (!found && (qtype != T_ANY || (flags & F_NXDOMAIN))) { - int substring, have_soa; - if (flags & F_NXDOMAIN) { flags &= ~(F_IPV4 | F_IPV6 | F_RR); @@ -1060,7 +1060,7 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t if (insert && !option_bool(OPT_NO_NEG)) { - int have_soa = find_soa(header, qlen, name, &substring, &ttl, no_cache_dnssec, now); + int substring, have_soa = find_soa(header, qlen, name, &substring, &ttl, no_cache_dnssec, now); /* If there's no SOA to get the TTL from, but there is a CNAME pointing at this, inherit its TTL */ @@ -1323,8 +1323,7 @@ static int check_bad_address(struct dns_header *header, size_t qlen, struct bogu GETSHORT(qtype, p); GETSHORT(qclass, p); GETLONG(ttl, p); - GETSHORT(rdlen, p); - + GETSHORT(rdlen, p) if (ttlp) *ttlp = ttl; @@ -1377,8 +1376,9 @@ int check_for_bogus_wildcard(struct dns_header *header, size_t qlen, char *name, /* Found a bogus address. Insert that info here, since there no SOA record to get the ttl from in the normal processing */ cache_start_insert(); - cache_insert(name, NULL, C_IN, now, ttl, F_IPV4 | F_FORWARD | F_NEG | F_NXDOMAIN); + cache_insert(name, NULL, C_IN, now, ttl, F_FORWARD | F_NEG | F_NXDOMAIN); cache_end_insert(); + log_query(F_CONFIG | F_FORWARD | F_NEG | F_NXDOMAIN, name, NULL, NULL, 0); return 1; }