mirror of
https://github.com/NLnetLabs/unbound.git
synced 2025-12-20 23:00:56 -05:00
localzone internal data structures.
git-svn-id: file:///svn/unbound/trunk@773 be551aaa-1e26-0410-a405-d3ace91eadb9
This commit is contained in:
parent
1cda904545
commit
8df2959959
5 changed files with 807 additions and 15 deletions
|
|
@ -1,3 +1,6 @@
|
|||
21 November 2007: Wouter
|
||||
- local zone internal data setup.
|
||||
|
||||
20 November 2007: Wouter
|
||||
- 0.8 - str2list config support for double string config options.
|
||||
- local-zone and local-data options, config storage and documentation.
|
||||
|
|
|
|||
|
|
@ -394,19 +394,19 @@ local-data: "1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.ip6
|
|||
local-data: "1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.ip6.arpa. 10800 IN PTR localhost."
|
||||
.fi
|
||||
.It \fIreverse RFC1918 local use zones\fR
|
||||
Reverse data for zones 10.IN-ADDR.ARPA, 16.172.IN-ADDR.ARPA to
|
||||
31.172.IN-ADDR.ARPA, 168.192.IN-ADDR.ARPA.
|
||||
Reverse data for zones 10.in-addr.arpa, 16.172.in-addr.arpa to
|
||||
31.172.in-addr.arpa, 168.192.in-addr.arpa.
|
||||
The \fBlocal-zone:\fR is set static and as \fBlocal-data:\fR SOA and NS
|
||||
records are provided.
|
||||
.It \fIreverse RFC3330 IP4 this, link-local, testnet and broadcast\fR
|
||||
Reverse data for zones 0.IN-ADDR.ARPA, 254.169.IN-ADDR.ARPA,
|
||||
2.0.192.IN-ADDR.ARPA, 255.255.255.255.IN-ADDR.ARPA.
|
||||
Reverse data for zones 0.in-addr.arpa, 254.169.in-addr.arpa,
|
||||
2.0.192.in-addr.arpa, 255.255.255.255.in-addr.arpa.
|
||||
.It \fIreverse RFC4291 IP6 unspecified\fR
|
||||
Reverse data for zone 0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.IP6.ARPA.
|
||||
Reverse data for zone 0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.ip6.arpa.
|
||||
.It \fIreverse RFC4193 IPv6 Locally Assigned Local Addresses\fR
|
||||
Reverse data for zone D.F.IP6.ARPA.
|
||||
Reverse data for zone D.F.ip6.arpa.
|
||||
.It \fIreverse RFC4291 IPv6 Link Local Addresses\fR
|
||||
Reverse data for zones 8.E.F.IP6.ARPA to B.E.F.IP6.ARPA.
|
||||
Reverse data for zones 8.E.F.ip6.arpa to B.E.F.ip6.arpa.
|
||||
.El
|
||||
.\" End of local-zone listing.
|
||||
.It \fBlocal-data:\fR "<resource record string>"
|
||||
|
|
|
|||
|
|
@ -40,4 +40,727 @@
|
|||
*/
|
||||
#include "config.h"
|
||||
#include "services/localzone.h"
|
||||
#include "util/regional.h"
|
||||
#include "util/config_file.h"
|
||||
#include "util/data/dname.h"
|
||||
#include "util/data/packed_rrset.h"
|
||||
#include "util/net_help.h"
|
||||
|
||||
struct local_zones*
|
||||
local_zones_create()
|
||||
{
|
||||
struct local_zones* zones = (struct local_zones*)calloc(1,
|
||||
sizeof(*zones));
|
||||
if(!zones)
|
||||
return NULL;
|
||||
rbtree_init(&zones->ztree, &local_zone_cmp);
|
||||
return zones;
|
||||
}
|
||||
|
||||
/** helper traverse to delete zones */
|
||||
static void
|
||||
lzdel(rbnode_t* n, void* ATTR_UNUSED(arg))
|
||||
{
|
||||
struct local_zone* z = (struct local_zone*)n->key;
|
||||
local_zone_delete(z);
|
||||
}
|
||||
|
||||
void
|
||||
local_zones_delete(struct local_zones* zones)
|
||||
{
|
||||
if(!zones)
|
||||
return;
|
||||
/* walk through zones and delete them all */
|
||||
traverse_postorder(&zones->ztree, lzdel, NULL);
|
||||
free(zones);
|
||||
}
|
||||
|
||||
void
|
||||
local_zone_delete(struct local_zone* z)
|
||||
{
|
||||
if(!z)
|
||||
return;
|
||||
regional_destroy(z->region);
|
||||
free(z->name);
|
||||
free(z);
|
||||
}
|
||||
|
||||
int
|
||||
local_zone_cmp(const void* z1, const void* z2)
|
||||
{
|
||||
/* first sort on class, so that hierarchy can be maintained within
|
||||
* a class */
|
||||
struct local_zone* a = (struct local_zone*)z1;
|
||||
struct local_zone* b = (struct local_zone*)z2;
|
||||
int m;
|
||||
if(a->dclass != b->dclass) {
|
||||
if(a->dclass < b->dclass)
|
||||
return -1;
|
||||
return 1;
|
||||
}
|
||||
return dname_lab_cmp(a->name, a->namelabs, b->name, b->namelabs, &m);
|
||||
}
|
||||
|
||||
int
|
||||
local_data_cmp(const void* d1, const void* d2)
|
||||
{
|
||||
struct local_data* a = (struct local_data*)d1;
|
||||
struct local_data* b = (struct local_data*)d2;
|
||||
int m;
|
||||
return dname_canon_lab_cmp(a->name, a->namelabs, b->name,
|
||||
b->namelabs, &m);
|
||||
}
|
||||
|
||||
/** form wireformat from text format domain name */
|
||||
static int
|
||||
parse_dname(const char* str, uint8_t** res, size_t* len, int* labs)
|
||||
{
|
||||
ldns_rdf* rdf;
|
||||
*res = NULL;
|
||||
*len = 0;
|
||||
*labs = 0;
|
||||
rdf = ldns_dname_new_frm_str(str);
|
||||
if(!rdf) {
|
||||
log_err("cannot parse name %s", str);
|
||||
return 0;
|
||||
}
|
||||
*res = memdup(ldns_rdf_data(rdf), ldns_rdf_size(rdf));
|
||||
ldns_rdf_deep_free(rdf);
|
||||
if(!*res) {
|
||||
log_err("out of memory");
|
||||
return 0;
|
||||
}
|
||||
*labs = dname_count_size_labels(*res, len);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/** enter a new zone with allocated dname */
|
||||
static struct local_zone*
|
||||
lz_enter_zone_dname(struct local_zones* zones, uint8_t* nm, size_t len,
|
||||
int labs, enum localzone_type t, uint16_t dclass)
|
||||
{
|
||||
struct local_zone* z = (struct local_zone*)calloc(1, sizeof(*z));
|
||||
if(!z) {
|
||||
log_err("out of memory");
|
||||
return NULL;
|
||||
}
|
||||
z->node.key = z;
|
||||
z->dclass = dclass;
|
||||
z->type = t;
|
||||
z->name = nm;
|
||||
z->namelen = len;
|
||||
z->namelabs = labs;
|
||||
z->region = regional_create();
|
||||
if(!z->region) {
|
||||
log_err("out of memory");
|
||||
free(z);
|
||||
return NULL;
|
||||
}
|
||||
rbtree_init(&z->data, &local_data_cmp);
|
||||
/* add to rbtree */
|
||||
if(!rbtree_insert(&zones->ztree, &z->node)) {
|
||||
log_warn("duplicate local-zone");
|
||||
local_zone_delete(z);
|
||||
return NULL;
|
||||
}
|
||||
return z;
|
||||
}
|
||||
|
||||
/** enter a new zone */
|
||||
static struct local_zone*
|
||||
lz_enter_zone(struct local_zones* zones, const char* name, const char* type,
|
||||
uint16_t dclass)
|
||||
{
|
||||
struct local_zone* z;
|
||||
enum localzone_type t;
|
||||
uint8_t* nm;
|
||||
size_t len;
|
||||
int labs;
|
||||
if(!parse_dname(name, &nm, &len, &labs)) {
|
||||
log_err("bad zone name %s %s", name, type);
|
||||
return NULL;
|
||||
}
|
||||
if(strcmp(type, "deny") == 0)
|
||||
t = local_zone_deny;
|
||||
else if(strcmp(type, "refuse") == 0)
|
||||
t = local_zone_refuse;
|
||||
else if(strcmp(type, "static") == 0)
|
||||
t = local_zone_static;
|
||||
else if(strcmp(type, "transparent") == 0)
|
||||
t = local_zone_transparent;
|
||||
else if(strcmp(type, "redirect") == 0)
|
||||
t = local_zone_redirect;
|
||||
else {
|
||||
log_err("bad lz_enter_zone type %s %s", name, type);
|
||||
return NULL;
|
||||
}
|
||||
if(!(z=lz_enter_zone_dname(zones, nm, len, labs, t, dclass))) {
|
||||
log_err("could not enter zone %s %s", name, type);
|
||||
return NULL;
|
||||
}
|
||||
return z;
|
||||
}
|
||||
|
||||
/** return name and class and rdata of rr; parses string */
|
||||
static int
|
||||
get_rr_content(const char* str, uint8_t** nm, uint16_t* type,
|
||||
uint16_t* dclass, uint32_t* ttl, ldns_buffer* rdata)
|
||||
{
|
||||
ldns_rr* rr = NULL;
|
||||
ldns_status status = ldns_rr_new_frm_str(&rr, str, 3600, NULL, NULL);
|
||||
if(status != LDNS_STATUS_OK) {
|
||||
log_err("error parsing local-data '%s': %s",
|
||||
str, ldns_get_errorstr_by_id(status));
|
||||
ldns_rr_free(rr);
|
||||
return 0;
|
||||
}
|
||||
*nm = memdup(ldns_rdf_data(ldns_rr_owner(rr)),
|
||||
ldns_rdf_size(ldns_rr_owner(rr)));
|
||||
if(!*nm) {
|
||||
log_err("out of memory");
|
||||
ldns_rr_free(rr);
|
||||
return 0;
|
||||
}
|
||||
*dclass = ldns_rr_get_class(rr);
|
||||
*type = ldns_rr_get_type(rr);
|
||||
*ttl = (uint32_t)ldns_rr_ttl(rr);
|
||||
ldns_buffer_clear(rdata);
|
||||
status = ldns_rr_rdata2buffer_wire(rdata, rr);
|
||||
ldns_rr_free(rr);
|
||||
if(status != LDNS_STATUS_OK) {
|
||||
log_err("error converting RR '%s' to wireformat: %s",
|
||||
str, ldns_get_errorstr_by_id(status));
|
||||
return 0;
|
||||
}
|
||||
ldns_buffer_flip(rdata);
|
||||
ldns_buffer_write_u16_at(rdata, 0, ldns_buffer_limit(rdata) - 2);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/** return name and class of rr; parses string */
|
||||
static int
|
||||
get_rr_nameclass(const char* str, uint8_t** nm, uint16_t* dclass)
|
||||
{
|
||||
ldns_rr* rr = NULL;
|
||||
ldns_status status = ldns_rr_new_frm_str(&rr, str, 3600, NULL, NULL);
|
||||
if(status != LDNS_STATUS_OK) {
|
||||
log_err("error parsing local-data '%s': %s",
|
||||
str, ldns_get_errorstr_by_id(status));
|
||||
ldns_rr_free(rr);
|
||||
return 0;
|
||||
}
|
||||
*nm = memdup(ldns_rdf_data(ldns_rr_owner(rr)),
|
||||
ldns_rdf_size(ldns_rr_owner(rr)));
|
||||
*dclass = ldns_rr_get_class(rr);
|
||||
ldns_rr_free(rr);
|
||||
if(!*nm) {
|
||||
log_err("out of memory");
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find an rrset in local data structure.
|
||||
* @param data: local data domain name structure.
|
||||
* @param type: type to look for (host order).
|
||||
* @return rrset pointer or NULL if not found.
|
||||
*/
|
||||
struct local_rrset*
|
||||
local_data_find_type(struct local_data* data, uint16_t type)
|
||||
{
|
||||
struct local_rrset* p;
|
||||
for(p = data->rrsets; p; p = p->next) {
|
||||
if(ntohs(p->rrset->rk.type) == type)
|
||||
return p;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/** check for RR duplicates */
|
||||
static int
|
||||
rr_is_duplicate(struct packed_rrset_data* pd, ldns_buffer* buf)
|
||||
{
|
||||
size_t i;
|
||||
for(i=0; i<pd->count; i++) {
|
||||
if(ldns_buffer_limit(buf) == pd->rr_len[i] &&
|
||||
memcmp(ldns_buffer_begin(buf), pd->rr_data[i],
|
||||
ldns_buffer_limit(buf)) == 0)
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/** new local_rrset */
|
||||
static struct local_rrset*
|
||||
new_local_rrset(struct regional* region, struct local_data* node,
|
||||
uint16_t rrtype, uint16_t rrclass)
|
||||
{
|
||||
struct packed_rrset_data* pd;
|
||||
struct local_rrset* rrset = (struct local_rrset*)
|
||||
regional_alloc_zero(region, sizeof(*rrset));
|
||||
if(!rrset) {
|
||||
log_err("out of memory");
|
||||
return NULL;
|
||||
}
|
||||
rrset->next = node->rrsets;
|
||||
node->rrsets = rrset;
|
||||
rrset->rrset = (struct ub_packed_rrset_key*)
|
||||
regional_alloc_zero(region, sizeof(*rrset->rrset));
|
||||
if(!rrset->rrset) {
|
||||
log_err("out of memory");
|
||||
return NULL;
|
||||
}
|
||||
rrset->rrset->entry.key = rrset->rrset;
|
||||
pd = (struct packed_rrset_data*)regional_alloc_zero(region,
|
||||
sizeof(*pd));
|
||||
if(!pd) {
|
||||
log_err("out of memory");
|
||||
return NULL;
|
||||
}
|
||||
pd->trust = rrset_trust_prim_noglue;
|
||||
pd->security = sec_status_insecure;
|
||||
rrset->rrset->entry.data = pd;
|
||||
rrset->rrset->rk.dname = node->name;
|
||||
rrset->rrset->rk.dname_len = node->namelen;
|
||||
rrset->rrset->rk.type = htons(rrtype);
|
||||
rrset->rrset->rk.rrset_class = htons(rrclass);
|
||||
return rrset;
|
||||
}
|
||||
|
||||
/** insert RR into RRset data structure; Wastes a couple of bytes */
|
||||
static int
|
||||
insert_rr(struct regional* region, struct packed_rrset_data* pd,
|
||||
ldns_buffer* buf, uint32_t ttl)
|
||||
{
|
||||
size_t* oldlen = pd->rr_len;
|
||||
uint32_t* oldttl = pd->rr_ttl;
|
||||
uint8_t** olddata = pd->rr_data;
|
||||
|
||||
/* add RR to rrset */
|
||||
pd->count++;
|
||||
pd->rr_len = regional_alloc(region, sizeof(*pd->rr_len)*pd->count);
|
||||
pd->rr_ttl = regional_alloc(region, sizeof(*pd->rr_ttl)*pd->count);
|
||||
pd->rr_data = regional_alloc(region, sizeof(*pd->rr_data)*pd->count);
|
||||
if(!pd->rr_len || !pd->rr_ttl || !pd->rr_data) {
|
||||
log_err("out of memory");
|
||||
return 0;
|
||||
}
|
||||
if(pd->count > 1) {
|
||||
memcpy(pd->rr_len+1, oldlen,
|
||||
sizeof(*pd->rr_len)*(pd->count-1));
|
||||
memcpy(pd->rr_ttl+1, oldttl,
|
||||
sizeof(*pd->rr_ttl)*(pd->count-1));
|
||||
memcpy(pd->rr_data+1, olddata,
|
||||
sizeof(*pd->rr_data)*(pd->count-1));
|
||||
}
|
||||
pd->rr_len[0] = ldns_buffer_limit(buf);
|
||||
pd->rr_ttl[0] = ttl;
|
||||
pd->rr_data[0] = regional_alloc_init(region,
|
||||
ldns_buffer_begin(buf), ldns_buffer_limit(buf));
|
||||
if(!pd->rr_data[0]) {
|
||||
log_err("out of memory");
|
||||
return 0;
|
||||
}
|
||||
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;
|
||||
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)) {
|
||||
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 */
|
||||
}
|
||||
}
|
||||
free(key.name);
|
||||
log_assert(node);
|
||||
|
||||
rrset = local_data_find_type(node, rrtype);
|
||||
if(!rrset) {
|
||||
rrset = new_local_rrset(z->region, node, rrtype, rrclass);
|
||||
if(!rrset)
|
||||
return 0;
|
||||
if(query_dname_compare(node->name, z->name) == 0) {
|
||||
if(rrtype == LDNS_RR_TYPE_NSEC)
|
||||
rrset->rrset->rk.flags = PACKED_RRSET_NSEC_AT_APEX;
|
||||
if(rrtype == LDNS_RR_TYPE_SOA)
|
||||
z->soa = rrset->rrset;
|
||||
}
|
||||
}
|
||||
pd = (struct packed_rrset_data*)rrset->rrset->entry.data;
|
||||
log_assert(rrset && pd);
|
||||
|
||||
/* check for duplicate RR */
|
||||
if(rr_is_duplicate(pd, buf)) {
|
||||
verbose(VERB_ALGO, "ignoring duplicate RR: %s", rrstr);
|
||||
return 1;
|
||||
}
|
||||
return insert_rr(z->region, pd, buf, ttl);
|
||||
}
|
||||
|
||||
/** enter a data RR into auth data; a zone for it must exist */
|
||||
static int
|
||||
lz_enter_rr_str(struct local_zones* zones, const char* rr, ldns_buffer* buf)
|
||||
{
|
||||
uint8_t* rr_name;
|
||||
uint16_t rr_class;
|
||||
size_t len;
|
||||
int labs;
|
||||
struct local_zone* z;
|
||||
if(!get_rr_nameclass(rr, &rr_name, &rr_class)) {
|
||||
log_err("bad rr %s", rr);
|
||||
return 0;
|
||||
}
|
||||
labs = dname_count_size_labels(rr_name, &len);
|
||||
z = local_zones_lookup(zones, rr_name, len, labs, rr_class);
|
||||
if(!z)
|
||||
fatal_exit("internal error: no zone for rr %s", rr);
|
||||
free(rr_name);
|
||||
return lz_enter_rr_into_zone(z, buf, rr);
|
||||
}
|
||||
|
||||
/** parse local-zone: statements */
|
||||
static int
|
||||
lz_enter_zones(struct local_zones* zones, struct config_file* cfg)
|
||||
{
|
||||
struct config_str2list* p;
|
||||
for(p = cfg->local_zones; p; p = p->next) {
|
||||
if(!lz_enter_zone(zones, p->str, p->str2, LDNS_RR_CLASS_IN))
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
/** lookup a zone in rbtree; exact match only; SLOW due to parse */
|
||||
static int
|
||||
lz_exists(struct local_zones* zones, const char* name)
|
||||
{
|
||||
struct local_zone z;
|
||||
z.node.key = &z;
|
||||
z.dclass = LDNS_RR_CLASS_IN;
|
||||
if(!parse_dname(name, &z.name, &z.namelen, &z.namelabs)) {
|
||||
log_err("bad name %s", name);
|
||||
return 0;
|
||||
}
|
||||
if(rbtree_search(&zones->ztree, &z.node)) {
|
||||
free(z.name);
|
||||
return 1;
|
||||
}
|
||||
free(z.name);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/** lookup a zone in cfg->nodefault list */
|
||||
static int
|
||||
lz_nodefault(struct config_file* cfg, const char* name)
|
||||
{
|
||||
struct config_strlist* p;
|
||||
for(p = cfg->local_zones_nodefault; p; p = p->next) {
|
||||
/* compare zone name, lowercase */
|
||||
if(strcasecmp(p->str, name) == 0)
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/** enter default zones */
|
||||
static int
|
||||
lz_enter_defaults(struct local_zones* zones, struct config_file* cfg,
|
||||
ldns_buffer* buf)
|
||||
{
|
||||
struct local_zone* z;
|
||||
|
||||
/* localhost. zone */
|
||||
if(!lz_exists(zones, "localhost.") &&
|
||||
!lz_nodefault(cfg, "localhost.")) {
|
||||
if(!(z=lz_enter_zone(zones, "localhost.", "static",
|
||||
LDNS_RR_CLASS_IN)) ||
|
||||
!lz_enter_rr_into_zone(z, buf,
|
||||
"localhost. 10800 IN NS localhost.") ||
|
||||
!lz_enter_rr_into_zone(z, buf,
|
||||
"localhost. 10800 IN SOA localhost. nobody.invalid. "
|
||||
"1 3600 1200 604800 10800") ||
|
||||
!lz_enter_rr_into_zone(z, buf,
|
||||
"localhost. 10800 IN A 127.0.0.1") ||
|
||||
!lz_enter_rr_into_zone(z, buf,
|
||||
"localhost. 10800 IN AAAA ::1")) {
|
||||
log_err("out of memory adding default zone");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
/* @@@ TODO other zones */
|
||||
return 0;
|
||||
}
|
||||
|
||||
/** setup parent pointers, so that a lookup can be done for closest match */
|
||||
static void
|
||||
init_parents(struct local_zones* zones)
|
||||
{
|
||||
struct local_zone* node, *prev = NULL, *p;
|
||||
int m;
|
||||
RBTREE_FOR(node, struct local_zone*, &zones->ztree) {
|
||||
node->parent = NULL;
|
||||
if(!prev || prev->dclass != node->dclass) {
|
||||
prev = node;
|
||||
continue;
|
||||
}
|
||||
(void)dname_lab_cmp(prev->name, prev->namelabs, node->name,
|
||||
node->namelabs, &m); /* we know prev is smaller */
|
||||
/* sort order like: . com. bla.com. zwb.com. net. */
|
||||
/* find the previous, or parent-parent-parent */
|
||||
for(p = prev; p; p = p->parent)
|
||||
/* looking for name with few labels, a parent */
|
||||
if(p->namelabs <= m) {
|
||||
/* ==: since prev matched m, this is closest*/
|
||||
/* <: prev matches more, but is not a parent,
|
||||
* this one is a (grand)parent */
|
||||
node->parent = p;
|
||||
break;
|
||||
}
|
||||
prev = node;
|
||||
}
|
||||
}
|
||||
|
||||
/** enter implicit transparent zone for local-data: without local-zone: */
|
||||
static int
|
||||
lz_setup_implicit(struct local_zones* zones, struct config_file* cfg)
|
||||
{
|
||||
/* walk over all items that have no parent zone and find
|
||||
* the name that covers them all (could be the root) and
|
||||
* add that as a transparent zone */
|
||||
struct config_strlist* p;
|
||||
int have_name = 0;
|
||||
int have_other_classes = 0;
|
||||
uint16_t dclass = 0;
|
||||
uint8_t* nm = 0;
|
||||
size_t nmlen = 0;
|
||||
int nmlabs = 0;
|
||||
int match = 0; /* number of labels match count */
|
||||
|
||||
init_parents(zones); /* to enable local_zones_lookup() */
|
||||
for(p = cfg->local_data; p; p = p->next) {
|
||||
uint8_t* rr_name;
|
||||
uint16_t rr_class;
|
||||
size_t len;
|
||||
int labs;
|
||||
if(!get_rr_nameclass(p->str, &rr_name, &rr_class)) {
|
||||
log_err("Bad local-data RR %s", p->str);
|
||||
return 0;
|
||||
}
|
||||
labs = dname_count_size_labels(rr_name, &len);
|
||||
if(!local_zones_lookup(zones, rr_name, len, labs, rr_class)) {
|
||||
if(!have_name) {
|
||||
dclass = rr_class;
|
||||
nm = rr_name;
|
||||
nmlen = len;
|
||||
nmlabs = labs;
|
||||
match = labs;
|
||||
have_name = 1;
|
||||
} else {
|
||||
int m;
|
||||
if(rr_class != dclass) {
|
||||
/* process other classes later */
|
||||
have_other_classes = 1;
|
||||
continue;
|
||||
}
|
||||
/* find smallest shared topdomain */
|
||||
(void)dname_lab_cmp(nm, nmlabs,
|
||||
rr_name, labs, &m);
|
||||
if(m < match)
|
||||
match = m;
|
||||
}
|
||||
}
|
||||
free(rr_name);
|
||||
}
|
||||
if(have_name) {
|
||||
uint8_t* n2;
|
||||
/* allocate zone of smallest shared topdomain to contain em */
|
||||
n2 = nm;
|
||||
dname_remove_labels(&n2, &nmlen, nmlabs - match);
|
||||
n2 = memdup(n2, nmlen);
|
||||
free(nm);
|
||||
if(!n2) {
|
||||
log_err("out of memory");
|
||||
return 0;
|
||||
}
|
||||
if(!lz_enter_zone_dname(zones, n2, nmlen, match,
|
||||
local_zone_transparent, dclass)) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
if(have_other_classes) {
|
||||
/* restart to setup other class */
|
||||
return lz_setup_implicit(zones, cfg);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/** enter auth data */
|
||||
static int
|
||||
lz_enter_data(struct local_zones* zones, struct config_file* cfg,
|
||||
ldns_buffer* buf)
|
||||
{
|
||||
struct config_strlist* p;
|
||||
for(p = cfg->local_data; p; p = p->next) {
|
||||
if(!lz_enter_rr_str(zones, p->str, buf))
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
/** free memory from config */
|
||||
static void
|
||||
lz_freeup_cfg(struct config_file* cfg)
|
||||
{
|
||||
config_deldblstrlist(cfg->local_zones);
|
||||
cfg->local_zones = NULL;
|
||||
config_delstrlist(cfg->local_zones_nodefault);
|
||||
cfg->local_zones_nodefault = NULL;
|
||||
config_delstrlist(cfg->local_data);
|
||||
cfg->local_data = NULL;
|
||||
}
|
||||
|
||||
int
|
||||
local_zones_apply_cfg(struct local_zones* zones, struct config_file* cfg)
|
||||
{
|
||||
ldns_buffer* buf = ldns_buffer_new(65535);
|
||||
if(!buf) fatal_exit("cannot create temporary buffer");
|
||||
|
||||
/* create zones from zone statements. */
|
||||
if(!lz_enter_zones(zones, cfg)) {
|
||||
ldns_buffer_free(buf);
|
||||
return 0;
|
||||
}
|
||||
/* apply default zones+content (unless disabled, or overridden) */
|
||||
if(!lz_enter_defaults(zones, cfg, buf)) {
|
||||
ldns_buffer_free(buf);
|
||||
return 0;
|
||||
}
|
||||
/* create implicit transparent zone from data. */
|
||||
if(!lz_setup_implicit(zones, cfg)) {
|
||||
ldns_buffer_free(buf);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* setup parent ptrs for lookup during data entry */
|
||||
init_parents(zones);
|
||||
/* insert local data */
|
||||
if(!lz_enter_data(zones, cfg, buf)) {
|
||||
ldns_buffer_free(buf);
|
||||
return 0;
|
||||
}
|
||||
/* freeup memory from cfg struct. */
|
||||
lz_freeup_cfg(cfg);
|
||||
ldns_buffer_free(buf);
|
||||
return 1;
|
||||
}
|
||||
|
||||
struct local_zone*
|
||||
local_zones_lookup(struct local_zones* zones,
|
||||
uint8_t* name, size_t len, int labs, uint16_t dclass)
|
||||
{
|
||||
rbnode_t* res = NULL;
|
||||
struct local_zone *result;
|
||||
struct local_zone key;
|
||||
key.node.key = &key;
|
||||
key.dclass = dclass;
|
||||
key.name = name;
|
||||
key.namelen = len;
|
||||
key.namelabs = labs;
|
||||
if(rbtree_find_less_equal(&zones->ztree, &key, &res)) {
|
||||
/* exact */
|
||||
return (struct local_zone*)res;
|
||||
} else {
|
||||
/* smaller element (or no element) */
|
||||
int m;
|
||||
result = (struct local_zone*)res;
|
||||
if(!result || result->dclass != dclass)
|
||||
return NULL;
|
||||
/* count number of labels matched */
|
||||
(void)dname_lab_cmp(result->name, result->namelabs, key.name,
|
||||
key.namelabs, &m);
|
||||
while(result) { /* go up until qname is subdomain of zone */
|
||||
if(result->namelabs <= m)
|
||||
break;
|
||||
result = result->parent;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
/** print all RRsets in local zone */
|
||||
static void
|
||||
local_zone_out(struct local_zone* z)
|
||||
{
|
||||
struct local_data* d;
|
||||
struct local_rrset* p;
|
||||
RBTREE_FOR(d, struct local_data*, &z->data) {
|
||||
for(p = d->rrsets; p; p = p->next) {
|
||||
log_nametypeclass(0, "rrset", d->name,
|
||||
ntohs(p->rrset->rk.type),
|
||||
ntohs(p->rrset->rk.rrset_class));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void local_zones_print(struct local_zones* zones)
|
||||
{
|
||||
struct local_zone* z;
|
||||
log_info("number of auth zones %u", (unsigned)zones->ztree.count);
|
||||
RBTREE_FOR(z, struct local_zone*, &zones->ztree) {
|
||||
switch(z->type) {
|
||||
case local_zone_deny:
|
||||
log_nametypeclass(0, "deny zone",
|
||||
z->name, 0, z->dclass);
|
||||
case local_zone_refuse:
|
||||
log_nametypeclass(0, "refuse zone",
|
||||
z->name, 0, z->dclass);
|
||||
case local_zone_redirect:
|
||||
log_nametypeclass(0, "redirect zone",
|
||||
z->name, 0, z->dclass);
|
||||
case local_zone_transparent:
|
||||
log_nametypeclass(0, "transparent zone",
|
||||
z->name, 0, z->dclass);
|
||||
case local_zone_static:
|
||||
log_nametypeclass(0, "static zone",
|
||||
z->name, 0, z->dclass);
|
||||
default:
|
||||
log_nametypeclass(0, "badtyped zone",
|
||||
z->name, 0, z->dclass);
|
||||
}
|
||||
local_zone_out(z);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -44,6 +44,7 @@
|
|||
#include "util/rbtree.h"
|
||||
struct ub_packed_rrset_key;
|
||||
struct regional;
|
||||
struct config_file;
|
||||
|
||||
/**
|
||||
* Local zone type
|
||||
|
|
@ -67,12 +68,12 @@ enum localzone_type {
|
|||
};
|
||||
|
||||
/**
|
||||
* Local zones storage, shared.
|
||||
* Fixed at startup, so, readonly, no locks or mutexes necessary.
|
||||
* Authoritative local zones storage, shared.
|
||||
* This tree is fixed at startup, so, readonly, no locks or mutexes necessary.
|
||||
*/
|
||||
struct local_zones {
|
||||
/** rbtree of struct local_zone */
|
||||
rbtree_t zones;
|
||||
rbtree_t ztree;
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -87,10 +88,11 @@ struct local_zone {
|
|||
/** zone name, in uncompressed wireformat */
|
||||
uint8_t* name;
|
||||
/** length of zone name */
|
||||
size_t name_len;
|
||||
size_t namelen;
|
||||
/** number of labels in zone name */
|
||||
int name_labs;
|
||||
/** the class of this zone */
|
||||
int namelabs;
|
||||
/** the class of this zone.
|
||||
* uses 'dclass' to not conflict with c++ keyword class. */
|
||||
uint16_t dclass;
|
||||
|
||||
/** how to process zone */
|
||||
|
|
@ -103,7 +105,7 @@ struct local_zone {
|
|||
* rbtree of struct local_data */
|
||||
rbtree_t data;
|
||||
/** if data contains zone apex SOA data, this is a ptr to it. */
|
||||
struct ub_packed_rrset_key* apex;
|
||||
struct ub_packed_rrset_key* soa;
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -118,7 +120,7 @@ struct local_data {
|
|||
size_t namelen;
|
||||
/** number of labels in name */
|
||||
int namelabs;
|
||||
/** the data rrsets, match type and class, linked list */
|
||||
/** the data rrsets, with different types, linked list */
|
||||
struct local_rrset* rrsets;
|
||||
};
|
||||
|
||||
|
|
@ -132,4 +134,65 @@ struct local_rrset {
|
|||
struct ub_packed_rrset_key* rrset;
|
||||
};
|
||||
|
||||
/**
|
||||
* Create local zones storage
|
||||
* @return new struct or NULL on error.
|
||||
*/
|
||||
struct local_zones* local_zones_create();
|
||||
|
||||
/**
|
||||
* Delete local zones storage
|
||||
* @param zones: to delete.
|
||||
*/
|
||||
void local_zones_delete(struct local_zones* zones);
|
||||
|
||||
/**
|
||||
* Apply config settings; setup the local authoritative data.
|
||||
* @param zones: is set up.
|
||||
* @param cfg: config data.
|
||||
* @return false on error.
|
||||
*/
|
||||
int local_zones_apply_cfg(struct local_zones* zones, struct config_file* cfg);
|
||||
|
||||
/**
|
||||
* Compare two local_zone entries in rbtree. Sort hierarchical but not
|
||||
* canonical
|
||||
* @param z1: zone 1
|
||||
* @param z2: zone 2
|
||||
* @return: -1, 0, +1 comparison value.
|
||||
*/
|
||||
int local_zone_cmp(const void* z1, const void* z2);
|
||||
|
||||
/**
|
||||
* Compare two local_data entries in rbtree. Sort canonical.
|
||||
* @param d1: data 1
|
||||
* @param d2: data 2
|
||||
* @return: -1, 0, +1 comparison value.
|
||||
*/
|
||||
int local_data_cmp(const void* d1, const void* d2);
|
||||
|
||||
/**
|
||||
* Delete one zone
|
||||
* @param z: to delete.
|
||||
*/
|
||||
void local_zone_delete(struct local_zone* z);
|
||||
|
||||
/**
|
||||
* Lookup zone that contains the given name, class.
|
||||
* @param zones: the zones tree
|
||||
* @param name: dname to lookup
|
||||
* @param len: length of name.
|
||||
* @param labs: labelcount of name.
|
||||
* @param dclass: class to lookup.
|
||||
* @return closest local_zone or NULL if no covering zone is found.
|
||||
*/
|
||||
struct local_zone* local_zones_lookup(struct local_zones* zones,
|
||||
uint8_t* name, size_t len, int labs, uint16_t dclass);
|
||||
|
||||
/**
|
||||
* Debug helper. Print all zones
|
||||
* @param zones: the zones tree
|
||||
*/
|
||||
void local_zones_print(struct local_zones* zones);
|
||||
|
||||
#endif /* SERVICES_LOCALZONE_H */
|
||||
|
|
|
|||
|
|
@ -49,6 +49,7 @@
|
|||
#include "daemon/worker.h"
|
||||
#include "services/outside_network.h"
|
||||
#include "services/mesh.h"
|
||||
#include "services/localzone.h"
|
||||
#include "services/cache/infra.h"
|
||||
#include "iterator/iterator.h"
|
||||
#include "iterator/iter_donotq.h"
|
||||
|
|
@ -132,6 +133,8 @@ fptr_whitelist_rbtree_cmp(int (*fptr) (const void *, const void *))
|
|||
if(fptr == &mesh_state_compare) return 1;
|
||||
else if(fptr == &mesh_state_ref_compare) return 1;
|
||||
else if(fptr == &acl_list_cmp) return 1;
|
||||
else if(fptr == &local_zone_cmp) return 1;
|
||||
else if(fptr == &local_data_cmp) return 1;
|
||||
else if(fptr == &donotq_cmp) return 1;
|
||||
else if(fptr == &fwd_cmp) return 1;
|
||||
else if(fptr == &stub_cmp) return 1;
|
||||
|
|
|
|||
Loading…
Reference in a new issue