diff -uNr dnsmasq-2.40/src/dnsmasq.h dnsmasq-2.40_stopdnsrebind/src/dnsmasq.h --- dnsmasq-2.40/src/dnsmasq.h 2007-08-09 14:17:41.000000000 +0200 +++ dnsmasq-2.40_stopdnsrebind/src/dnsmasq.h 2007-10-20 16:57:31.000000000 +0200 @@ -146,6 +146,7 @@ #define OPT_TFTP_NOBLOCK (1<<27) #define OPT_LOG_OPTS (1<<28) #define OPT_TFTP_APREF (1<<29) +#define OPT_STOPDNSREBIND (1<<30) struct all_addr { union { @@ -601,7 +602,7 @@ size_t setup_reply(HEADER *header, size_t qlen, struct all_addr *addrp, unsigned short flags, unsigned long local_ttl); -void extract_addresses(HEADER *header, size_t qlen, char *namebuff, time_t now); +int extract_addresses(HEADER *header, size_t qlen, char *namebuff, time_t now); size_t answer_request(HEADER *header, char *limit, size_t qlen, struct in_addr local_addr, struct in_addr local_netmask, time_t now); int check_for_bogus_wildcard(HEADER *header, size_t qlen, char *name, diff -uNr dnsmasq-2.40/src/forward.c dnsmasq-2.40_stopdnsrebind/src/forward.c --- dnsmasq-2.40/src/forward.c 2007-07-23 20:22:31.000000000 +0200 +++ dnsmasq-2.40_stopdnsrebind/src/forward.c 2007-10-20 15:43:08.000000000 +0200 @@ -399,7 +399,9 @@ header->rcode = NOERROR; } - extract_addresses(header, n, daemon->namebuff, now); + if (!extract_addresses(header, n, daemon->namebuff, now)) { + return 0; + } } /* do this after extract_addresses. Ensure NODATA reply and remove diff -uNr dnsmasq-2.40/src/option.c dnsmasq-2.40_stopdnsrebind/src/option.c --- dnsmasq-2.40/src/option.c 2007-08-17 22:05:08.000000000 +0200 +++ dnsmasq-2.40_stopdnsrebind/src/option.c 2007-10-20 17:17:22.000000000 +0200 @@ -46,6 +46,7 @@ #define LOPT_BANK 272 #define LOPT_DHCP_HOST 273 #define LOPT_APREF 274 +#define LOPT_STOPDNSREBIND 275 #ifdef HAVE_GETOPT_LONG static const struct option opts[] = @@ -138,6 +139,7 @@ {"dhcp-subscrid", 1, 0, LOPT_SUBSCR }, {"interface-name", 1, 0, LOPT_INTNAME }, {"dhcp-hostsfile", 1, 0, LOPT_DHCP_HOST }, + {"stopdnsrebind", 0, 0, LOPT_STOPDNSREBIND }, { NULL, 0, 0, 0 } }; @@ -175,6 +177,7 @@ { LOPT_NOBLOCK, OPT_TFTP_NOBLOCK }, { LOPT_LOG_OPTS, OPT_LOG_OPTS }, { LOPT_APREF, OPT_TFTP_APREF }, + { LOPT_STOPDNSREBIND, OPT_STOPDNSREBIND }, { 'v', 0}, { 'w', 0}, { 0, 0 } @@ -268,6 +271,7 @@ { " --tftp-no-blocksize", gettext_noop("Disable the TFTP blocksize extension."), NULL }, { " --log-dhcp", gettext_noop("Extra logging for DHCP."), NULL }, { " --log-async[=]", gettext_noop("Enable async. logging; optionally set queue length."), NULL }, + { " --stop-dns-rebinding", gettext_noop("Stop DNS rebinding. Filter private IP ranges when resolving."), NULL }, { NULL, NULL, NULL } }; diff -uNr dnsmasq-2.40/src/rfc1035.c dnsmasq-2.40_stopdnsrebind/src/rfc1035.c --- dnsmasq-2.40/src/rfc1035.c 2007-08-07 12:47:08.000000000 +0200 +++ dnsmasq-2.40_stopdnsrebind/src/rfc1035.c 2007-10-20 17:35:18.000000000 +0200 @@ -587,7 +587,7 @@ /* 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. */ -void extract_addresses(HEADER *header, size_t qlen, char *name, time_t now) +int extract_addresses(HEADER *header, size_t qlen, char *name, time_t now) { unsigned char *p, *p1, *endrr; int i, j, qtype, qclass, aqtype, aqclass, ardlen, res, searched_soa = 0; @@ -614,8 +614,8 @@ unsigned long cttl = ULONG_MAX, attl; if (!extract_name(header, qlen, &p, name, 1)) - return; /* bad packet */ - + return 0; /* bad packet */ + GETSHORT(qtype, p); GETSHORT(qclass, p); @@ -627,7 +627,7 @@ if (qtype == T_PTR) { int name_encoding = in_arpa_name_2_addr(name, &addr); - + if (!name_encoding) continue; @@ -635,12 +635,12 @@ { cname_loop: if (!(p1 = skip_questions(header, qlen))) - return; + return 0; for (j = ntohs(header->ancount); j != 0; j--) { if (!(res = extract_name(header, qlen, &p1, name, 0))) - return; /* bad packet */ + return 0; /* bad packet */ GETSHORT(aqtype, p1); GETSHORT(aqclass, p1); @@ -655,12 +655,12 @@ if (aqclass == C_IN && res != 2 && (aqtype == T_CNAME || aqtype == T_PTR)) { if (!extract_name(header, qlen, &p1, name, 1)) - return; + return 0; if (aqtype == T_CNAME) { if (!cname_count--) - return; /* looped CNAMES */ + return 0; /* looped CNAMES */ goto cname_loop; } @@ -670,7 +670,7 @@ p1 = endrr; if ((size_t)(p1 - (unsigned char *)header) > qlen) - return; /* bad packet */ + return 0; /* bad packet */ } } @@ -710,12 +710,12 @@ { cname_loop1: if (!(p1 = skip_questions(header, qlen))) - return; + return 0; for (j = ntohs(header->ancount); j != 0; j--) { if (!(res = extract_name(header, qlen, &p1, name, 0))) - return; /* bad packet */ + return 0; /* bad packet */ GETSHORT(aqtype, p1); GETSHORT(aqclass, p1); @@ -728,7 +728,8 @@ if (aqtype == T_CNAME) { if (!cname_count--) - return; /* looped CNAMES */ + return 0; /* looped CNAMES */ + newc = cache_insert(name, NULL, now, attl, F_CNAME | F_FORWARD); if (newc && cpp) { @@ -741,7 +742,7 @@ cttl = attl; if (!extract_name(header, qlen, &p1, name, 1)) - return; + return 0; goto cname_loop1; } else @@ -749,19 +750,26 @@ found = 1; /* copy address into aligned storage */ memcpy(&addr, p1, addrlen); + + if ((daemon->options & OPT_STOPDNSREBIND) && private_net(addr.addr.addr4)) { + //printf("possible dns rebinding attack detected for %s -> %s\n", name, inet_ntoa(addr.addr.addr4)); + return 0; + } + else { newc = cache_insert(name, &addr, now, attl, flags | F_FORWARD); if (newc && cpp) { cpp->addr.cname.cache = newc; cpp->addr.cname.uid = newc->uid; } + } cpp = NULL; } } p1 = endrr; if ((size_t)(p1 - (unsigned char *)header) > qlen) - return; /* bad packet */ + return 0; /* bad packet */ } } @@ -788,6 +796,7 @@ } cache_end_insert(); + return 1; } /* If the packet holds exactly one query