From 50d7f721ee2038be5c1baebeb4bf6a5828b58164 Mon Sep 17 00:00:00 2001 From: Jeremy Lavergne Date: Mon, 28 Oct 2013 11:26:30 +0000 Subject: [PATCH 1/9] Fix FTBFS on MacOS --- src/bpf.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bpf.c b/src/bpf.c index 390d076..718a836 100644 --- a/src/bpf.c +++ b/src/bpf.c @@ -145,7 +145,7 @@ int iface_enumerate(int family, void *parm, int (*callback)()) int i, j, prefix = 0; u32 valid = 0xffffffff, preferred = 0xffffffff; int flags = 0; -#ifdef HAVE_BSD_NETWORK +#if defined(HAVE_BSD_NETWORK) && !defined(__APPLE__) struct in6_ifreq ifr6; memset(&ifr6, 0, sizeof(ifr6)); From 7b174c250df2bc97b503fd03b9e473998676b1a6 Mon Sep 17 00:00:00 2001 From: Simon Kelley Date: Mon, 28 Oct 2013 13:14:03 +0000 Subject: [PATCH 2/9] Fix check for local domains in CNAME case. Fixes d56a604a9600c08d4a863527d549713c07f0186d --- src/cache.c | 2 +- src/rfc1035.c | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/cache.c b/src/cache.c index d99aba6..6c5f601 100644 --- a/src/cache.c +++ b/src/cache.c @@ -330,7 +330,7 @@ static int cache_scan_free(char *name, struct all_addr *addr, time_t now, unsign ((flags & crecp->flags & F_TYPE) || ((crecp->flags | flags) & F_CNAME)) && hostname_isequal(cache_get_name(crecp), name)) { - if (crecp->flags & (F_HOSTS | F_DHCP)) + if (crecp->flags & (F_HOSTS | F_DHCP | F_CONFIG)) return 0; *up = crecp->hash_next; cache_unlink(crecp); diff --git a/src/rfc1035.c b/src/rfc1035.c index fc6d09c..ff97576 100644 --- a/src/rfc1035.c +++ b/src/rfc1035.c @@ -1221,7 +1221,7 @@ int check_for_local_domain(char *name, time_t now) struct naptr *naptr; if ((crecp = cache_find_by_name(NULL, name, now, F_IPV4 | F_IPV6 | F_CNAME)) && - (crecp->flags & (F_HOSTS | F_DHCP))) + (crecp->flags & (F_HOSTS | F_DHCP | F_CONFIG))) return 1; for (naptr = daemon->naptr; naptr; naptr = naptr->next) @@ -1861,7 +1861,7 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen, if (qtype == T_CNAME || qtype == T_ANY) { if ((crecp = cache_find_by_name(NULL, name, now, F_CNAME)) && - (qtype == T_CNAME || (crecp->flags & (F_HOSTS | F_DHCP)))) + (qtype == T_CNAME || (crecp->flags & (F_HOSTS | F_DHCP | F_CONFIG)))) { ans = 1; if (!dryrun) From 6dbdc972c4509b4c31577fca0402ad7a6b6e2c5d Mon Sep 17 00:00:00 2001 From: Simon Kelley Date: Mon, 28 Oct 2013 14:22:57 +0000 Subject: [PATCH 3/9] Fix FTBFS on OS X >=10.7 Need to select a IPv6 sockopt API. --- src/config.h | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/config.h b/src/config.h index 0edc4af..b351a37 100644 --- a/src/config.h +++ b/src/config.h @@ -254,7 +254,10 @@ HAVE_SOCKADDR_SA_LEN #define HAVE_SOCKADDR_SA_LEN /* Define before sys/socket.h is included so we get socklen_t */ #define _BSD_SOCKLEN_T_ - +/* Select the RFC_3542 version of the IPv6 socket API. + Define before netinet6/in6.h is included. */ +#define __APPLE_USE_RFC_3542 + #elif defined(__NetBSD__) #define HAVE_BSD_NETWORK #define HAVE_GETOPT_LONG From 29c122af83536faedf08f0ce7373a431940696af Mon Sep 17 00:00:00 2001 From: Brad Smith Date: Mon, 4 Nov 2013 14:11:18 +0000 Subject: [PATCH 4/9] Fix FTBFS on openBSD-current. --- src/bpf.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/bpf.c b/src/bpf.c index 718a836..98a073b 100644 --- a/src/bpf.c +++ b/src/bpf.c @@ -29,6 +29,7 @@ # include #endif #include +#include #ifndef SA_SIZE #define SA_SIZE(sa) \ From 44de649e5cb3e333d7ff702249374100d0ebbd79 Mon Sep 17 00:00:00 2001 From: Simon Kelley Date: Wed, 6 Nov 2013 11:36:57 +0000 Subject: [PATCH 5/9] Make private functions "static" --- src/rfc1035.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rfc1035.c b/src/rfc1035.c index ff97576..64ad5bf 100644 --- a/src/rfc1035.c +++ b/src/rfc1035.c @@ -637,7 +637,7 @@ struct subnet_opt { #endif }; -size_t calc_subnet_opt(struct subnet_opt *opt, union mysockaddr *source) +static size_t calc_subnet_opt(struct subnet_opt *opt, union mysockaddr *source) { /* http://tools.ietf.org/html/draft-vandergaast-edns-client-subnet-02 */ From 3511a928691cda102d4cb395309ba46ae5920be8 Mon Sep 17 00:00:00 2001 From: Simon Kelley Date: Thu, 7 Nov 2013 10:28:11 +0000 Subject: [PATCH 6/9] Fix start-up order for making DHCPv6 DUID Previously, if the DUID wasn't read from the lease-file or script, a new one was created _after_ the helper process fork, so for that first run, the script calls got an empty DUID. Also, use a DUID_LL format DUID when there's no stable lease storage, as well as when the RTC is broken. That has a chance of evaluating to the same value on each startup. --- src/dhcp6.c | 42 +++++++++++++++++++++++++++--------------- src/dnsmasq.c | 3 +++ src/dnsmasq.h | 1 + src/lease.c | 10 ++++++++-- 4 files changed, 39 insertions(+), 17 deletions(-) diff --git a/src/dhcp6.c b/src/dhcp6.c index 7b0adcf..7c72872 100644 --- a/src/dhcp6.c +++ b/src/dhcp6.c @@ -523,6 +523,8 @@ int config_valid(struct dhcp_config *config, struct dhcp_context *context, struc void make_duid(time_t now) { + (void)now; + if (daemon->duid_config) { unsigned char *p; @@ -535,8 +537,14 @@ void make_duid(time_t now) } else { + time_t newnow = 0; + + /* If we have no persistent lease database, or a non-stable RTC, use DUID_LL (newnow == 0) */ +#ifndef HAVE_BROKEN_RTC /* rebase epoch to 1/1/2000 */ - time_t newnow = now - 946684800; + if (!option_bool(OPT_LEASE_RO) || daemon->lease_change_command) + newnow = now - 946684800; +#endif iface_enumerate(AF_LOCAL, &newnow, make_duid1); @@ -555,23 +563,27 @@ static int make_duid1(int index, unsigned int type, char *mac, size_t maclen, vo unsigned char *p; (void)index; (void)parm; - + time_t newnow = *((time_t *)parm); + if (type >= 256) return 1; -#ifdef HAVE_BROKEN_RTC - daemon->duid = p = safe_malloc(maclen + 4); - daemon->duid_len = maclen + 4; - PUTSHORT(3, p); /* DUID_LL */ - PUTSHORT(type, p); /* address type */ -#else - daemon->duid = p = safe_malloc(maclen + 8); - daemon->duid_len = maclen + 8; - PUTSHORT(1, p); /* DUID_LLT */ - PUTSHORT(type, p); /* address type */ - PUTLONG(*((time_t *)parm), p); /* time */ -#endif - + if (newnow == 0) + { + daemon->duid = p = safe_malloc(maclen + 4); + daemon->duid_len = maclen + 4; + PUTSHORT(3, p); /* DUID_LL */ + PUTSHORT(type, p); /* address type */ + } + else + { + daemon->duid = p = safe_malloc(maclen + 8); + daemon->duid_len = maclen + 8; + PUTSHORT(1, p); /* DUID_LLT */ + PUTSHORT(type, p); /* address type */ + PUTLONG(*((time_t *)parm), p); /* time */ + } + memcpy(p, mac, maclen); return 0; diff --git a/src/dnsmasq.c b/src/dnsmasq.c index a2b37dc..a701ab5 100644 --- a/src/dnsmasq.c +++ b/src/dnsmasq.c @@ -273,6 +273,9 @@ int main (int argc, char **argv) /* after enumerate_interfaces() */ if (daemon->doing_dhcp6 || daemon->relay6 || daemon->doing_ra) join_multicast(1); + + /* After netlink_init() and before create_helper() */ + lease_make_duid(now); #endif if (daemon->port != 0) diff --git a/src/dnsmasq.h b/src/dnsmasq.h index 4d368c5..62b1d68 100644 --- a/src/dnsmasq.h +++ b/src/dnsmasq.h @@ -1120,6 +1120,7 @@ u64 lease_find_max_addr6(struct dhcp_context *context); void lease_ping_reply(struct in6_addr *sender, unsigned char *packet, char *interface); void lease_update_slaac(time_t now); void lease_set_iaid(struct dhcp_lease *lease, int iaid); +void lease_make_duid(time_t now); #endif void lease_set_hwaddr(struct dhcp_lease *lease, unsigned char *hwaddr, unsigned char *clid, int hw_len, int hw_type, int clid_len, time_t now, int force); diff --git a/src/lease.c b/src/lease.c index 4b4d10a..7e00804 100644 --- a/src/lease.c +++ b/src/lease.c @@ -417,15 +417,21 @@ void lease_find_interfaces(time_t now) iface_enumerate(AF_INET, &now, find_interface_v4); #ifdef HAVE_DHCP6 iface_enumerate(AF_INET6, &now, find_interface_v6); +#endif +} +#ifdef HAVE_DHCP6 +void lease_make_duid(time_t now) +{ /* If we're not doing DHCPv6, and there are not v6 leases, don't add the DUID to the database */ - if (!daemon->duid && daemon->dhcp6) + if (!daemon->duid && daemon->doing_dhcp6) { file_dirty = 1; make_duid(now); } -#endif } +#endif + From 6586e8352a56104b850c2ce55912cc630dd8c195 Mon Sep 17 00:00:00 2001 From: Simon Kelley Date: Thu, 7 Nov 2013 14:20:13 +0000 Subject: [PATCH 7/9] Use random address allocation for DHCPv6 temporary addresses. --- src/dhcp6.c | 14 +++++++++----- src/dnsmasq.h | 3 ++- src/rfc3315.c | 3 ++- src/util.c | 37 +++++++++++++++++++++++++++++++------ 4 files changed, 44 insertions(+), 13 deletions(-) diff --git a/src/dhcp6.c b/src/dhcp6.c index 7c72872..5da2d0f 100644 --- a/src/dhcp6.c +++ b/src/dhcp6.c @@ -394,7 +394,7 @@ struct dhcp_config *config_find_by_address6(struct dhcp_config *configs, struct return NULL; } -struct dhcp_context *address6_allocate(struct dhcp_context *context, unsigned char *clid, int clid_len, +struct dhcp_context *address6_allocate(struct dhcp_context *context, unsigned char *clid, int clid_len, int temp_addr, int iaid, int serial, struct dhcp_netid *netids, int plain_range, struct in6_addr *ans) { /* Find a free address: exclude anything in use and anything allocated to @@ -411,9 +411,13 @@ struct dhcp_context *address6_allocate(struct dhcp_context *context, unsigned c u64 j; /* hash hwaddr: use the SDBM hashing algorithm. This works - for MAC addresses, let's see how it manages with client-ids! */ - for (j = iaid, i = 0; i < clid_len; i++) - j += clid[i] + (j << 6) + (j << 16) - j; + for MAC addresses, let's see how it manages with client-ids! + For temporary addresses, we generate a new random one each time. */ + if (temp_addr) + j = rand64(); + else + for (j = iaid, i = 0; i < clid_len; i++) + j += clid[i] + (j << 6) + (j << 16) - j; for (pass = 0; pass <= plain_range ? 1 : 0; pass++) for (c = context; c; c = c->current) @@ -423,7 +427,7 @@ struct dhcp_context *address6_allocate(struct dhcp_context *context, unsigned c continue; else { - if (option_bool(OPT_CONSEC_ADDR)) + if (!temp_addr && option_bool(OPT_CONSEC_ADDR)) /* seed is largest extant lease addr in this context */ start = lease_find_max_addr6(c) + serial; else diff --git a/src/dnsmasq.h b/src/dnsmasq.h index 62b1d68..98266c6 100644 --- a/src/dnsmasq.h +++ b/src/dnsmasq.h @@ -1000,6 +1000,7 @@ int in_zone(struct auth_zone *zone, char *name, char **cut); /* util.c */ void rand_init(void); unsigned short rand16(void); +u64 rand64(void); int legal_hostname(char *c); char *canonicalise(char *s, int *nomem); unsigned char *do_rfc1035_name(unsigned char *p, char *sval); @@ -1221,7 +1222,7 @@ int get_incoming_mark(union mysockaddr *peer_addr, struct all_addr *local_addr, #ifdef HAVE_DHCP6 void dhcp6_init(void); void dhcp6_packet(time_t now); -struct dhcp_context *address6_allocate(struct dhcp_context *context, unsigned char *clid, int clid_len, +struct dhcp_context *address6_allocate(struct dhcp_context *context, unsigned char *clid, int clid_len, int temp_addr, int iaid, int serial, struct dhcp_netid *netids, int plain_range, struct in6_addr *ans); int config_valid(struct dhcp_config *config, struct dhcp_context *context, struct in6_addr *addr); struct dhcp_context *address6_available(struct dhcp_context *context, diff --git a/src/rfc3315.c b/src/rfc3315.c index bf3bacf..8a2660f 100644 --- a/src/rfc3315.c +++ b/src/rfc3315.c @@ -764,7 +764,8 @@ static int dhcp6_no_relay(struct state *state, int msg_type, void *inbuff, size_ } /* Return addresses for all valid contexts which don't yet have one */ - while ((c = address6_allocate(state->context, state->clid, state->clid_len, state->iaid, ia_counter, solicit_tags, plain_range, &addr))) + while ((c = address6_allocate(state->context, state->clid, state->clid_len, state->ia_type == OPTION6_IA_TA, + state->iaid, ia_counter, solicit_tags, plain_range, &addr))) { #ifdef OPTION6_PREFIX_CLASS if (dump_all_prefix_classes && state->ia_type == OPTION6_IA_NA) diff --git a/src/util.c b/src/util.c index 94cc570..b267558 100644 --- a/src/util.c +++ b/src/util.c @@ -39,6 +39,15 @@ unsigned short rand16(void) return (unsigned short) (arc4random() >> 15); } +u64 rand64(void) +{ + u64 ret; + + arc4random_buf(&ret, sizeof(ret)); + + return ret; +} + #else /* SURF random number generator */ @@ -46,6 +55,7 @@ unsigned short rand16(void) static u32 seed[32]; static u32 in[12]; static u32 out[8]; +static int outleft = 0; void rand_init() { @@ -82,16 +92,31 @@ static void surf(void) } unsigned short rand16(void) +{ + if (!outleft) + { + if (!++in[0]) if (!++in[1]) if (!++in[2]) ++in[3]; + surf(); + outleft = 8; + } + + return (unsigned short) out[--outleft]; +} + +u64 rand64(void) { static int outleft = 0; - if (!outleft) { - if (!++in[0]) if (!++in[1]) if (!++in[2]) ++in[3]; - surf(); - outleft = 8; - } + if (outleft < 2) + { + if (!++in[0]) if (!++in[1]) if (!++in[2]) ++in[3]; + surf(); + outleft = 8; + } + + outleft -= 2; - return (unsigned short) out[--outleft]; + return (u64)out[outleft+1] + (((u64)out[outleft]) << 32); } #endif From 376d48c7f187cd3c3e941929a4cd5e9c9903fc89 Mon Sep 17 00:00:00 2001 From: Simon Kelley Date: Wed, 13 Nov 2013 13:04:30 +0000 Subject: [PATCH 8/9] Allow interface name to specify subnets in --auth-zone. --- CHANGELOG | 31 +++++++++++++ man/dnsmasq.8 | 39 +++++++++------- src/auth.c | 81 +++++++++++++--------------------- src/dnsmasq.h | 39 ++++++++-------- src/network.c | 120 +++++++++++++++++++++++++++++++++++++++----------- src/option.c | 58 +++++++++++++++--------- src/rfc1035.c | 38 +++++++--------- 7 files changed, 255 insertions(+), 151 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 77acde2..10b8da9 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,34 @@ +version 2.68 + Use random addresses for DHCPv6 temporary address + allocations, instead of algorithmically determined stable + addresses. + + Fix bug which meant that the DHCPv6 DUID was not available + in DHCP script runs during the lifetime of the dnsmasq + process which created the DUID de-novo. Once the DUID was + created and stored in the lease file and dnsmasq + restarted, this bug disappeared. + + Fix bug introduced in 2.67 which could result in erroneous + NXDOMAIN returns to CNAME queries. + + Fix build failures on MacOS X and openBSD. + + Allow subnet specifications in --auth-zone to be interface + names as well as address literals. This makes it possible + to configure authoritative DNS when local address ranges + are dynamic and works much better than the previous + work-around which exempted contructed DHCP ranges from the + IP address filtering. As a consequence, that work-around + is removed. Under certain circumstances, this change wil + break existing configuration: if you're relying on the + contructed-range exception, you need to change --auth-zone + to specify the same interface as is used to construct your + DHCP ranges, probably with a trailing "/6" like this: + --auth-zone=example.com,eth0/6 to limit the addresses to + IPv6 addresses of eth0. + + version 2.67 Fix crash if upstream server returns SERVFAIL when --conntrack in use. Thanks to Giacomo Tazzari for finding diff --git a/man/dnsmasq.8 b/man/dnsmasq.8 index 123c98f..deb3eb6 100644 --- a/man/dnsmasq.8 +++ b/man/dnsmasq.8 @@ -589,10 +589,19 @@ needed for a client to do validation itself. .B --auth-zone=[,[/][,[/].....]] Define a DNS zone for which dnsmasq acts as authoritative server. Locally defined DNS records which are in the domain will be served. A and AAAA records must be in one of the -specified subnets, or in a subnet corresponding to a constructed DHCP -range. (This can be overridden with -.B constructor-noauth: -) The subnet(s) are also used to define in-addr.arpa and +specified subnets. As alternative to directly specifying the subnets, it's possible to +give the name of an interface, in which case the subnets implied by +that interface's configured addresses and netmask/prefix-length are +used; this is useful when using constructed DHCP ranges as the actual +address is dynamic and not known when configuring dnsmasq. The +interface addresses may be confined to only IPv6 addresses using +/6 or to only IPv4 using /4. This is useful when +an interface has dynamically determined global IPv6 addresses which should +appear in the zone, but RFC1918 IPv4 addresses which should not. +Interface-name and address-literal subnet specifications may be used +freely in the same --auth-zone declaration. + +The subnet(s) are also used to define in-addr.arpa and ipv6.arpa domains which are served for reverse-DNS queries. If not specified, the prefix length defaults to 24 for IPv4 and 64 for IPv6. For IPv4 subnets, the prefix length should be have the value 8, 16 or 24 @@ -680,12 +689,6 @@ then the address can be simply :: .B --dhcp-range=::,constructor:eth0 -There is a variant of the constructor: syntax using the keyword -.B constructor-noauth. -See -.B --auth-zone -for an explanation of this. - The optional .B set: sets an alphanumeric label which marks this network so that @@ -1959,18 +1962,20 @@ IPv4 and IPv6 addresses from /etc/hosts (and .B --addn-hosts ) and .B --host-record +and +.B --interface-name provided the address falls into one of the subnets specified in the .B --auth-zone. -.PP -Addresses specified by -.B --interface-name. -In this case, the address is not contrained to a subnet from -.B --auth-zone. - .PP Addresses of DHCP leases, provided the address falls into one of the subnets specified in the +.B --auth-zone. +(If contructed DHCP ranges are is use, which depend on the address dynamically +assigned to an interface, then the form of .B --auth-zone -OR a constructed DHCP range. In the default mode, where a DHCP lease +which defines subnets by the dynamic address of an interface should +be used to ensure this condition is met.) +.PP +In the default mode, where a DHCP lease has an unqualified name, and possibly a qualified name constructed using .B --domain diff --git a/src/auth.c b/src/auth.c index 4a1075b..e7168c2 100644 --- a/src/auth.c +++ b/src/auth.c @@ -18,27 +18,26 @@ #ifdef HAVE_AUTH -static struct subnet *filter_zone(struct auth_zone *zone, int flag, struct all_addr *addr_u) +static struct addrlist *filter_zone(struct auth_zone *zone, int flag, struct all_addr *addr_u) { - struct subnet *subnet; + struct addrlist *subnet; for (subnet = zone->subnet; subnet; subnet = subnet->next) { - if (subnet->is6 && (flag & F_IPV4)) - continue; - - if (!subnet->is6) + if (!(subnet->flags & ADDRLIST_IPV6)) { - struct in_addr addr = addr_u->addr.addr4; - struct in_addr mask; + struct in_addr netmask, addr = addr_u->addr.addr4; + + if (!(flag & F_IPV4)) + continue; - mask.s_addr = htonl(~((1 << (32 - subnet->prefixlen)) - 1)); + netmask.s_addr = htonl(~((1 << (32 - subnet->prefixlen)) - 1)); - if (is_same_net(addr, subnet->addr4, mask)) + if (is_same_net(addr, subnet->addr.addr.addr4, netmask)) return subnet; } #ifdef HAVE_IPV6 - else if (is_same_net6(&(addr_u->addr.addr6), &subnet->addr6, subnet->prefixlen)) + else if (is_same_net6(&(addr_u->addr.addr6), &subnet->addr.addr.addr6, subnet->prefixlen)) return subnet; #endif @@ -46,22 +45,6 @@ static struct subnet *filter_zone(struct auth_zone *zone, int flag, struct all_a return NULL; } -static int filter_constructed_dhcp(struct auth_zone *zone, int flag, struct all_addr *addr_u) -{ -#ifdef HAVE_DHCP6 - struct dhcp_context *context; - - if (flag & F_IPV6) - for (context = daemon->dhcp6; context; context = context->next) - if ((context->flags & CONTEXT_CONSTRUCTED) && - !(context->flags & CONTEXT_NOAUTH) && - is_same_net6(&(addr_u->addr.addr6), &context->start6, context->prefix)) - return 1; -#endif - - return filter_zone(zone, flag, addr_u) != NULL; -} - int in_zone(struct auth_zone *zone, char *name, char **cut) { size_t namelen = strlen(name); @@ -99,7 +82,7 @@ size_t answer_auth(struct dns_header *header, char *limit, size_t qlen, time_t n struct crec *crecp; int auth = !local_query, trunc = 0, nxdomain = 1, soa = 0, ns = 0, axfr = 0; struct auth_zone *zone = NULL; - struct subnet *subnet = NULL; + struct addrlist *subnet = NULL; char *cut; struct mx_srv_record *rec, *move, **up; struct txt_record *txt; @@ -164,8 +147,8 @@ size_t answer_auth(struct dns_header *header, char *limit, size_t qlen, time_t n { struct addrlist *addrlist; - for (addrlist = intr->addr4; addrlist; addrlist = addrlist->next) - if (addr.addr.addr4.s_addr == addrlist->addr.addr.addr4.s_addr) + for (addrlist = intr->addr; addrlist; addrlist = addrlist->next) + if (!(addrlist->flags & ADDRLIST_IPV6) && addr.addr.addr4.s_addr == addrlist->addr.addr.addr4.s_addr) break; if (addrlist) @@ -180,8 +163,8 @@ size_t answer_auth(struct dns_header *header, char *limit, size_t qlen, time_t n { struct addrlist *addrlist; - for (addrlist = intr->addr6; addrlist; addrlist = addrlist->next) - if (IN6_ARE_ADDR_EQUAL(&addr.addr.addr6, &addrlist->addr.addr.addr6)) + for (addrlist = intr->addr; addrlist; addrlist = addrlist->next) + if ((addrlist->flags & ADDRLIST_IPV6) && IN6_ARE_ADDR_EQUAL(&addr.addr.addr6, &addrlist->addr.addr.addr6)) break; if (addrlist) @@ -362,16 +345,12 @@ size_t answer_auth(struct dns_header *header, char *limit, size_t qlen, time_t n { struct addrlist *addrlist; - addrlist = intr->addr4; -#ifdef HAVE_IPV6 - if (qtype == T_AAAA) - addrlist = intr->addr6; -#endif nxdomain = 0; if (flag) - for (; addrlist; addrlist = addrlist->next) - if (local_query || filter_constructed_dhcp(zone, flag, &addrlist->addr)) + for (addrlist = intr->addr; addrlist; addrlist = addrlist->next) + if (((addrlist->flags & ADDRLIST_IPV6) ? T_AAAA : T_A) == qtype && + (local_query || filter_zone(zone, flag, &addrlist->addr))) { found = 1; log_query(F_FORWARD | F_CONFIG | flag, name, &addrlist->addr, NULL); @@ -468,7 +447,7 @@ size_t answer_auth(struct dns_header *header, char *limit, size_t qlen, time_t n { nxdomain = 0; if ((crecp->flags & flag) && - (local_query || filter_constructed_dhcp(zone, flag, &(crecp->addr.addr)))) + (local_query || filter_zone(zone, flag, &(crecp->addr.addr)))) { *cut = '.'; /* restore domain part */ log_query(crecp->flags, name, &crecp->addr.addr, record_source(crecp->uid)); @@ -491,7 +470,7 @@ size_t answer_auth(struct dns_header *header, char *limit, size_t qlen, time_t n do { nxdomain = 0; - if ((crecp->flags & flag) && (local_query || filter_constructed_dhcp(zone, flag, &(crecp->addr.addr)))) + if ((crecp->flags & flag) && (local_query || filter_zone(zone, flag, &(crecp->addr.addr)))) { log_query(crecp->flags, name, &crecp->addr.addr, record_source(crecp->uid)); found = 1; @@ -522,9 +501,9 @@ size_t answer_auth(struct dns_header *header, char *limit, size_t qlen, time_t n authname = name; - if (!subnet->is6) + if (!(subnet->flags & ADDRLIST_IPV6)) { - in_addr_t a = ntohl(subnet->addr4.s_addr) >> 8; + in_addr_t a = ntohl(subnet->addr.addr.addr4.s_addr) >> 8; char *p = name; if (subnet->prefixlen >= 24) @@ -544,7 +523,7 @@ size_t answer_auth(struct dns_header *header, char *limit, size_t qlen, time_t n for (i = subnet->prefixlen-1; i >= 0; i -= 4) { - int dig = ((unsigned char *)&subnet->addr6)[i>>3]; + int dig = ((unsigned char *)&subnet->addr.addr.addr6)[i>>3]; p += sprintf(p, "%.1x.", (i>>2) & 1 ? dig & 15 : dig >> 4); } p += sprintf(p, "ip6.arpa"); @@ -680,15 +659,17 @@ size_t answer_auth(struct dns_header *header, char *limit, size_t qlen, time_t n if (cut) *cut = 0; - for (addrlist = intr->addr4; addrlist; addrlist = addrlist->next) - if ((local_query || filter_constructed_dhcp(zone, F_IPV4, &addrlist->addr)) && + for (addrlist = intr->addr; addrlist; addrlist = addrlist->next) + if (!(subnet->flags & ADDRLIST_IPV6) && + (local_query || filter_zone(zone, F_IPV4, &addrlist->addr)) && add_resource_record(header, limit, &trunc, -axfroffset, &ansp, daemon->auth_ttl, NULL, T_A, C_IN, "4", cut ? intr->name : NULL, &addrlist->addr)) anscount++; #ifdef HAVE_IPV6 - for (addrlist = intr->addr6; addrlist; addrlist = addrlist->next) - if ((local_query || filter_constructed_dhcp(zone, F_IPV6, &addrlist->addr)) && + for (addrlist = intr->addr; addrlist; addrlist = addrlist->next) + if ((subnet->flags & ADDRLIST_IPV6) && + (local_query || filter_zone(zone, F_IPV6, &addrlist->addr)) && add_resource_record(header, limit, &trunc, -axfroffset, &ansp, daemon->auth_ttl, NULL, T_AAAA, C_IN, "6", cut ? intr->name : NULL, &addrlist->addr)) anscount++; @@ -729,7 +710,7 @@ size_t answer_auth(struct dns_header *header, char *limit, size_t qlen, time_t n { char *cache_name = cache_get_name(crecp); if (!strchr(cache_name, '.') && - (local_query || filter_constructed_dhcp(zone, (crecp->flags & (F_IPV6 | F_IPV4)), &(crecp->addr.addr)))) + (local_query || filter_zone(zone, (crecp->flags & (F_IPV6 | F_IPV4)), &(crecp->addr.addr)))) { qtype = T_A; #ifdef HAVE_IPV6 @@ -747,7 +728,7 @@ size_t answer_auth(struct dns_header *header, char *limit, size_t qlen, time_t n { strcpy(name, cache_get_name(crecp)); if (in_zone(zone, name, &cut) && - (local_query || filter_constructed_dhcp(zone, (crecp->flags & (F_IPV6 | F_IPV4)), &(crecp->addr.addr)))) + (local_query || filter_zone(zone, (crecp->flags & (F_IPV6 | F_IPV4)), &(crecp->addr.addr)))) { qtype = T_A; #ifdef HAVE_IPV6 diff --git a/src/dnsmasq.h b/src/dnsmasq.h index 98266c6..c316b50 100644 --- a/src/dnsmasq.h +++ b/src/dnsmasq.h @@ -280,18 +280,28 @@ struct ptr_record { struct cname { char *alias, *target; struct cname *next; +}; + +#define ADDRLIST_LITERAL 1 +#define ADDRLIST_IPV6 2 + +struct addrlist { + struct all_addr addr; + int flags, prefixlen; + struct addrlist *next; }; +#define AUTH6 1 +#define AUTH4 2 + struct auth_zone { char *domain; - struct subnet { - int is6, prefixlen; - struct in_addr addr4; -#ifdef HAVE_IPV6 - struct in6_addr addr6; -#endif - struct subnet *next; - } *subnet; + struct auth_name_list { + char *name; + int flags; + struct auth_name_list *next; + } *interface_names; + struct addrlist *subnet; struct auth_zone *next; }; @@ -311,13 +321,7 @@ struct host_record { struct interface_name { char *name; /* domain name */ char *intr; /* interface name */ - struct addrlist { - struct all_addr addr; - struct addrlist *next; - } *addr4; -#ifdef HAVE_IPV6 - struct addrlist *addr6; -#endif + struct addrlist *addr; struct interface_name *next; }; @@ -748,9 +752,8 @@ struct dhcp_context { #define CONTEXT_RA (1u<<13) #define CONTEXT_CONF_USED (1u<<14) #define CONTEXT_USED (1u<<15) -#define CONTEXT_NOAUTH (1u<<16) -#define CONTEXT_OLD (1u<<17) -#define CONTEXT_V6 (1u<<18) +#define CONTEXT_OLD (1u<<16) +#define CONTEXT_V6 (1u<<17) struct ping_result { diff --git a/src/network.c b/src/network.c index de5d9f2..3515a52 100644 --- a/src/network.c +++ b/src/network.c @@ -239,7 +239,7 @@ struct iface_param { }; static int iface_allowed(struct iface_param *param, int if_index, char *label, - union mysockaddr *addr, struct in_addr netmask, int dad) + union mysockaddr *addr, struct in_addr netmask, int prefixlen, int dad) { struct irec *iface; int mtu = 0, loopback; @@ -267,15 +267,67 @@ static int iface_allowed(struct iface_param *param, int if_index, char *label, label = ifr.ifr_name; - /* Update addresses from interface_names. These are a set independent - of the set we're listening on. */ #ifdef HAVE_IPV6 if (addr->sa.sa_family != AF_INET6 || !IN6_IS_ADDR_LINKLOCAL(&addr->in6.sin6_addr)) #endif { struct interface_name *int_name; struct addrlist *al; +#ifdef HAVE_AUTH + struct auth_zone *zone; + struct auth_name_list *name; + /* Find subnets in auth_zones */ + for (zone = daemon->auth_zones; zone; zone = zone->next) + for (name = zone->interface_names; name; name = name->next) + if (wildcard_match(name->name, label)) + { + if (addr->sa.sa_family == AF_INET && (name->flags & AUTH4)) + { + if (param->spare) + { + al = param->spare; + param->spare = al->next; + } + else + al = whine_malloc(sizeof(struct addrlist)); + + if (al) + { + al->next = zone->subnet; + zone->subnet = al; + al->prefixlen = prefixlen; + al->addr.addr.addr4 = addr->in.sin_addr; + al->flags = 0; + } + } + +#ifdef HAVE_IPV6 + if (addr->sa.sa_family == AF_INET6 && (name->flags & AUTH6)) + { + if (param->spare) + { + al = param->spare; + param->spare = al->next; + } + else + al = whine_malloc(sizeof(struct addrlist)); + + if (al) + { + al->next = zone->subnet; + zone->subnet = al; + al->prefixlen = prefixlen;al->addr.addr.addr6 = addr->in6.sin6_addr; + al->flags = ADDRLIST_IPV6; + } + } +#endif + + } +#endif + + /* Update addresses from interface_names. These are a set independent + of the set we're listening on. */ for (int_name = daemon->int_names; int_name; int_name = int_name->next) if (strncmp(label, int_name->intr, IF_NAMESIZE) == 0) { @@ -289,18 +341,19 @@ static int iface_allowed(struct iface_param *param, int if_index, char *label, if (al) { + al->next = int_name->addr; + int_name->addr = al; + if (addr->sa.sa_family == AF_INET) { al->addr.addr.addr4 = addr->in.sin_addr; - al->next = int_name->addr4; - int_name->addr4 = al; + al->flags = 0; } #ifdef HAVE_IPV6 else { al->addr.addr.addr6 = addr->in6.sin6_addr; - al->next = int_name->addr6; - int_name->addr6 = al; + al->flags = ADDRLIST_IPV6; } #endif } @@ -413,7 +466,6 @@ static int iface_allowed_v6(struct in6_addr *local, int prefix, struct in_addr netmask; /* dummy */ netmask.s_addr = 0; - (void)prefix; /* warning */ (void)scope; /* warning */ (void)preferred; (void)valid; @@ -427,7 +479,7 @@ static int iface_allowed_v6(struct in6_addr *local, int prefix, addr.in6.sin6_port = htons(daemon->port); addr.in6.sin6_scope_id = if_index; - return iface_allowed((struct iface_param *)vparam, if_index, NULL, &addr, netmask, !!(flags & IFACE_TENTATIVE)); + return iface_allowed((struct iface_param *)vparam, if_index, NULL, &addr, netmask, prefix, !!(flags & IFACE_TENTATIVE)); } #endif @@ -435,6 +487,7 @@ static int iface_allowed_v4(struct in_addr local, int if_index, char *label, struct in_addr netmask, struct in_addr broadcast, void *vparam) { union mysockaddr addr; + int prefix, bit; memset(&addr, 0, sizeof(addr)); #ifdef HAVE_SOCKADDR_SA_LEN @@ -445,7 +498,10 @@ static int iface_allowed_v4(struct in_addr local, int if_index, char *label, addr.in.sin_addr = local; addr.in.sin_port = htons(daemon->port); - return iface_allowed((struct iface_param *)vparam, if_index, label, &addr, netmask, 0); + /* determine prefix length from netmask */ + for (prefix = 32, bit = 1; (bit & ntohl(netmask.s_addr)) == 0 && prefix != 0; bit = bit << 1, prefix--); + + return iface_allowed((struct iface_param *)vparam, if_index, label, &addr, netmask, prefix, 0); } int enumerate_interfaces(int reset) @@ -456,7 +512,10 @@ int enumerate_interfaces(int reset) int errsave, ret = 1; struct addrlist *addr, *tmp; struct interface_name *intname; - +#ifdef HAVE_AUTH + struct auth_zone *zone; +#endif + /* Do this max once per select cycle - also inhibits netlink socket use in TCP child processes. */ @@ -480,27 +539,38 @@ int enumerate_interfaces(int reset) /* remove addresses stored against interface_names */ for (intname = daemon->int_names; intname; intname = intname->next) { - for (addr = intname->addr4; addr; addr = tmp) + for (addr = intname->addr; addr; addr = tmp) { tmp = addr->next; addr->next = spare; spare = addr; } - intname->addr4 = NULL; - -#ifdef HAVE_IPV6 - for (addr = intname->addr6; addr; addr = tmp) - { - tmp = addr->next; - addr->next = spare; - spare = addr; - } - - intname->addr6 = NULL; -#endif + intname->addr = NULL; } - + +#ifdef HAVE_AUTH + /* remove addresses stored against auth_zone subnets, but not + ones configured as address literals */ + for (zone = daemon->auth_zones; zone; zone = zone->next) + if (zone->interface_names) + { + struct addrlist **up; + for (up = &zone->subnet, addr = zone->subnet; addr; addr = tmp) + { + tmp = addr->next; + if (addr->flags & ADDRLIST_LITERAL) + up = &addr->next; + else + { + *up = addr->next; + addr->next = spare; + spare = addr; + } + } + } +#endif + param.spare = spare; #ifdef HAVE_IPV6 diff --git a/src/option.c b/src/option.c index 0ba2664..ae5fad5 100644 --- a/src/option.c +++ b/src/option.c @@ -1649,6 +1649,7 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma new = opt_malloc(sizeof(struct auth_zone)); new->domain = opt_string_alloc(arg); new->subnet = NULL; + new->interface_names = NULL; new->next = daemon->auth_zones; daemon->auth_zones = new; @@ -1656,10 +1657,8 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma { int prefixlen = 0; char *prefix; - struct subnet *subnet = opt_malloc(sizeof(struct subnet)); - - subnet->next = new->subnet; - new->subnet = subnet; + struct addrlist *subnet = NULL; + struct all_addr addr; comma = split(arg); prefix = split_chr(arg, '/'); @@ -1667,24 +1666,50 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma if (prefix && !atoi_check(prefix, &prefixlen)) ret_err(gen_err); - if (inet_pton(AF_INET, arg, &subnet->addr4)) + if (inet_pton(AF_INET, arg, &addr.addr.addr4)) { + subnet = opt_malloc(sizeof(struct addrlist)); subnet->prefixlen = (prefixlen == 0) ? 24 : prefixlen; - subnet->is6 = 0; + subnet->flags = ADDRLIST_LITERAL; } #ifdef HAVE_IPV6 - else if (inet_pton(AF_INET6, arg, &subnet->addr6)) + else if (inet_pton(AF_INET6, arg, &addr.addr.addr6)) { + subnet = opt_malloc(sizeof(struct addrlist)); subnet->prefixlen = (prefixlen == 0) ? 64 : prefixlen; - subnet->is6 = 1; + subnet->flags = ADDRLIST_LITERAL | ADDRLIST_IPV6; } #endif - else - ret_err(gen_err); + else + { + struct auth_name_list *name = opt_malloc(sizeof(struct auth_name_list)); + name->name = opt_string_alloc(arg); + name->flags = AUTH4 | AUTH6; + name->next = new->interface_names; + new->interface_names = name; + if (prefix) + { + if (prefixlen == 4) + name->flags &= ~AUTH6; +#ifdef HAVE_IPV6 + else if (prefixlen == 6) + name->flags &= ~AUTH4; +#endif + else + ret_err(gen_err); + } + } + + if (subnet) + { + subnet->addr = addr; + subnet->next = new->subnet; + new->subnet = subnet; + } } break; } - + case LOPT_AUTHSOA: /* --auth-soa */ comma = split(arg); daemon->soa_sn = (u32)atoi(arg); @@ -2464,11 +2489,6 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma new->template_interface = opt_string_alloc(a[leasepos] + 12); new->flags |= CONTEXT_TEMPLATE; } - else if (strstr(a[leasepos], "constructor-noauth:") == a[leasepos]) - { - new->template_interface = opt_string_alloc(a[leasepos] + 19); - new->flags |= CONTEXT_TEMPLATE | CONTEXT_NOAUTH; - } else break; } @@ -3335,10 +3355,8 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma new = opt_malloc(sizeof(struct interface_name)); new->next = NULL; - new->addr4 = NULL; -#ifdef HAVE_IPV6 - new->addr6 = NULL; -#endif + new->addr = NULL; + /* Add to the end of the list, so that first name of an interface is used for PTR lookups. */ for (up = &daemon->int_names; *up; up = &((*up)->next)); diff --git a/src/rfc1035.c b/src/rfc1035.c index 64ad5bf..9fdba05 100644 --- a/src/rfc1035.c +++ b/src/rfc1035.c @@ -1550,8 +1550,8 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen, { struct addrlist *addrlist; - for (addrlist = intr->addr4; addrlist; addrlist = addrlist->next) - if (addr.addr.addr4.s_addr == addrlist->addr.addr.addr4.s_addr) + for (addrlist = intr->addr; addrlist; addrlist = addrlist->next) + if (!(addrlist->flags & ADDRLIST_IPV6) && addr.addr.addr4.s_addr == addrlist->addr.addr.addr4.s_addr) break; if (addrlist) @@ -1566,8 +1566,8 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen, { struct addrlist *addrlist; - for (addrlist = intr->addr6; addrlist; addrlist = addrlist->next) - if (IN6_ARE_ADDR_EQUAL(&addr.addr.addr6, &addrlist->addr.addr.addr6)) + for (addrlist = intr->addr; addrlist; addrlist = addrlist->next) + if ((addrlist->flags & ADDRLIST_IPV6) && IN6_ARE_ADDR_EQUAL(&addr.addr.addr6, &addrlist->addr.addr.addr6)) break; if (addrlist) @@ -1732,26 +1732,22 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen, for (intr = daemon->int_names; intr; intr = intr->next) if (hostname_isequal(name, intr->name)) { - addrlist = intr->addr4; -#ifdef HAVE_IPV6 - if (type == T_AAAA) - addrlist = intr->addr6; -#endif ans = 1; if (!dryrun) { - if (addrlist) - { - gotit = 1; - for (; addrlist; addrlist = addrlist->next) - { - log_query(F_FORWARD | F_CONFIG | flag, name, &addrlist->addr, NULL); - if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, - daemon->local_ttl, NULL, type, C_IN, - type == T_A ? "4" : "6", &addrlist->addr)) - anscount++; - } - } + + for (addrlist = intr->addr; addrlist; addrlist = addrlist->next) +#ifdef HAVE_IPV6 + if (((addrlist->flags & ADDRLIST_IPV6) ? T_AAAA : T_A) == type) +#endif + { + gotit = 1; + log_query(F_FORWARD | F_CONFIG | flag, name, &addrlist->addr, NULL); + if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, + daemon->local_ttl, NULL, type, C_IN, + type == T_A ? "4" : "6", &addrlist->addr)) + anscount++; + } } } From 32b4e4cb7c48638c62dc0865dbf86e584e620a6b Mon Sep 17 00:00:00 2001 From: Simon Kelley Date: Thu, 14 Nov 2013 10:36:55 +0000 Subject: [PATCH 9/9] Auth-DNS manpage update. --- man/dnsmasq.8 | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/man/dnsmasq.8 b/man/dnsmasq.8 index deb3eb6..eb7f76f 100644 --- a/man/dnsmasq.8 +++ b/man/dnsmasq.8 @@ -1902,9 +1902,13 @@ Something like: .nf .B auth-server=our.zone.com,eth0 .B interface-name=our.zone.com,eth0 -.B auth-zone=our.zone.com,1.2.3.0/24 +.B auth-zone=our.zone.com,1.2.3.0/24,eth0 .fi +(The "eth0" argument in auth-zone adds the subnet containing eth0's +dynamic address to the zone, so that the interface-name returns the +address in outside queries.) + Our final configuration builds on that above, but also adds a secondary DNS server. This is another DNS server which learns the DNS data for the zone by doing zones transfer, and acts as a backup should