mirror of
https://github.com/NLnetLabs/unbound.git
synced 2025-12-30 11:29:35 -05:00
local zone answers.
git-svn-id: file:///svn/unbound/trunk@775 be551aaa-1e26-0410-a405-d3ace91eadb9
This commit is contained in:
parent
6b0cf42b32
commit
b72563dcb7
5 changed files with 233 additions and 28 deletions
|
|
@ -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 */
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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).
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 */
|
||||
|
|
|
|||
Loading…
Reference in a new issue