mirror of
https://github.com/NLnetLabs/unbound.git
synced 2024-09-21 06:37:08 +00:00
- Patch that resolves CNAMEs entered in local-data conf statements that
point to data on the internet, from Jinmei Tatuya (Infoblox). git-svn-id: file:///svn/unbound/trunk@3885 be551aaa-1e26-0410-a405-d3ace91eadb9
This commit is contained in:
parent
70dbd1c382
commit
503df095b2
@ -227,15 +227,48 @@ acl_list_tag_action_cfg(struct acl_list* acl, struct config_file* cfg,
|
||||
|
||||
/** check wire data parse */
|
||||
static int
|
||||
check_data(const char* data)
|
||||
check_data(const char* data, const struct config_strlist* head)
|
||||
{
|
||||
char buf[65536];
|
||||
uint8_t rr[LDNS_RR_BUF_SIZE];
|
||||
size_t len = sizeof(rr);
|
||||
int res;
|
||||
snprintf(buf, sizeof(buf), "%s %s", "example.com.", data);
|
||||
/* '.' is sufficient for validation, and it makes the call to
|
||||
* sldns_wirerr_get_type() simpler below.
|
||||
* (once adopted, this comment can be removed) */
|
||||
snprintf(buf, sizeof(buf), "%s %s", ".", data);
|
||||
res = sldns_str2wire_rr_buf(buf, rr, &len, NULL, 3600, NULL, 0,
|
||||
NULL, 0);
|
||||
|
||||
/* Reject it if we would end up having CNAME and other data (including
|
||||
* another CNAME) for the same tag. */
|
||||
if(res == 0 && head) {
|
||||
const char* err_data = NULL;
|
||||
|
||||
if(sldns_wirerr_get_type(rr, len, 1) == LDNS_RR_TYPE_CNAME) {
|
||||
/* adding CNAME while other data already exists. */
|
||||
err_data = data;
|
||||
} else {
|
||||
snprintf(buf, sizeof(buf), "%s %s", ".", head->str);
|
||||
len = sizeof(rr);
|
||||
res = sldns_str2wire_rr_buf(buf, rr, &len, NULL, 3600,
|
||||
NULL, 0, NULL, 0);
|
||||
if(res != 0) {
|
||||
/* This should be impossible here as head->str
|
||||
* has been validated, but we check it just in
|
||||
* case. */
|
||||
return 0;
|
||||
}
|
||||
if(sldns_wirerr_get_type(rr, len, 1) ==
|
||||
LDNS_RR_TYPE_CNAME) /* already have CNAME */
|
||||
err_data = head->str;
|
||||
}
|
||||
if(err_data) {
|
||||
log_err("redirect tag data '%s' must not coexist with "
|
||||
"other data.", err_data);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
if(res == 0)
|
||||
return 1;
|
||||
log_err("rr data [char %d] parse error %s",
|
||||
@ -275,7 +308,7 @@ acl_list_tag_data_cfg(struct acl_list* acl, struct config_file* cfg,
|
||||
}
|
||||
|
||||
/* check data? */
|
||||
if(!check_data(data)) {
|
||||
if(!check_data(data, node->tag_datas[tagid])) {
|
||||
log_err("cannot parse access-control-tag data: %s %s '%s'",
|
||||
str, tag, data);
|
||||
return 0;
|
||||
|
@ -485,6 +485,10 @@ answer_norec_from_cache(struct worker* worker, struct query_info* qinfo,
|
||||
if(!dp) { /* no delegation, need to reprime */
|
||||
return 0;
|
||||
}
|
||||
/* In case we have a local alias, copy it into the delegation message.
|
||||
* Shallow copy should be fine, as we'll be done with msg in this
|
||||
* function. */
|
||||
msg->qinfo.local_alias = qinfo->local_alias;
|
||||
if(must_validate) {
|
||||
switch(check_delegation_secure(msg->rep)) {
|
||||
case sec_status_unchecked:
|
||||
@ -986,6 +990,26 @@ worker_handle_request(struct comm_point* c, void* arg, int error,
|
||||
&repinfo->addr, repinfo->addrlen);
|
||||
goto send_reply;
|
||||
}
|
||||
|
||||
/* If we've found a local alias, replace the qname with the alias
|
||||
* target before resolving it. */
|
||||
if(qinfo.local_alias) {
|
||||
struct ub_packed_rrset_key* rrset = qinfo.local_alias->rrset;
|
||||
struct packed_rrset_data* d = rrset->entry.data;
|
||||
|
||||
/* Sanity check: our current implementation only supports
|
||||
* a single CNAME RRset as a local alias. */
|
||||
if(qinfo.local_alias->next ||
|
||||
rrset->rk.type != htons(LDNS_RR_TYPE_CNAME) ||
|
||||
d->count != 1) {
|
||||
log_err("assumption failure: unexpected local alias");
|
||||
regional_free_all(worker->scratchpad);
|
||||
return 0; /* drop it */
|
||||
}
|
||||
qinfo.qname = d->rr_data[0] + 2;
|
||||
qinfo.qname_len = d->rr_len[0] - 2;
|
||||
}
|
||||
|
||||
h = query_info_hash(&qinfo, sldns_buffer_read_u16_at(c->buffer, 2));
|
||||
if((e=slabhash_lookup(worker->env.msg_cache, h, &qinfo, 0))) {
|
||||
/* answer from cache - we have acquired a readlock on it */
|
||||
|
@ -1,3 +1,7 @@
|
||||
18 October 2016: Wouter
|
||||
- Patch that resolves CNAMEs entered in local-data conf statements that
|
||||
point to data on the internet, from Jinmei Tatuya (Infoblox).
|
||||
|
||||
17 October 2016: Wouter
|
||||
- Re-fix #839 from view commit overwrite.
|
||||
- Fixup const void cast warning.
|
||||
|
@ -551,6 +551,7 @@ generate_sub_request(uint8_t* qname, size_t qnamelen, uint16_t qtype,
|
||||
qinf.qname_len = qnamelen;
|
||||
qinf.qtype = qtype;
|
||||
qinf.qclass = qclass;
|
||||
qinf.local_alias = NULL;
|
||||
|
||||
/* RD should be set only when sending the query back through the INIT
|
||||
* state. */
|
||||
|
@ -577,6 +577,7 @@ setup_qinfo_edns(struct libworker* w, struct ctx_query* q,
|
||||
if(!qinfo->qname) {
|
||||
return 0;
|
||||
}
|
||||
qinfo->local_alias = NULL;
|
||||
edns->edns_present = 1;
|
||||
edns->ext_rcode = 0;
|
||||
edns->edns_version = 0;
|
||||
|
@ -280,16 +280,20 @@ get_rr_nameclass(const char* str, uint8_t** nm, uint16_t* dclass)
|
||||
* Find an rrset in local data structure.
|
||||
* @param data: local data domain name structure.
|
||||
* @param type: type to look for (host order).
|
||||
* @param alias_ok: 1 if matching a non-exact, alias type such as CNAME is
|
||||
* allowed. otherwise 0.
|
||||
* @return rrset pointer or NULL if not found.
|
||||
*/
|
||||
static struct local_rrset*
|
||||
local_data_find_type(struct local_data* data, uint16_t type)
|
||||
local_data_find_type(struct local_data* data, uint16_t type, int alias_ok)
|
||||
{
|
||||
struct local_rrset* p;
|
||||
type = htons(type);
|
||||
for(p = data->rrsets; p; p = p->next) {
|
||||
if(p->rrset->rk.type == type)
|
||||
return p;
|
||||
if(alias_ok && p->rrset->rk.type == htons(LDNS_RR_TYPE_CNAME))
|
||||
return p;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
@ -469,7 +473,23 @@ lz_enter_rr_into_zone(struct local_zone* z, const char* rrstr)
|
||||
log_assert(node);
|
||||
free(nm);
|
||||
|
||||
rrset = local_data_find_type(node, rrtype);
|
||||
/* Reject it if we would end up having CNAME and other data (including
|
||||
* another CNAME) for a redirect zone. */
|
||||
if(z->type == local_zone_redirect && node->rrsets) {
|
||||
const char* othertype = NULL;
|
||||
if (rrtype == LDNS_RR_TYPE_CNAME)
|
||||
othertype = "other";
|
||||
else if (node->rrsets->rrset->rk.type ==
|
||||
htons(LDNS_RR_TYPE_CNAME)) {
|
||||
othertype = "CNAME";
|
||||
}
|
||||
if(othertype) {
|
||||
log_err("local-data '%s' in redirect zone must not "
|
||||
"coexist with %s local-data", rrstr, othertype);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
rrset = local_data_find_type(node, rrtype, 0);
|
||||
if(!rrset) {
|
||||
rrset = new_local_rrset(z->region, node, rrtype, rrclass);
|
||||
if(!rrset)
|
||||
@ -1203,6 +1223,8 @@ find_tag_datas(struct query_info* qinfo, struct config_strlist* list,
|
||||
int res;
|
||||
struct packed_rrset_data* d;
|
||||
for(p=list; p; p=p->next) {
|
||||
uint16_t rdr_type;
|
||||
|
||||
len = sizeof(rr);
|
||||
/* does this element match the type? */
|
||||
snprintf(buf, sizeof(buf), ". %s", p->str);
|
||||
@ -1214,7 +1236,8 @@ find_tag_datas(struct query_info* qinfo, struct config_strlist* list,
|
||||
continue;
|
||||
if(len < 1 /* . */ + 8 /* typeclassttl*/ + 2 /*rdatalen*/)
|
||||
continue;
|
||||
if(sldns_wirerr_get_type(rr, len, 1) != qinfo->qtype)
|
||||
rdr_type = sldns_wirerr_get_type(rr, len, 1);
|
||||
if(rdr_type != qinfo->qtype && rdr_type != LDNS_RR_TYPE_CNAME)
|
||||
continue;
|
||||
|
||||
/* do we have entries already? if not setup key */
|
||||
@ -1222,7 +1245,7 @@ find_tag_datas(struct query_info* qinfo, struct config_strlist* list,
|
||||
r->entry.key = r;
|
||||
r->rk.dname = qinfo->qname;
|
||||
r->rk.dname_len = qinfo->qname_len;
|
||||
r->rk.type = htons(qinfo->qtype);
|
||||
r->rk.type = htons(rdr_type);
|
||||
r->rk.rrset_class = htons(qinfo->qclass);
|
||||
r->rk.flags = 0;
|
||||
d = (struct packed_rrset_data*)regional_alloc_zero(
|
||||
@ -1271,6 +1294,20 @@ find_tag_datas(struct query_info* qinfo, struct config_strlist* list,
|
||||
if(!d) return 0; /* out of memory */
|
||||
d->count++;
|
||||
}
|
||||
/* If we've found a non-exact alias type of local data, make a shallow
|
||||
* copy of the RRset and remember it in qinfo to complete the alias
|
||||
* chain later. */
|
||||
if(r->rk.dname && qinfo->qtype != LDNS_RR_TYPE_CNAME &&
|
||||
r->rk.type == htons(LDNS_RR_TYPE_CNAME)) {
|
||||
qinfo->local_alias =
|
||||
regional_alloc_zero(temp, sizeof(struct local_rrset));
|
||||
if(!qinfo->local_alias)
|
||||
return 0; /* out of memory */
|
||||
qinfo->local_alias->rrset =
|
||||
regional_alloc_init(temp, r, sizeof(*r));
|
||||
if(!qinfo->local_alias->rrset)
|
||||
return 0; /* out of memory */
|
||||
}
|
||||
if(r->rk.dname)
|
||||
return 1;
|
||||
return 0;
|
||||
@ -1302,6 +1339,13 @@ local_data_answer(struct local_zone* z, struct query_info* qinfo,
|
||||
z->name, z->namelen)) {
|
||||
verbose(VERB_ALGO, "redirect with tag data [%d] %s",
|
||||
tag, (tag<num_tags?tagname[tag]:"null"));
|
||||
|
||||
/* If we found a matching alias, we should
|
||||
* use it as part of the answer, but we can't
|
||||
* encode it until we complete the alias
|
||||
* chain. */
|
||||
if(qinfo->local_alias)
|
||||
return 1;
|
||||
return local_encode(qinfo, edns, buf, temp,
|
||||
&r, 1, LDNS_RCODE_NOERROR);
|
||||
}
|
||||
@ -1312,9 +1356,26 @@ local_data_answer(struct local_zone* z, struct query_info* qinfo,
|
||||
if(!ld) {
|
||||
return 0;
|
||||
}
|
||||
lr = local_data_find_type(ld, qinfo->qtype);
|
||||
lr = local_data_find_type(ld, qinfo->qtype, 1);
|
||||
if(!lr)
|
||||
return 0;
|
||||
|
||||
/* Special case for alias matching. See local_data_answer(). */
|
||||
if(lz_type == local_zone_redirect &&
|
||||
qinfo->qtype != LDNS_RR_TYPE_CNAME &&
|
||||
lr->rrset->rk.type == htons(LDNS_RR_TYPE_CNAME)) {
|
||||
qinfo->local_alias =
|
||||
regional_alloc_zero(temp, sizeof(struct local_rrset));
|
||||
if(!qinfo->local_alias)
|
||||
return 0; /* out of memory */
|
||||
qinfo->local_alias->rrset =
|
||||
regional_alloc_init(temp, lr->rrset, sizeof(*lr->rrset));
|
||||
if(!qinfo->local_alias->rrset)
|
||||
return 0; /* out of memory */
|
||||
qinfo->local_alias->rrset->rk.dname = qinfo->qname;
|
||||
qinfo->local_alias->rrset->rk.dname_len = qinfo->qname_len;
|
||||
return 1;
|
||||
}
|
||||
if(lz_type == local_zone_redirect) {
|
||||
/* convert rrset name to query name; like a wildcard */
|
||||
struct ub_packed_rrset_key r = *lr->rrset;
|
||||
@ -1518,11 +1579,13 @@ local_zones_answer(struct local_zones* zones, struct query_info* qinfo,
|
||||
&& local_data_answer(z, qinfo, edns, buf, temp, labs, &ld, lzt,
|
||||
tag, tag_datas, tag_datas_size, tagname, num_tags)) {
|
||||
lock_rw_unlock(&z->lock);
|
||||
return 1;
|
||||
/* We should tell the caller that encode is deferred if we found
|
||||
* a local alias. */
|
||||
return !qinfo->local_alias;
|
||||
}
|
||||
r = lz_zone_answer(z, qinfo, edns, buf, temp, ld, lzt);
|
||||
lock_rw_unlock(&z->lock);
|
||||
return r;
|
||||
return r && !qinfo->local_alias; /* see above */
|
||||
}
|
||||
|
||||
const char* local_zone_type2str(enum localzone_type t)
|
||||
|
@ -282,6 +282,14 @@ void local_zones_print(struct local_zones* zones);
|
||||
* @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).
|
||||
* It can also return true if a non-exact alias answer is found. In this
|
||||
* case qinfo->local_alias points to the corresponding alias RRset but the
|
||||
* answer is NOT encoded in buffer. It's the caller's responsibility to
|
||||
* complete the alias chain (if needed) and encode the final set of answer.
|
||||
* Data pointed to by qinfo->local_alias is allocated in 'temp' or refers to
|
||||
* configuration data. So the caller will need to make a deep copy of it
|
||||
* if it needs to keep it beyond the lifetime of 'temp' or a dynamic update
|
||||
* to local zone data.
|
||||
*/
|
||||
int local_zones_answer(struct local_zones* zones, struct query_info* qinfo,
|
||||
struct edns_data* edns, struct sldns_buffer* buf, struct regional* temp,
|
||||
|
@ -56,6 +56,8 @@
|
||||
#include "util/alloc.h"
|
||||
#include "util/config_file.h"
|
||||
#include "sldns/sbuffer.h"
|
||||
#include "services/localzone.h"
|
||||
#include "util/data/dname.h"
|
||||
|
||||
/** subtract timers and the values do not overflow or become negative */
|
||||
static void
|
||||
@ -338,7 +340,7 @@ void mesh_new_client(struct mesh_area* mesh, struct query_info* qinfo,
|
||||
if(!s->reply_list && !s->cb_list)
|
||||
was_noreply = 1;
|
||||
/* add reply to s */
|
||||
if(!mesh_state_add_reply(s, edns, rep, qid, qflags, qinfo->qname)) {
|
||||
if(!mesh_state_add_reply(s, edns, rep, qid, qflags, qinfo)) {
|
||||
log_err("mesh_new_client: out of memory; SERVFAIL");
|
||||
if(!edns_opt_inplace_reply(edns, mesh->env->scratch))
|
||||
edns->opt_list = NULL;
|
||||
@ -847,6 +849,10 @@ mesh_send_reply(struct mesh_state* m, int rcode, struct reply_info* rep,
|
||||
struct timeval end_time;
|
||||
struct timeval duration;
|
||||
int secure;
|
||||
/* Copy the client's EDNS for later restore to fix a bug of the
|
||||
* original code. See unbound bug #1125. Once NLNet Labs fixes it
|
||||
* we should replace this local fix with the upstream one. */
|
||||
struct edns_data edns_bak = r->edns;
|
||||
/* examine security status */
|
||||
if(m->s.env->need_to_validate && (!(r->qflags&BIT_CD) ||
|
||||
m->s.env->cfg->ignore_cd) && rep &&
|
||||
@ -861,7 +867,13 @@ mesh_send_reply(struct mesh_state* m, int rcode, struct reply_info* rep,
|
||||
if(!rep && rcode == LDNS_RCODE_NOERROR)
|
||||
rcode = LDNS_RCODE_SERVFAIL;
|
||||
/* send the reply */
|
||||
/* We don't reuse the encoded answer if either the previous or current
|
||||
* response has a local alias. We could compare the alias records
|
||||
* and still reuse the previous answer if they are the same, but that
|
||||
* would be complicated and error prone for the relatively minor case.
|
||||
* So we err on the side of safety. */
|
||||
if(prev && prev->qflags == r->qflags &&
|
||||
!prev->local_alias && !r->local_alias &&
|
||||
prev->edns.edns_present == r->edns.edns_present &&
|
||||
prev->edns.bits == r->edns.bits &&
|
||||
prev->edns.udp_size == r->edns.udp_size &&
|
||||
@ -880,6 +892,7 @@ mesh_send_reply(struct mesh_state* m, int rcode, struct reply_info* rep,
|
||||
m->s.qinfo.qname = r->qname;
|
||||
if(!edns_opt_inplace_reply(&r->edns, m->s.region))
|
||||
r->edns.opt_list = NULL;
|
||||
m->s.qinfo.local_alias = r->local_alias;
|
||||
error_encode(r->query_reply.c->buffer, rcode, &m->s.qinfo,
|
||||
r->qid, r->qflags, &r->edns);
|
||||
comm_point_send_reply(&r->query_reply);
|
||||
@ -890,6 +903,7 @@ mesh_send_reply(struct mesh_state* m, int rcode, struct reply_info* rep,
|
||||
r->edns.ext_rcode = 0;
|
||||
r->edns.bits &= EDNS_DO;
|
||||
m->s.qinfo.qname = r->qname;
|
||||
m->s.qinfo.local_alias = r->local_alias;
|
||||
if(!edns_opt_inplace_reply(&r->edns, m->s.region) ||
|
||||
!reply_info_answer_encode(&m->s.qinfo, rep, r->qid,
|
||||
r->qflags, r->query_reply.c->buffer, 0, 1,
|
||||
@ -900,6 +914,7 @@ mesh_send_reply(struct mesh_state* m, int rcode, struct reply_info* rep,
|
||||
LDNS_RCODE_SERVFAIL, &m->s.qinfo, r->qid,
|
||||
r->qflags, &r->edns);
|
||||
}
|
||||
r->edns = edns_bak; /* Fix unbound bug #1125. See above. */
|
||||
comm_point_send_reply(&r->query_reply);
|
||||
}
|
||||
/* account */
|
||||
@ -998,7 +1013,8 @@ int mesh_state_add_cb(struct mesh_state* s, struct edns_data* edns,
|
||||
}
|
||||
|
||||
int mesh_state_add_reply(struct mesh_state* s, struct edns_data* edns,
|
||||
struct comm_reply* rep, uint16_t qid, uint16_t qflags, uint8_t* qname)
|
||||
struct comm_reply* rep, uint16_t qid, uint16_t qflags,
|
||||
const struct query_info* qinfo)
|
||||
{
|
||||
struct mesh_reply* r = regional_alloc(s->s.region,
|
||||
sizeof(struct mesh_reply));
|
||||
@ -1016,10 +1032,62 @@ int mesh_state_add_reply(struct mesh_state* s, struct edns_data* edns,
|
||||
r->qflags = qflags;
|
||||
r->start_time = *s->s.env->now_tv;
|
||||
r->next = s->reply_list;
|
||||
r->qname = regional_alloc_init(s->s.region, qname,
|
||||
r->qname = regional_alloc_init(s->s.region, qinfo->qname,
|
||||
s->s.qinfo.qname_len);
|
||||
if(!r->qname)
|
||||
return 0;
|
||||
|
||||
/* Data related to local alias stored in 'qinfo' (if any) is ephemeral
|
||||
* and can be different for different original queries (even if the
|
||||
* replaced query name is the same). So we need to make a deep copy
|
||||
* and store the copy for each reply info. */
|
||||
if(qinfo->local_alias) {
|
||||
struct packed_rrset_data* d;
|
||||
struct packed_rrset_data* dsrc;
|
||||
r->local_alias = regional_alloc_zero(s->s.region,
|
||||
sizeof(*qinfo->local_alias));
|
||||
if(!r->local_alias)
|
||||
return 0;
|
||||
r->local_alias->rrset = regional_alloc_init(s->s.region,
|
||||
qinfo->local_alias->rrset,
|
||||
sizeof(*qinfo->local_alias->rrset));
|
||||
if(!r->local_alias->rrset)
|
||||
return 0;
|
||||
dsrc = qinfo->local_alias->rrset->entry.data;
|
||||
|
||||
/* In the current implementation, a local alias must be
|
||||
* a single CNAME RR (see worker_handle_request()). */
|
||||
log_assert(!qinfo->local_alias->next && dsrc->count == 1 &&
|
||||
qinfo->local_alias->rrset->rk.type ==
|
||||
htons(LDNS_RR_TYPE_CNAME));
|
||||
/* Technically, we should make a local copy for the owner
|
||||
* name of the RRset, but in the case of the first (and
|
||||
* currently only) local alias RRset, the owner name should
|
||||
* point to the qname of the corresponding query, which should
|
||||
* be valid throughout the lifetime of this mesh_reply. So
|
||||
* we can skip copying. */
|
||||
log_assert(qinfo->local_alias->rrset->rk.dname ==
|
||||
sldns_buffer_at(rep->c->buffer, LDNS_HEADER_SIZE));
|
||||
|
||||
d = regional_alloc_init(s->s.region, dsrc,
|
||||
sizeof(struct packed_rrset_data)
|
||||
+ sizeof(size_t) + sizeof(uint8_t*) + sizeof(time_t));
|
||||
if(!d)
|
||||
return 0;
|
||||
r->local_alias->rrset->entry.data = d;
|
||||
d->rr_len = (size_t*)((uint8_t*)d +
|
||||
sizeof(struct packed_rrset_data));
|
||||
d->rr_data = (uint8_t**)&(d->rr_len[1]);
|
||||
d->rr_ttl = (time_t*)&(d->rr_data[1]);
|
||||
d->rr_len[0] = dsrc->rr_len[0];
|
||||
d->rr_ttl[0] = dsrc->rr_ttl[0];
|
||||
d->rr_data[0] = regional_alloc_init(s->s.region,
|
||||
dsrc->rr_data[0], d->rr_len[0]);
|
||||
if(!d->rr_data[0])
|
||||
return 0;
|
||||
} else
|
||||
r->local_alias = NULL;
|
||||
|
||||
s->reply_list = r;
|
||||
return 1;
|
||||
}
|
||||
|
@ -214,6 +214,8 @@ struct mesh_reply {
|
||||
uint16_t qflags;
|
||||
/** qname from this query. len same as mesh qinfo. */
|
||||
uint8_t* qname;
|
||||
/** same as that in query_info. */
|
||||
struct local_rrset* local_alias;
|
||||
};
|
||||
|
||||
/**
|
||||
@ -460,10 +462,12 @@ int mesh_state_attachment(struct mesh_state* super, struct mesh_state* sub);
|
||||
* @param qid: ID of reply.
|
||||
* @param qflags: original query flags.
|
||||
* @param qname: original query name.
|
||||
* @param qinfo: original query info.
|
||||
* @return: 0 on alloc error.
|
||||
*/
|
||||
int mesh_state_add_reply(struct mesh_state* s, struct edns_data* edns,
|
||||
struct comm_reply* rep, uint16_t qid, uint16_t qflags, uint8_t* qname);
|
||||
int mesh_state_add_reply(struct mesh_state* s, struct edns_data* edns,
|
||||
struct comm_reply* rep, uint16_t qid, uint16_t qflags,
|
||||
const struct query_info* qinfo);
|
||||
|
||||
/**
|
||||
* Create new callback structure and attach it to a mesh state.
|
||||
|
@ -487,6 +487,7 @@ qlist_parse_line(sldns_buffer* buf, char* p)
|
||||
qinfo.qname = sldns_str2wire_dname(nm, &qinfo.qname_len);
|
||||
if(!qinfo.qname)
|
||||
return 0;
|
||||
qinfo.local_alias = NULL;
|
||||
qinfo_query_encode(buf, &qinfo);
|
||||
sldns_buffer_write_u16_at(buf, 0, 0); /* zero ID */
|
||||
if(rec) LDNS_RD_SET(sldns_buffer_begin(buf));
|
||||
|
@ -128,6 +128,9 @@ write_q(int fd, int udp, SSL* ssl, sldns_buffer* buf, uint16_t id,
|
||||
qinfo.qtype = sldns_get_rr_type_by_name(strtype);
|
||||
qinfo.qclass = sldns_get_rr_class_by_name(strclass);
|
||||
|
||||
/* clear local alias */
|
||||
qinfo.local_alias = NULL;
|
||||
|
||||
/* make query */
|
||||
qinfo_query_encode(buf, &qinfo);
|
||||
sldns_buffer_write_u16_at(buf, 0, id);
|
||||
|
@ -48,6 +48,7 @@
|
||||
#include "util/regional.h"
|
||||
#include "util/net_help.h"
|
||||
#include "sldns/sbuffer.h"
|
||||
#include "services/localzone.h"
|
||||
|
||||
/** return code that means the function ran out of memory. negative so it does
|
||||
* not conflict with DNS rcodes. */
|
||||
@ -534,7 +535,14 @@ insert_section(struct reply_info* rep, size_t num_rrsets, uint16_t* num_rrs,
|
||||
{
|
||||
int r;
|
||||
size_t i, setstart;
|
||||
*num_rrs = 0;
|
||||
/* we now allow this function to be called multiple times for the
|
||||
* same section, incrementally updating num_rrs. The caller is
|
||||
* responsible for initializing it (which is the case in the current
|
||||
* implementation).
|
||||
* Note: once approved, this comment and the following line should be
|
||||
* removed. */
|
||||
/**num_rrs = 0;*/
|
||||
|
||||
if(s != LDNS_SECTION_ADDITIONAL) {
|
||||
if(s == LDNS_SECTION_ANSWER && qtype == LDNS_RR_TYPE_ANY)
|
||||
dnssec = 1; /* include all types in ANY answer */
|
||||
@ -581,17 +589,20 @@ static int
|
||||
insert_query(struct query_info* qinfo, struct compress_tree_node** tree,
|
||||
sldns_buffer* buffer, struct regional* region)
|
||||
{
|
||||
uint8_t* qname = qinfo->local_alias ?
|
||||
qinfo->local_alias->rrset->rk.dname : qinfo->qname;
|
||||
size_t qname_len = qinfo->local_alias ?
|
||||
qinfo->local_alias->rrset->rk.dname_len : qinfo->qname_len;
|
||||
if(sldns_buffer_remaining(buffer) <
|
||||
qinfo->qname_len+sizeof(uint16_t)*2)
|
||||
return RETVAL_TRUNC; /* buffer too small */
|
||||
/* the query is the first name inserted into the tree */
|
||||
if(!compress_tree_store(qinfo->qname,
|
||||
dname_count_labels(qinfo->qname),
|
||||
if(!compress_tree_store(qname, dname_count_labels(qname),
|
||||
sldns_buffer_position(buffer), region, NULL, tree))
|
||||
return RETVAL_OUTMEM;
|
||||
if(sldns_buffer_current(buffer) == qinfo->qname)
|
||||
sldns_buffer_skip(buffer, (ssize_t)qinfo->qname_len);
|
||||
else sldns_buffer_write(buffer, qinfo->qname, qinfo->qname_len);
|
||||
if(sldns_buffer_current(buffer) == qname)
|
||||
sldns_buffer_skip(buffer, (ssize_t)qname_len);
|
||||
else sldns_buffer_write(buffer, qname, qname_len);
|
||||
sldns_buffer_write_u16(buffer, qinfo->qtype);
|
||||
sldns_buffer_write_u16(buffer, qinfo->qclass);
|
||||
return RETVAL_OK;
|
||||
@ -662,6 +673,33 @@ reply_info_encode(struct query_info* qinfo, struct reply_info* rep,
|
||||
* for different roundrobins for sequential id client senders. */
|
||||
rr_offset = RRSET_ROUNDROBIN?ntohs(id):0;
|
||||
|
||||
/* "prepend" any local alias records in the answer section if this
|
||||
* response is supposed to be authoritative. Currently it should
|
||||
* be a single CNAME record (sanity-checked in worker_handle_request())
|
||||
* but it can be extended if and when we support more variations of
|
||||
* aliases. */
|
||||
if(qinfo->local_alias && (flags & BIT_AA)) {
|
||||
struct reply_info arep;
|
||||
time_t timezero = 0; /* to use the 'authoritative' TTL */
|
||||
memset(&arep, 0, sizeof(arep));
|
||||
arep.flags = rep->flags;
|
||||
arep.an_numrrsets = 1;
|
||||
arep.rrset_count = 1;
|
||||
arep.rrsets = &qinfo->local_alias->rrset;
|
||||
if((r=insert_section(&arep, 1, &ancount, buffer, 0,
|
||||
timezero, region, &tree, LDNS_SECTION_ANSWER,
|
||||
qinfo->qtype, dnssec, rr_offset)) != RETVAL_OK) {
|
||||
if(r == RETVAL_TRUNC) {
|
||||
/* create truncated message */
|
||||
sldns_buffer_write_u16_at(buffer, 6, ancount);
|
||||
LDNS_TC_SET(sldns_buffer_begin(buffer));
|
||||
sldns_buffer_flip(buffer);
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* insert answer section */
|
||||
if((r=insert_section(rep, rep->an_numrrsets, &ancount, buffer,
|
||||
0, timenow, region, &tree, LDNS_SECTION_ANSWER, qinfo->qtype,
|
||||
@ -782,6 +820,15 @@ reply_info_answer_encode(struct query_info* qinf, struct reply_info* rep,
|
||||
}
|
||||
if(secure && (dnssec || (qflags&BIT_AD)))
|
||||
flags |= BIT_AD;
|
||||
/* restore AA bit if we have a local alias and the response can be
|
||||
* authoritative. Also clear AD bit if set as the local data is the
|
||||
* primary answer. */
|
||||
if(qinf->local_alias &&
|
||||
(FLAGS_GET_RCODE(rep->flags) == LDNS_RCODE_NOERROR ||
|
||||
FLAGS_GET_RCODE(rep->flags) == LDNS_RCODE_NXDOMAIN)) {
|
||||
flags |= BIT_AA;
|
||||
flags &= ~BIT_AD;
|
||||
}
|
||||
log_assert(flags & BIT_QR); /* QR bit must be on in our replies */
|
||||
if(udpsize < LDNS_HEADER_SIZE)
|
||||
return 0;
|
||||
@ -807,13 +854,17 @@ void
|
||||
qinfo_query_encode(sldns_buffer* pkt, struct query_info* qinfo)
|
||||
{
|
||||
uint16_t flags = 0; /* QUERY, NOERROR */
|
||||
const uint8_t* qname = qinfo->local_alias ?
|
||||
qinfo->local_alias->rrset->rk.dname : qinfo->qname;
|
||||
size_t qname_len = qinfo->local_alias ?
|
||||
qinfo->local_alias->rrset->rk.dname_len : qinfo->qname_len;
|
||||
sldns_buffer_clear(pkt);
|
||||
log_assert(sldns_buffer_remaining(pkt) >= 12+255+4/*max query*/);
|
||||
sldns_buffer_skip(pkt, 2); /* id done later */
|
||||
sldns_buffer_write_u16(pkt, flags);
|
||||
sldns_buffer_write_u16(pkt, 1); /* query count */
|
||||
sldns_buffer_write(pkt, "\000\000\000\000\000\000", 6); /* counts */
|
||||
sldns_buffer_write(pkt, qinfo->qname, qinfo->qname_len);
|
||||
sldns_buffer_write(pkt, qname, qname_len);
|
||||
sldns_buffer_write_u16(pkt, qinfo->qtype);
|
||||
sldns_buffer_write_u16(pkt, qinfo->qclass);
|
||||
sldns_buffer_flip(pkt);
|
||||
@ -838,9 +889,14 @@ error_encode(sldns_buffer* buf, int r, struct query_info* qinfo,
|
||||
sldns_buffer_write(buf, &flags, sizeof(uint16_t));
|
||||
sldns_buffer_write(buf, &flags, sizeof(uint16_t));
|
||||
if(qinfo) {
|
||||
if(sldns_buffer_current(buf) == qinfo->qname)
|
||||
sldns_buffer_skip(buf, (ssize_t)qinfo->qname_len);
|
||||
else sldns_buffer_write(buf, qinfo->qname, qinfo->qname_len);
|
||||
const uint8_t* qname = qinfo->local_alias ?
|
||||
qinfo->local_alias->rrset->rk.dname : qinfo->qname;
|
||||
size_t qname_len = qinfo->local_alias ?
|
||||
qinfo->local_alias->rrset->rk.dname_len :
|
||||
qinfo->qname_len;
|
||||
if(sldns_buffer_current(buf) == qname)
|
||||
sldns_buffer_skip(buf, (ssize_t)qname_len);
|
||||
else sldns_buffer_write(buf, qname, qname_len);
|
||||
sldns_buffer_write_u16(buf, qinfo->qtype);
|
||||
sldns_buffer_write_u16(buf, qinfo->qclass);
|
||||
}
|
||||
|
@ -76,6 +76,7 @@ parse_create_qinfo(sldns_buffer* pkt, struct msg_parse* msg,
|
||||
qinf->qname_len = msg->qname_len;
|
||||
qinf->qtype = msg->qtype;
|
||||
qinf->qclass = msg->qclass;
|
||||
qinf->local_alias = NULL;
|
||||
return 1;
|
||||
}
|
||||
|
||||
@ -451,6 +452,7 @@ int reply_info_parse(sldns_buffer* pkt, struct alloc_cache* alloc,
|
||||
int ret;
|
||||
|
||||
qinf->qname = NULL;
|
||||
qinf->local_alias = NULL;
|
||||
*rep = NULL;
|
||||
if(!(msg = regional_alloc(region, sizeof(*msg)))) {
|
||||
return LDNS_RCODE_SERVFAIL;
|
||||
@ -542,6 +544,7 @@ query_info_parse(struct query_info* m, sldns_buffer* query)
|
||||
return 0; /* need qtype, qclass */
|
||||
m->qtype = sldns_buffer_read_u16(query);
|
||||
m->qclass = sldns_buffer_read_u16(query);
|
||||
m->local_alias = NULL;
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
@ -51,6 +51,7 @@ struct regional;
|
||||
struct edns_data;
|
||||
struct msg_parse;
|
||||
struct rrset_parse;
|
||||
struct local_rrset;
|
||||
|
||||
/** calculate the prefetch TTL as 90% of original. Calculation
|
||||
* without numerical overflow (uin32_t) */
|
||||
@ -73,6 +74,23 @@ struct query_info {
|
||||
uint16_t qtype;
|
||||
/** qclass, host byte order */
|
||||
uint16_t qclass;
|
||||
/**
|
||||
* Alias local answer(s) for the qname. If 'qname' is an alias defined
|
||||
* in a local zone, this field will be set to the corresponding local
|
||||
* RRset when the alias is determined.
|
||||
* In the initial implementation this can only be a single CNAME RR
|
||||
* (or NULL), but it could possibly be extended to be a DNAME or a
|
||||
* chain of aliases.
|
||||
* Users of this structure are responsible to initialize this field
|
||||
* to be NULL; otherwise other part of query handling code may be
|
||||
* confused.
|
||||
* Users also have to be careful about the lifetime of data. On return
|
||||
* from local zone lookup, it may point to data derived from
|
||||
* configuration that may be dynamically invalidated or data allocated
|
||||
* in an ephemeral regional allocator. A deep copy of the data may
|
||||
* have to be generated if it has to be kept during iterative
|
||||
* resolution. */
|
||||
struct local_rrset* local_alias;
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -2328,6 +2328,7 @@ probe_anchor(struct module_env* env, struct trust_anchor* tp)
|
||||
qinfo.qname_len = tp->namelen;
|
||||
qinfo.qtype = LDNS_RR_TYPE_DNSKEY;
|
||||
qinfo.qclass = tp->dclass;
|
||||
qinfo.local_alias = NULL;
|
||||
log_query_info(VERB_ALGO, "autotrust probe", &qinfo);
|
||||
verbose(VERB_ALGO, "retry probe set in %d seconds",
|
||||
(int)tp->autr->next_probe_time - (int)*env->now);
|
||||
|
@ -377,6 +377,7 @@ generate_request(struct module_qstate* qstate, int id, uint8_t* name,
|
||||
ask.qname_len = namelen;
|
||||
ask.qtype = qtype;
|
||||
ask.qclass = qclass;
|
||||
ask.local_alias = NULL;
|
||||
log_query_info(VERB_ALGO, "generate request", &ask);
|
||||
fptr_ok(fptr_whitelist_modenv_attach_sub(qstate->env->attach_sub));
|
||||
/* enable valrec flag to avoid recursion to the same validation
|
||||
|
Loading…
Reference in New Issue
Block a user