local zone answers.

git-svn-id: file:///svn/unbound/trunk@775 be551aaa-1e26-0410-a405-d3ace91eadb9
This commit is contained in:
Wouter Wijngaards 2007-11-22 13:48:58 +00:00
parent 6b0cf42b32
commit b72563dcb7
5 changed files with 233 additions and 28 deletions

View file

@ -58,6 +58,7 @@
#include "services/cache/infra.h"
#include "services/cache/dns.h"
#include "services/mesh.h"
#include "services/localzone.h"
#include "util/data/msgparse.h"
#include "util/data/msgencode.h"
#include "util/data/dname.h"
@ -755,6 +756,10 @@ worker_handle_request(struct comm_point* c, void* arg, int error,
&edns, c->buffer)) {
return 1;
}
if(local_zones_answer(worker->daemon->local_zones, &qinfo, &edns,
c->buffer, worker->scratchpad)) {
return (ldns_buffer_limit(c->buffer) != 0);
}
h = query_info_hash(&qinfo);
if((e=slabhash_lookup(worker->env.msg_cache, h, &qinfo, 0))) {
/* answer from cache - we have acquired a readlock on it */

View file

@ -2,6 +2,13 @@
- noted EDNS in-the-middle dropping trouble as a TODO.
At this point theoretical, no user trouble has been reported.
- added all default AS112 zones.
- answers from local zone content.
* positive answer, the rrset in question
* nodata answer (exist, but not that type).
* nxdomain answer (domain does not exist).
* empty-nonterminal answer.
* But not: wildcard, nsec, referral, rrsig, cname/dname,
or additional section processing, NS put in auth.
21 November 2007: Wouter
- local zone internal data setup.

View file

@ -175,3 +175,8 @@ o authority features.
You can put authority data on a separate server, and set the server in
unbound.conf as stub for those zones, this allows clients to access data
from the server without making unbound authoritative for the zones.
o the access control denies queries before any other processing.
This denies queries that are not authoritative, or version.bind, or any.
And thus prevents cache-snooping (denied hosts cannot make non-recursive
queries and get answers from the cache).

View file

@ -44,7 +44,10 @@
#include "util/config_file.h"
#include "util/data/dname.h"
#include "util/data/packed_rrset.h"
#include "util/data/msgencode.h"
#include "util/net_help.h"
#include "util/data/msgreply.h"
#include "util/data/msgparse.h"
struct local_zones*
local_zones_create()
@ -225,6 +228,7 @@ get_rr_content(const char* str, uint8_t** nm, uint16_t* type,
*type = ldns_rr_get_type(rr);
*ttl = (uint32_t)ldns_rr_ttl(rr);
ldns_buffer_clear(rdata);
ldns_buffer_skip(rdata, 2);
status = ldns_rr_rdata2buffer_wire(rdata, rr);
ldns_rr_free(rr);
if(status != LDNS_STATUS_OK) {
@ -270,8 +274,9 @@ struct local_rrset*
local_data_find_type(struct local_data* data, uint16_t type)
{
struct local_rrset* p;
type = htons(type);
for(p = data->rrsets; p; p = p->next) {
if(ntohs(p->rrset->rk.type) == type)
if(p->rrset->rk.type == type)
return p;
}
return NULL;
@ -365,48 +370,79 @@ insert_rr(struct regional* region, struct packed_rrset_data* pd,
return 1;
}
/** find a node, create it if not and all its empty nonterminal parents */
static int
lz_find_create_node(struct local_zone* z, uint8_t* nm, size_t nmlen,
int nmlabs, struct local_data** res)
{
struct local_data key;
struct local_data* ld;
key.node.key = &key;
key.name = nm;
key.namelen = nmlen;
key.namelabs = nmlabs;
ld = (struct local_data*)rbtree_search(&z->data, &key.node);
if(!ld) {
/* create a domain name to store rr. */
ld = (struct local_data*)regional_alloc_zero(z->region,
sizeof(*ld));
if(!ld) {
log_err("out of memory adding local data");
return 0;
}
ld->node.key = ld;
ld->name = regional_alloc_init(z->region, nm, nmlen);
if(!ld->name) {
log_err("out of memory");
return 0;
}
ld->namelen = nmlen;
ld->namelabs = nmlabs;
if(!rbtree_insert(&z->data, &ld->node)) {
log_assert(0); /* duplicate name */
}
/* see if empty nonterminals need to be created */
if(nmlabs > z->namelabs) {
dname_remove_label(&nm, &nmlen);
if(!lz_find_create_node(z, nm, nmlen, nmlabs-1, res))
return 0;
}
}
*res = ld;
return 1;
}
/** enter data RR into auth zone */
static int
lz_enter_rr_into_zone(struct local_zone* z, ldns_buffer* buf,
const char* rrstr)
{
struct local_data key;
uint8_t* nm;
size_t nmlen;
int nmlabs;
struct local_data* node;
struct local_rrset* rrset;
struct packed_rrset_data* pd;
uint16_t rrtype, rrclass;
uint32_t ttl;
if(!get_rr_content(rrstr, &key.name, &rrtype, &rrclass, &ttl, buf)) {
if(!get_rr_content(rrstr, &nm, &rrtype, &rrclass, &ttl, buf)) {
log_err("bad local-data: %s", rrstr);
return 0;
}
log_assert(z->dclass == rrclass);
key.node.key = &key;
key.namelabs = dname_count_size_labels(key.name, &key.namelen);
node = (struct local_data*)rbtree_search(&z->data, &key.node);
if(!node) {
/* create a domain name to store rr. */
node = (struct local_data*)regional_alloc_zero(z->region,
sizeof(*node));
if(!node) {
log_err("out of memory adding local data");
return 0;
}
node->node.key = node;
node->name = regional_alloc_init(z->region, key.name,
key.namelen);
if(!node->name) {
log_err("out of memory");
return 0;
}
node->namelen = key.namelen;
node->namelabs = key.namelabs;
if(!rbtree_insert(&z->data, &node->node)) {
log_assert(0); /* duplicate name */
}
if(z->type == local_zone_redirect &&
query_dname_compare(z->name, nm) != 0) {
log_err("local-data in redirect zone must reside at top of zone"
", not at %s", rrstr);
return 0;
}
nmlabs = dname_count_size_labels(nm, &nmlen);
if(!lz_find_create_node(z, nm, nmlen, nmlabs, &node)) {
free(nm);
return 0;
}
free(key.name);
log_assert(node);
free(nm);
rrset = local_data_find_type(node, rrtype);
if(!rrset) {
@ -853,3 +889,138 @@ void local_zones_print(struct local_zones* zones)
local_zone_out(z);
}
}
/** encode answer consisting of 1 rrset */
static int
local_encode(struct query_info* qinfo, struct edns_data* edns,
ldns_buffer* buf, struct regional* temp,
struct ub_packed_rrset_key* rrset, int ansec, int rcode)
{
struct reply_info rep;
uint16_t udpsize;
/* make answer with time=0 for fixed TTL values */
memset(&rep, 0, sizeof(rep));
rep.flags = (uint16_t)((BIT_QR | BIT_AA | BIT_RA) | rcode);
rep.qdcount = 1;
if(ansec)
rep.an_numrrsets = 1;
else rep.ns_numrrsets = 1;
rep.rrset_count = 1;
rep.rrsets = &rrset;
udpsize = edns->udp_size;
edns->edns_version = EDNS_ADVERTISED_VERSION;
edns->udp_size = EDNS_ADVERTISED_SIZE;
edns->ext_rcode = 0;
edns->bits &= EDNS_DO;
if(!reply_info_answer_encode(qinfo, &rep,
*(uint16_t*)ldns_buffer_begin(buf),
ldns_buffer_read_u16_at(buf, 2),
buf, 0, 0, temp, udpsize, edns,
(int)(edns->bits&EDNS_DO), 0))
error_encode(buf, LDNS_RCODE_SERVFAIL, qinfo,
*(uint16_t*)ldns_buffer_begin(buf),
ldns_buffer_read_u16_at(buf, 2), edns);
return 1;
}
/** answer local data match */
static int
local_data_answer(struct local_zone* z, struct query_info* qinfo,
struct edns_data* edns, ldns_buffer* buf, struct regional* temp,
int labs, struct local_data** ldp)
{
struct local_data key;
struct local_data* ld;
struct local_rrset* lr;
key.node.key = &key;
key.name = qinfo->qname;
key.namelen = qinfo->qname_len;
key.namelabs = labs;
if(z->type == local_zone_redirect) {
key.name = z->name;
key.namelen = z->namelen;
key.namelabs = z->namelabs;
}
ld = (struct local_data*)rbtree_search(&z->data, &key.node);
*ldp = ld;
if(!ld) {
return 0;
}
lr = local_data_find_type(ld, qinfo->qtype);
if(!lr)
return 0;
if(z->type == local_zone_redirect) {
/* convert rrset name to zone name; like a wildcard */
struct ub_packed_rrset_key r = *lr->rrset;
r.rk.dname = z->name;
r.rk.dname_len = z->namelen;
return local_encode(qinfo, edns, buf, temp, &r, 1,
LDNS_RCODE_NOERROR);
}
return local_encode(qinfo, edns, buf, temp, lr->rrset, 1,
LDNS_RCODE_NOERROR);
}
/**
* answer in case where no exact match is found
* @param z: zone for query
* @param qinfo: query
* @param edns: edns from query
* @param buf: buffer for answer.
* @param temp: temp region for encoding
* @param ld: local data, if NULL, no such name exists in localdata.
* @return 1 if a reply is to be sent, 0 if not.
*/
static int
lz_zone_answer(struct local_zone* z, struct query_info* qinfo,
struct edns_data* edns, ldns_buffer* buf, struct regional* temp,
struct local_data* ld)
{
if(z->type == local_zone_deny) {
/** no reply at all, signal caller by clearing buffer. */
ldns_buffer_clear(buf);
return 1;
} else if(z->type == local_zone_refuse) {
error_encode(buf, LDNS_RCODE_REFUSED, qinfo,
*(uint16_t*)ldns_buffer_begin(buf),
ldns_buffer_read_u16_at(buf, 2), edns);
return 1;
} else if(z->type == local_zone_static ||
z->type == local_zone_redirect) {
/* for static, reply nodata or nxdomain
* for redirect, reply nodata */
/* no additional section processing,
* cname, dname or wildcard processing,
* or using closest match for NSEC.
* or using closest match for returning delegation downwards
*/
int rcode = ld?LDNS_RCODE_NOERROR:LDNS_RCODE_NXDOMAIN;
if(z->soa)
return local_encode(qinfo, edns, buf, temp,
z->soa, 0, rcode);
error_encode(buf, rcode, qinfo,
*(uint16_t*)ldns_buffer_begin(buf),
ldns_buffer_read_u16_at(buf, 2), edns);
return 1;
}
/* else z->type == local_zone_transparent */
/* stop here, and resolve further on */
return 0;
}
int
local_zones_answer(struct local_zones* zones, struct query_info* qinfo,
struct edns_data* edns, ldns_buffer* buf, struct regional* temp)
{
/* see if query is covered by a zone,
* if so: - try to match (exact) local data
* - look at zone type for negative response. */
int labs = dname_count_labels(qinfo->qname);
struct local_data* ld;
struct local_zone* z = local_zones_lookup(zones, qinfo->qname,
qinfo->qname_len, labs, qinfo->qclass);
if(!z) return 0;
if(local_data_answer(z, qinfo, edns, buf, temp, labs, &ld))
return 1;
return lz_zone_answer(z, qinfo, edns, buf, temp, ld);
}

View file

@ -45,6 +45,8 @@
struct ub_packed_rrset_key;
struct regional;
struct config_file;
struct edns_data;
struct query_info;
/**
* Local zone type
@ -120,7 +122,8 @@ struct local_data {
size_t namelen;
/** number of labels in name */
int namelabs;
/** the data rrsets, with different types, linked list */
/** the data rrsets, with different types, linked list.
* If this list is NULL, the node is an empty non-terminal. */
struct local_rrset* rrsets;
};
@ -195,4 +198,18 @@ struct local_zone* local_zones_lookup(struct local_zones* zones,
*/
void local_zones_print(struct local_zones* zones);
/**
* Answer authoritatively for local zones.
* @param zones: the stored zones (shared, read only).
* @param qinfo: query info (parsed).
* @param edns: edns info (parsed).
* @param buf: buffer with query ID and flags, also for reply.
* @param temp: temporary storage region.
* @return true if answer is in buffer. false if query is not answered
* by authority data. If the reply should be dropped altogether, the return
* value is true, but the buffer is cleared (empty).
*/
int local_zones_answer(struct local_zones* zones, struct query_info* qinfo,
struct edns_data* edns, ldns_buffer* buf, struct regional* temp);
#endif /* SERVICES_LOCALZONE_H */