From 9eaa91bfc32e40f3e9c6ac1d86c0c284f09e0d3f Mon Sep 17 00:00:00 2001 From: Simon Kelley Date: Wed, 17 Mar 2021 20:31:06 +0000 Subject: [PATCH] Teach --bogus-nxdomain and --ignore-address to take a subnet argument. --- CHANGELOG | 4 ++- man/dnsmasq.8 | 8 ++--- src/dnsmasq.h | 6 ++-- src/forward.c | 4 +-- src/option.c | 17 +++++++---- src/rfc1035.c | 81 +++++++++++++++++++++------------------------------ 6 files changed, 57 insertions(+), 63 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index c7a9649..4d5bfb2 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -25,7 +25,9 @@ version 2.85 for routers with dynamically prefixes. Thanks to Fred F for the suggestion. - + Teach --bogus-nxdomain and --ignore-address to take an IPv4 subnet. + + version 2.84 Fix a problem, introduced in 2.83, which could see DNS replies being sent via the wrong socket. On machines running both diff --git a/man/dnsmasq.8 b/man/dnsmasq.8 index 6dee5a4..8473acf 100644 --- a/man/dnsmasq.8 +++ b/man/dnsmasq.8 @@ -326,8 +326,8 @@ are re-written. So .B --alias=192.168.0.10-192.168.0.40,10.0.0.0,255.255.255.0 maps 192.168.0.10->192.168.0.40 to 10.0.0.10->10.0.0.40 .TP -.B \-B, --bogus-nxdomain= -Transform replies which contain the IP address given into "No such +.B \-B, --bogus-nxdomain=[/prefix] +Transform replies which contain the IP specified address or subnet into "No such domain" replies. This is intended to counteract a devious move made by Verisign in September 2003 when they started returning the address of an advertising web page in response to queries for unregistered names, @@ -335,8 +335,8 @@ instead of the correct NXDOMAIN response. This option tells dnsmasq to fake the correct response when it sees this behaviour. As at Sept 2003 the IP address being returned by Verisign is 64.94.110.11 .TP -.B --ignore-address= -Ignore replies to A-record queries which include the specified address. +.B --ignore-address=[/prefix] +Ignore replies to A-record queries which include the specified address or subnet. No error is generated, dnsmasq simply continues to listen for another reply. This is useful to defeat blocking strategies which rely on quickly supplying a forged answer to a DNS request for certain domain, before the correct answer can arrive. diff --git a/src/dnsmasq.h b/src/dnsmasq.h index e201b7a..20d687a 100644 --- a/src/dnsmasq.h +++ b/src/dnsmasq.h @@ -325,7 +325,7 @@ union all_addr { struct bogus_addr { - struct in_addr addr; + struct in_addr addr, mask; struct bogus_addr *next; }; @@ -1241,8 +1241,8 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen, struct in_addr local_addr, struct in_addr local_netmask, time_t now, int ad_reqd, int do_bit, int have_pseudoheader); int check_for_bogus_wildcard(struct dns_header *header, size_t qlen, char *name, - struct bogus_addr *baddr, time_t now); -int check_for_ignored_address(struct dns_header *header, size_t qlen, struct bogus_addr *baddr); + time_t now); +int check_for_ignored_address(struct dns_header *header, size_t qlen); int check_for_local_domain(char *name, time_t now); size_t resize_packet(struct dns_header *header, size_t plen, unsigned char *pheader, size_t hlen); diff --git a/src/forward.c b/src/forward.c index 6bbf8a4..0ea829c 100644 --- a/src/forward.c +++ b/src/forward.c @@ -738,7 +738,7 @@ static size_t process_reply(struct dns_header *header, time_t now, struct server } if (daemon->bogus_addr && rcode != NXDOMAIN && - check_for_bogus_wildcard(header, n, daemon->namebuff, daemon->bogus_addr, now)) + check_for_bogus_wildcard(header, n, daemon->namebuff, now)) { munged = 1; SET_RCODE(header, NXDOMAIN); @@ -866,7 +866,7 @@ void reply_query(int fd, int family, time_t now) daemon->log_source_addr = &forward->frec_src.source; if (daemon->ignore_addr && RCODE(header) == NOERROR && - check_for_ignored_address(header, n, daemon->ignore_addr)) + check_for_ignored_address(header, n)) return; /* Note: if we send extra options in the EDNS0 header, we can't recreate diff --git a/src/option.c b/src/option.c index bfda212..b6ab7ce 100644 --- a/src/option.c +++ b/src/option.c @@ -2487,8 +2487,14 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma case LOPT_IGNORE_ADDR: /* --ignore-address */ { struct in_addr addr; + int prefix = 32; unhide_metas(arg); - if (arg && (inet_pton(AF_INET, arg, &addr) > 0)) + + if (!arg || + ((comma = split_chr(arg, '/')) && !atoi_check(comma, &prefix)) || + (inet_pton(AF_INET, arg, &addr) != 1)) + ret_err(gen_err); /* error */ + else { struct bogus_addr *baddr = opt_malloc(sizeof(struct bogus_addr)); if (option == 'B') @@ -2501,12 +2507,11 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma baddr->next = daemon->ignore_addr; daemon->ignore_addr = baddr; } - baddr->addr = addr; + baddr->mask.s_addr = htonl(~((1 << (32 - prefix)) - 1)); + baddr->addr.s_addr = addr.s_addr & baddr->mask.s_addr; } - else - ret_err(gen_err); /* error */ - break; - } + break; + } case 'a': /* --listen-address */ case LOPT_AUTHPEER: /* --auth-peer */ diff --git a/src/rfc1035.c b/src/rfc1035.c index ea438c2..5a961b8 100644 --- a/src/rfc1035.c +++ b/src/rfc1035.c @@ -1017,30 +1017,33 @@ int check_for_local_domain(char *name, time_t now) return 0; } -/* Is the packet a reply with the answer address equal to addr? - If so mung is into an NXDOMAIN reply and also put that information - in the cache. */ -int check_for_bogus_wildcard(struct dns_header *header, size_t qlen, char *name, - struct bogus_addr *baddr, time_t now) +static int check_bad_address(struct dns_header *header, size_t qlen, struct bogus_addr *baddr, char *name, unsigned long *ttlp) { unsigned char *p; int i, qtype, qclass, rdlen; unsigned long ttl; struct bogus_addr *baddrp; - + struct in_addr addr; + /* skip over questions */ if (!(p = skip_questions(header, qlen))) return 0; /* bad packet */ for (i = ntohs(header->ancount); i != 0; i--) { - if (!extract_name(header, qlen, &p, name, 1, 10)) + if (name && !extract_name(header, qlen, &p, name, 1, 10)) return 0; /* bad packet */ - + + if (!name && !(p = skip_name(p, header, qlen, 10))) + return 0; + GETSHORT(qtype, p); GETSHORT(qclass, p); GETLONG(ttl, p); GETSHORT(rdlen, p); + + if (ttlp) + *ttlp = ttl; if (qclass == C_IN && qtype == T_A) { @@ -1048,16 +1051,12 @@ int check_for_bogus_wildcard(struct dns_header *header, size_t qlen, char *name, return 0; for (baddrp = baddr; baddrp; baddrp = baddrp->next) - if (memcmp(&baddrp->addr, p, INADDRSZ) == 0) - { - /* 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_end_insert(); - + { + memcpy(&addr, p, INADDRSZ); + + if ((addr.s_addr & baddrp->mask.s_addr) == baddrp->addr.s_addr) return 1; - } + } } if (!ADD_RDLEN(header, p, qlen, rdlen)) @@ -1067,43 +1066,31 @@ int check_for_bogus_wildcard(struct dns_header *header, size_t qlen, char *name, return 0; } -int check_for_ignored_address(struct dns_header *header, size_t qlen, struct bogus_addr *baddr) +/* Is the packet a reply with the answer address equal to addr? + If so mung is into an NXDOMAIN reply and also put that information + in the cache. */ +int check_for_bogus_wildcard(struct dns_header *header, size_t qlen, char *name, time_t now) { - unsigned char *p; - int i, qtype, qclass, rdlen; - struct bogus_addr *baddrp; + unsigned long ttl; - /* skip over questions */ - if (!(p = skip_questions(header, qlen))) - return 0; /* bad packet */ - - for (i = ntohs(header->ancount); i != 0; i--) + if (check_bad_address(header, qlen, daemon->bogus_addr, name, &ttl)) { - if (!(p = skip_name(p, header, qlen, 10))) - return 0; /* bad packet */ - - GETSHORT(qtype, p); - GETSHORT(qclass, p); - p += 4; /* TTL */ - GETSHORT(rdlen, p); - - if (qclass == C_IN && qtype == T_A) - { - if (!CHECK_LEN(header, p, qlen, INADDRSZ)) - return 0; - - for (baddrp = baddr; baddrp; baddrp = baddrp->next) - if (memcmp(&baddrp->addr, p, INADDRSZ) == 0) - return 1; - } - - if (!ADD_RDLEN(header, p, qlen, rdlen)) - return 0; + /* 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_end_insert(); + + return 1; } - + return 0; } +int check_for_ignored_address(struct dns_header *header, size_t qlen) +{ + return check_bad_address(header, qlen, daemon->ignore_addr, NULL, NULL); +} int add_resource_record(struct dns_header *header, char *limit, int *truncp, int nameoffset, unsigned char **pp, unsigned long ttl, int *offset, unsigned short type, unsigned short class, char *format, ...)