diff options
Diffstat (limited to 'src/auth.c')
-rw-r--r-- | src/auth.c | 160 |
1 files changed, 110 insertions, 50 deletions
@@ -1,4 +1,4 @@ -/* dnsmasq is Copyright (c) 2000-2015 Simon Kelley +/* dnsmasq is Copyright (c) 2000-2018 Simon Kelley This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -18,36 +18,53 @@ #ifdef HAVE_AUTH -static struct addrlist *find_subnet(struct auth_zone *zone, int flag, struct all_addr *addr_u) +static struct addrlist *find_addrlist(struct addrlist *list, int flag, struct all_addr *addr_u) { - struct addrlist *subnet; - - for (subnet = zone->subnet; subnet; subnet = subnet->next) - { - if (!(subnet->flags & ADDRLIST_IPV6)) - { - struct in_addr netmask, addr = addr_u->addr.addr4; - - if (!(flag & F_IPV4)) - continue; - - netmask.s_addr = htonl(~(in_addr_t)0 << (32 - subnet->prefixlen)); - - if (is_same_net(addr, subnet->addr.addr.addr4, netmask)) - return subnet; - } + do { + if (!(list->flags & ADDRLIST_IPV6)) + { + struct in_addr netmask, addr = addr_u->addr.addr4; + + if (!(flag & F_IPV4)) + continue; + + netmask.s_addr = htonl(~(in_addr_t)0 << (32 - list->prefixlen)); + + if (is_same_net(addr, list->addr.addr.addr4, netmask)) + return list; + } #ifdef HAVE_IPV6 - else if (is_same_net6(&(addr_u->addr.addr6), &subnet->addr.addr.addr6, subnet->prefixlen)) - return subnet; + else if (is_same_net6(&(addr_u->addr.addr6), &list->addr.addr.addr6, list->prefixlen)) + return list; #endif - - } + + } while ((list = list->next)); + return NULL; } +static struct addrlist *find_subnet(struct auth_zone *zone, int flag, struct all_addr *addr_u) +{ + if (!zone->subnet) + return NULL; + + return find_addrlist(zone->subnet, flag, addr_u); +} + +static struct addrlist *find_exclude(struct auth_zone *zone, int flag, struct all_addr *addr_u) +{ + if (!zone->exclude) + return NULL; + + return find_addrlist(zone->exclude, flag, addr_u); +} + static int filter_zone(struct auth_zone *zone, int flag, struct all_addr *addr_u) { - /* No zones specified, no filter */ + if (find_exclude(zone, flag, addr_u)) + return 0; + + /* No subnets specified, no filter */ if (!zone->subnet) return 1; @@ -81,7 +98,8 @@ int in_zone(struct auth_zone *zone, char *name, char **cut) } -size_t answer_auth(struct dns_header *header, char *limit, size_t qlen, time_t now, union mysockaddr *peer_addr, int local_query) +size_t answer_auth(struct dns_header *header, char *limit, size_t qlen, time_t now, union mysockaddr *peer_addr, + int local_query, int do_bit, int have_pseudoheader) { char *name = daemon->namebuff; unsigned char *p, *ansp; @@ -98,7 +116,8 @@ size_t answer_auth(struct dns_header *header, char *limit, size_t qlen, time_t n struct interface_name *intr; struct naptr *na; struct all_addr addr; - struct cname *a; + struct cname *a, *candidate; + unsigned int wclen; if (ntohs(header->qdcount) == 0 || OPCODE(header) != QUERY ) return 0; @@ -114,6 +133,7 @@ size_t answer_auth(struct dns_header *header, char *limit, size_t qlen, time_t n { unsigned short flag = 0; int found = 0; + int cname_wildcard = 0; /* save pointer to name for copying into answers */ nameoffset = p - (unsigned char *)header; @@ -388,25 +408,6 @@ size_t answer_auth(struct dns_header *header, char *limit, size_t qlen, time_t n } } - for (a = daemon->cnames; a; a = a->next) - if (hostname_isequal(name, a->alias) ) - { - log_query(F_CONFIG | F_CNAME, name, NULL, NULL); - strcpy(name, a->target); - if (!strchr(name, '.')) - { - strcat(name, "."); - strcat(name, zone->domain); - } - found = 1; - if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, - daemon->auth_ttl, &nameoffset, - T_CNAME, C_IN, "d", name)) - anscount++; - - goto cname_restart; - } - if (!cut) { nxdomain = 0; @@ -512,8 +513,62 @@ size_t answer_auth(struct dns_header *header, char *limit, size_t qlen, time_t n } while ((crecp = cache_find_by_name(crecp, name, now, F_IPV4 | F_IPV6))); } - if (!found) - log_query(flag | F_NEG | (nxdomain ? F_NXDOMAIN : 0) | F_FORWARD | F_AUTH, name, NULL, NULL); + /* Only supply CNAME if no record for any type is known. */ + if (nxdomain) + { + /* Check for possible wildcard match against *.domain + return length of match, to get longest. + Note that if return length of wildcard section, so + we match b.simon to _both_ *.simon and b.simon + but return a longer (better) match to b.simon. + */ + for (wclen = 0, candidate = NULL, a = daemon->cnames; a; a = a->next) + if (a->alias[0] == '*') + { + char *test = name; + + while ((test = strchr(test+1, '.'))) + { + if (hostname_isequal(test, &(a->alias[1]))) + { + if (strlen(test) > wclen && !cname_wildcard) + { + wclen = strlen(test); + candidate = a; + cname_wildcard = 1; + } + break; + } + } + + } + else if (hostname_isequal(a->alias, name) && strlen(a->alias) > wclen) + { + /* Simple case, no wildcard */ + wclen = strlen(a->alias); + candidate = a; + } + + if (candidate) + { + log_query(F_CONFIG | F_CNAME, name, NULL, NULL); + strcpy(name, candidate->target); + if (!strchr(name, '.')) + { + strcat(name, "."); + strcat(name, zone->domain); + } + found = 1; + if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, + daemon->auth_ttl, &nameoffset, + T_CNAME, C_IN, "d", name)) + anscount++; + + goto cname_restart; + } + + log_query(flag | F_NEG | (nxdomain ? F_NXDOMAIN : 0) | F_FORWARD | F_AUTH, name, NULL, NULL); + } } @@ -537,12 +592,12 @@ size_t answer_auth(struct dns_header *header, char *limit, size_t qlen, time_t n char *p = name; if (subnet->prefixlen >= 24) - p += sprintf(p, "%d.", a & 0xff); + p += sprintf(p, "%u.", a & 0xff); a = a >> 8; if (subnet->prefixlen >= 16 ) - p += sprintf(p, "%d.", a & 0xff); + p += sprintf(p, "%u.", a & 0xff); a = a >> 8; - p += sprintf(p, "%d.in-addr.arpa", a & 0xff); + p += sprintf(p, "%u.in-addr.arpa", a & 0xff); } #ifdef HAVE_IPV6 @@ -805,7 +860,7 @@ size_t answer_auth(struct dns_header *header, char *limit, size_t qlen, time_t n header->hb4 &= ~HB4_RA; } - /* authoritive */ + /* authoritative */ if (auth) header->hb3 |= HB3_AA; @@ -820,6 +875,11 @@ size_t answer_auth(struct dns_header *header, char *limit, size_t qlen, time_t n header->ancount = htons(anscount); header->nscount = htons(authcount); header->arcount = htons(0); + + /* Advertise our packet size limit in our reply */ + if (have_pseudoheader) + return add_pseudoheader(header, ansp - (unsigned char *)header, (unsigned char *)limit, daemon->edns_pktsz, 0, NULL, 0, do_bit, 0); + return ansp - (unsigned char *)header; } |