- 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:
Wouter Wijngaards 2016-10-18 13:18:20 +00:00
parent 70dbd1c382
commit 503df095b2
16 changed files with 314 additions and 25 deletions

View File

@ -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;

View File

@ -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 */

View File

@ -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.

View File

@ -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. */

View File

@ -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;

View File

@ -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)

View File

@ -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,

View File

@ -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;
}

View File

@ -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.

View File

@ -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));

View File

@ -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);

View File

@ -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);
}

View File

@ -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;
}

View File

@ -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;
};
/**

View File

@ -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);

View File

@ -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