First cut at zone transfer.

This commit is contained in:
Simon Kelley
2012-12-07 11:50:41 +00:00
parent aa79235194
commit b75e936372
5 changed files with 163 additions and 46 deletions

View File

@@ -16,6 +16,7 @@
#include "dnsmasq.h"
static struct subnet *filter_zone(struct auth_zone *zone, int flag, struct all_addr *addr_u)
{
struct subnet *subnet;
@@ -44,6 +45,31 @@ static struct subnet *filter_zone(struct auth_zone *zone, int flag, struct all_a
return NULL;
}
static int in_zone(struct auth_zone *zone, char *name, char **cut)
{
size_t namelen = strlen(name);
size_t domainlen = strlen(zone->domain);
if (cut)
*cut = NULL;
if (namelen >= domainlen &&
hostname_isequal(zone->domain, &name[namelen - domainlen]))
{
if (namelen == domainlen)
return 1;
if (name[namelen - domainlen - 1] == '.')
{
if (cut)
*cut = &name[namelen - domainlen - 1];
return 1;
}
}
return 0;
}
size_t answer_auth(struct dns_header *header, char *limit, size_t qlen, time_t now)
@@ -51,12 +77,13 @@ size_t answer_auth(struct dns_header *header, char *limit, size_t qlen, time_t n
char *name = daemon->namebuff;
unsigned char *p, *ansp;
int qtype, qclass;
unsigned int nameoffset;
int nameoffset, axfroffset = 0;
int q, anscount = 0, authcount = 0;
struct crec *crecp;
int auth = 1, trunc = 0, nxdomain = 1, soa = 0, ns = 0;
int auth = 1, trunc = 0, nxdomain = 1, soa = 0, ns = 0, axfr = 0;
struct auth_zone *zone = NULL;
struct subnet *subnet = NULL;
char *cut;
if (ntohs(header->qdcount) == 0 || OPCODE(header) != QUERY )
return 0;
@@ -70,7 +97,6 @@ size_t answer_auth(struct dns_header *header, char *limit, size_t qlen, time_t n
for (q = ntohs(header->qdcount); q != 0; q--)
{
size_t domainlen, namelen;
unsigned short flag = 0;
int found = 0;
struct mx_srv_record *rec, *move, **up;
@@ -108,8 +134,6 @@ size_t answer_auth(struct dns_header *header, char *limit, size_t qlen, time_t n
continue;
}
domainlen = strlen(zone->domain);
if (flag == F_IPV4)
{
for (intr = daemon->int_names; intr; intr = intr->next)
@@ -123,10 +147,7 @@ size_t answer_auth(struct dns_header *header, char *limit, size_t qlen, time_t n
if (intr)
{
namelen = strlen(intr->name);
if (namelen >= domainlen && hostname_isequal(zone->domain, &intr->name[namelen - domainlen]) &&
(namelen == domainlen || intr->name[namelen - domainlen - 1] == '.'))
if (in_zone(zone, intr->name, NULL))
{
found = 1;
log_query(F_IPV4 | F_REVERSE | F_CONFIG, intr->name, &addr, NULL);
@@ -158,17 +179,8 @@ size_t answer_auth(struct dns_header *header, char *limit, size_t qlen, time_t n
T_PTR, C_IN, "d", name))
anscount++;
}
else if (crecp->flags & (F_DHCP | F_HOSTS))
else if (crecp->flags & (F_DHCP | F_HOSTS) && in_zone(zone, name, NULL))
{
namelen = strlen(name);
if (namelen > domainlen + 1 &&
name[namelen - domainlen - 1] != '.')
continue;
if (namelen < domainlen ||
!hostname_isequal(zone->domain, &name[namelen - domainlen]))
continue; /* wrong domain */
log_query(crecp->flags & ~F_FORWARD, name, &addr, record_source(crecp->uid));
found = 1;
if (add_resource_record(header, limit, &trunc, nameoffset, &ansp,
@@ -188,16 +200,9 @@ size_t answer_auth(struct dns_header *header, char *limit, size_t qlen, time_t n
}
cname_restart:
namelen = strlen(name);
for (zone = daemon->auth_zones; zone; zone = zone->next)
{
domainlen = strlen(zone->domain);
if (namelen >= domainlen &&
hostname_isequal(zone->domain, &name[namelen - domainlen]) &&
(namelen == domainlen || name[namelen - domainlen - 1] == '.'))
if (in_zone(zone, name, &cut))
break;
}
if (!zone)
{
@@ -340,14 +345,23 @@ size_t answer_auth(struct dns_header *header, char *limit, size_t qlen, time_t n
flag = F_IPV6;
#endif
if (qtype == T_SOA && namelen == domainlen)
if (qtype == T_SOA && !cut)
{
soa = 1; /* inhibits auth section */
found = 1;
log_query(F_RRNAME | F_AUTH, zone->domain, NULL, "<SOA>");
}
if (qtype == T_NS && namelen == domainlen)
if (qtype == T_AXFR && !cut)
{
soa = 1; /* inhibits auth section */
axfr = 1;
found = 1;
axfroffset = nameoffset;
log_query(F_RRNAME | F_AUTH, zone->domain, NULL, "<AXFR>");
}
if (qtype == T_NS && !cut)
{
ns = 1; /* inhibits auth section */
found = 1;
@@ -355,9 +369,9 @@ size_t answer_auth(struct dns_header *header, char *limit, size_t qlen, time_t n
}
if (!option_bool(OPT_DHCP_FQDN) && namelen > domainlen + 1)
if (!option_bool(OPT_DHCP_FQDN) && cut)
{
name[namelen - domainlen - 1] = 0; /* remove domain part */
*cut = 0; /* remove domain part */
if (!strchr(name, '.') && (crecp = cache_find_by_name(NULL, name, now, F_IPV4 | F_IPV6)))
{
@@ -367,9 +381,9 @@ size_t answer_auth(struct dns_header *header, char *limit, size_t qlen, time_t n
nxdomain = 0;
if ((crecp->flags & flag) && filter_zone(zone, flag, &(crecp->addr.addr)))
{
name[namelen - domainlen - 1] = '.'; /* restore domain part */
*cut = '.'; /* restore domain part */
log_query(crecp->flags, name, &crecp->addr.addr, record_source(crecp->uid));
name[namelen - domainlen - 1] = 0; /* remove domain part */
*cut = 0; /* remove domain part */
found = 1;
if (add_resource_record(header, limit, &trunc, nameoffset, &ansp,
daemon->auth_ttl, NULL, qtype, C_IN,
@@ -379,7 +393,7 @@ 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)));
}
name[namelen - domainlen - 1] = '.'; /* restore domain part */
*cut = '.'; /* restore domain part */
}
if ((crecp = cache_find_by_name(NULL, name, now, F_IPV4 | F_IPV6)))
@@ -408,11 +422,16 @@ size_t answer_auth(struct dns_header *header, char *limit, size_t qlen, time_t n
/* Add auth section */
if (auth)
{
char *authname;
if (!subnet)
name = zone->domain;
authname = zone->domain;
else
{
/* handle NS and SOA for PTR records */
authname = name;
if (!subnet->is6)
{
in_addr_t a = ntohl(subnet->addr4.s_addr) >> 8;
@@ -447,7 +466,7 @@ size_t answer_auth(struct dns_header *header, char *limit, size_t qlen, time_t n
/* handle NS and SOA in auth section or for explicit queries */
if ((anscount != 0 || ns) &&
add_resource_record(header, limit, &trunc, 0, &ansp,
daemon->auth_ttl, NULL, T_NS, C_IN, "d", name, daemon->authserver))
daemon->auth_ttl, NULL, T_NS, C_IN, "d", authname, daemon->authserver))
{
if (ns)
anscount++;
@@ -458,7 +477,7 @@ size_t answer_auth(struct dns_header *header, char *limit, size_t qlen, time_t n
if ((anscount == 0 || soa) &&
add_resource_record(header, limit, &trunc, 0, &ansp,
daemon->auth_ttl, NULL, T_SOA, C_IN, "ddlllll",
name, daemon->authserver, daemon->hostmaster,
authname, daemon->authserver, daemon->hostmaster,
daemon->soa_sn, daemon->soa_refresh,
daemon->soa_retry, daemon->soa_expiry,
daemon->auth_ttl))
@@ -468,6 +487,74 @@ size_t answer_auth(struct dns_header *header, char *limit, size_t qlen, time_t n
else
authcount++;
}
if (axfr)
{
cache_enumerate(1);
while ((crecp = cache_enumerate(0)))
{
if ((crecp->flags & (F_IPV4 | F_IPV6)) &&
!(crecp->flags & (F_NEG | F_NXDOMAIN)) &&
(crecp->flags & F_FORWARD))
{
if ((crecp->flags & F_DHCP) && !option_bool(OPT_DHCP_FQDN))
{
char *cache_name = cache_get_name(crecp);
if (!strchr(cache_name, '.') && filter_zone(zone, (crecp->flags & (F_IPV6 | F_IPV4)), &(crecp->addr.addr)))
{
qtype = T_A;
#ifdef HAVE_IPV6
if (crecp->flags & F_IPV6)
qtype = T_AAAA;
#endif
if (add_resource_record(header, limit, &trunc, -axfroffset, &ansp,
daemon->auth_ttl, NULL, qtype, C_IN,
(crecp->flags & F_IPV4) ? "4" : "6", cache_name, &crecp->addr))
anscount++;
}
}
if ((crecp->flags & F_HOSTS) || (((crecp->flags & F_DHCP) && option_bool(OPT_DHCP_FQDN))))
{
strcpy(name, cache_get_name(crecp));
if (in_zone(zone, name, &cut) && filter_zone(zone, (crecp->flags & (F_IPV6 | F_IPV4)), &(crecp->addr.addr)))
{
qtype = T_A;
#ifdef HAVE_IPV6
if (crecp->flags & F_IPV6)
qtype = T_AAAA;
#endif
if (cut)
{
*cut = 0;
if (add_resource_record(header, limit, &trunc, -axfroffset, &ansp,
daemon->auth_ttl, NULL, qtype, C_IN,
(crecp->flags & F_IPV4) ? "4" : "6", name, &crecp->addr))
anscount++;
}
else
{
if (add_resource_record(header, limit, &trunc, axfroffset, &ansp,
daemon->auth_ttl, NULL, qtype, C_IN,
(crecp->flags & F_IPV4) ? "4" : "6", &crecp->addr))
anscount++;
}
}
}
}
}
/* repeat SOA as last record */
if (add_resource_record(header, limit, &trunc, axfroffset, &ansp,
daemon->auth_ttl, NULL, T_SOA, C_IN, "ddlllll",
daemon->authserver, daemon->hostmaster,
daemon->soa_sn, daemon->soa_refresh,
daemon->soa_retry, daemon->soa_expiry,
daemon->auth_ttl))
anscount++;
}
}
/* done all questions, set up header and return length of result */

View File

@@ -235,6 +235,29 @@ char *cache_get_name(struct crec *crecp)
return crecp->name.sname;
}
struct crec *cache_enumerate(int init)
{
static int bucket;
static struct crec *cache;
if (init)
{
bucket = 0;
cache = NULL;
}
else if (cache && cache->hash_next)
cache = cache->hash_next;
else
{
cache = NULL;
while (bucket < hash_size)
if ((cache = hash_table[bucket++]))
break;
}
return cache;
}
static int is_outdated_cname_pointer(struct crec *crecp)
{
if (!(crecp->flags & F_CNAME))

View File

@@ -52,6 +52,7 @@
#define T_OPT 41
#define T_TKEY 249
#define T_TSIG 250
#define T_AXFR 252
#define T_MAILB 253
#define T_ANY 255

View File

@@ -870,6 +870,7 @@ struct in_addr a_record_from_hosts(char *name, time_t now);
void cache_unhash_dhcp(void);
void dump_cache(time_t now);
char *cache_get_name(struct crec *crecp);
struct crec *cache_enumerate(int init);
char *get_domain(struct in_addr addr);
#ifdef HAVE_IPV6
char *get_domain6(struct in6_addr *addr);
@@ -899,7 +900,7 @@ size_t resize_packet(struct dns_header *header, size_t plen,
unsigned char *pheader, size_t hlen);
size_t add_mac(struct dns_header *header, size_t plen, char *limit, union mysockaddr *l3);
int add_resource_record(struct dns_header *header, char *limit, int *truncp,
unsigned int nameoffset, unsigned char **pp, unsigned long ttl,
int nameoffset, unsigned char **pp, unsigned long ttl,
unsigned int *offset, unsigned short type, unsigned short class, char *format, ...);
unsigned char *skip_questions(struct dns_header *header, size_t plen);
int extract_name(struct dns_header *header, size_t plen, unsigned char **pp,

View File

@@ -1185,7 +1185,7 @@ int check_for_bogus_wildcard(struct dns_header *header, size_t qlen, char *name,
return 0;
}
int add_resource_record(struct dns_header *header, char *limit, int *truncp, unsigned int nameoffset, unsigned char **pp,
int add_resource_record(struct dns_header *header, char *limit, int *truncp, int nameoffset, unsigned char **pp,
unsigned long ttl, unsigned int *offset, unsigned short type, unsigned short class, char *format, ...)
{
va_list ap;
@@ -1200,13 +1200,18 @@ int add_resource_record(struct dns_header *header, char *limit, int *truncp, uns
va_start(ap, format); /* make ap point to 1st unamed argument */
if (nameoffset != 0)
if (nameoffset > 0)
{
PUTSHORT(nameoffset | 0xc000, p);
}
else
{
p = do_rfc1035_name(p, va_arg(ap, char *));
if (nameoffset < 0)
{
PUTSHORT(-nameoffset | 0xc000, p);
}
else
*p++ = 0;
}
@@ -1312,7 +1317,7 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
unsigned char *p, *ansp, *pheader;
int qtype, qclass;
struct all_addr addr;
unsigned int nameoffset;
int nameoffset;
unsigned short flag;
int q, ans, anscount = 0, addncount = 0;
int dryrun = 0, sec_reqd = 0;