Compare commits

...
3 Commits
Author SHA1 Message Date
Simon Kelley ee86ce68fc Fix TCP query forwarding to non-default servers. 2012-12-07 11:54:46 +00:00
Simon Kelley b75e936372 First cut at zone transfer. 2012-12-07 11:50:41 +00:00
Simon Kelley aa79235194 zero arcount. 2012-12-06 19:41:35 +00:00
7 changed files with 166 additions and 42 deletions

No files matched your search

+5
View File
@@ -1,3 +1,8 @@
version 2.65
Fix regression which broke forwarding of queries sent via
TCP which are not for A and AAAA and which were directed to
non-default servers. Thanks to Niax for the bug report.
version 2.64 version 2.64
Handle DHCP FQDN options with all flag bits zero and Handle DHCP FQDN options with all flag bits zero and
--dhcp-client-update set. Thanks to Bernd Krumbroeck for --dhcp-client-update set. Thanks to Bernd Krumbroeck for
+125 -37
View File
@@ -16,6 +16,7 @@
#include "dnsmasq.h" #include "dnsmasq.h"
static struct subnet *filter_zone(struct auth_zone *zone, int flag, struct all_addr *addr_u) static struct subnet *filter_zone(struct auth_zone *zone, int flag, struct all_addr *addr_u)
{ {
struct subnet *subnet; struct subnet *subnet;
@@ -44,6 +45,31 @@ static struct subnet *filter_zone(struct auth_zone *zone, int flag, struct all_a
return NULL; 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) 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; char *name = daemon->namebuff;
unsigned char *p, *ansp; unsigned char *p, *ansp;
int qtype, qclass; int qtype, qclass;
unsigned int nameoffset; int nameoffset, axfroffset = 0;
int q, anscount = 0, authcount = 0; int q, anscount = 0, authcount = 0;
struct crec *crecp; 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 auth_zone *zone = NULL;
struct subnet *subnet = NULL; struct subnet *subnet = NULL;
char *cut;
if (ntohs(header->qdcount) == 0 || OPCODE(header) != QUERY ) if (ntohs(header->qdcount) == 0 || OPCODE(header) != QUERY )
return 0; 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--) for (q = ntohs(header->qdcount); q != 0; q--)
{ {
size_t domainlen, namelen;
unsigned short flag = 0; unsigned short flag = 0;
int found = 0; int found = 0;
struct mx_srv_record *rec, *move, **up; 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; continue;
} }
domainlen = strlen(zone->domain);
if (flag == F_IPV4) if (flag == F_IPV4)
{ {
for (intr = daemon->int_names; intr; intr = intr->next) 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) if (intr)
{ {
namelen = strlen(intr->name); if (in_zone(zone, intr->name, NULL))
if (namelen >= domainlen && hostname_isequal(zone->domain, &intr->name[namelen - domainlen]) &&
(namelen == domainlen || intr->name[namelen - domainlen - 1] == '.'))
{ {
found = 1; found = 1;
log_query(F_IPV4 | F_REVERSE | F_CONFIG, intr->name, &addr, NULL); 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)) T_PTR, C_IN, "d", name))
anscount++; 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)); log_query(crecp->flags & ~F_FORWARD, name, &addr, record_source(crecp->uid));
found = 1; found = 1;
if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, 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: cname_restart:
namelen = strlen(name);
for (zone = daemon->auth_zones; zone; zone = zone->next) for (zone = daemon->auth_zones; zone; zone = zone->next)
{ if (in_zone(zone, name, &cut))
domainlen = strlen(zone->domain);
if (namelen >= domainlen &&
hostname_isequal(zone->domain, &name[namelen - domainlen]) &&
(namelen == domainlen || name[namelen - domainlen - 1] == '.'))
break; break;
}
if (!zone) 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; flag = F_IPV6;
#endif #endif
if (qtype == T_SOA && namelen == domainlen) if (qtype == T_SOA && !cut)
{ {
soa = 1; /* inhibits auth section */ soa = 1; /* inhibits auth section */
found = 1; found = 1;
log_query(F_RRNAME | F_AUTH, zone->domain, NULL, "<SOA>"); 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 */ ns = 1; /* inhibits auth section */
found = 1; 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))) 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; nxdomain = 0;
if ((crecp->flags & flag) && filter_zone(zone, flag, &(crecp->addr.addr))) 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)); 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; found = 1;
if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, if (add_resource_record(header, limit, &trunc, nameoffset, &ansp,
daemon->auth_ttl, NULL, qtype, C_IN, 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))); } 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))) 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 */ /* Add auth section */
if (auth) if (auth)
{ {
char *authname;
if (!subnet) if (!subnet)
name = zone->domain; authname = zone->domain;
else else
{ {
/* handle NS and SOA for PTR records */ /* handle NS and SOA for PTR records */
authname = name;
if (!subnet->is6) if (!subnet->is6)
{ {
in_addr_t a = ntohl(subnet->addr4.s_addr) >> 8; 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 */ /* handle NS and SOA in auth section or for explicit queries */
if ((anscount != 0 || ns) && if ((anscount != 0 || ns) &&
add_resource_record(header, limit, &trunc, 0, &ansp, 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) if (ns)
anscount++; 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) && if ((anscount == 0 || soa) &&
add_resource_record(header, limit, &trunc, 0, &ansp, add_resource_record(header, limit, &trunc, 0, &ansp,
daemon->auth_ttl, NULL, T_SOA, C_IN, "ddlllll", 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_sn, daemon->soa_refresh,
daemon->soa_retry, daemon->soa_expiry, daemon->soa_retry, daemon->soa_expiry,
daemon->auth_ttl)) daemon->auth_ttl))
@@ -468,6 +487,74 @@ size_t answer_auth(struct dns_header *header, char *limit, size_t qlen, time_t n
else else
authcount++; 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 */ /* done all questions, set up header and return length of result */
@@ -490,6 +577,7 @@ size_t answer_auth(struct dns_header *header, char *limit, size_t qlen, time_t n
SET_RCODE(header, NOERROR); /* no error */ SET_RCODE(header, NOERROR); /* no error */
header->ancount = htons(anscount); header->ancount = htons(anscount);
header->nscount = htons(authcount); header->nscount = htons(authcount);
header->arcount = htons(0);
return ansp - (unsigned char *)header; return ansp - (unsigned char *)header;
} }
+23
View File
@@ -235,6 +235,29 @@ char *cache_get_name(struct crec *crecp)
return crecp->name.sname; 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) static int is_outdated_cname_pointer(struct crec *crecp)
{ {
if (!(crecp->flags & F_CNAME)) if (!(crecp->flags & F_CNAME))
+1
View File
@@ -52,6 +52,7 @@
#define T_OPT 41 #define T_OPT 41
#define T_TKEY 249 #define T_TKEY 249
#define T_TSIG 250 #define T_TSIG 250
#define T_AXFR 252
#define T_MAILB 253 #define T_MAILB 253
#define T_ANY 255 #define T_ANY 255
+2 -1
View File
@@ -870,6 +870,7 @@ struct in_addr a_record_from_hosts(char *name, time_t now);
void cache_unhash_dhcp(void); void cache_unhash_dhcp(void);
void dump_cache(time_t now); void dump_cache(time_t now);
char *cache_get_name(struct crec *crecp); char *cache_get_name(struct crec *crecp);
struct crec *cache_enumerate(int init);
char *get_domain(struct in_addr addr); char *get_domain(struct in_addr addr);
#ifdef HAVE_IPV6 #ifdef HAVE_IPV6
char *get_domain6(struct in6_addr *addr); 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); unsigned char *pheader, size_t hlen);
size_t add_mac(struct dns_header *header, size_t plen, char *limit, union mysockaddr *l3); 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, 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 int *offset, unsigned short type, unsigned short class, char *format, ...);
unsigned char *skip_questions(struct dns_header *header, size_t plen); unsigned char *skip_questions(struct dns_header *header, size_t plen);
int extract_name(struct dns_header *header, size_t plen, unsigned char **pp, int extract_name(struct dns_header *header, size_t plen, unsigned char **pp,
+2 -1
View File
@@ -849,7 +849,8 @@ unsigned char *tcp_request(int confd, time_t now,
int norebind = 0; int norebind = 0;
int checking_disabled; int checking_disabled;
size_t m; size_t m;
unsigned short qtype, gotname; unsigned short qtype;
unsigned int gotname;
unsigned char c1, c2; unsigned char c1, c2;
/* Max TCP packet + slop */ /* Max TCP packet + slop */
unsigned char *packet = whine_malloc(65536 + MAXDNAME + RRFIXEDSZ); unsigned char *packet = whine_malloc(65536 + MAXDNAME + RRFIXEDSZ);
+8 -3
View File
@@ -1185,7 +1185,7 @@ int check_for_bogus_wildcard(struct dns_header *header, size_t qlen, char *name,
return 0; 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, ...) unsigned long ttl, unsigned int *offset, unsigned short type, unsigned short class, char *format, ...)
{ {
va_list ap; 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 */ va_start(ap, format); /* make ap point to 1st unamed argument */
if (nameoffset != 0) if (nameoffset > 0)
{ {
PUTSHORT(nameoffset | 0xc000, p); PUTSHORT(nameoffset | 0xc000, p);
} }
else else
{ {
p = do_rfc1035_name(p, va_arg(ap, char *)); p = do_rfc1035_name(p, va_arg(ap, char *));
if (nameoffset < 0)
{
PUTSHORT(-nameoffset | 0xc000, p);
}
else
*p++ = 0; *p++ = 0;
} }
@@ -1312,7 +1317,7 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
unsigned char *p, *ansp, *pheader; unsigned char *p, *ansp, *pheader;
int qtype, qclass; int qtype, qclass;
struct all_addr addr; struct all_addr addr;
unsigned int nameoffset; int nameoffset;
unsigned short flag; unsigned short flag;
int q, ans, anscount = 0, addncount = 0; int q, ans, anscount = 0, addncount = 0;
int dryrun = 0, sec_reqd = 0; int dryrun = 0, sec_reqd = 0;