mirror of
https://github.com/NLnetLabs/unbound.git
synced 2024-09-21 06:37:08 +00:00
- Added generic EDNS code for registering known EDNS option codes,
bypassing the cache response stage and uniquifying mesh states. Four EDNS option lists were added to module_qstate (module_qstate.edns_opts_*) to store EDNS options from/to front/back side. - Added two flags to module_qstate (no_cache_lookup, no_cache_store) that control the modules' cache interactions. - Added code for registering inplace callback functions. The registered functions can be called just before replying with local data or Chaos, replying from cache, replying with SERVFAIL, replying with a resolved query, sending a query to a nameserver. The functions can inspect the available data and maybe change response/query related data (i.e. append EDNS options). - Updated Python module for the above. - Updated Python documentation. git-svn-id: file:///svn/unbound/trunk@3947 be551aaa-1e26-0410-a405-d3ace91eadb9
This commit is contained in:
parent
3e1ff464f1
commit
7b948b0647
@ -547,8 +547,8 @@ cachedb_handle_query(struct module_qstate* qstate,
|
||||
return;
|
||||
}
|
||||
|
||||
if(qstate->blacklist) {
|
||||
/* cache is blacklisted */
|
||||
if(qstate->blacklist || qstate->no_cache_lookup) {
|
||||
/* cache is blacklisted or we are instructed from edns to not look */
|
||||
/* pass request to next module */
|
||||
qstate->ext_state[id] = module_wait_module;
|
||||
return;
|
||||
@ -600,8 +600,8 @@ static void
|
||||
cachedb_handle_response(struct module_qstate* qstate,
|
||||
struct cachedb_qstate* ATTR_UNUSED(iq), struct cachedb_env* ie, int id)
|
||||
{
|
||||
/* check if we are enabled, and skip if not */
|
||||
if(!ie->enabled) {
|
||||
/* check if we are not enabled or instructed to not cache, and skip */
|
||||
if(!ie->enabled || qstate->no_cache_store) {
|
||||
/* we are done with the query */
|
||||
qstate->ext_state[id] = module_finished;
|
||||
return;
|
||||
|
@ -249,9 +249,16 @@ daemon_init(void)
|
||||
free(daemon);
|
||||
return NULL;
|
||||
}
|
||||
/* init edns_known_options */
|
||||
if(!edns_known_options_init(daemon->env)) {
|
||||
free(daemon->env);
|
||||
free(daemon);
|
||||
return NULL;
|
||||
}
|
||||
alloc_init(&daemon->superalloc, NULL, 0);
|
||||
daemon->acl = acl_list_create();
|
||||
if(!daemon->acl) {
|
||||
edns_known_options_delete(daemon->env);
|
||||
free(daemon->env);
|
||||
free(daemon);
|
||||
return NULL;
|
||||
@ -348,6 +355,7 @@ static void daemon_setup_modules(struct daemon* daemon)
|
||||
daemon->env)) {
|
||||
fatal_exit("failed to setup modules");
|
||||
}
|
||||
log_edns_known_options(VERB_ALGO, daemon->env);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -644,6 +652,8 @@ daemon_delete(struct daemon* daemon)
|
||||
slabhash_delete(daemon->env->msg_cache);
|
||||
rrset_cache_delete(daemon->env->rrset_cache);
|
||||
infra_delete(daemon->env->infra_cache);
|
||||
edns_known_options_delete(daemon->env);
|
||||
inplace_cb_lists_delete(daemon->env);
|
||||
}
|
||||
ub_randfree(daemon->rand);
|
||||
alloc_clear(&daemon->superalloc);
|
||||
|
145
daemon/worker.c
145
daemon/worker.c
@ -460,8 +460,9 @@ answer_norec_from_cache(struct worker* worker, struct query_info* qinfo,
|
||||
edns->udp_size = EDNS_ADVERTISED_SIZE;
|
||||
edns->ext_rcode = 0;
|
||||
edns->bits &= EDNS_DO;
|
||||
if(!edns_opt_inplace_reply(edns, worker->scratchpad))
|
||||
return 0;
|
||||
if(!inplace_cb_reply_servfail_call(&worker->env, qinfo, NULL,
|
||||
msg->rep, LDNS_RCODE_SERVFAIL, edns, worker->scratchpad))
|
||||
return 0;
|
||||
error_encode(repinfo->c->buffer, LDNS_RCODE_SERVFAIL,
|
||||
&msg->qinfo, id, flags, edns);
|
||||
if(worker->stats.extended) {
|
||||
@ -489,12 +490,16 @@ answer_norec_from_cache(struct worker* worker, struct query_info* qinfo,
|
||||
edns->udp_size = EDNS_ADVERTISED_SIZE;
|
||||
edns->ext_rcode = 0;
|
||||
edns->bits &= EDNS_DO;
|
||||
if(!edns_opt_inplace_reply(edns, worker->scratchpad))
|
||||
return 0;
|
||||
if(!inplace_cb_reply_cache_call(&worker->env, qinfo, NULL, msg->rep,
|
||||
flags & LDNS_RCODE_MASK, edns, worker->scratchpad))
|
||||
return 0;
|
||||
msg->rep->flags |= BIT_QR|BIT_RA;
|
||||
if(!reply_info_answer_encode(&msg->qinfo, msg->rep, id, flags,
|
||||
repinfo->c->buffer, 0, 1, worker->scratchpad,
|
||||
udpsize, edns, (int)(edns->bits & EDNS_DO), secure)) {
|
||||
if(!inplace_cb_reply_servfail_call(&worker->env, qinfo, NULL, NULL,
|
||||
LDNS_RCODE_SERVFAIL, edns, worker->scratchpad))
|
||||
edns->opt_list = NULL;
|
||||
error_encode(repinfo->c->buffer, LDNS_RCODE_SERVFAIL,
|
||||
&msg->qinfo, id, flags, edns);
|
||||
}
|
||||
@ -559,8 +564,9 @@ answer_from_cache(struct worker* worker, struct query_info* qinfo,
|
||||
edns->udp_size = EDNS_ADVERTISED_SIZE;
|
||||
edns->ext_rcode = 0;
|
||||
edns->bits &= EDNS_DO;
|
||||
if(!edns_opt_inplace_reply(edns, worker->scratchpad))
|
||||
return 0;
|
||||
if(!inplace_cb_reply_servfail_call(&worker->env, qinfo, NULL, rep,
|
||||
LDNS_RCODE_SERVFAIL, edns, worker->scratchpad))
|
||||
return 0;
|
||||
error_encode(repinfo->c->buffer, LDNS_RCODE_SERVFAIL,
|
||||
qinfo, id, flags, edns);
|
||||
rrset_array_unlock_touch(worker->env.rrset_cache,
|
||||
@ -591,11 +597,15 @@ answer_from_cache(struct worker* worker, struct query_info* qinfo,
|
||||
edns->udp_size = EDNS_ADVERTISED_SIZE;
|
||||
edns->ext_rcode = 0;
|
||||
edns->bits &= EDNS_DO;
|
||||
if(!edns_opt_inplace_reply(edns, worker->scratchpad))
|
||||
return 0;
|
||||
if(!inplace_cb_reply_cache_call(&worker->env, qinfo, NULL, rep,
|
||||
flags & LDNS_RCODE_MASK, edns, worker->scratchpad))
|
||||
return 0;
|
||||
if(!reply_info_answer_encode(qinfo, rep, id, flags,
|
||||
repinfo->c->buffer, timenow, 1, worker->scratchpad,
|
||||
udpsize, edns, (int)(edns->bits & EDNS_DO), secure)) {
|
||||
if(!inplace_cb_reply_servfail_call(&worker->env, qinfo, NULL, NULL,
|
||||
LDNS_RCODE_SERVFAIL, edns, worker->scratchpad))
|
||||
edns->opt_list = NULL;
|
||||
error_encode(repinfo->c->buffer, LDNS_RCODE_SERVFAIL,
|
||||
qinfo, id, flags, edns);
|
||||
}
|
||||
@ -667,8 +677,9 @@ chaos_replystr(sldns_buffer* pkt, const char* str, struct edns_data* edns,
|
||||
edns->edns_version = EDNS_ADVERTISED_VERSION;
|
||||
edns->udp_size = EDNS_ADVERTISED_SIZE;
|
||||
edns->bits &= EDNS_DO;
|
||||
if(!edns_opt_inplace_reply(edns, worker->scratchpad))
|
||||
edns->opt_list = NULL;
|
||||
if(!inplace_cb_reply_local_call(&worker->env, NULL, NULL, NULL,
|
||||
LDNS_RCODE_NOERROR, edns, worker->scratchpad))
|
||||
edns->opt_list = NULL;
|
||||
attach_edns_record(pkt, edns);
|
||||
}
|
||||
|
||||
@ -919,9 +930,9 @@ worker_handle_request(struct comm_point* c, void* arg, int error,
|
||||
regional_free_all(worker->scratchpad);
|
||||
goto send_reply;
|
||||
}
|
||||
if(local_zones_answer(worker->daemon->local_zones, &qinfo, &edns,
|
||||
c->buffer, worker->scratchpad, repinfo,
|
||||
acladdr->taglist, acladdr->taglen, acladdr->tag_actions,
|
||||
if(local_zones_answer(worker->daemon->local_zones, &worker->env, &qinfo,
|
||||
&edns, c->buffer, worker->scratchpad, repinfo, acladdr->taglist,
|
||||
acladdr->taglen, acladdr->tag_actions,
|
||||
acladdr->tag_actions_size, acladdr->tag_datas,
|
||||
acladdr->tag_datas_size, worker->daemon->cfg->tagname,
|
||||
worker->daemon->cfg->num_tags, acladdr->view)) {
|
||||
@ -981,48 +992,50 @@ worker_handle_request(struct comm_point* c, void* arg, int error,
|
||||
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 */
|
||||
if(answer_from_cache(worker, &qinfo,
|
||||
(struct reply_info*)e->data,
|
||||
*(uint16_t*)(void *)sldns_buffer_begin(c->buffer),
|
||||
sldns_buffer_read_u16_at(c->buffer, 2), repinfo,
|
||||
&edns)) {
|
||||
/* prefetch it if the prefetch TTL expired */
|
||||
if((worker->env.cfg->prefetch || worker->env.cfg->serve_expired)
|
||||
&& *worker->env.now >=
|
||||
((struct reply_info*)e->data)->prefetch_ttl) {
|
||||
time_t leeway = ((struct reply_info*)e->
|
||||
data)->ttl - *worker->env.now;
|
||||
if(((struct reply_info*)e->data)->ttl
|
||||
< *worker->env.now)
|
||||
leeway = 0;
|
||||
if(!edns_bypass_cache_stage(edns.opt_list, &worker->env)) {
|
||||
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 */
|
||||
if(answer_from_cache(worker, &qinfo,
|
||||
(struct reply_info*)e->data,
|
||||
*(uint16_t*)(void *)sldns_buffer_begin(c->buffer),
|
||||
sldns_buffer_read_u16_at(c->buffer, 2), repinfo,
|
||||
&edns)) {
|
||||
/* prefetch it if the prefetch TTL expired */
|
||||
if((worker->env.cfg->prefetch || worker->env.cfg->serve_expired)
|
||||
&& *worker->env.now >=
|
||||
((struct reply_info*)e->data)->prefetch_ttl) {
|
||||
time_t leeway = ((struct reply_info*)e->
|
||||
data)->ttl - *worker->env.now;
|
||||
if(((struct reply_info*)e->data)->ttl
|
||||
< *worker->env.now)
|
||||
leeway = 0;
|
||||
lock_rw_unlock(&e->lock);
|
||||
reply_and_prefetch(worker, &qinfo,
|
||||
sldns_buffer_read_u16_at(c->buffer, 2),
|
||||
repinfo, leeway);
|
||||
rc = 0;
|
||||
regional_free_all(worker->scratchpad);
|
||||
goto send_reply_rc;
|
||||
}
|
||||
lock_rw_unlock(&e->lock);
|
||||
reply_and_prefetch(worker, &qinfo,
|
||||
sldns_buffer_read_u16_at(c->buffer, 2),
|
||||
repinfo, leeway);
|
||||
rc = 0;
|
||||
regional_free_all(worker->scratchpad);
|
||||
goto send_reply_rc;
|
||||
goto send_reply;
|
||||
}
|
||||
verbose(VERB_ALGO, "answer from the cache failed");
|
||||
lock_rw_unlock(&e->lock);
|
||||
regional_free_all(worker->scratchpad);
|
||||
goto send_reply;
|
||||
}
|
||||
verbose(VERB_ALGO, "answer from the cache failed");
|
||||
lock_rw_unlock(&e->lock);
|
||||
}
|
||||
if(!LDNS_RD_WIRE(sldns_buffer_begin(c->buffer))) {
|
||||
if(answer_norec_from_cache(worker, &qinfo,
|
||||
*(uint16_t*)(void *)sldns_buffer_begin(c->buffer),
|
||||
sldns_buffer_read_u16_at(c->buffer, 2), repinfo,
|
||||
&edns)) {
|
||||
regional_free_all(worker->scratchpad);
|
||||
goto send_reply;
|
||||
if(!LDNS_RD_WIRE(sldns_buffer_begin(c->buffer))) {
|
||||
if(answer_norec_from_cache(worker, &qinfo,
|
||||
*(uint16_t*)(void *)sldns_buffer_begin(c->buffer),
|
||||
sldns_buffer_read_u16_at(c->buffer, 2), repinfo,
|
||||
&edns)) {
|
||||
regional_free_all(worker->scratchpad);
|
||||
goto send_reply;
|
||||
}
|
||||
verbose(VERB_ALGO, "answer norec from cache -- "
|
||||
"need to validate or not primed");
|
||||
}
|
||||
verbose(VERB_ALGO, "answer norec from cache -- "
|
||||
"need to validate or not primed");
|
||||
}
|
||||
sldns_buffer_rewind(c->buffer);
|
||||
server_stats_querymiss(&worker->stats, worker);
|
||||
@ -1376,11 +1389,10 @@ worker_delete(struct worker* worker)
|
||||
}
|
||||
|
||||
struct outbound_entry*
|
||||
worker_send_query(uint8_t* qname, size_t qnamelen, uint16_t qtype,
|
||||
uint16_t qclass, uint16_t flags, int dnssec, int want_dnssec,
|
||||
int nocaps, struct edns_option* opt_list,
|
||||
struct sockaddr_storage* addr, socklen_t addrlen, uint8_t* zone,
|
||||
size_t zonelen, int ssl_upstream, struct module_qstate* q)
|
||||
worker_send_query(struct query_info* qinfo, uint16_t flags, int dnssec,
|
||||
int want_dnssec, int nocaps, struct sockaddr_storage* addr,
|
||||
socklen_t addrlen, uint8_t* zone, size_t zonelen, int ssl_upstream,
|
||||
struct module_qstate* q)
|
||||
{
|
||||
struct worker* worker = q->env->worker;
|
||||
struct outbound_entry* e = (struct outbound_entry*)regional_alloc(
|
||||
@ -1388,11 +1400,10 @@ worker_send_query(uint8_t* qname, size_t qnamelen, uint16_t qtype,
|
||||
if(!e)
|
||||
return NULL;
|
||||
e->qstate = q;
|
||||
e->qsent = outnet_serviced_query(worker->back, qname,
|
||||
qnamelen, qtype, qclass, flags, dnssec, want_dnssec, nocaps,
|
||||
q->env->cfg->tcp_upstream, ssl_upstream, opt_list,
|
||||
addr, addrlen, zone, zonelen, worker_handle_service_reply, e,
|
||||
worker->back->udp_buff);
|
||||
e->qsent = outnet_serviced_query(worker->back, qinfo, flags, dnssec,
|
||||
want_dnssec, nocaps, q->env->cfg->tcp_upstream,
|
||||
ssl_upstream, addr, addrlen, zone, zonelen, q,
|
||||
worker_handle_service_reply, e, worker->back->udp_buff, q->env);
|
||||
if(!e->qsent) {
|
||||
return NULL;
|
||||
}
|
||||
@ -1432,15 +1443,13 @@ void worker_stop_accept(void* arg)
|
||||
}
|
||||
|
||||
/* --- fake callbacks for fptr_wlist to work --- */
|
||||
struct outbound_entry* libworker_send_query(uint8_t* ATTR_UNUSED(qname),
|
||||
size_t ATTR_UNUSED(qnamelen), uint16_t ATTR_UNUSED(qtype),
|
||||
uint16_t ATTR_UNUSED(qclass), uint16_t ATTR_UNUSED(flags),
|
||||
int ATTR_UNUSED(dnssec), int ATTR_UNUSED(want_dnssec),
|
||||
int ATTR_UNUSED(nocaps), struct edns_option* ATTR_UNUSED(opt_list),
|
||||
struct sockaddr_storage* ATTR_UNUSED(addr),
|
||||
socklen_t ATTR_UNUSED(addrlen), uint8_t* ATTR_UNUSED(zone),
|
||||
size_t ATTR_UNUSED(zonelen), int ATTR_UNUSED(ssl_upstream),
|
||||
struct module_qstate* ATTR_UNUSED(q))
|
||||
struct outbound_entry* libworker_send_query(
|
||||
struct query_info* ATTR_UNUSED(qinfo),
|
||||
uint16_t ATTR_UNUSED(flags), int ATTR_UNUSED(dnssec),
|
||||
int ATTR_UNUSED(want_dnssec), int ATTR_UNUSED(nocaps),
|
||||
struct sockaddr_storage* ATTR_UNUSED(addr), socklen_t ATTR_UNUSED(addrlen),
|
||||
uint8_t* ATTR_UNUSED(zone), size_t ATTR_UNUSED(zonelen),
|
||||
int ATTR_UNUSED(ssl_upstream), struct module_qstate* ATTR_UNUSED(q))
|
||||
{
|
||||
log_assert(0);
|
||||
return 0;
|
||||
|
@ -61,6 +61,7 @@ struct ub_randstate;
|
||||
struct regional;
|
||||
struct tube;
|
||||
struct daemon_remote;
|
||||
struct query_info;
|
||||
|
||||
/** worker commands */
|
||||
enum worker_commands {
|
||||
|
@ -825,8 +825,9 @@ dns64_inform_super(struct module_qstate* qstate, int id,
|
||||
}
|
||||
|
||||
/* Store the generated response in cache. */
|
||||
if (!dns_cache_store(super->env, &super->qinfo, super->return_msg->rep,
|
||||
0, 0, 0, NULL, super->query_flags))
|
||||
if (!super->no_cache_store &&
|
||||
!dns_cache_store(super->env, &super->qinfo, super->return_msg->rep,
|
||||
0, 0, 0, NULL, super->query_flags))
|
||||
log_err("out of memory");
|
||||
}
|
||||
|
||||
|
@ -1,3 +1,19 @@
|
||||
6 December 2016: George
|
||||
- Added generic EDNS code for registering known EDNS option codes,
|
||||
bypassing the cache response stage and uniquifying mesh states. Four EDNS
|
||||
option lists were added to module_qstate (module_qstate.edns_opts_*) to
|
||||
store EDNS options from/to front/back side.
|
||||
- Added two flags to module_qstate (no_cache_lookup, no_cache_store) that
|
||||
control the modules' cache interactions.
|
||||
- Added code for registering inplace callback functions. The registered
|
||||
functions can be called just before replying with local data or Chaos,
|
||||
replying from cache, replying with SERVFAIL, replying with a resolved
|
||||
query, sending a query to a nameserver. The functions can inspect the
|
||||
available data and maybe change response/query related data (i.e. append
|
||||
EDNS options).
|
||||
- Updated Python module for the above.
|
||||
- Updated Python documentation.
|
||||
|
||||
5 December 2016: Ralph
|
||||
- Fix #1173: differ local-zone type deny from unset
|
||||
tag_actions element.
|
||||
|
@ -230,9 +230,8 @@ error_supers(struct module_qstate* qstate, int id, struct module_qstate* super)
|
||||
return;
|
||||
} else {
|
||||
/* see if the failure did get (parent-lame) info */
|
||||
if(!cache_fill_missing(super->env,
|
||||
super_iq->qchase.qclass, super->region,
|
||||
super_iq->dp))
|
||||
if(!cache_fill_missing(super->env, super_iq->qchase.qclass,
|
||||
super->region, super_iq->dp))
|
||||
log_err("out of memory adding missing");
|
||||
}
|
||||
dpns->resolved = 1; /* mark as failed */
|
||||
@ -278,27 +277,29 @@ error_response(struct module_qstate* qstate, int id, int rcode)
|
||||
static int
|
||||
error_response_cache(struct module_qstate* qstate, int id, int rcode)
|
||||
{
|
||||
/* store in cache */
|
||||
struct reply_info err;
|
||||
if(qstate->prefetch_leeway > NORR_TTL) {
|
||||
verbose(VERB_ALGO, "error response for prefetch in cache");
|
||||
/* attempt to adjust the cache entry prefetch */
|
||||
if(dns_cache_prefetch_adjust(qstate->env, &qstate->qinfo,
|
||||
NORR_TTL, qstate->query_flags))
|
||||
return error_response(qstate, id, rcode);
|
||||
/* if that fails (not in cache), fall through to store err */
|
||||
if(!qstate->no_cache_store) {
|
||||
/* store in cache */
|
||||
struct reply_info err;
|
||||
if(qstate->prefetch_leeway > NORR_TTL) {
|
||||
verbose(VERB_ALGO, "error response for prefetch in cache");
|
||||
/* attempt to adjust the cache entry prefetch */
|
||||
if(dns_cache_prefetch_adjust(qstate->env, &qstate->qinfo,
|
||||
NORR_TTL, qstate->query_flags))
|
||||
return error_response(qstate, id, rcode);
|
||||
/* if that fails (not in cache), fall through to store err */
|
||||
}
|
||||
memset(&err, 0, sizeof(err));
|
||||
err.flags = (uint16_t)(BIT_QR | BIT_RA);
|
||||
FLAGS_SET_RCODE(err.flags, rcode);
|
||||
err.qdcount = 1;
|
||||
err.ttl = NORR_TTL;
|
||||
err.prefetch_ttl = PREFETCH_TTL_CALC(err.ttl);
|
||||
/* do not waste time trying to validate this servfail */
|
||||
err.security = sec_status_indeterminate;
|
||||
verbose(VERB_ALGO, "store error response in message cache");
|
||||
iter_dns_store(qstate->env, &qstate->qinfo, &err, 0, 0, 0, NULL,
|
||||
qstate->query_flags);
|
||||
}
|
||||
memset(&err, 0, sizeof(err));
|
||||
err.flags = (uint16_t)(BIT_QR | BIT_RA);
|
||||
FLAGS_SET_RCODE(err.flags, rcode);
|
||||
err.qdcount = 1;
|
||||
err.ttl = NORR_TTL;
|
||||
err.prefetch_ttl = PREFETCH_TTL_CALC(err.ttl);
|
||||
/* do not waste time trying to validate this servfail */
|
||||
err.security = sec_status_indeterminate;
|
||||
verbose(VERB_ALGO, "store error response in message cache");
|
||||
iter_dns_store(qstate->env, &qstate->qinfo, &err, 0, 0, 0, NULL,
|
||||
qstate->query_flags);
|
||||
return error_response(qstate, id, rcode);
|
||||
}
|
||||
|
||||
@ -969,7 +970,7 @@ processInitRequest(struct module_qstate* qstate, struct iter_qstate* iq,
|
||||
{
|
||||
uint8_t* delname;
|
||||
size_t delnamelen;
|
||||
struct dns_msg* msg;
|
||||
struct dns_msg* msg = NULL;
|
||||
|
||||
log_query_info(VERB_DETAIL, "resolving", &qstate->qinfo);
|
||||
/* check effort */
|
||||
@ -1009,13 +1010,13 @@ processInitRequest(struct module_qstate* qstate, struct iter_qstate* iq,
|
||||
* getting older results from cache is a bad idea, no cache */
|
||||
verbose(VERB_ALGO, "cache blacklisted, going to the network");
|
||||
msg = NULL;
|
||||
} else {
|
||||
} else if(!qstate->no_cache_lookup) {
|
||||
msg = dns_cache_lookup(qstate->env, iq->qchase.qname,
|
||||
iq->qchase.qname_len, iq->qchase.qtype,
|
||||
iq->qchase.qclass, qstate->query_flags,
|
||||
qstate->region, qstate->env->scratch);
|
||||
if(!msg && qstate->env->neg_cache) {
|
||||
/* lookup in negative cache; may result in
|
||||
/* lookup in negative cache; may result in
|
||||
* NOERROR/NODATA or NXDOMAIN answers that need validation */
|
||||
msg = val_neg_getmsg(qstate->env->neg_cache, &iq->qchase,
|
||||
qstate->region, qstate->env->rrset_cache,
|
||||
@ -1701,10 +1702,11 @@ processLastResort(struct module_qstate* qstate, struct iter_qstate* iq,
|
||||
|
||||
/* if this was a parent-side glue query itself, then store that
|
||||
* failure in cache. */
|
||||
if(iq->query_for_pside_glue && !iq->pside_glue)
|
||||
iter_store_parentside_neg(qstate->env, &qstate->qinfo,
|
||||
iq->deleg_msg?iq->deleg_msg->rep:
|
||||
(iq->response?iq->response->rep:NULL));
|
||||
if(!qstate->no_cache_store && iq->query_for_pside_glue
|
||||
&& !iq->pside_glue)
|
||||
iter_store_parentside_neg(qstate->env, &qstate->qinfo,
|
||||
iq->deleg_msg?iq->deleg_msg->rep:
|
||||
(iq->response?iq->response->rep:NULL));
|
||||
|
||||
verbose(VERB_QUERY, "out of query targets -- returning SERVFAIL");
|
||||
/* fail -- no more targets, no more hope of targets, no hope
|
||||
@ -1788,8 +1790,6 @@ processQueryTargets(struct module_qstate* qstate, struct iter_qstate* iq,
|
||||
int tf_policy;
|
||||
struct delegpt_addr* target;
|
||||
struct outbound_entry* outq;
|
||||
/* EDNS options to set on outgoing packet */
|
||||
struct edns_option* opt_list = NULL;
|
||||
|
||||
/* NOTE: a request will encounter this state for each target it
|
||||
* needs to send a query to. That is, at least one per referral,
|
||||
@ -2070,7 +2070,7 @@ processQueryTargets(struct module_qstate* qstate, struct iter_qstate* iq,
|
||||
|| iq->qchase.qtype == LDNS_RR_TYPE_A)))
|
||||
/* Stop minimising this query, resolve "as usual" */
|
||||
iq->minimisation_state = DONOT_MINIMISE_STATE;
|
||||
else {
|
||||
else if(!qstate->no_cache_lookup) {
|
||||
struct dns_msg* msg = dns_cache_lookup(qstate->env,
|
||||
iq->qinfo_out.qname, iq->qinfo_out.qname_len,
|
||||
iq->qinfo_out.qtype, iq->qinfo_out.qclass,
|
||||
@ -2108,9 +2108,7 @@ processQueryTargets(struct module_qstate* qstate, struct iter_qstate* iq,
|
||||
iq->dnssec_lame_query?" but lame_query anyway": "");
|
||||
}
|
||||
fptr_ok(fptr_whitelist_modenv_send_query(qstate->env->send_query));
|
||||
outq = (*qstate->env->send_query)(
|
||||
iq->qinfo_out.qname, iq->qinfo_out.qname_len,
|
||||
iq->qinfo_out.qtype, iq->qinfo_out.qclass,
|
||||
outq = (*qstate->env->send_query)(&iq->qinfo_out,
|
||||
iq->chase_flags | (iq->chase_to_rd?BIT_RD:0),
|
||||
/* unset CD if to forwarder(RD set) and not dnssec retry
|
||||
* (blacklist nonempty) and no trust-anchors are configured
|
||||
@ -2119,7 +2117,7 @@ processQueryTargets(struct module_qstate* qstate, struct iter_qstate* iq,
|
||||
!qstate->blacklist&&(!iter_indicates_dnssec_fwd(qstate->env,
|
||||
&iq->qinfo_out)||target->attempts==1)?0:BIT_CD),
|
||||
iq->dnssec_expected, iq->caps_fallback || is_caps_whitelisted(
|
||||
ie, iq), opt_list, &target->addr, target->addrlen,
|
||||
ie, iq), &target->addr, target->addrlen,
|
||||
iq->dp->name, iq->dp->namelen,
|
||||
(iq->dp->ssl_upstream || qstate->env->cfg->ssl_upstream), qstate);
|
||||
if(!outq) {
|
||||
@ -2262,10 +2260,11 @@ processQueryResponse(struct module_qstate* qstate, struct iter_qstate* iq,
|
||||
iq->num_target_queries = 0;
|
||||
return processDSNSFind(qstate, iq, id);
|
||||
}
|
||||
iter_dns_store(qstate->env, &iq->response->qinfo,
|
||||
iq->response->rep, 0, qstate->prefetch_leeway,
|
||||
iq->dp&&iq->dp->has_parent_side_NS,
|
||||
qstate->region, qstate->query_flags);
|
||||
if(!qstate->no_cache_store)
|
||||
iter_dns_store(qstate->env, &iq->response->qinfo,
|
||||
iq->response->rep, 0, qstate->prefetch_leeway,
|
||||
iq->dp&&iq->dp->has_parent_side_NS,
|
||||
qstate->region, qstate->query_flags);
|
||||
/* close down outstanding requests to be discarded */
|
||||
outbound_list_clear(&iq->outlist);
|
||||
iq->num_current_queries = 0;
|
||||
@ -2333,7 +2332,8 @@ processQueryResponse(struct module_qstate* qstate, struct iter_qstate* iq,
|
||||
}
|
||||
|
||||
/* if hardened, only store referral if we asked for it */
|
||||
if(!qstate->env->cfg->harden_referral_path ||
|
||||
if(!qstate->no_cache_store &&
|
||||
(!qstate->env->cfg->harden_referral_path ||
|
||||
( qstate->qinfo.qtype == LDNS_RR_TYPE_NS
|
||||
&& (qstate->query_flags&BIT_RD)
|
||||
&& !(qstate->query_flags&BIT_CD)
|
||||
@ -2348,7 +2348,7 @@ processQueryResponse(struct module_qstate* qstate, struct iter_qstate* iq,
|
||||
iq->qchase.qname, iq->qchase.qname_len,
|
||||
LDNS_RR_TYPE_NS, iq->qchase.qclass)
|
||||
)
|
||||
)) {
|
||||
))) {
|
||||
/* Store the referral under the current query */
|
||||
/* no prefetch-leeway, since its not the answer */
|
||||
iter_dns_store(qstate->env, &iq->response->qinfo,
|
||||
@ -2361,16 +2361,17 @@ processQueryResponse(struct module_qstate* qstate, struct iter_qstate* iq,
|
||||
iq->response->rep, iq->dp->name);
|
||||
}
|
||||
/* store parent-side-in-zone-glue, if directly queried for */
|
||||
if(iq->query_for_pside_glue && !iq->pside_glue) {
|
||||
iq->pside_glue = reply_find_rrset(iq->response->rep,
|
||||
iq->qchase.qname, iq->qchase.qname_len,
|
||||
iq->qchase.qtype, iq->qchase.qclass);
|
||||
if(iq->pside_glue) {
|
||||
log_rrset_key(VERB_ALGO, "found parent-side "
|
||||
"glue", iq->pside_glue);
|
||||
iter_store_parentside_rrset(qstate->env,
|
||||
iq->pside_glue);
|
||||
}
|
||||
if(!qstate->no_cache_store && iq->query_for_pside_glue
|
||||
&& !iq->pside_glue) {
|
||||
iq->pside_glue = reply_find_rrset(iq->response->rep,
|
||||
iq->qchase.qname, iq->qchase.qname_len,
|
||||
iq->qchase.qtype, iq->qchase.qclass);
|
||||
if(iq->pside_glue) {
|
||||
log_rrset_key(VERB_ALGO, "found parent-side "
|
||||
"glue", iq->pside_glue);
|
||||
iter_store_parentside_rrset(qstate->env,
|
||||
iq->pside_glue);
|
||||
}
|
||||
}
|
||||
|
||||
/* Reset the event state, setting the current delegation
|
||||
@ -2451,10 +2452,11 @@ processQueryResponse(struct module_qstate* qstate, struct iter_qstate* iq,
|
||||
/* NOTE : set referral=1, so that rrsets get stored but not
|
||||
* the partial query answer (CNAME only). */
|
||||
/* prefetchleeway applied because this updates answer parts */
|
||||
iter_dns_store(qstate->env, &iq->response->qinfo,
|
||||
iq->response->rep, 1, qstate->prefetch_leeway,
|
||||
iq->dp&&iq->dp->has_parent_side_NS, NULL,
|
||||
qstate->query_flags);
|
||||
if(!qstate->no_cache_store)
|
||||
iter_dns_store(qstate->env, &iq->response->qinfo,
|
||||
iq->response->rep, 1, qstate->prefetch_leeway,
|
||||
iq->dp&&iq->dp->has_parent_side_NS, NULL,
|
||||
qstate->query_flags);
|
||||
/* set the current request's qname to the new value. */
|
||||
iq->qchase.qname = sname;
|
||||
iq->qchase.qname_len = snamelen;
|
||||
@ -2934,10 +2936,11 @@ processFinished(struct module_qstate* qstate, struct iter_qstate* iq,
|
||||
&qstate->qinfo);
|
||||
|
||||
/* store negative cache element for parent side glue. */
|
||||
if(iq->query_for_pside_glue && !iq->pside_glue)
|
||||
iter_store_parentside_neg(qstate->env, &qstate->qinfo,
|
||||
iq->deleg_msg?iq->deleg_msg->rep:
|
||||
(iq->response?iq->response->rep:NULL));
|
||||
if(!qstate->no_cache_store && iq->query_for_pside_glue
|
||||
&& !iq->pside_glue)
|
||||
iter_store_parentside_neg(qstate->env, &qstate->qinfo,
|
||||
iq->deleg_msg?iq->deleg_msg->rep:
|
||||
(iq->response?iq->response->rep:NULL));
|
||||
if(!iq->response) {
|
||||
verbose(VERB_ALGO, "No response is set, servfail");
|
||||
return error_response(qstate, id, LDNS_RCODE_SERVFAIL);
|
||||
@ -2973,7 +2976,7 @@ processFinished(struct module_qstate* qstate, struct iter_qstate* iq,
|
||||
/* store message with the finished prepended items,
|
||||
* but only if we did recursion. The nonrecursion referral
|
||||
* from cache does not need to be stored in the msg cache. */
|
||||
if(qstate->query_flags&BIT_RD) {
|
||||
if(!qstate->no_cache_store && qstate->query_flags&BIT_RD) {
|
||||
iter_dns_store(qstate->env, &qstate->qinfo,
|
||||
iq->response->rep, 0, qstate->prefetch_leeway,
|
||||
iq->dp&&iq->dp->has_parent_side_NS,
|
||||
@ -3148,6 +3151,18 @@ process_response(struct module_qstate* qstate, struct iter_qstate* iq,
|
||||
if(parse_extract_edns(prs, &edns, qstate->env->scratch) !=
|
||||
LDNS_RCODE_NOERROR)
|
||||
goto handle_it;
|
||||
|
||||
/* Copy the edns options we may got from the back end */
|
||||
if(edns.opt_list) {
|
||||
qstate->edns_opts_back_in = edns_opt_copy_region(edns.opt_list,
|
||||
qstate->region);
|
||||
if(!qstate->edns_opts_back_in) {
|
||||
log_err("out of memory on incoming message");
|
||||
/* like packet got dropped */
|
||||
goto handle_it;
|
||||
}
|
||||
}
|
||||
|
||||
/* remove CD-bit, we asked for in case we handle validation ourself */
|
||||
prs->flags &= ~BIT_CD;
|
||||
|
||||
|
@ -62,6 +62,7 @@ context_finalize(struct ub_ctx* ctx)
|
||||
config_apply(cfg);
|
||||
if(!modstack_setup(&ctx->mods, cfg->module_conf, ctx->env))
|
||||
return UB_INITFAIL;
|
||||
log_edns_known_options(VERB_ALGO, ctx->env);
|
||||
ctx->local_zones = local_zones_create();
|
||||
if(!ctx->local_zones)
|
||||
return UB_NOMEM;
|
||||
|
@ -132,6 +132,15 @@ static struct ub_ctx* ub_ctx_create_nopipe(void)
|
||||
errno = ENOMEM;
|
||||
return NULL;
|
||||
}
|
||||
/* init edns_known_options */
|
||||
if(!edns_known_options_init(ctx->env)) {
|
||||
config_delete(ctx->env->cfg);
|
||||
free(ctx->env);
|
||||
ub_randfree(ctx->seed_rnd);
|
||||
free(ctx);
|
||||
errno = ENOMEM;
|
||||
return NULL;
|
||||
}
|
||||
ctx->env->alloc = &ctx->superalloc;
|
||||
ctx->env->worker = NULL;
|
||||
ctx->env->need_to_validate = 0;
|
||||
@ -151,6 +160,7 @@ ub_ctx_create(void)
|
||||
ub_randfree(ctx->seed_rnd);
|
||||
config_delete(ctx->env->cfg);
|
||||
modstack_desetup(&ctx->mods, ctx->env);
|
||||
edns_known_options_delete(ctx->env);
|
||||
free(ctx->env);
|
||||
free(ctx);
|
||||
errno = e;
|
||||
@ -162,6 +172,7 @@ ub_ctx_create(void)
|
||||
ub_randfree(ctx->seed_rnd);
|
||||
config_delete(ctx->env->cfg);
|
||||
modstack_desetup(&ctx->mods, ctx->env);
|
||||
edns_known_options_delete(ctx->env);
|
||||
free(ctx->env);
|
||||
free(ctx);
|
||||
errno = e;
|
||||
@ -298,6 +309,8 @@ ub_ctx_delete(struct ub_ctx* ctx)
|
||||
rrset_cache_delete(ctx->env->rrset_cache);
|
||||
infra_delete(ctx->env->infra_cache);
|
||||
config_delete(ctx->env->cfg);
|
||||
edns_known_options_delete(ctx->env);
|
||||
inplace_cb_lists_delete(ctx->env);
|
||||
free(ctx->env);
|
||||
}
|
||||
ub_randfree(ctx->seed_rnd);
|
||||
|
@ -609,7 +609,7 @@ int libworker_fg(struct ub_ctx* ctx, struct ctx_query* q)
|
||||
/* see if there is a fixed answer */
|
||||
sldns_buffer_write_u16_at(w->back->udp_buff, 0, qid);
|
||||
sldns_buffer_write_u16_at(w->back->udp_buff, 2, qflags);
|
||||
if(local_zones_answer(ctx->local_zones, &qinfo, &edns,
|
||||
if(local_zones_answer(ctx->local_zones, w->env, &qinfo, &edns,
|
||||
w->back->udp_buff, w->env->scratch, NULL, NULL, 0, NULL, 0,
|
||||
NULL, 0, NULL, 0, NULL)) {
|
||||
regional_free_all(w->env->scratch);
|
||||
@ -680,7 +680,7 @@ int libworker_attach_mesh(struct ub_ctx* ctx, struct ctx_query* q,
|
||||
/* see if there is a fixed answer */
|
||||
sldns_buffer_write_u16_at(w->back->udp_buff, 0, qid);
|
||||
sldns_buffer_write_u16_at(w->back->udp_buff, 2, qflags);
|
||||
if(local_zones_answer(ctx->local_zones, &qinfo, &edns,
|
||||
if(local_zones_answer(ctx->local_zones, w->env, &qinfo, &edns,
|
||||
w->back->udp_buff, w->env->scratch, NULL, NULL, 0, NULL, 0,
|
||||
NULL, 0, NULL, 0, NULL)) {
|
||||
regional_free_all(w->env->scratch);
|
||||
@ -801,7 +801,7 @@ handle_newq(struct libworker* w, uint8_t* buf, uint32_t len)
|
||||
/* see if there is a fixed answer */
|
||||
sldns_buffer_write_u16_at(w->back->udp_buff, 0, qid);
|
||||
sldns_buffer_write_u16_at(w->back->udp_buff, 2, qflags);
|
||||
if(local_zones_answer(w->ctx->local_zones, &qinfo, &edns,
|
||||
if(local_zones_answer(w->ctx->local_zones, w->env, &qinfo, &edns,
|
||||
w->back->udp_buff, w->env->scratch, NULL, NULL, 0, NULL, 0,
|
||||
NULL, 0, NULL, 0, NULL)) {
|
||||
regional_free_all(w->env->scratch);
|
||||
@ -826,9 +826,8 @@ void libworker_alloc_cleanup(void* arg)
|
||||
slabhash_clear(w->env->msg_cache);
|
||||
}
|
||||
|
||||
struct outbound_entry* libworker_send_query(uint8_t* qname, size_t qnamelen,
|
||||
uint16_t qtype, uint16_t qclass, uint16_t flags, int dnssec,
|
||||
int want_dnssec, int nocaps, struct edns_option* opt_list,
|
||||
struct outbound_entry* libworker_send_query(struct query_info* qinfo,
|
||||
uint16_t flags, int dnssec, int want_dnssec, int nocaps,
|
||||
struct sockaddr_storage* addr, socklen_t addrlen, uint8_t* zone,
|
||||
size_t zonelen, int ssl_upstream, struct module_qstate* q)
|
||||
{
|
||||
@ -838,11 +837,10 @@ struct outbound_entry* libworker_send_query(uint8_t* qname, size_t qnamelen,
|
||||
if(!e)
|
||||
return NULL;
|
||||
e->qstate = q;
|
||||
e->qsent = outnet_serviced_query(w->back, qname,
|
||||
qnamelen, qtype, qclass, flags, dnssec, want_dnssec, nocaps,
|
||||
q->env->cfg->tcp_upstream, ssl_upstream, opt_list,
|
||||
addr, addrlen, zone, zonelen, libworker_handle_service_reply,
|
||||
e, w->back->udp_buff);
|
||||
e->qsent = outnet_serviced_query(w->back, qinfo, flags, dnssec,
|
||||
want_dnssec, nocaps, q->env->cfg->tcp_upstream, ssl_upstream,
|
||||
addr, addrlen, zone, zonelen, q, libworker_handle_service_reply,
|
||||
e, w->back->udp_buff, q->env);
|
||||
if(!e->qsent) {
|
||||
return NULL;
|
||||
}
|
||||
@ -957,15 +955,12 @@ void worker_sighandler(int ATTR_UNUSED(sig), void* ATTR_UNUSED(arg))
|
||||
log_assert(0);
|
||||
}
|
||||
|
||||
struct outbound_entry* worker_send_query(uint8_t* ATTR_UNUSED(qname),
|
||||
size_t ATTR_UNUSED(qnamelen), uint16_t ATTR_UNUSED(qtype),
|
||||
uint16_t ATTR_UNUSED(qclass), uint16_t ATTR_UNUSED(flags),
|
||||
int ATTR_UNUSED(dnssec), int ATTR_UNUSED(want_dnssec),
|
||||
int ATTR_UNUSED(nocaps), struct edns_option* ATTR_UNUSED(opt_list),
|
||||
struct sockaddr_storage* ATTR_UNUSED(addr),
|
||||
socklen_t ATTR_UNUSED(addrlen), uint8_t* ATTR_UNUSED(zone),
|
||||
size_t ATTR_UNUSED(zonelen), int ATTR_UNUSED(ssl_upstream),
|
||||
struct module_qstate* ATTR_UNUSED(q))
|
||||
struct outbound_entry* worker_send_query(struct query_info* ATTR_UNUSED(qinfo),
|
||||
uint16_t ATTR_UNUSED(flags), int ATTR_UNUSED(dnssec),
|
||||
int ATTR_UNUSED(want_dnssec), int ATTR_UNUSED(nocaps),
|
||||
struct sockaddr_storage* ATTR_UNUSED(addr), socklen_t ATTR_UNUSED(addrlen),
|
||||
uint8_t* ATTR_UNUSED(zone), size_t ATTR_UNUSED(zonelen),
|
||||
int ATTR_UNUSED(ssl_upstream), struct module_qstate* ATTR_UNUSED(q))
|
||||
{
|
||||
log_assert(0);
|
||||
return 0;
|
||||
|
@ -59,6 +59,7 @@ struct regional;
|
||||
struct tube;
|
||||
struct sldns_buffer;
|
||||
struct ub_event_base;
|
||||
struct query_info;
|
||||
|
||||
/**
|
||||
* The library-worker status structure
|
||||
|
@ -82,10 +82,13 @@ pygments_style = 'sphinx'
|
||||
# Options for HTML output
|
||||
# -----------------------
|
||||
|
||||
# The theme that the html output should use.
|
||||
html_theme = "classic"
|
||||
|
||||
# The style sheet to use for HTML and HTML Help pages. A file of that name
|
||||
# must exist either in Sphinx' static/ path, or in one of the custom paths
|
||||
# given in html_static_path.
|
||||
html_style = 'default.css'
|
||||
#html_style = 'default.css'
|
||||
|
||||
# The name for this set of Sphinx documents. If None, it defaults to
|
||||
# "<project> v<release> documentation".
|
||||
|
@ -1,26 +1,33 @@
|
||||
.. _example_resolve_name:
|
||||
|
||||
==============================
|
||||
Resolve a name
|
||||
==============================
|
||||
==============
|
||||
|
||||
This basic example shows how to create a context and resolve a host address (DNS record of A type).
|
||||
This basic example shows how to create a context and resolve a host address
|
||||
(DNS record of A type).
|
||||
|
||||
Source code
|
||||
-----------
|
||||
|
||||
::
|
||||
|
||||
#!/usr/bin/python
|
||||
import unbound
|
||||
|
||||
ctx = unbound.ub_ctx()
|
||||
ctx.resolvconf("/etc/resolv.conf")
|
||||
|
||||
status, result = ctx.resolve("www.google.com")
|
||||
if status == 0 and result.havedata:
|
||||
print "Result.data:", result.data.address_list
|
||||
elif status != 0:
|
||||
print "Resolve error:", unbound.ub_strerror(status)
|
||||
#!/usr/bin/python
|
||||
import unbound
|
||||
|
||||
In contrast with C API, the source code is more compact while the performance of C implementation is preserved.
|
||||
The main advantage is that you need not take care about the deallocation and allocation of context and result structures; pyUnbound module do it automatically for you.
|
||||
ctx = unbound.ub_ctx()
|
||||
ctx.resolvconf("/etc/resolv.conf")
|
||||
|
||||
If only domain name is given, the :meth:`unbound.ub_ctx.resolve` looks for A records in IN class.
|
||||
status, result = ctx.resolve("www.google.com")
|
||||
if status == 0 and result.havedata:
|
||||
print "Result.data:", result.data.address_list
|
||||
elif status != 0:
|
||||
print "Resolve error:", unbound.ub_strerror(status)
|
||||
|
||||
In contrast with the C API, the source code is more compact while the
|
||||
performance of C implementation is preserved.
|
||||
The main advantage is that you need not take care about the deallocation and
|
||||
allocation of context and result structures; pyUnbound module does it
|
||||
automatically for you.
|
||||
|
||||
If only domain name is given, the :meth:`unbound.ub_ctx.resolve` looks for
|
||||
A records in IN class.
|
||||
|
@ -1,33 +1,37 @@
|
||||
.. _example_reverse_lookup:
|
||||
|
||||
==============================
|
||||
Reverse DNS lookup
|
||||
==============================
|
||||
==================
|
||||
|
||||
Reverse DNS lookup involves determining the hostname associated with a given IP address.
|
||||
Reverse DNS lookup involves determining the hostname associated with a given IP
|
||||
address.
|
||||
This example shows how reverse lookup can be done using unbound module.
|
||||
|
||||
For the reverse DNS records, the special domain in-addr.arpa is reserved.
|
||||
For example, a host name for the IP address 74.125.43.147 can be obtained by issuing a DNS query for the PTR record for address 147.43.125.74.in-addr.arpa.
|
||||
For example, a host name for the IP address ``74.125.43.147`` can be obtained
|
||||
by issuing a DNS query for the PTR record for address
|
||||
``147.43.125.74.in-addr.arpa.``
|
||||
|
||||
Source code
|
||||
-----------
|
||||
|
||||
::
|
||||
|
||||
#!/usr/bin/python
|
||||
import unbound
|
||||
|
||||
ctx = unbound.ub_ctx()
|
||||
ctx.resolvconf("/etc/resolv.conf")
|
||||
|
||||
status, result = ctx.resolve(unbound.reverse("74.125.43.147") + ".in-addr.arpa.", unbound.RR_TYPE_PTR, unbound.RR_CLASS_IN)
|
||||
if status == 0 and result.havedata:
|
||||
print "Result.data:", result.data.domain_list
|
||||
elif status != 0:
|
||||
print "Resolve error:", unbound.ub_strerror(status)
|
||||
#!/usr/bin/python
|
||||
import unbound
|
||||
|
||||
In order to simplify the python code, unbound module contains function which reverses the hostname components.
|
||||
ctx = unbound.ub_ctx()
|
||||
ctx.resolvconf("/etc/resolv.conf")
|
||||
|
||||
status, result = ctx.resolve(unbound.reverse("74.125.43.147") + ".in-addr.arpa.", unbound.RR_TYPE_PTR, unbound.RR_CLASS_IN)
|
||||
if status == 0 and result.havedata:
|
||||
print "Result.data:", result.data.domain_list
|
||||
elif status != 0:
|
||||
print "Resolve error:", unbound.ub_strerror(status)
|
||||
|
||||
In order to simplify the python code, unbound module contains the
|
||||
:meth:`unbound.reverse` function which reverses the hostname components.
|
||||
This function is defined as follows::
|
||||
|
||||
def reverse(domain):
|
||||
return '.'.join([a for a in domain.split(".")][::-1])
|
||||
|
||||
|
||||
def reverse(domain):
|
||||
return '.'.join([a for a in domain.split(".")][::-1])
|
||||
|
@ -1,41 +1,41 @@
|
||||
.. _example_setup_ctx:
|
||||
|
||||
==============================
|
||||
Lookup from threads
|
||||
==============================
|
||||
===================
|
||||
|
||||
This example shows how to use unbound module from a threaded program.
|
||||
In this example, three lookup threads are created which work in background.
|
||||
Each thread resolves different DNS record.
|
||||
This example shows how to use unbound module from a threaded program.
|
||||
In this example, three lookup threads are created which work in background.
|
||||
Each thread resolves different DNS record.
|
||||
|
||||
Source code
|
||||
-----------
|
||||
|
||||
::
|
||||
|
||||
#!/usr/bin/python
|
||||
from unbound import ub_ctx, RR_TYPE_A, RR_CLASS_IN
|
||||
from threading import Thread
|
||||
|
||||
ctx = ub_ctx()
|
||||
ctx.resolvconf("/etc/resolv.conf")
|
||||
|
||||
class LookupThread(Thread):
|
||||
def __init__(self,ctx, name):
|
||||
Thread.__init__(self)
|
||||
self.ctx = ctx
|
||||
self.name = name
|
||||
#!/usr/bin/python
|
||||
from unbound import ub_ctx, RR_TYPE_A, RR_CLASS_IN
|
||||
from threading import Thread
|
||||
|
||||
def run(self):
|
||||
print "Thread lookup started:",self.name
|
||||
status, result = self.ctx.resolve(self.name, RR_TYPE_A, RR_CLASS_IN)
|
||||
if status == 0 and result.havedata:
|
||||
print " Result:",self.name,":", result.data.address_list
|
||||
|
||||
threads = []
|
||||
for name in ["www.fit.vutbr.cz","www.vutbr.cz","www.google.com"]:
|
||||
thread = LookupThread(ctx, name)
|
||||
thread.start()
|
||||
threads.append(thread)
|
||||
|
||||
for thread in threads:
|
||||
thread.join()
|
||||
ctx = ub_ctx()
|
||||
ctx.resolvconf("/etc/resolv.conf")
|
||||
|
||||
class LookupThread(Thread):
|
||||
def __init__(self,ctx, name):
|
||||
Thread.__init__(self)
|
||||
self.ctx = ctx
|
||||
self.name = name
|
||||
|
||||
def run(self):
|
||||
print "Thread lookup started:",self.name
|
||||
status, result = self.ctx.resolve(self.name, RR_TYPE_A, RR_CLASS_IN)
|
||||
if status == 0 and result.havedata:
|
||||
print " Result:",self.name,":", result.data.address_list
|
||||
|
||||
threads = []
|
||||
for name in ["www.fit.vutbr.cz","www.vutbr.cz","www.google.com"]:
|
||||
thread = LookupThread(ctx, name)
|
||||
thread.start()
|
||||
threads.append(thread)
|
||||
|
||||
for thread in threads:
|
||||
thread.join()
|
||||
|
@ -1,12 +1,14 @@
|
||||
.. _example_asynch:
|
||||
|
||||
==============================
|
||||
Asynchronous lookup
|
||||
==============================
|
||||
===================
|
||||
|
||||
This example performs the name lookup in the background.
|
||||
The main program keeps running while the name is resolved.
|
||||
|
||||
Source code
|
||||
-----------
|
||||
|
||||
::
|
||||
|
||||
#!/usr/bin/python
|
||||
@ -33,4 +35,5 @@ The main program keeps running while the name is resolved.
|
||||
if (status != 0):
|
||||
print "Resolve error:", unbound.ub_strerror(status)
|
||||
|
||||
The :meth:`unbound.ub_ctx.resolve_async` method is able to pass on any Python object. In this example, we used a dictionary object `my_data`.
|
||||
The :meth:`unbound.ub_ctx.resolve_async` method is able to pass on any Python
|
||||
object. In this example, we used a dictionary object ``my_data``.
|
||||
|
@ -1,33 +1,35 @@
|
||||
.. _example_examine:
|
||||
|
||||
==============================
|
||||
DNSSEC validator
|
||||
==============================
|
||||
================
|
||||
|
||||
This example program performs DNSSEC validation of a DNS lookup.
|
||||
|
||||
Source code
|
||||
-----------
|
||||
|
||||
::
|
||||
|
||||
#!/usr/bin/python
|
||||
import os
|
||||
from unbound import ub_ctx,RR_TYPE_A,RR_CLASS_IN
|
||||
|
||||
ctx = ub_ctx()
|
||||
ctx.resolvconf("/etc/resolv.conf")
|
||||
if (os.path.isfile("keys")):
|
||||
ctx.add_ta_file("keys") #read public keys for DNSSEC verification
|
||||
|
||||
status, result = ctx.resolve("www.nic.cz", RR_TYPE_A, RR_CLASS_IN)
|
||||
if status == 0 and result.havedata:
|
||||
|
||||
print "Result:", result.data.address_list
|
||||
|
||||
if result.secure:
|
||||
print "Result is secure"
|
||||
elif result.bogus:
|
||||
print "Result is bogus"
|
||||
else:
|
||||
print "Result is insecure"
|
||||
#!/usr/bin/python
|
||||
import os
|
||||
from unbound import ub_ctx,RR_TYPE_A,RR_CLASS_IN
|
||||
|
||||
ctx = ub_ctx()
|
||||
ctx.resolvconf("/etc/resolv.conf")
|
||||
if (os.path.isfile("keys")):
|
||||
ctx.add_ta_file("keys") #read public keys for DNSSEC verification
|
||||
|
||||
status, result = ctx.resolve("www.nic.cz", RR_TYPE_A, RR_CLASS_IN)
|
||||
if status == 0 and result.havedata:
|
||||
|
||||
print "Result:", result.data.address_list
|
||||
|
||||
if result.secure:
|
||||
print "Result is secure"
|
||||
elif result.bogus:
|
||||
print "Result is bogus"
|
||||
else:
|
||||
print "Result is insecure"
|
||||
|
||||
More detailed informations can be seen in libUnbound DNSSEC tutorial `here`_.
|
||||
|
||||
|
@ -1,13 +1,17 @@
|
||||
.. _example_resolver_only:
|
||||
|
||||
==============================
|
||||
Resolver only
|
||||
==============================
|
||||
=============
|
||||
|
||||
This example program shows how to perform DNS resolution only.
|
||||
Unbound contains two basic modules: resolver and validator.
|
||||
In case, the validator is not necessary, the validator module can be turned off using "module-config" option.
|
||||
This option contains a list of module names separated by the space char. This list determined which modules should be employed and in what order.
|
||||
In case, the validator is not necessary, the validator module can be turned off
|
||||
using "module-config" option.
|
||||
This option contains a list of module names separated by the space char. This
|
||||
list determined which modules should be employed and in what order.
|
||||
|
||||
Source code
|
||||
-----------
|
||||
|
||||
::
|
||||
|
||||
@ -25,5 +29,6 @@ This option contains a list of module names separated by the space char. This li
|
||||
print "Result:", result.data.address_list
|
||||
|
||||
.. note::
|
||||
The :meth:`unbound.ub_ctx.set_option` method must be used before the first resolution (i.e. before :meth:`unbound.ub_ctx.resolve` or :meth:`unbound.ub_ctx.resolve_async` call).
|
||||
|
||||
The :meth:`unbound.ub_ctx.set_option` method must be used before the first
|
||||
resolution (i.e. before :meth:`unbound.ub_ctx.resolve` or
|
||||
:meth:`unbound.ub_ctx.resolve_async` call).
|
||||
|
@ -1,11 +1,13 @@
|
||||
.. _example_localzone:
|
||||
|
||||
==============================
|
||||
Local zone manipulation
|
||||
==============================
|
||||
=======================
|
||||
|
||||
This example program shows how to define local zone containing custom DNS records.
|
||||
This example program shows how to define local zone containing custom DNS
|
||||
records.
|
||||
|
||||
.. literalinclude:: example6-1.py
|
||||
:language: python
|
||||
Source code
|
||||
-----------
|
||||
|
||||
.. literalinclude:: example6-1.py
|
||||
:language: python
|
||||
|
@ -1,18 +1,33 @@
|
||||
.. _example_idna:
|
||||
|
||||
=================================================
|
||||
Internationalized domain name support
|
||||
=================================================
|
||||
=====================================
|
||||
|
||||
Unlike the libUnbound, pyUnbound is able to handle IDN queries.
|
||||
|
||||
.. literalinclude:: example7-1.py
|
||||
:language: python
|
||||
Automatic IDN DNAME conversion
|
||||
-------------------------------
|
||||
|
||||
If we use unicode string in :meth:`unbound.ub_ctx.resolve` method, the IDN DNAME conversion (if it is necessary) is performed on background.
|
||||
If we use unicode string in :meth:`unbound.ub_ctx.resolve` method,
|
||||
the IDN DNAME conversion (if it is necessary) is performed on background.
|
||||
|
||||
.. literalinclude:: example7-2.py
|
||||
:language: python
|
||||
Source code
|
||||
...........
|
||||
|
||||
The :class:`unbound.ub_data` class contains attributes suffix which converts the dname to UTF string. These attributes have the '_idn' suffix.
|
||||
Apart from this aproach, two conversion functions exist (:func:`unbound.idn2dname` and :func:`unbound.dname2idn`).
|
||||
.. literalinclude:: example7-1.py
|
||||
:language: python
|
||||
|
||||
IDN converted attributes
|
||||
------------------------
|
||||
|
||||
The :class:`unbound.ub_data` class contains attributes suffix which converts
|
||||
the dname to UTF string. These attributes have the ``_idn`` suffix.
|
||||
|
||||
Apart from this aproach, two conversion functions exist
|
||||
(:func:`unbound.idn2dname` and :func:`unbound.dname2idn`).
|
||||
|
||||
Source code
|
||||
...........
|
||||
|
||||
.. literalinclude:: example7-2.py
|
||||
:language: python
|
||||
|
@ -1,28 +1,34 @@
|
||||
.. _example_mxlookup:
|
||||
|
||||
=================================================
|
||||
Lookup for MX and NS records
|
||||
=================================================
|
||||
============================
|
||||
|
||||
The pyUnbound extension provides functions which are able to encode RAW RDATA produces by unbound resolver (see :class:`unbound.ub_data`).
|
||||
The pyUnbound extension provides functions which are able to encode RAW RDATA
|
||||
produces by unbound resolver (see :class:`unbound.ub_data`).
|
||||
|
||||
.. literalinclude:: example8-1.py
|
||||
:language: python
|
||||
Source code
|
||||
-----------
|
||||
|
||||
Previous example produces following output::
|
||||
.. literalinclude:: example8-1.py
|
||||
:language: python
|
||||
|
||||
Result:
|
||||
raw data: 00 0F 05 6D 61 69 6C 34 03 6E 69 63 02 63 7A 00;00 14 02 6D 78 05 63 7A 6E 69 63 03 6F 72 67 00;00 0A 04 6D 61 69 6C 03 6E 69 63 02 63 7A 00
|
||||
priority:15 address: mail4.nic.cz.
|
||||
priority:20 address: mx.cznic.org.
|
||||
priority:10 address: mail.nic.cz.
|
||||
Output
|
||||
------
|
||||
|
||||
Result:
|
||||
raw data: D9 1F CD 32
|
||||
address: 217.31.205.50
|
||||
The previous example produces the following output::
|
||||
|
||||
Result:
|
||||
raw data: 01 61 02 6E 73 03 6E 69 63 02 63 7A 00;01 65 02 6E 73 03 6E 69 63 02 63 7A 00;01 63 02 6E 73 03 6E 69 63 02 63 7A 00
|
||||
host: a.ns.nic.cz.
|
||||
host: e.ns.nic.cz.
|
||||
host: c.ns.nic.cz.
|
||||
Result:
|
||||
raw data: 00 0F 05 6D 61 69 6C 34 03 6E 69 63 02 63 7A 00;00 14 02 6D 78 05 63 7A 6E 69 63 03 6F 72 67 00;00 0A 04 6D 61 69 6C 03 6E 69 63 02 63 7A 00
|
||||
priority:15 address: mail4.nic.cz.
|
||||
priority:20 address: mx.cznic.org.
|
||||
priority:10 address: mail.nic.cz.
|
||||
|
||||
Result:
|
||||
raw data: D9 1F CD 32
|
||||
address: 217.31.205.50
|
||||
|
||||
Result:
|
||||
raw data: 01 61 02 6E 73 03 6E 69 63 02 63 7A 00;01 65 02 6E 73 03 6E 69 63 02 63 7A 00;01 63 02 6E 73 03 6E 69 63 02 63 7A 00
|
||||
host: a.ns.nic.cz.
|
||||
host: e.ns.nic.cz.
|
||||
host: c.ns.nic.cz.
|
||||
|
@ -1,14 +1,16 @@
|
||||
Examples
|
||||
==============================
|
||||
========
|
||||
|
||||
Here you can find several examples which utilizes the unbound library in Python environment.
|
||||
Unbound is a caching validator and resolver and can be linked into an application, as a library where can answer DNS queries for the application.
|
||||
Here you can find several examples which utilizes the unbound library in Python
|
||||
environment. Unbound is a caching validator and resolver and can be linked into
|
||||
an application, as a library where can answer DNS queries for the application.
|
||||
This set of examples shows how to use the functions from Python environment.
|
||||
|
||||
`Tutorials`
|
||||
Tutorials
|
||||
---------
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
:glob:
|
||||
:maxdepth: 1
|
||||
:glob:
|
||||
|
||||
example*
|
||||
example*
|
||||
|
@ -1,31 +1,38 @@
|
||||
Installation
|
||||
===================================
|
||||
============
|
||||
|
||||
**Prerequisites**
|
||||
Prerequisites
|
||||
-------------
|
||||
|
||||
Python 2.4 or higher, SWIG 1.3 or higher, GNU make
|
||||
|
||||
**Compiling**
|
||||
Compiling
|
||||
---------
|
||||
|
||||
After downloading, you can compile the pyUnbound library by doing::
|
||||
|
||||
> tar -xzf unbound-x.x.x-py.tar.gz
|
||||
> cd unbound-x.x.x
|
||||
> ./configure --with-pyunbound
|
||||
> make
|
||||
> tar -xzf unbound-x.x.x-py.tar.gz
|
||||
> cd unbound-x.x.x
|
||||
> ./configure --with-pyunbound
|
||||
> make
|
||||
|
||||
You may want to --with-pythonmodule as well if you want to use python as
|
||||
a module in the resolver.
|
||||
You may want to enable ``--with-pythonmodule`` as well if you want to use
|
||||
python as a module in the resolver.
|
||||
|
||||
You need GNU make to compile sources; SWIG and Python devel libraries to compile extension module.
|
||||
You need ``GNU make`` to compile sources; ``SWIG`` and ``Python devel``
|
||||
libraries to compile extension module.
|
||||
|
||||
|
||||
**Testing**
|
||||
Testing
|
||||
-------
|
||||
|
||||
If the compilation is successful, you can test the python LDNS extension module by::
|
||||
If the compilation is successful, you can test the python LDNS extension module
|
||||
by::
|
||||
|
||||
> cd contrib/python
|
||||
> make testenv
|
||||
> ./dns-lookup.py
|
||||
> cd contrib/python
|
||||
> make testenv
|
||||
> ./dns-lookup.py
|
||||
|
||||
You may want to make install in the main directory since make testenv is for debugging. In contrib/examples you can find simple applications written in Python using the Unbound extension.
|
||||
You may want to ``make install`` in the main directory since ``make testenv``
|
||||
is for debugging. In contrib/examples you can find simple applications written
|
||||
in Python using the Unbound extension.
|
||||
|
@ -1,39 +1,58 @@
|
||||
Introduction
|
||||
===================================
|
||||
============
|
||||
|
||||
**Unbound**
|
||||
Unbound
|
||||
-------
|
||||
|
||||
`Unbound`_ is an implementation of a DNS resolver, that performs caching and DNSSEC validation.
|
||||
Together with unbound, the libunbound library is provided.
|
||||
This library can be used to convert hostnames to ip addresses, and back, as well as obtain other information.
|
||||
Since the resolver allows to specify the class and type of a query (A record, NS, MX, ...), this library offers powerful resolving tool.
|
||||
The library also performs public-key validation of results with DNSSEC.
|
||||
|
||||
.. _Unbound: http://www.unbound.net/documentation
|
||||
`Unbound`_ is an implementation of a DNS resolver, that performs caching and
|
||||
DNSSEC validation.
|
||||
Together with unbound, the libunbound library is provided.
|
||||
This library can be used to convert hostnames to ip addresses, and back, as
|
||||
well as obtain other information.
|
||||
Since the resolver allows to specify the class and type of a query (A record,
|
||||
NS, MX, ...), this library offers powerful resolving tool.
|
||||
The library also performs public-key validation of results with DNSSEC.
|
||||
|
||||
**pyUnbound**
|
||||
.. _Unbound: http://www.unbound.net/documentation
|
||||
|
||||
The pyUnbound is an extension module for Python which provides an object-oriented interface to libunbound.
|
||||
It is the first Python module which offers thread-safe caching resolver.
|
||||
|
||||
The interface was designed with the emphasis on the simplicity of use.
|
||||
There are two main classes :class:`unbound.ub_ctx` (a validation and resolution context) and :class:`unbound.ub_result` which contains the validation and resolution results.
|
||||
The objects are thread-safe, and a context can be used in non-threaded as well as threaded environment.
|
||||
Resolution can be performed blocking and non-blocking (i.e. asynchronous).
|
||||
The asynchronous method returns from the call immediately, so that processing can go on, while the results become available later.
|
||||
pyUnbound
|
||||
---------
|
||||
|
||||
**Features**
|
||||
* customizable caching validation resolver for synchronous and asynchronous lookups
|
||||
* easy to use object interface
|
||||
* easy to integrate extension module
|
||||
* designed for thread environment (i.e. thread-safe)
|
||||
* allows define and customize of local zone and its RR's during the operation (i.e. without restart)
|
||||
* includes encoding functions to simplify the results retrieval
|
||||
* Internationalized domain name (`IDN`_) support
|
||||
The pyUnbound is an extension module for Python which provides an
|
||||
object-oriented interface to libunbound.
|
||||
It is the first Python module which offers thread-safe caching resolver.
|
||||
|
||||
.. _IDN: http://en.wikipedia.org/wiki/Internationalized_domain_name
|
||||
The interface was designed with the emphasis on the simplicity of use.
|
||||
There are two main classes :class:`unbound.ub_ctx` (a validation and resolution
|
||||
context) and :class:`unbound.ub_result` which contains the validation and
|
||||
resolution results.
|
||||
The objects are thread-safe, and a context can be used in non-threaded as well
|
||||
as threaded environment.
|
||||
Resolution can be performed blocking and non-blocking (i.e. asynchronous).
|
||||
The asynchronous method returns from the call immediately, so that processing
|
||||
can go on, while the results become available later.
|
||||
|
||||
**Application area**
|
||||
* DNS-based applications performing DNS lookups; the caching resolver can reduce overhead
|
||||
* Applications where the validation of DNS records is required
|
||||
* Great solution for customizable and dynamic DNS-based white/blacklists (spam rejection, connection rejection, ...) using the dynamic local zone manipulation
|
||||
Features
|
||||
--------
|
||||
|
||||
* Customizable caching validation resolver for synchronous and asynchronous
|
||||
lookups
|
||||
* Easy to use object interface
|
||||
* Easy to integrate extension module
|
||||
* Designed for thread environment (i.e. thread-safe)
|
||||
* Allows define and customize of local zone and its RR's during the operation
|
||||
(i.e. without restart)
|
||||
* Includes encoding functions to simplify the results retrieval
|
||||
* Internationalized domain name (`IDN`_) support
|
||||
|
||||
.. _IDN: http://en.wikipedia.org/wiki/Internationalized_domain_name
|
||||
|
||||
Application area
|
||||
----------------
|
||||
|
||||
* DNS-based applications performing DNS lookups; the caching resolver can
|
||||
reduce overhead
|
||||
* Applications where the validation of DNS records is required
|
||||
* Great solution for customizable and dynamic DNS-based white/blacklists (spam
|
||||
rejection, connection rejection, ...) using the dynamic local zone
|
||||
manipulation
|
||||
|
@ -49,18 +49,15 @@ struct comm_point;
|
||||
struct module_qstate;
|
||||
struct tube;
|
||||
struct edns_option;
|
||||
struct query_info;
|
||||
|
||||
/**
|
||||
* Worker service routine to send serviced queries to authoritative servers.
|
||||
* @param qname: query name. (host order)
|
||||
* @param qnamelen: length in bytes of qname, including trailing 0.
|
||||
* @param qtype: query type. (host order)
|
||||
* @param qclass: query class. (host order)
|
||||
* @param qinfo: query info.
|
||||
* @param flags: host order flags word, with opcode and CD bit.
|
||||
* @param dnssec: if set, EDNS record will have DO bit set.
|
||||
* @param want_dnssec: signatures needed.
|
||||
* @param nocaps: ignore capsforid(if in config), do not perturb qname.
|
||||
* @param opt_list: EDNS options on outgoing packet.
|
||||
* @param addr: where to.
|
||||
* @param addrlen: length of addr.
|
||||
* @param zone: delegation point name.
|
||||
@ -70,9 +67,8 @@ struct edns_option;
|
||||
* @return: false on failure (memory or socket related). no query was
|
||||
* sent.
|
||||
*/
|
||||
struct outbound_entry* libworker_send_query(uint8_t* qname, size_t qnamelen,
|
||||
uint16_t qtype, uint16_t qclass, uint16_t flags, int dnssec,
|
||||
int want_dnssec, int nocaps, struct edns_option* opt_list,
|
||||
struct outbound_entry* libworker_send_query(struct query_info* qinfo,
|
||||
uint16_t flags, int dnssec, int want_dnssec, int nocaps,
|
||||
struct sockaddr_storage* addr, socklen_t addrlen, uint8_t* zone,
|
||||
size_t zonelen, int ssl_upstream, struct module_qstate* q);
|
||||
|
||||
@ -109,15 +105,11 @@ void worker_sighandler(int sig, void* arg);
|
||||
|
||||
/**
|
||||
* Worker service routine to send serviced queries to authoritative servers.
|
||||
* @param qname: query name. (host order)
|
||||
* @param qnamelen: length in bytes of qname, including trailing 0.
|
||||
* @param qtype: query type. (host order)
|
||||
* @param qclass: query class. (host order)
|
||||
* @param qinfo: query info.
|
||||
* @param flags: host order flags word, with opcode and CD bit.
|
||||
* @param dnssec: if set, EDNS record will have DO bit set.
|
||||
* @param want_dnssec: signatures needed.
|
||||
* @param nocaps: ignore capsforid(if in config), do not perturb qname.
|
||||
* @param opt_list: EDNS options on outgoing packet.
|
||||
* @param addr: where to.
|
||||
* @param addrlen: length of addr.
|
||||
* @param zone: wireformat dname of the zone.
|
||||
@ -127,9 +119,8 @@ void worker_sighandler(int sig, void* arg);
|
||||
* @return: false on failure (memory or socket related). no query was
|
||||
* sent.
|
||||
*/
|
||||
struct outbound_entry* worker_send_query(uint8_t* qname, size_t qnamelen,
|
||||
uint16_t qtype, uint16_t qclass, uint16_t flags, int dnssec,
|
||||
int want_dnssec, int nocaps, struct edns_option* opt_list,
|
||||
struct outbound_entry* worker_send_query(struct query_info* qinfo,
|
||||
uint16_t flags, int dnssec, int want_dnssec, int nocaps,
|
||||
struct sockaddr_storage* addr, socklen_t addrlen, uint8_t* zone,
|
||||
size_t zonelen, int ssl_upstream, struct module_qstate* q);
|
||||
|
||||
|
@ -80,10 +80,13 @@ pygments_style = 'sphinx'
|
||||
# Options for HTML output
|
||||
# -----------------------
|
||||
|
||||
# The theme that the html output should use.
|
||||
html_theme = "classic"
|
||||
|
||||
# The style sheet to use for HTML and HTML Help pages. A file of that name
|
||||
# must exist either in Sphinx' static/ path, or in one of the custom paths
|
||||
# given in html_static_path.
|
||||
html_style = 'default.css'
|
||||
#html_style = 'default.css'
|
||||
|
||||
# The name for this set of Sphinx documents. If None, it defaults to
|
||||
# "<project> v<release> documentation".
|
||||
|
@ -1,12 +1,14 @@
|
||||
Response generation
|
||||
=====================
|
||||
===================
|
||||
|
||||
This example shows how to handle queries and generate response packet.
|
||||
|
||||
.. note::
|
||||
If the python module is the first module and validator module is enabled (``module-config: "python validator iterator"``),
|
||||
a return_msg security flag has to be set at least to 2. Leaving security flag untouched causes that the
|
||||
response will be refused by unbound worker as unbound will consider it as non-valid response.
|
||||
If the python module is the first module and validator module is enabled
|
||||
(``module-config: "python validator iterator"``), a return_msg security flag
|
||||
has to be set at least to 2. Leaving security flag untouched causes that the
|
||||
response will be refused by unbound worker as unbound will consider it as
|
||||
non-valid response.
|
||||
|
||||
Complete source code
|
||||
--------------------
|
||||
@ -27,20 +29,21 @@ Query for a A record ending with .localdomain
|
||||
|
||||
Dig produces the following output::
|
||||
|
||||
;; global options: printcmd
|
||||
;; Got answer:
|
||||
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 48426
|
||||
;; flags: qr aa rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 0
|
||||
|
||||
;; QUESTION SECTION:
|
||||
;test.xxx.localdomain. IN A
|
||||
|
||||
;; ANSWER SECTION:
|
||||
test.xxx.localdomain. 10 IN A 127.0.0.1
|
||||
|
||||
;; Query time: 2 msec
|
||||
;; SERVER: 127.0.0.1#53(127.0.0.1)
|
||||
;; WHEN: Mon Jan 01 12:46:02 2009
|
||||
;; MSG SIZE rcvd: 54
|
||||
;; global options: printcmd
|
||||
;; Got answer:
|
||||
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 48426
|
||||
;; flags: qr aa rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 0
|
||||
|
||||
As we handle (override) in python module only queries ending with "localdomain.", the unboud can still resolve host names.
|
||||
;; QUESTION SECTION:
|
||||
;test.xxx.localdomain. IN A
|
||||
|
||||
;; ANSWER SECTION:
|
||||
test.xxx.localdomain. 10 IN A 127.0.0.1
|
||||
|
||||
;; Query time: 2 msec
|
||||
;; SERVER: 127.0.0.1#53(127.0.0.1)
|
||||
;; WHEN: Mon Jan 01 12:46:02 2009
|
||||
;; MSG SIZE rcvd: 54
|
||||
|
||||
As we handle (override) in the python module only queries ending with
|
||||
``localdomain.``, unboud can still resolve host names.
|
||||
|
@ -1,15 +1,19 @@
|
||||
DNS-based language dictionary
|
||||
===============================
|
||||
=============================
|
||||
|
||||
This example shows how to create a simple language dictionary based on **DNS**
|
||||
service within 15 minutes. The translation will be performed using TXT resource records.
|
||||
service within 15 minutes. The translation will be performed using TXT resource
|
||||
records.
|
||||
|
||||
Key parts
|
||||
-----------
|
||||
---------
|
||||
|
||||
Initialization
|
||||
~~~~~~~~~~~~~~~~~~~~~~~
|
||||
On **init()** module loads dictionary from a text file containing records in ``word [tab] translation`` format.
|
||||
~~~~~~~~~~~~~~
|
||||
|
||||
On **init()** module loads dictionary from a text file containing records in
|
||||
``word [tab] translation`` format.
|
||||
|
||||
::
|
||||
|
||||
def init(id, cfg):
|
||||
@ -20,11 +24,14 @@ On **init()** module loads dictionary from a text file containing records in ``w
|
||||
The suitable file can be found at http://slovnik.zcu.cz
|
||||
|
||||
DNS query and word lookup
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Let's define the following format od DNS queries: ``word1[.]word2[.] ... wordN[.]{en,cs}[._dict_.cz.]``.
|
||||
Let's define the following format od DNS queries:
|
||||
``word1[.]word2[.] ... wordN[.]{en,cs}[._dict_.cz.]``.
|
||||
Word lookup is done by simple ``dict`` lookup from broken DNS request.
|
||||
Query name is divided into a list of labels. This list is accessible as qname_list attribute.
|
||||
Query name is divided into a list of labels. This list is accessible as
|
||||
``qname_list`` attribute.
|
||||
|
||||
::
|
||||
|
||||
aword = ' '.join(qstate.qinfo.qname_list[0:-4]) #skip last four labels
|
||||
@ -37,35 +44,40 @@ Query name is divided into a list of labels. This list is accessible as qname_li
|
||||
if (adict == "cs") and (aword in cz_dict):
|
||||
words = cz_dict[aword] # CS -> EN
|
||||
|
||||
In the first step, we get a string in the form: ``word1[space]word2[space]...word[space]``.
|
||||
In the second assignment, fourth label from the end is obtained. This label should contains *"cs"* or *"en"*.
|
||||
This label determines the direction of translation.
|
||||
|
||||
In the first step, we get a string in the form:
|
||||
``word1[space]word2[space]...word[space]``.
|
||||
In the second assignment, fourth label from the end is obtained. This label
|
||||
should contains *"cs"* or *"en"*. This label determines the direction of
|
||||
translation.
|
||||
|
||||
Forming of a DNS reply
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
DNS reply is formed only on valid match and added as TXT answer.
|
||||
|
||||
::
|
||||
|
||||
msg = DNSMessage(qstate.qinfo.qname_str, RR_TYPE_TXT, RR_CLASS_IN, PKT_AA)
|
||||
msg = DNSMessage(qstate.qinfo.qname_str, RR_TYPE_TXT, RR_CLASS_IN, PKT_AA)
|
||||
|
||||
for w in words:
|
||||
msg.answer.append("%s 300 IN TXT \"%s\"" % (qstate.qinfo.qname_str, w.replace("\"", "\\\"")))
|
||||
for w in words:
|
||||
msg.answer.append("%s 300 IN TXT \"%s\"" % (qstate.qinfo.qname_str, w.replace("\"", "\\\"")))
|
||||
|
||||
if not msg.set_return_msg(qstate):
|
||||
qstate.ext_state[id] = MODULE_ERROR
|
||||
return True
|
||||
if not msg.set_return_msg(qstate):
|
||||
qstate.ext_state[id] = MODULE_ERROR
|
||||
return True
|
||||
|
||||
qstate.return_rcode = RCODE_NOERROR
|
||||
qstate.ext_state[id] = MODULE_FINISHED
|
||||
return True
|
||||
qstate.return_rcode = RCODE_NOERROR
|
||||
qstate.ext_state[id] = MODULE_FINISHED
|
||||
return True
|
||||
|
||||
In the first step, a :class:`DNSMessage` instance is created for a given query *(type TXT)*.
|
||||
In the first step, a :class:`DNSMessage` instance is created for a given query
|
||||
*(type TXT)*.
|
||||
The fourth argument specifies the flags *(authoritative answer)*.
|
||||
In the second step, we append TXT records containing the translation *(on the right side of RR)*.
|
||||
In the second step, we append TXT records containing the translation *(on the
|
||||
right side of RR)*.
|
||||
Then, the response is finished and ``qstate.return_msg`` contains new response.
|
||||
If no error, the module sets :attr:`module_qstate.return_rcode` and :attr:`module_qstate.ext_state`.
|
||||
If no error, the module sets :attr:`module_qstate.return_rcode` and
|
||||
:attr:`module_qstate.ext_state`.
|
||||
|
||||
**Steps:**
|
||||
|
||||
@ -82,80 +94,82 @@ Run the Unbound server:
|
||||
|
||||
In case you use own configuration file, don't forget to enable Python module::
|
||||
|
||||
module-config: "validator python iterator"
|
||||
module-config: "validator python iterator"
|
||||
|
||||
and use valid script path::
|
||||
|
||||
python-script: "./examples/dict.py"
|
||||
python-script: "./examples/dict.py"
|
||||
|
||||
The translation from english word *"a bar fly"* to Czech can be done by doing:
|
||||
|
||||
``>>>dig TXT @127.0.0.1 a.bar.fly.en._dict_.cz``
|
||||
|
||||
::
|
||||
::
|
||||
|
||||
; (1 server found)
|
||||
;; global options: printcmd
|
||||
;; Got answer:
|
||||
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 48691
|
||||
;; flags: aa rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 0
|
||||
|
||||
;; QUESTION SECTION:
|
||||
;a.bar.fly.en._dict_.cz. IN TXT
|
||||
|
||||
;; ANSWER SECTION:
|
||||
a.bar.fly.en._dict_.cz. 300 IN TXT "barov\253 povale\232"
|
||||
|
||||
;; Query time: 5 msec
|
||||
;; SERVER: 127.0.0.1#53(127.0.0.1)
|
||||
;; WHEN: Mon Jan 01 17:44:18 2009
|
||||
;; MSG SIZE rcvd: 67
|
||||
|
||||
; (1 server found)
|
||||
;; global options: printcmd
|
||||
;; Got answer:
|
||||
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 48691
|
||||
;; flags: aa rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 0
|
||||
|
||||
;; QUESTION SECTION:
|
||||
;a.bar.fly.en._dict_.cz. IN TXT
|
||||
|
||||
;; ANSWER SECTION:
|
||||
a.bar.fly.en._dict_.cz. 300 IN TXT "barov\253 povale\232"
|
||||
|
||||
;; Query time: 5 msec
|
||||
;; SERVER: 127.0.0.1#53(127.0.0.1)
|
||||
;; WHEN: Mon Jan 01 17:44:18 2009
|
||||
;; MSG SIZE rcvd: 67
|
||||
|
||||
``>>>dig TXT @127.0.0.1 nic.cs._dict_.cz``
|
||||
|
||||
::
|
||||
|
||||
; <<>> DiG 9.5.0-P2 <<>> TXT @127.0.0.1 nic.cs._dict_.cz
|
||||
; (1 server found)
|
||||
;; global options: printcmd
|
||||
;; Got answer:
|
||||
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 58710
|
||||
;; flags: aa rd ra; QUERY: 1, ANSWER: 6, AUTHORITY: 0, ADDITIONAL: 0
|
||||
|
||||
;; QUESTION SECTION:
|
||||
;nic.cs._dict_.cz. IN TXT
|
||||
|
||||
;; ANSWER SECTION:
|
||||
nic.cs._dict_.cz. 300 IN TXT "aught"
|
||||
nic.cs._dict_.cz. 300 IN TXT "naught"
|
||||
nic.cs._dict_.cz. 300 IN TXT "nihil"
|
||||
nic.cs._dict_.cz. 300 IN TXT "nix"
|
||||
nic.cs._dict_.cz. 300 IN TXT "nothing"
|
||||
nic.cs._dict_.cz. 300 IN TXT "zilch"
|
||||
|
||||
;; Query time: 0 msec
|
||||
;; SERVER: 127.0.0.1#53(127.0.0.1)
|
||||
;; WHEN: Mon Jan 01 17:45:39 2009
|
||||
;; MSG SIZE rcvd: 143
|
||||
; <<>> DiG 9.5.0-P2 <<>> TXT @127.0.0.1 nic.cs._dict_.cz
|
||||
; (1 server found)
|
||||
;; global options: printcmd
|
||||
;; Got answer:
|
||||
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 58710
|
||||
;; flags: aa rd ra; QUERY: 1, ANSWER: 6, AUTHORITY: 0, ADDITIONAL: 0
|
||||
|
||||
Proof that the unbound still works as resolver.
|
||||
;; QUESTION SECTION:
|
||||
;nic.cs._dict_.cz. IN TXT
|
||||
|
||||
;; ANSWER SECTION:
|
||||
nic.cs._dict_.cz. 300 IN TXT "aught"
|
||||
nic.cs._dict_.cz. 300 IN TXT "naught"
|
||||
nic.cs._dict_.cz. 300 IN TXT "nihil"
|
||||
nic.cs._dict_.cz. 300 IN TXT "nix"
|
||||
nic.cs._dict_.cz. 300 IN TXT "nothing"
|
||||
nic.cs._dict_.cz. 300 IN TXT "zilch"
|
||||
|
||||
;; Query time: 0 msec
|
||||
;; SERVER: 127.0.0.1#53(127.0.0.1)
|
||||
;; WHEN: Mon Jan 01 17:45:39 2009
|
||||
;; MSG SIZE rcvd: 143
|
||||
|
||||
Proof that the unbound still works as resolver.
|
||||
|
||||
``>>>dig A @127.0.0.1 www.nic.cz``
|
||||
|
||||
::
|
||||
|
||||
; (1 server found)
|
||||
;; global options: printcmd
|
||||
;; Got answer:
|
||||
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 19996
|
||||
;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 3, ADDITIONAL: 5
|
||||
|
||||
;; QUESTION SECTION:
|
||||
;www.nic.cz. IN A
|
||||
|
||||
;; ANSWER SECTION:
|
||||
www.nic.cz. 1662 IN A 217.31.205.50
|
||||
|
||||
;; AUTHORITY SECTION:
|
||||
...
|
||||
; (1 server found)
|
||||
;; global options: printcmd
|
||||
;; Got answer:
|
||||
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 19996
|
||||
;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 3, ADDITIONAL: 5
|
||||
|
||||
;; QUESTION SECTION:
|
||||
;www.nic.cz. IN A
|
||||
|
||||
;; ANSWER SECTION:
|
||||
www.nic.cz. 1662 IN A 217.31.205.50
|
||||
|
||||
;; AUTHORITY SECTION:
|
||||
...
|
||||
|
||||
Complete source code
|
||||
--------------------
|
||||
|
191
pythonmod/doc/examples/example5.rst
Normal file
191
pythonmod/doc/examples/example5.rst
Normal file
@ -0,0 +1,191 @@
|
||||
EDNS options
|
||||
============
|
||||
|
||||
This example shows how to interact with EDNS options.
|
||||
|
||||
When quering unbound with the EDNS option ``65001`` and data ``0xc001`` we
|
||||
expect an answer with the same EDNS option code and data ``0xdeadbeef``.
|
||||
|
||||
|
||||
Key parts
|
||||
~~~~~~~~~
|
||||
|
||||
This example relies on the following functionalities:
|
||||
|
||||
|
||||
Registering EDNS options
|
||||
------------------------
|
||||
|
||||
By registering EDNS options we can tune unbound's behavior when encountering a
|
||||
query with a known EDNS option. The two available options are:
|
||||
|
||||
- ``bypass_cache_stage``: If set to ``True`` unbound will not try to answer
|
||||
from cache. Instead execution is passed to the modules
|
||||
- ``no_aggregation``: If set to ``True`` unbound will consider this query
|
||||
unique and will not aggregate it with similar queries
|
||||
|
||||
Both values default to ``False``.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
if not register_edns_option(env, 65001, bypass_cache_stage=True,
|
||||
no_aggregation=True):
|
||||
log_info("python: Could not register EDNS option {}".format(65001))
|
||||
|
||||
|
||||
EDNS option lists
|
||||
-----------------
|
||||
|
||||
EDNS option lists can be found in the :class:`module_qstate` class. There are
|
||||
four available lists in total:
|
||||
|
||||
- :class:`module_qstate.edns_opts_front_in`: options that came from the client
|
||||
side. **Should not** be changed
|
||||
- :class:`module_qstate.edns_opts_back_out`: options that will be sent to the
|
||||
server side. Can be populated by edns literate modules
|
||||
- :class:`module_qstate.edns_opts_back_in`: options that came from the server
|
||||
side. **Should not** be changed
|
||||
- :class:`module_qstate.edns_opts_front_out`: options that will be sent to the
|
||||
client side. Can be populated by edns literate modules
|
||||
|
||||
Each list element has the following members:
|
||||
|
||||
- ``code``: the EDNS option code;
|
||||
- ``data``: the EDNS option data.
|
||||
|
||||
|
||||
Reading an EDNS option list
|
||||
...........................
|
||||
|
||||
The lists' contents can be accessed in python by their ``_iter`` counterpart as
|
||||
an iterator:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
if not edns_opt_list_is_empty(qstate.edns_opts_front_in):
|
||||
for o in qstate.edns_opts_front_in_iter:
|
||||
log_info("python: Code: {}, Data: '{}'".format(o.code,
|
||||
"".join('{:02x}'.format(x) for x in o.data)))
|
||||
|
||||
|
||||
Writing to an EDNS option list
|
||||
..............................
|
||||
|
||||
By appending to an EDNS option list we can add new EDNS options. The new
|
||||
element is going to be allocated in :class:`module_qstate.region`. The data
|
||||
**must** be represented with a python ``bytearray``:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
b = bytearray.fromhex("deadbeef")
|
||||
if not edns_opt_list_append(qstate.edns_opts_front_out,
|
||||
o.code, b, qstate.region):
|
||||
log_info("python: Could not append EDNS option {}".format(o.code))
|
||||
|
||||
We can also remove an EDNS option code from an EDNS option list.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
if not edns_opt_list_remove(edns_opt_list, code):
|
||||
log_info("python: Option code {} was not found in the "
|
||||
"list.".format(code))
|
||||
|
||||
.. note:: All occurences of the EDNS option code will be removed from the list:
|
||||
|
||||
|
||||
Controlling other modules' cache behavior
|
||||
-----------------------------------------
|
||||
|
||||
During the modules' operation, some modules may interact with the cache
|
||||
(e.g., iterator). This behavior can be controlled by using the following
|
||||
:class:`module_qstate` flags:
|
||||
|
||||
- :class:`module_qstate.no_cache_lookup`: Modules *operating after* this module
|
||||
will not lookup the cache for an answer
|
||||
- :class:`module_qstate.no_cache_store`: Modules *operating after* this module
|
||||
will not store the response in the cache
|
||||
|
||||
Both values default to ``0``.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
def operate(id, event, qstate, qdata):
|
||||
if (event == MODULE_EVENT_NEW) or (event == MODULE_EVENT_PASS):
|
||||
# Detect if edns option code 56001 is present from the client side. If
|
||||
# so turn on the flags for cache management.
|
||||
if not edns_opt_list_is_empty(qstate.edns_opts_front_in):
|
||||
log_info("python: searching for edns option code 65001 during NEW "
|
||||
"or PASS event ")
|
||||
for o in qstate.edns_opts_front_in_iter:
|
||||
if o.code == 65001:
|
||||
log_info("python: found edns option code 65001")
|
||||
# Instruct other modules to not lookup for an
|
||||
# answer in the cache.
|
||||
qstate.no_cache_lookup = 1
|
||||
log_info("python: enabled no_cache_lookup")
|
||||
|
||||
# Instruct other modules to not store the answer in
|
||||
# the cache.
|
||||
qstate.no_cache_store = 1
|
||||
log_info("python: enabled no_cache_store")
|
||||
|
||||
|
||||
Testing
|
||||
~~~~~~~
|
||||
|
||||
Run the Unbound server: ::
|
||||
|
||||
root@localhost$ unbound -dv -c ./test-edns.conf
|
||||
|
||||
In case you use your own configuration file, don't forget to enable the Python
|
||||
module::
|
||||
|
||||
module-config: "validator python iterator"
|
||||
|
||||
and use a valid script path::
|
||||
|
||||
python-script: "./examples/edns.py"
|
||||
|
||||
Quering with EDNS option ``65001:0xc001``:
|
||||
|
||||
::
|
||||
|
||||
root@localhost$ dig @localhost nlnetlabs.nl +ednsopt=65001:c001
|
||||
|
||||
; <<>> DiG 9.10.3-P4-Ubuntu <<>> @localhost nlnetlabs.nl +ednsopt=65001:c001
|
||||
; (1 server found)
|
||||
;; global options: +cmd
|
||||
;; Got answer:
|
||||
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 33450
|
||||
;; flags: qr rd ra ad; QUERY: 1, ANSWER: 1, AUTHORITY: 4, ADDITIONAL: 3
|
||||
|
||||
;; OPT PSEUDOSECTION:
|
||||
; EDNS: version: 0, flags:; udp: 4096
|
||||
; OPT=65001: de ad be ef ("....")
|
||||
;; QUESTION SECTION:
|
||||
;nlnetlabs.nl. IN A
|
||||
|
||||
;; ANSWER SECTION:
|
||||
nlnetlabs.nl. 10200 IN A 185.49.140.10
|
||||
|
||||
;; AUTHORITY SECTION:
|
||||
nlnetlabs.nl. 10200 IN NS anyns.pch.net.
|
||||
nlnetlabs.nl. 10200 IN NS ns.nlnetlabs.nl.
|
||||
nlnetlabs.nl. 10200 IN NS ns-ext1.sidn.nl.
|
||||
nlnetlabs.nl. 10200 IN NS sec2.authdns.ripe.net.
|
||||
|
||||
;; ADDITIONAL SECTION:
|
||||
ns.nlnetlabs.nl. 10200 IN AAAA 2a04:b900::8:0:0:60
|
||||
ns.nlnetlabs.nl. 10200 IN A 185.49.140.60
|
||||
|
||||
;; Query time: 10 msec
|
||||
;; SERVER: 127.0.0.1#53(127.0.0.1)
|
||||
;; WHEN: Mon Dec 05 14:50:56 CET 2016
|
||||
;; MSG SIZE rcvd: 212
|
||||
|
||||
|
||||
Complete source code
|
||||
~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
.. literalinclude:: ../../examples/edns.py
|
||||
:language: python
|
299
pythonmod/doc/examples/example6.rst
Normal file
299
pythonmod/doc/examples/example6.rst
Normal file
@ -0,0 +1,299 @@
|
||||
Inplace callbacks
|
||||
=================
|
||||
|
||||
This example shows how to register and use inplace callback functions. These
|
||||
functions are going to be called just before unbound replies back to a client.
|
||||
They can perform certain actions without interrupting unbound's execution flow
|
||||
(e.g. add/remove EDNS options, manipulate the reply).
|
||||
|
||||
Two different scenarios will be shown:
|
||||
|
||||
- If answering from cache and the client used EDNS option code ``65002`` we
|
||||
will answer with the same code but with data ``0xdeadbeef``;
|
||||
- When answering with a SERVFAIL we also add an empty EDNS option code
|
||||
``65003``.
|
||||
|
||||
|
||||
Key parts
|
||||
~~~~~~~~~
|
||||
|
||||
This example relies on the following functionalities:
|
||||
|
||||
|
||||
Registering inplace callback functions
|
||||
--------------------------------------
|
||||
|
||||
There are four types of inplace callback functions:
|
||||
|
||||
- `inplace callback reply functions`_
|
||||
- `inplace callback reply_cache functions`_
|
||||
- `inplace callback reply_local functions`_
|
||||
- `inplace callback reply_servfail functions`_
|
||||
|
||||
|
||||
Inplace callback reply functions
|
||||
................................
|
||||
|
||||
Called when answering with a *resolved* query.
|
||||
|
||||
The callback function's prototype is the following:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
def inplace_reply_callback(qinfo, qstate, rep, rcode, edns, opt_list_out, region):
|
||||
"""Function that will be registered as an inplace callback function.
|
||||
It will be called when answering with a resolved query.
|
||||
:param qinfo: query_info struct;
|
||||
:param qstate: module qstate. It contains the available opt_lists; It
|
||||
SHOULD NOT be altered;
|
||||
:param rep: reply_info struct;
|
||||
:param rcode: return code for the query;
|
||||
:param edns: edns_data to be sent to the client side. It SHOULD NOT be
|
||||
altered;
|
||||
:param opt_list_out: the list with the EDNS options that will be sent as a
|
||||
reply. It can be populated with EDNS options;
|
||||
:param region: region to allocate temporary data. Needs to be used when we
|
||||
want to append a new option to opt_list_out.
|
||||
:return: True on success, False on failure.
|
||||
"""
|
||||
|
||||
.. note:: The function's name is irrelevant.
|
||||
|
||||
We can register such function as:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
if not register_inplace_cb_reply(inplace_reply_callback, env):
|
||||
log_info("python: Could not register inplace callback function.")
|
||||
|
||||
|
||||
Inplace callback reply_cache functions
|
||||
......................................
|
||||
|
||||
Called when answering *from cache*.
|
||||
|
||||
The callback function's prototype is the following:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
def inplace_cache_callback(qinfo, qstate, rep, rcode, edns, opt_list_out, region):
|
||||
"""Function that will be registered as an inplace callback function.
|
||||
It will be called when answering from the cache.
|
||||
:param qinfo: query_info struct;
|
||||
:param qstate: module qstate. None;
|
||||
:param rep: reply_info struct;
|
||||
:param rcode: return code for the query;
|
||||
:param edns: edns_data sent from the client side. The list with the EDNS
|
||||
options is accesible through edns.opt_list. It SHOULD NOT be
|
||||
altered;
|
||||
:param opt_list_out: the list with the EDNS options that will be sent as a
|
||||
reply. It can be populated with EDNS options;
|
||||
:param region: region to allocate temporary data. Needs to be used when we
|
||||
want to append a new option to opt_list_out.
|
||||
:return: True on success, False on failure.
|
||||
"""
|
||||
|
||||
.. note:: The function's name is irrelevant.
|
||||
|
||||
We can register such function as:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
if not register_inplace_cb_reply_cache(inplace_cache_callback, env):
|
||||
log_info("python: Could not register inplace callback function.")
|
||||
|
||||
|
||||
Inplace callback reply_local functions
|
||||
......................................
|
||||
|
||||
Called when answering with *local data* or a *Chaos(CH) reply*.
|
||||
|
||||
The callback function's prototype is the following:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
def inplace_local_callback(qinfo, qstate, rep, rcode, edns, opt_list_out, region):
|
||||
"""Function that will be registered as an inplace callback function.
|
||||
It will be called when answering from local data.
|
||||
:param qinfo: query_info struct;
|
||||
:param qstate: module qstate. None;
|
||||
:param rep: reply_info struct;
|
||||
:param rcode: return code for the query;
|
||||
:param edns: edns_data sent from the client side. The list with the
|
||||
EDNS options is accesible through edns.opt_list. It
|
||||
SHOULD NOT be altered;
|
||||
:param opt_list_out: the list with the EDNS options that will be sent as a
|
||||
reply. It can be populated with EDNS options;
|
||||
:param region: region to allocate temporary data. Needs to be used when we
|
||||
want to append a new option to opt_list_out.
|
||||
:return: True on success, False on failure.
|
||||
"""
|
||||
|
||||
.. note:: The function's name is irrelevant.
|
||||
|
||||
We can register such function as:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
if not register_inplace_cb_reply_local(inplace_local_callback, env):
|
||||
log_info("python: Could not register inplace callback function.")
|
||||
|
||||
|
||||
Inplace callback reply_servfail functions
|
||||
.........................................
|
||||
|
||||
Called when answering with *SERVFAIL*.
|
||||
|
||||
The callback function's prototype is the following:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
def inplace_servfail_callback(qinfo, qstate, rep, rcode, edns, opt_list_out, region):
|
||||
"""Function that will be registered as an inplace callback function.
|
||||
It will be called when answering with SERVFAIL.
|
||||
:param qinfo: query_info struct;
|
||||
:param qstate: module qstate. If not None the relevant opt_lists are
|
||||
available here;
|
||||
:param rep: reply_info struct. None;
|
||||
:param rcode: return code for the query. LDNS_RCODE_SERVFAIL;
|
||||
:param edns: edns_data to be sent to the client side. If qstate is None
|
||||
edns.opt_list contains the EDNS options sent from the client
|
||||
side. It SHOULD NOT be altered;
|
||||
:param opt_list_out: the list with the EDNS options that will be sent as a
|
||||
reply. It can be populated with EDNS options;
|
||||
:param region: region to allocate temporary data. Needs to be used when we
|
||||
want to append a new option to opt_list_out.
|
||||
:return: True on success, False on failure.
|
||||
"""
|
||||
|
||||
.. note:: The function's name is irrelevant.
|
||||
|
||||
We can register such function as:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
if not register_inplace_cb_reply_servfail(inplace_servfail_callback, env):
|
||||
log_info("python: Could not register inplace callback function.")
|
||||
|
||||
|
||||
Testing
|
||||
~~~~~~~
|
||||
|
||||
Run the Unbound server: ::
|
||||
|
||||
root@localhost$ unbound -dv -c ./test-inplace_callbacks.conf
|
||||
|
||||
In case you use your own configuration file, don't forget to enable the Python
|
||||
module::
|
||||
|
||||
module-config: "validator python iterator"
|
||||
|
||||
and use a valid script path ::
|
||||
|
||||
python-script: "./examples/inplace_callbacks.py"
|
||||
|
||||
On the first query for the nlnetlabs.nl A record we get no EDNS option back:
|
||||
|
||||
::
|
||||
|
||||
root@localhost$ dig @localhost nlnetlabs.nl +ednsopt=65002
|
||||
|
||||
; <<>> DiG 9.10.3-P4-Ubuntu <<>> @localhost nlnetlabs.nl +ednsopt=65002
|
||||
; (1 server found)
|
||||
;; global options: +cmd
|
||||
;; Got answer:
|
||||
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 48057
|
||||
;; flags: qr rd ra ad; QUERY: 1, ANSWER: 1, AUTHORITY: 4, ADDITIONAL: 3
|
||||
|
||||
;; OPT PSEUDOSECTION:
|
||||
; EDNS: version: 0, flags:; udp: 4096
|
||||
;; QUESTION SECTION:
|
||||
;nlnetlabs.nl. IN A
|
||||
|
||||
;; ANSWER SECTION:
|
||||
nlnetlabs.nl. 10200 IN A 185.49.140.10
|
||||
|
||||
;; AUTHORITY SECTION:
|
||||
nlnetlabs.nl. 10200 IN NS ns.nlnetlabs.nl.
|
||||
nlnetlabs.nl. 10200 IN NS sec2.authdns.ripe.net.
|
||||
nlnetlabs.nl. 10200 IN NS anyns.pch.net.
|
||||
nlnetlabs.nl. 10200 IN NS ns-ext1.sidn.nl.
|
||||
|
||||
;; ADDITIONAL SECTION:
|
||||
ns.nlnetlabs.nl. 10200 IN A 185.49.140.60
|
||||
ns.nlnetlabs.nl. 10200 IN AAAA 2a04:b900::8:0:0:60
|
||||
|
||||
;; Query time: 813 msec
|
||||
;; SERVER: 127.0.0.1#53(127.0.0.1)
|
||||
;; WHEN: Mon Dec 05 16:15:32 CET 2016
|
||||
;; MSG SIZE rcvd: 204
|
||||
|
||||
When we issue the same query again we get a cached response and the expected
|
||||
``65002: 0xdeadbeef`` EDNS option:
|
||||
|
||||
::
|
||||
|
||||
root@localhost$ dig @localhost nlnetlabs.nl +ednsopt=65002
|
||||
|
||||
; <<>> DiG 9.10.3-P4-Ubuntu <<>> @localhost nlnetlabs.nl +ednsopt=65002
|
||||
; (1 server found)
|
||||
;; global options: +cmd
|
||||
;; Got answer:
|
||||
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 26489
|
||||
;; flags: qr rd ra ad; QUERY: 1, ANSWER: 1, AUTHORITY: 4, ADDITIONAL: 3
|
||||
|
||||
;; OPT PSEUDOSECTION:
|
||||
; EDNS: version: 0, flags:; udp: 4096
|
||||
; OPT=65002: de ad be ef ("....")
|
||||
;; QUESTION SECTION:
|
||||
;nlnetlabs.nl. IN A
|
||||
|
||||
;; ANSWER SECTION:
|
||||
nlnetlabs.nl. 10197 IN A 185.49.140.10
|
||||
|
||||
;; AUTHORITY SECTION:
|
||||
nlnetlabs.nl. 10197 IN NS ns.nlnetlabs.nl.
|
||||
nlnetlabs.nl. 10197 IN NS sec2.authdns.ripe.net.
|
||||
nlnetlabs.nl. 10197 IN NS anyns.pch.net.
|
||||
nlnetlabs.nl. 10197 IN NS ns-ext1.sidn.nl.
|
||||
|
||||
;; ADDITIONAL SECTION:
|
||||
ns.nlnetlabs.nl. 10197 IN AAAA 2a04:b900::8:0:0:60
|
||||
ns.nlnetlabs.nl. 10197 IN A 185.49.140.60
|
||||
|
||||
;; Query time: 0 msec
|
||||
;; SERVER: 127.0.0.1#53(127.0.0.1)
|
||||
;; WHEN: Mon Dec 05 16:50:04 CET 2016
|
||||
;; MSG SIZE rcvd: 212
|
||||
|
||||
By issuing a query for a bogus domain unbound replies with SERVFAIL and an
|
||||
empty EDNS option code ``65003``. *For this example to work unbound needs to be
|
||||
validating*:
|
||||
|
||||
::
|
||||
|
||||
root@localhost$ dig @localhost bogus.nlnetlabs.nl txt
|
||||
|
||||
; <<>> DiG 9.10.3-P4-Ubuntu <<>> @localhost bogus.nlnetlabs.nl txt
|
||||
; (1 server found)
|
||||
;; global options: +cmd
|
||||
;; Got answer:
|
||||
;; ->>HEADER<<- opcode: QUERY, status: SERVFAIL, id: 19865
|
||||
;; flags: qr rd ra; QUERY: 1, ANSWER: 0, AUTHORITY: 0, ADDITIONAL: 1
|
||||
|
||||
;; OPT PSEUDOSECTION:
|
||||
; EDNS: version: 0, flags:; udp: 4096
|
||||
; OPT=65003
|
||||
;; QUESTION SECTION:
|
||||
;bogus.nlnetlabs.nl. IN TXT
|
||||
|
||||
;; Query time: 11 msec
|
||||
;; SERVER: 127.0.0.1#53(127.0.0.1)
|
||||
;; WHEN: Mon Dec 05 17:06:01 CET 2016
|
||||
;; MSG SIZE rcvd: 51
|
||||
|
||||
|
||||
Complete source code
|
||||
~~~~~~~~~~~~~~~~~~~~
|
||||
.. literalinclude:: ../../examples/inplace_callbacks.py
|
||||
:language: python
|
@ -1,15 +1,16 @@
|
||||
.. _Tutorials:
|
||||
|
||||
==============================
|
||||
Examples
|
||||
========
|
||||
|
||||
Here you can find several tutorials which clarify the usage and capabilities of
|
||||
the Unbound scriptable interface.
|
||||
|
||||
Tutorials
|
||||
==============================
|
||||
|
||||
Here you can find several tutorials which clarify the usage and capabilities of Unbound scriptable interface.
|
||||
|
||||
`Tutorials`
|
||||
---------
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
:glob:
|
||||
:maxdepth: 2
|
||||
:glob:
|
||||
|
||||
example*
|
||||
example*
|
||||
|
@ -1,39 +1,44 @@
|
||||
Installation
|
||||
===================================
|
||||
============
|
||||
|
||||
**Prerequisites**
|
||||
Prerequisites
|
||||
-------------
|
||||
|
||||
Python 2.4 or higher, SWIG 1.3 or higher, GNU make
|
||||
|
||||
**Download**
|
||||
Download
|
||||
--------
|
||||
|
||||
You can download the source codes `here`_.
|
||||
The latest release is 1.1.1, Jan 15, 2009.
|
||||
|
||||
.. _here: unbound-1.1.1-py.tar.gz
|
||||
|
||||
**Compiling**
|
||||
Compiling
|
||||
---------
|
||||
|
||||
After downloading, you can compile the Unbound library by doing::
|
||||
|
||||
> tar -xzf unbound-1.1.1-py.tar.gz
|
||||
> cd unbound-1.1.1
|
||||
> ./configure --with-pythonmodule
|
||||
> make
|
||||
> tar -xzf unbound-1.1.1-py.tar.gz
|
||||
> cd unbound-1.1.1
|
||||
> ./configure --with-pythonmodule
|
||||
> make
|
||||
|
||||
You need GNU make to compile sources.
|
||||
SWIG and Python devel libraries to compile extension module.
|
||||
|
||||
**Testing**
|
||||
Testing
|
||||
-------
|
||||
|
||||
If the compilation is successful, you can test the extension module by::
|
||||
|
||||
> cd pythonmod
|
||||
> make sudo # or "make test" or "make suexec"
|
||||
> cd pythonmod
|
||||
> make sudo # or "make test" or "make suexec"
|
||||
|
||||
This will start unbound server with language dictionary service (see :ref:`Tutorials`).
|
||||
This will start unbound server with language dictionary service
|
||||
(see :ref:`Tutorials`).
|
||||
In order to test this service, type::
|
||||
|
||||
|
||||
> dig TXT @127.0.0.1 aught.en._dict_.cz
|
||||
|
||||
Dig should print this message (czech equivalent of aught)::
|
||||
@ -44,16 +49,17 @@ Dig should print this message (czech equivalent of aught)::
|
||||
;; Got answer:
|
||||
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 30085
|
||||
;; flags: aa rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 0
|
||||
|
||||
|
||||
;; QUESTION SECTION:
|
||||
;aught.en._dict_.cz. IN TXT
|
||||
|
||||
;aught.en._dict_.cz. IN TXT
|
||||
|
||||
;; ANSWER SECTION:
|
||||
aught.en._dict_.cz. 300 IN TXT "nic"
|
||||
|
||||
aught.en._dict_.cz. 300 IN TXT "nic"
|
||||
|
||||
;; Query time: 11 msec
|
||||
;; SERVER: 127.0.0.1#53(127.0.0.1)
|
||||
;; WHEN: Thu Jan 10 16:45:58 2009
|
||||
;; MSG SIZE rcvd: 52
|
||||
|
||||
The ``pythonmod/examples`` directory contains simple applications written in Python.
|
||||
The ``pythonmod/examples`` directory contains simple applications written in
|
||||
Python.
|
||||
|
@ -7,25 +7,26 @@ Network
|
||||
.. function:: ntohs(netshort)
|
||||
|
||||
This subroutine converts values between the host and network byte order.
|
||||
Specifically, **ntohs()** converts 16-bit quantities from network byte order to host byte order.
|
||||
|
||||
Specifically, **ntohs()** converts 16-bit quantities from network byte order
|
||||
to host byte order.
|
||||
|
||||
:param netshort: 16-bit short addr
|
||||
:rtype: converted addr
|
||||
|
||||
|
||||
|
||||
|
||||
Cache
|
||||
-----
|
||||
|
||||
.. function:: storeQueryInCache(qstate, qinfo, msgrep, is_referral)
|
||||
|
||||
Store pending query in local cache.
|
||||
|
||||
|
||||
:param qstate: :class:`module_qstate`
|
||||
:param qinfo: :class:`query_info`
|
||||
:param msgrep: :class:`reply_info`
|
||||
:param is_referal: integer
|
||||
:rtype: boolean
|
||||
|
||||
|
||||
.. function:: invalidateQueryInCache(qstate, qinfo)
|
||||
|
||||
Invalidate record in local cache.
|
||||
@ -34,6 +35,111 @@ Cache
|
||||
:param qinfo: :class:`query_info`
|
||||
|
||||
|
||||
EDNS options
|
||||
------------
|
||||
|
||||
.. function:: register_edns_option(env, code, bypass_cache_stage=False, no_aggregation=False)
|
||||
|
||||
Register EDNS option code.
|
||||
|
||||
:param env: :class:`module_env`
|
||||
:param code: option code(integer)
|
||||
:param bypass_cache_stage: whether to bypass the cache response stage
|
||||
:param no_aggregation: whether this query should be unique
|
||||
:return: ``1`` if successful, ``0`` otherwise
|
||||
:rtype: integer
|
||||
|
||||
.. function:: edns_opt_list_find(list, code)
|
||||
|
||||
Find the EDNS option code in the EDNS option list.
|
||||
|
||||
:param list: linked list of :class:`edns_option`
|
||||
:param code: option code (integer)
|
||||
:return: the edns option if found or None
|
||||
:rtype: :class:`edns_option` or None
|
||||
|
||||
.. function:: edns_opt_list_remove(list, code);
|
||||
|
||||
Remove an ENDS option code from the list.
|
||||
.. note:: All :class:`edns_option` with the code will be removed
|
||||
|
||||
:param list: linked list of :class:`edns_option`
|
||||
:param code: option code (integer)
|
||||
:return: ``1`` if at least one :class:`edns_option` was removed, ``0`` otherwise
|
||||
:rtype: integer
|
||||
|
||||
.. function:: edns_opt_list_append(list, code, data, region)
|
||||
|
||||
Append given EDNS option code with data to the list.
|
||||
|
||||
:param list: linked list of :class:`edns_option`
|
||||
:param code: option code (integer)
|
||||
:param data: EDNS data. **Must** be a :class:`bytearray`
|
||||
:param region: :class:`regional`
|
||||
|
||||
.. function:: edns_opt_list_is_empty(list)
|
||||
|
||||
Check if an EDNS option list is empty.
|
||||
|
||||
:param list: linked list of :class:`edns_option`
|
||||
:return: ``1`` if list is empty, ``0`` otherwise
|
||||
:rtype: integer
|
||||
|
||||
|
||||
Inplace callbacks
|
||||
-----------------
|
||||
|
||||
.. function:: inplace_cb_reply(qinfo, qstate, rep, rcode, edns, opt_list_out, region)
|
||||
|
||||
Function prototype for callback functions used in
|
||||
`register_inplace_cb_reply`_, `register_inplace_cb_reply_cache`_,
|
||||
`register_inplace_cb_reply_local` and `register_inplace_cb_reply_servfail`.
|
||||
|
||||
:param qinfo: :class:`query_info`
|
||||
:param qstate: :class:`module_qstate`
|
||||
:param rep: :class:`reply_info`
|
||||
:param rcode: return code (integer), check ``RCODE_`` constants.
|
||||
:param edns: :class:`edns_data`
|
||||
:param opt_list_out: :class:`edns_option`. EDNS option list to append options to.
|
||||
:param region: :class:`regional`
|
||||
|
||||
.. function:: register_inplace_cb_reply(py_cb, env)
|
||||
|
||||
Register py_cb as an inplace reply callback function.
|
||||
|
||||
:param py_cb: Python function that follows `inplace_cb_reply`_'s prototype. **Must** be callable.
|
||||
:param env: :class:`module_env`
|
||||
:return: True on success, False otherwise
|
||||
:rtype: boolean
|
||||
|
||||
.. function:: register_inplace_cb_reply_cache(py_cb, env)
|
||||
|
||||
Register py_cb as an inplace reply_cache callback function.
|
||||
|
||||
:param py_cb: Python function that follows `inplace_cb_reply`_'s prototype. **Must** be callable.
|
||||
:param env: :class:`module_env`
|
||||
:return: True on success, False otherwise
|
||||
:rtype: boolean
|
||||
|
||||
.. function:: register_inplace_cb_reply_local(py_cb, env)
|
||||
|
||||
Register py_cb as an inplace reply_local callback function.
|
||||
|
||||
:param py_cb: Python function that follows `inplace_cb_reply`_'s prototype. **Must** be callable.
|
||||
:param env: :class:`module_env`
|
||||
:return: True on success, False otherwise
|
||||
:rtype: boolean
|
||||
|
||||
.. function:: register_inplace_cb_reply_servfail(py_cb, env)
|
||||
|
||||
Register py_cb as an inplace reply_servfail callback function.
|
||||
|
||||
:param py_cb: Python function that follows `inplace_cb_reply`_'s prototype. **Must** be callable.
|
||||
:param env: :class:`module_env`
|
||||
:return: True on success, False otherwise
|
||||
:rtype: boolean
|
||||
|
||||
|
||||
Logging
|
||||
-------
|
||||
|
||||
@ -71,50 +177,51 @@ Logging
|
||||
:param msg: string desc to accompany the hexdump.
|
||||
:param data: data to dump in hex format.
|
||||
:param length: length of data.
|
||||
|
||||
|
||||
.. function:: log_dns_msg(str, qinfo, reply)
|
||||
|
||||
Log DNS message.
|
||||
|
||||
|
||||
:param str: string message
|
||||
:param qinfo: :class:`query_info`
|
||||
:param reply: :class:`reply_info`
|
||||
|
||||
|
||||
.. function:: log_query_info(verbosity_value, str, qinf)
|
||||
|
||||
Log query information.
|
||||
|
||||
|
||||
:param verbosity_value: see constants
|
||||
:param str: string message
|
||||
:param qinf: :class:`query_info`
|
||||
|
||||
|
||||
.. function:: regional_log_stats(r)
|
||||
|
||||
Log regional statistics.
|
||||
|
||||
|
||||
:param r: :class:`regional`
|
||||
|
||||
|
||||
Debugging
|
||||
---------
|
||||
|
||||
.. function:: strextstate(module_ext_state)
|
||||
|
||||
Debug utility, module external qstate to string.
|
||||
|
||||
|
||||
:param module_ext_state: the state value.
|
||||
:rtype: descriptive string.
|
||||
|
||||
.. function:: strmodulevent(module_event)
|
||||
|
||||
Debug utility, module event to string.
|
||||
|
||||
|
||||
:param module_event: the module event value.
|
||||
:rtype: descriptive string.
|
||||
|
||||
|
||||
.. function:: ldns_rr_type2str(atype)
|
||||
|
||||
Convert RR type to string.
|
||||
|
||||
|
||||
.. function:: ldns_rr_class2str(aclass)
|
||||
|
||||
Convert RR class to string.
|
||||
|
@ -6,55 +6,94 @@ module_qstate
|
||||
|
||||
.. class:: module_qstate
|
||||
|
||||
Module state, per query.
|
||||
|
||||
This class provides these data attributes:
|
||||
|
||||
.. attribute:: qinfo
|
||||
|
||||
(:class:`query_info`) Informations about query being answered. Name, RR type, RR class.
|
||||
|
||||
.. attribute:: query_flags
|
||||
|
||||
(uint16) Flags for query. See QF_BIT\_ predefined constants.
|
||||
|
||||
.. attribute:: is_priming
|
||||
|
||||
If this is a (stub or root) priming query (with hints).
|
||||
|
||||
.. attribute:: reply
|
||||
|
||||
comm_reply contains server replies.
|
||||
|
||||
.. attribute:: return_msg
|
||||
|
||||
(:class:`dns_msg`) The reply message, with message for client and calling module (read-only attribute).
|
||||
Note that if you want to create of modify return_msg you should use :class:`DNSMessage`.
|
||||
|
||||
.. attribute:: return_rcode
|
||||
|
||||
The rcode, in case of error, instead of a reply message. Determines whether the return_msg contains reply.
|
||||
|
||||
.. attribute:: region
|
||||
|
||||
Region for this query. Cleared when query process finishes.
|
||||
|
||||
.. attribute:: curmod
|
||||
|
||||
Which module is executing.
|
||||
|
||||
.. attribute:: ext_state[]
|
||||
|
||||
Module states.
|
||||
|
||||
.. attribute:: env
|
||||
|
||||
Environment for this query.
|
||||
|
||||
.. attribute:: mesh_info
|
||||
|
||||
Mesh related information for this query.
|
||||
Module state, per query.
|
||||
|
||||
This class provides these data attributes:
|
||||
|
||||
.. attribute:: qinfo
|
||||
|
||||
(:class:`query_info`) Informations about query being answered. Name, RR type, RR class.
|
||||
|
||||
.. attribute:: query_flags
|
||||
|
||||
(uint16) Flags for query. See QF_BIT\_ predefined constants.
|
||||
|
||||
.. attribute:: is_priming
|
||||
|
||||
If this is a (stub or root) priming query (with hints).
|
||||
|
||||
.. attribute:: reply
|
||||
|
||||
comm_reply contains server replies.
|
||||
|
||||
.. attribute:: return_msg
|
||||
|
||||
(:class:`dns_msg`) The reply message, with message for client and calling module (read-only attribute).
|
||||
Note that if you want to create of modify return_msg you should use :class:`DNSMessage`.
|
||||
|
||||
.. attribute:: return_rcode
|
||||
|
||||
The rcode, in case of error, instead of a reply message. Determines whether the return_msg contains reply.
|
||||
|
||||
.. attribute:: region
|
||||
|
||||
Region for this query. Cleared when query process finishes.
|
||||
|
||||
.. attribute:: curmod
|
||||
|
||||
Which module is executing.
|
||||
|
||||
.. attribute:: ext_state[]
|
||||
|
||||
Module states.
|
||||
|
||||
.. attribute:: env
|
||||
|
||||
Environment for this query.
|
||||
|
||||
.. attribute:: mesh_info
|
||||
|
||||
Mesh related information for this query.
|
||||
|
||||
.. attribute:: edns_opts_front_in
|
||||
|
||||
Incoming EDNS options from the front end.
|
||||
|
||||
.. attribute:: edns_opts_front_in_iter
|
||||
|
||||
Iterator for `edns_opts_front_in`.
|
||||
|
||||
.. attribute:: edns_opts_back_out
|
||||
|
||||
Outgoing EDNS options to the back end.
|
||||
|
||||
.. attribute:: edns_opts_back_out_iter
|
||||
|
||||
Iterator for `edns_opts_back_out`.
|
||||
|
||||
.. attribute:: edns_opts_back_in
|
||||
|
||||
Incoming EDNS options from the back end.
|
||||
|
||||
.. attribute:: edns_opts_back_in_iter
|
||||
|
||||
Iterator for `ends_opts_back_in`.
|
||||
|
||||
.. attribute:: edns_opts_front_out
|
||||
|
||||
Outgoing EDNS options to the front end.
|
||||
|
||||
.. attribute:: edns_opts_front_out_iter
|
||||
|
||||
Iterator for `edns_opts_front_out`.
|
||||
|
||||
.. attribute:: no_cache_lookup
|
||||
|
||||
Flag to indicate whether modules should answer from the cache.
|
||||
|
||||
.. attribute:: no_cache_store
|
||||
|
||||
Flag to indicate whether modules should store answer in the cache.
|
||||
|
||||
query_info
|
||||
----------------
|
||||
@ -94,7 +133,57 @@ query_info
|
||||
.. attribute:: qclass_str
|
||||
|
||||
The ``qclass`` in display presentation format (string).
|
||||
|
||||
|
||||
edns_data
|
||||
---------
|
||||
|
||||
.. class:: edns_data
|
||||
|
||||
This class represents the EDNS information parsed/encoded from/to a packet. It provides these data attributes:
|
||||
|
||||
.. attribute:: edns_present
|
||||
|
||||
If EDNS OPT record is present.
|
||||
|
||||
.. attribute:: ext_rcode
|
||||
|
||||
Extended RCODE.
|
||||
|
||||
.. attribute:: edns_version
|
||||
|
||||
The EDNS version number.
|
||||
|
||||
.. attribute:: bits
|
||||
|
||||
The EDNS bits field from ttl (host order): Z.
|
||||
|
||||
.. attribute:: udp_size
|
||||
|
||||
UDP reassembly size.
|
||||
|
||||
.. attribute:: opt_list
|
||||
|
||||
The EDNS option list.
|
||||
|
||||
.. attribute:: opt_list_iter
|
||||
|
||||
Iterator for `opt_list`.
|
||||
|
||||
edns_option
|
||||
-----------
|
||||
|
||||
.. class:: edns_option
|
||||
|
||||
This class represents an EDNS option (code, data) found in EDNS option lists. It provides these data attributes:
|
||||
|
||||
.. attribute:: code
|
||||
|
||||
The EDNS option code.
|
||||
|
||||
.. attribute:: data
|
||||
|
||||
The EDNS option data.
|
||||
|
||||
reply_info
|
||||
--------------------
|
||||
|
||||
|
194
pythonmod/examples/edns.py
Normal file
194
pythonmod/examples/edns.py
Normal file
@ -0,0 +1,194 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
'''
|
||||
edns.py: python module showcasing EDNS option functionality.
|
||||
|
||||
Copyright (c) 2016, NLnet Labs.
|
||||
|
||||
This software is open source.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions
|
||||
are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright notice,
|
||||
this list of conditions and the following disclaimer.
|
||||
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
* Neither the name of the organization nor the names of its
|
||||
contributors may be used to endorse or promote products derived from this
|
||||
software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||
TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE
|
||||
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGE.
|
||||
'''
|
||||
#Try:
|
||||
# - dig @localhost nlnetlabs.nl +ednsopt=65001:c001
|
||||
# This query will always reach the modules stage as EDNS option 65001 is
|
||||
# registered to bypass the cache response stage. It will also be handled
|
||||
# as a unique query because of the no_aggregation flag. This means that
|
||||
# it will not be aggregated with other queries for the same qinfo.
|
||||
# For demonstration purposes when option 65001 with hexdata 'c001' is
|
||||
# sent from the client side this module will reply with the same code and
|
||||
# data 'deadbeef'.
|
||||
|
||||
# Useful functions:
|
||||
# edns_opt_list_is_empty(edns_opt_list):
|
||||
# Check if the option list is empty.
|
||||
# Return True if empty, False otherwise.
|
||||
#
|
||||
# edns_opt_list_append(edns_opt_list, code, data_bytearray, region):
|
||||
# Append the EDNS option with code and data_bytearray to the given
|
||||
# edns_opt_list.
|
||||
# NOTE: data_bytearray MUST be a Python bytearray.
|
||||
# Return True on success, False on failure.
|
||||
#
|
||||
# edns_opt_list_remove(edns_opt_list, code):
|
||||
# Remove all occurences of the given EDNS option code from the
|
||||
# edns_opt_list.
|
||||
# Return True when at least one EDNS option was removed, False otherwise.
|
||||
#
|
||||
# register_edns_option(env, code, bypass_cache_stage=True,
|
||||
# no_aggregation=True):
|
||||
# Register EDNS option code as a known EDNS option.
|
||||
# bypass_cache_stage:
|
||||
# bypasses answering from cache and allows the query to reach the
|
||||
# modules for further EDNS handling.
|
||||
# no_aggregation:
|
||||
# makes every query with the said EDNS option code unique.
|
||||
# Return True on success, False on failure.
|
||||
#
|
||||
# Examples on how to use the functions are given in this file.
|
||||
|
||||
|
||||
def init_standard(id, env):
|
||||
"""New version of the init function.
|
||||
The function's signature is the same as the C counterpart and allows for
|
||||
extra functionality during init.
|
||||
..note:: This function is preferred by unbound over the old init function.
|
||||
..note:: The previously accesible configuration options can now be found in
|
||||
env.cgf.
|
||||
"""
|
||||
log_info("python: inited script {}".format(env.cfg.python_script))
|
||||
|
||||
# Register EDNS option 65001 as a known EDNS option.
|
||||
if not register_edns_option(env, 65001, bypass_cache_stage=True,
|
||||
no_aggregation=True):
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
|
||||
def init(id, cfg):
|
||||
"""Previous version init function.
|
||||
..note:: This function is still supported for backwards compatibility when
|
||||
the init_standard function is missing. When init_standard is
|
||||
present this function SHOULD be ommited to avoid confusion to the
|
||||
reader.
|
||||
"""
|
||||
return True
|
||||
|
||||
|
||||
def deinit(id): return True
|
||||
|
||||
|
||||
def inform_super(id, qstate, superqstate, qdata): return True
|
||||
|
||||
|
||||
def operate(id, event, qstate, qdata):
|
||||
if (event == MODULE_EVENT_NEW) or (event == MODULE_EVENT_PASS):
|
||||
# Detect if EDNS option code 56001 is present from the client side. If
|
||||
# so turn on the flags for cache management.
|
||||
if not edns_opt_list_is_empty(qstate.edns_opts_front_in):
|
||||
log_info("python: searching for EDNS option code 65001 during NEW "
|
||||
"or PASS event ")
|
||||
for o in qstate.edns_opts_front_in_iter:
|
||||
if o.code == 65001:
|
||||
log_info("python: found EDNS option code 65001")
|
||||
# Instruct other modules to not lookup for an
|
||||
# answer in the cache.
|
||||
qstate.no_cache_lookup = 1
|
||||
log_info("python: enabled no_cache_lookup")
|
||||
|
||||
# Instruct other modules to not store the answer in
|
||||
# the cache.
|
||||
qstate.no_cache_store = 1
|
||||
log_info("python: enabled no_cache_store")
|
||||
|
||||
#Pass on the query
|
||||
qstate.ext_state[id] = MODULE_WAIT_MODULE
|
||||
return True
|
||||
|
||||
elif event == MODULE_EVENT_MODDONE:
|
||||
# If the client sent EDNS option code 65001 and data 'c001' reply
|
||||
# with the same code and data 'deadbeef'.
|
||||
if not edns_opt_list_is_empty(qstate.edns_opts_front_in):
|
||||
log_info("python: searching for EDNS option code 65001 during "
|
||||
"MODDONE")
|
||||
for o in qstate.edns_opts_front_in_iter:
|
||||
if o.code == 65001 and o.data == bytearray.fromhex("c001"):
|
||||
b = bytearray.fromhex("deadbeef")
|
||||
if not edns_opt_list_append(qstate.edns_opts_front_out,
|
||||
o.code, b, qstate.region):
|
||||
qstate.ext_state[id] = MODULE_ERROR
|
||||
return False
|
||||
|
||||
# List every EDNS option in all lists.
|
||||
# The available lists are:
|
||||
# - qstate.edns_opts_front_in: EDNS options that came from the
|
||||
# client side. SHOULD NOT be changed;
|
||||
#
|
||||
# - qstate.edns_opts_back_out: EDNS options that will be sent to the
|
||||
# server side. Can be populated by
|
||||
# EDNS literate modules;
|
||||
#
|
||||
# - qstate.edns_opts_back_in: EDNS options that came from the
|
||||
# server side. SHOULD NOT be changed;
|
||||
#
|
||||
# - qstate.edns_opts_front_out: EDNS options that will be sent to the
|
||||
# client side. Can be populated by
|
||||
# EDNS literate modules;
|
||||
#
|
||||
# The lists' contents can be accessed in python by their _iter
|
||||
# counterpart as an iterator.
|
||||
if not edns_opt_list_is_empty(qstate.edns_opts_front_in):
|
||||
log_info("python: EDNS options in edns_opts_front_in:")
|
||||
for o in qstate.edns_opts_front_in_iter:
|
||||
log_info("python: Code: {}, Data: '{}'".format(o.code,
|
||||
"".join('{:02x}'.format(x) for x in o.data)))
|
||||
|
||||
if not edns_opt_list_is_empty(qstate.edns_opts_back_out):
|
||||
log_info("python: EDNS options in edns_opts_back_out:")
|
||||
for o in qstate.edns_opts_back_out_iter:
|
||||
log_info("python: Code: {}, Data: '{}'".format(o.code,
|
||||
"".join('{:02x}'.format(x) for x in o.data)))
|
||||
|
||||
if not edns_opt_list_is_empty(qstate.edns_opts_back_in):
|
||||
log_info("python: EDNS options in edns_opts_back_in:")
|
||||
for o in qstate.edns_opts_back_in_iter:
|
||||
log_info("python: Code: {}, Data: '{}'".format(o.code,
|
||||
"".join('{:02x}'.format(x) for x in o.data)))
|
||||
|
||||
if not edns_opt_list_is_empty(qstate.edns_opts_front_out):
|
||||
log_info("python: EDNS options in edns_opts_front_out:")
|
||||
for o in qstate.edns_opts_front_out_iter:
|
||||
log_info("python: Code: {}, Data: '{}'".format(o.code,
|
||||
"".join('{:02x}'.format(x) for x in o.data)))
|
||||
|
||||
qstate.ext_state[id] = MODULE_FINISHED
|
||||
return True
|
||||
|
||||
log_err("pythonmod: Unknown event")
|
||||
qstate.ext_state[id] = MODULE_ERROR
|
||||
return True
|
244
pythonmod/examples/inplace_callbacks.py
Normal file
244
pythonmod/examples/inplace_callbacks.py
Normal file
@ -0,0 +1,244 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
'''
|
||||
inplace_callbacks.py: python module showcasing inplace callback function
|
||||
registration and functionality.
|
||||
|
||||
Copyright (c) 2016, NLnet Labs.
|
||||
|
||||
This software is open source.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions
|
||||
are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright notice,
|
||||
this list of conditions and the following disclaimer.
|
||||
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
* Neither the name of the organization nor the names of its
|
||||
contributors may be used to endorse or promote products derived from this
|
||||
software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||
TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE
|
||||
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGE.
|
||||
'''
|
||||
#Try:
|
||||
# - dig @localhost nlnetlabs.nl +ednsopt=65002:
|
||||
# This query *could* be answered from cache. If so, unbound will reply
|
||||
# with the same EDNS option 65002, but with hexdata 'deadbeef' as data.
|
||||
#
|
||||
# - dig @localhost bogus.nlnetlabs.nl txt:
|
||||
# This query returns SERVFAIL as the txt record of bogus.nlnetlabs.nl is
|
||||
# intentionally bogus. The reply will contain an empty EDNS option
|
||||
# with option code 65003.
|
||||
# (unbound needs to be validating for this example to work)
|
||||
|
||||
# Useful functions:
|
||||
# register_inplace_cb_reply(inplace_reply_callback, env):
|
||||
# Register the reply_callback function as an inplace callback function
|
||||
# when answering with a resolved query.
|
||||
# Return True on success, False on failure.
|
||||
#
|
||||
# register_inplace_cb_reply_cache(inplace_reply_cache_callback, env):
|
||||
# Register the reply_cache_callback function as an inplace callback
|
||||
# function when answering from cache.
|
||||
# Return True on success, False on failure.
|
||||
#
|
||||
# register_inplace_cb_reply_local(inplace_reply_local_callback, env):
|
||||
# Register the reply_local_callback function as an inplace callback
|
||||
# function when answering from local data or chaos reply.
|
||||
# Return True on success, False on failure.
|
||||
#
|
||||
# register_inplace_cb_reply_servfail(inplace_reply_servfail_callback, env):
|
||||
# Register the reply_servfail_callback function as an inplace callback
|
||||
# function when answering with servfail.
|
||||
# Return True on success, False on failure.
|
||||
#
|
||||
# Examples on how to use the functions are given in this file.
|
||||
|
||||
|
||||
def inplace_reply_callback(qinfo, qstate, rep, rcode, edns, opt_list_out,
|
||||
region):
|
||||
"""Function that will be registered as an inplace callback function.
|
||||
It will be called when answering with a resolved query.
|
||||
:param qinfo: query_info struct;
|
||||
:param qstate: module qstate. It contains the available opt_lists; It
|
||||
SHOULD NOT be altered;
|
||||
:param rep: reply_info struct;
|
||||
:param rcode: return code for the query;
|
||||
:param edns: edns_data to be sent to the client side. It SHOULD NOT be
|
||||
altered;
|
||||
:param opt_list_out: the list with the EDNS options that will be sent as a
|
||||
reply. It can be populated with EDNS options;
|
||||
:param region: region to allocate temporary data. Needs to be used when we
|
||||
want to append a new option to opt_list_out.
|
||||
:return: True on success, False on failure.
|
||||
"""
|
||||
log_info("python: called back while replying.")
|
||||
return True
|
||||
|
||||
|
||||
def inplace_cache_callback(qinfo, qstate, rep, rcode, edns, opt_list_out,
|
||||
region):
|
||||
"""Function that will be registered as an inplace callback function.
|
||||
It will be called when answering from the cache.
|
||||
:param qinfo: query_info struct;
|
||||
:param qstate: module qstate. None;
|
||||
:param rep: reply_info struct;
|
||||
:param rcode: return code for the query;
|
||||
:param edns: edns_data sent from the client side. The list with the EDNS
|
||||
options is accesible through edns.opt_list. It SHOULD NOT be
|
||||
altered;
|
||||
:param opt_list_out: the list with the EDNS options that will be sent as a
|
||||
reply. It can be populated with EDNS options;
|
||||
:param region: region to allocate temporary data. Needs to be used when we
|
||||
want to append a new option to opt_list_out.
|
||||
:return: True on success, False on failure.
|
||||
|
||||
For demostration purposes we want to see if EDNS option 65002 is present
|
||||
and reply with a new value.
|
||||
"""
|
||||
log_info("python: called back while answering from cache.")
|
||||
# Inspect the incoming EDNS options.
|
||||
if not edns_opt_list_is_empty(edns.opt_list):
|
||||
log_info("python: available EDNS options:")
|
||||
for o in edns.opt_list_iter:
|
||||
log_info("python: Code: {}, Data: '{}'".format(o.code,
|
||||
"".join('{:02x}'.format(x) for x in o.data)))
|
||||
if o.code == 65002:
|
||||
log_info("python: *found option code 65002*")
|
||||
|
||||
# add to opt_list
|
||||
# Data MUST be represented in a bytearray.
|
||||
b = bytearray.fromhex("deadbeef")
|
||||
if edns_opt_list_append(opt_list_out, o.code, b, region):
|
||||
log_info("python: *added new option code 65002*")
|
||||
else:
|
||||
log_info("python: *failed to add new option code 65002*")
|
||||
return False
|
||||
break
|
||||
|
||||
return True
|
||||
|
||||
|
||||
def inplace_local_callback(qinfo, qstate, rep, rcode, edns, opt_list_out,
|
||||
region):
|
||||
"""Function that will be registered as an inplace callback function.
|
||||
It will be called when answering from local data.
|
||||
:param qinfo: query_info struct;
|
||||
:param qstate: module qstate. None;
|
||||
:param rep: reply_info struct;
|
||||
:param rcode: return code for the query;
|
||||
:param edns: edns_data sent from the client side. The list with the
|
||||
EDNS options is accesible through edns.opt_list. It
|
||||
SHOULD NOT be altered;
|
||||
:param opt_list_out: the list with the EDNS options that will be sent as a
|
||||
reply. It can be populated with EDNS options;
|
||||
:param region: region to allocate temporary data. Needs to be used when we
|
||||
want to append a new option to opt_list_out.
|
||||
:return: True on success, False on failure.
|
||||
"""
|
||||
log_info("python: called back while replying with local data or chaos"
|
||||
" reply.")
|
||||
return True
|
||||
|
||||
|
||||
def inplace_servfail_callback(qinfo, qstate, rep, rcode, edns, opt_list_out,
|
||||
region):
|
||||
"""Function that will be registered as an inplace callback function.
|
||||
It will be called when answering with SERVFAIL.
|
||||
:param qinfo: query_info struct;
|
||||
:param qstate: module qstate. If not None the relevant opt_lists are
|
||||
available here;
|
||||
:param rep: reply_info struct. None;
|
||||
:param rcode: return code for the query. LDNS_RCODE_SERVFAIL;
|
||||
:param edns: edns_data to be sent to the client side. If qstate is None
|
||||
edns.opt_list contains the EDNS options sent from the client
|
||||
side. It SHOULD NOT be altered;
|
||||
:param opt_list_out: the list with the EDNS options that will be sent as a
|
||||
reply. It can be populated with EDNS options;
|
||||
:param region: region to allocate temporary data. Needs to be used when we
|
||||
want to append a new option to opt_list_out.
|
||||
:return: True on success, False on failure.
|
||||
|
||||
For demostration purposes we want to reply with an empty EDNS code '65003'.
|
||||
"""
|
||||
log_info("python: called back while servfail.")
|
||||
b = bytearray.fromhex("")
|
||||
edns_opt_list_append(opt_list_out, 65003, b, region)
|
||||
return True
|
||||
|
||||
|
||||
def init_standard(id, env):
|
||||
"""New version of the init function.
|
||||
The function's signature is the same as the C counterpart and allows for
|
||||
extra functionality during init.
|
||||
..note:: This function is preferred by unbound over the old init function.
|
||||
..note:: The previously accesible configuration options can now be found in
|
||||
env.cgf.
|
||||
"""
|
||||
log_info("python: inited script {}".format(env.cfg.python_script))
|
||||
|
||||
# Register the inplace_reply_callback function as an inplace callback
|
||||
# function when answering a resolved query.
|
||||
if not register_inplace_cb_reply(inplace_reply_callback, env):
|
||||
return False
|
||||
|
||||
# Register the inplace_cache_callback function as an inplace callback
|
||||
# function when answering from cache.
|
||||
if not register_inplace_cb_reply_cache(inplace_cache_callback, env):
|
||||
return False
|
||||
|
||||
# Register the inplace_local_callback function as an inplace callback
|
||||
# function when answering from local data.
|
||||
if not register_inplace_cb_reply_local(inplace_local_callback, env):
|
||||
return False
|
||||
|
||||
# Register the inplace_servfail_callback function as an inplace callback
|
||||
# function when answering with SERVFAIL.
|
||||
if not register_inplace_cb_reply_servfail(inplace_servfail_callback, env):
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
|
||||
def init(id, cfg):
|
||||
"""Previous version init function.
|
||||
..note:: This function is still supported for backwards compatibility when
|
||||
the init_standard function is missing. When init_standard is
|
||||
present this function SHOULD be ommited to avoid confusion to the
|
||||
reader.
|
||||
"""
|
||||
return True
|
||||
|
||||
|
||||
def deinit(id): return True
|
||||
|
||||
|
||||
def inform_super(id, qstate, superqstate, qdata): return True
|
||||
|
||||
|
||||
def operate(id, event, qstate, qdata):
|
||||
if (event == MODULE_EVENT_NEW) or (event == MODULE_EVENT_PASS):
|
||||
qstate.ext_state[id] = MODULE_WAIT_MODULE
|
||||
return True
|
||||
|
||||
elif event == MODULE_EVENT_MODDONE:
|
||||
qstate.ext_state[id] = MODULE_FINISHED
|
||||
return True
|
||||
|
||||
log_err("pythonmod: Unknown event")
|
||||
qstate.ext_state[id] = MODULE_ERROR
|
||||
return True
|
@ -1,7 +1,6 @@
|
||||
/*
|
||||
* interface.i: unbound python module
|
||||
*/
|
||||
|
||||
%module unboundmodule
|
||||
%{
|
||||
/**
|
||||
@ -34,10 +33,10 @@
|
||||
#include "sldns/pkthdr.h"
|
||||
%}
|
||||
|
||||
%include "stdint.i" // uint_16_t can be known type now
|
||||
%include "stdint.i" /* uint_16_t can be known type now */
|
||||
|
||||
%inline %{
|
||||
//converts [len][data][len][data][0] string to a List of labels (PyBytes)
|
||||
/* converts [len][data][len][data][0] string to a List of labels (PyBytes) */
|
||||
PyObject* GetNameAsLabelList(const char* name, int len) {
|
||||
PyObject* list;
|
||||
int cnt=0, i;
|
||||
@ -202,13 +201,16 @@ struct packed_rrset_key {
|
||||
char* dname;
|
||||
size_t dname_len;
|
||||
uint32_t flags;
|
||||
uint16_t type; //rrset type in network format
|
||||
uint16_t rrset_class; //rrset class in network format
|
||||
uint16_t type; /* rrset type in network format */
|
||||
uint16_t rrset_class; /* rrset class in network format */
|
||||
%mutable;
|
||||
};
|
||||
|
||||
//This subroutine converts values between the host and network byte order.
|
||||
//Specifically, ntohs() converts 16-bit quantities from network byte order to host byte order.
|
||||
/**
|
||||
* This subroutine converts values between the host and network byte order.
|
||||
* Specifically, ntohs() converts 16-bit quantities from network byte order to
|
||||
* host byte order.
|
||||
*/
|
||||
uint16_t ntohs(uint16_t netshort);
|
||||
|
||||
%inline %{
|
||||
@ -269,17 +271,24 @@ struct lruhash_entry {
|
||||
%ignore packed_rrset_data::rr_data;
|
||||
|
||||
struct packed_rrset_data {
|
||||
uint32_t ttl; //TTL (in seconds like time())
|
||||
/* TTL (in seconds like time()) */
|
||||
uint32_t ttl;
|
||||
|
||||
size_t count; //number of rrs
|
||||
size_t rrsig_count; //number of rrsigs
|
||||
/* number of rrs */
|
||||
size_t count;
|
||||
/* number of rrsigs */
|
||||
size_t rrsig_count;
|
||||
|
||||
enum rrset_trust trust;
|
||||
enum sec_status security;
|
||||
|
||||
size_t* rr_len; //length of every rr's rdata
|
||||
uint32_t *rr_ttl; //ttl of every rr
|
||||
uint8_t** rr_data; //array of pointers to every rr's rdata; The rr_data[i] rdata is stored in uncompressed wireformat.
|
||||
/* length of every rr's rdata */
|
||||
size_t* rr_len;
|
||||
/* ttl of every rr */
|
||||
uint32_t *rr_ttl;
|
||||
/* array of pointers to every rr's rdata. The rr_data[i] rdata is stored in
|
||||
* uncompressed wireformat. */
|
||||
uint8_t** rr_data;
|
||||
};
|
||||
|
||||
%pythoncode %{
|
||||
@ -359,10 +368,10 @@ struct reply_info {
|
||||
size_t an_numrrsets;
|
||||
size_t ns_numrrsets;
|
||||
size_t ar_numrrsets;
|
||||
size_t rrset_count; // an_numrrsets + ns_numrrsets + ar_numrrsets
|
||||
size_t rrset_count; /* an_numrrsets + ns_numrrsets + ar_numrrsets */
|
||||
|
||||
struct ub_packed_rrset_key** rrsets;
|
||||
struct rrset_ref ref[1]; //?
|
||||
struct rrset_ref ref[1]; /* ? */
|
||||
};
|
||||
|
||||
struct rrset_ref {
|
||||
@ -396,11 +405,11 @@ struct dns_msg {
|
||||
|
||||
struct rrset_ref* _rrset_ref_get(struct reply_info* r, int idx) {
|
||||
if ((r != NULL) && (idx >= 0) && ((size_t)idx < r->rrset_count)) {
|
||||
//printf("_rrset_ref_get: %lX key:%lX\n", r->ref + idx, r->ref[idx].key);
|
||||
/* printf("_rrset_ref_get: %lX key:%lX\n", r->ref + idx, r->ref[idx].key); */
|
||||
return &(r->ref[idx]);
|
||||
// return &(r->ref[idx]);
|
||||
/* return &(r->ref[idx]); */
|
||||
}
|
||||
//printf("_rrset_ref_get: NULL\n");
|
||||
/* printf("_rrset_ref_get: NULL\n"); */
|
||||
return NULL;
|
||||
}
|
||||
%}
|
||||
@ -479,30 +488,166 @@ struct comm_reply {
|
||||
if _newclass:family = _swig_property(_family_get)
|
||||
%}
|
||||
}
|
||||
|
||||
/* ************************************************************************************ *
|
||||
Structure edns_option
|
||||
* ************************************************************************************ */
|
||||
/* Rename the members to follow the python convention of marking them as
|
||||
* private. Access to the opt_code and opt_data members is given by the later
|
||||
* python defined code and data members respectively. */
|
||||
%rename(_next) edns_option::next;
|
||||
%rename(_opt_code) edns_option::opt_code;
|
||||
%rename(_opt_len) edns_option::opt_len;
|
||||
%rename(_opt_data) edns_option::opt_data;
|
||||
struct edns_option {
|
||||
struct edns_option* next;
|
||||
uint16_t opt_code;
|
||||
size_t opt_len;
|
||||
uint8_t* opt_data;
|
||||
};
|
||||
|
||||
%inline %{
|
||||
PyObject* _edns_option_opt_code_get(struct edns_option* option) {
|
||||
uint16_t opt_code = option->opt_code;
|
||||
return PyInt_FromLong(opt_code);
|
||||
}
|
||||
|
||||
PyObject* _edns_option_opt_data_get(struct edns_option* option) {
|
||||
return PyByteArray_FromStringAndSize((uint8_t*)option->opt_data,
|
||||
option->opt_len);
|
||||
}
|
||||
%}
|
||||
%extend edns_option {
|
||||
%pythoncode %{
|
||||
def _opt_code_get(self): return _edns_option_opt_code_get(self)
|
||||
__swig_getmethods__["code"] = _opt_code_get
|
||||
if _newclass: opt_code = _swig_property(_opt_code_get)
|
||||
|
||||
def _opt_data_get(self): return _edns_option_opt_data_get(self)
|
||||
__swig_getmethods__["data"] = _opt_data_get
|
||||
if _newclass: opt_data = _swig_property(_opt_data_get)
|
||||
%}
|
||||
}
|
||||
|
||||
/* ************************************************************************************ *
|
||||
Structure edns_data
|
||||
* ************************************************************************************ */
|
||||
/* This is ignored because we will pass a double pointer of this to Python
|
||||
* with custom getmethods. This is done to bypass Swig's behavior to pass NULL
|
||||
* pointers as None. */
|
||||
%ignore edns_data::opt_list;
|
||||
struct edns_data {
|
||||
int edns_present;
|
||||
uint8_t ext_rcode;
|
||||
uint8_t edns_version;
|
||||
uint16_t bits;
|
||||
uint16_t udp_size;
|
||||
struct edns_option* opt_list;
|
||||
};
|
||||
%inline %{
|
||||
struct edns_option** _edns_data_opt_list_get(struct edns_data* edns) {
|
||||
return &edns->opt_list;
|
||||
}
|
||||
%}
|
||||
%extend edns_data {
|
||||
%pythoncode %{
|
||||
def _opt_list_iter(self): return EdnsOptsListIter(self.opt_list)
|
||||
__swig_getmethods__["opt_list_iter"] = _opt_list_iter
|
||||
if _newclass:opt_list_iter = _swig_property(_opt_list_iter)
|
||||
def _opt_list(self): return _edns_data_opt_list_get(self)
|
||||
__swig_getmethods__["opt_list"] = _opt_list
|
||||
if _newclass:opt_list = _swig_property(_opt_list)
|
||||
%}
|
||||
}
|
||||
|
||||
/* ************************************************************************************ *
|
||||
Structure module_env
|
||||
* ************************************************************************************ */
|
||||
struct module_env {
|
||||
struct config_file* cfg;
|
||||
struct slabhash* msg_cache;
|
||||
struct rrset_cache* rrset_cache;
|
||||
struct infra_cache* infra_cache;
|
||||
struct key_cache* key_cache;
|
||||
|
||||
/* --- services --- */
|
||||
struct outbound_entry* (*send_query)(struct query_info* qinfo,
|
||||
uint16_t flags, int dnssec, int want_dnssec, int nocaps,
|
||||
struct sockaddr_storage* addr, socklen_t addrlen,
|
||||
uint8_t* zone, size_t zonelen, int ssl_upstream,
|
||||
struct module_qstate* q);
|
||||
void (*detach_subs)(struct module_qstate* qstate);
|
||||
int (*attach_sub)(struct module_qstate* qstate,
|
||||
struct query_info* qinfo, uint16_t qflags, int prime,
|
||||
int valrec, struct module_qstate** newq);
|
||||
void (*kill_sub)(struct module_qstate* newq);
|
||||
int (*detect_cycle)(struct module_qstate* qstate,
|
||||
struct query_info* qinfo, uint16_t flags, int prime,
|
||||
int valrec);
|
||||
|
||||
struct regional* scratch;
|
||||
struct sldns_buffer* scratch_buffer;
|
||||
struct worker* worker;
|
||||
struct mesh_area* mesh;
|
||||
struct alloc_cache* alloc;
|
||||
struct ub_randstate* rnd;
|
||||
time_t* now;
|
||||
struct timeval* now_tv;
|
||||
int need_to_validate;
|
||||
struct val_anchors* anchors;
|
||||
struct val_neg_cache* neg_cache;
|
||||
struct comm_timer* probe_timer;
|
||||
struct iter_forwards* fwds;
|
||||
struct iter_hints* hints;
|
||||
void* modinfo[MAX_MODULE];
|
||||
|
||||
void* inplace_cb_lists[inplace_cb_types_total];
|
||||
struct edns_known_option* edns_known_options;
|
||||
size_t edns_known_options_num;
|
||||
};
|
||||
|
||||
|
||||
/* ************************************************************************************ *
|
||||
Structure module_qstate
|
||||
* ************************************************************************************ */
|
||||
%ignore module_qstate::ext_state;
|
||||
%ignore module_qstate::minfo;
|
||||
|
||||
/* These are ignored because we will pass a double pointer of them to Python
|
||||
* with custom getmethods. This is done to bypass Swig's behavior to pass NULL
|
||||
* pointers as None. */
|
||||
%ignore module_qstate::edns_opts_front_in;
|
||||
%ignore module_qstate::edns_opts_back_out;
|
||||
%ignore module_qstate::edns_opts_back_in;
|
||||
%ignore module_qstate::edns_opts_front_out;
|
||||
|
||||
/* Query state */
|
||||
struct module_qstate {
|
||||
struct query_info qinfo;
|
||||
uint16_t query_flags; //See QF_BIT_xx constants
|
||||
int is_priming;
|
||||
uint16_t query_flags; /* See QF_BIT_xx constants */
|
||||
int is_priming;
|
||||
int is_valrec;
|
||||
|
||||
struct comm_reply* reply;
|
||||
struct dns_msg* return_msg;
|
||||
int return_rcode;
|
||||
int return_rcode;
|
||||
struct regional* region; /* unwrapped */
|
||||
|
||||
int curmod;
|
||||
int curmod;
|
||||
|
||||
enum module_ext_state ext_state[MAX_MODULE];
|
||||
void* minfo[MAX_MODULE];
|
||||
enum module_ext_state ext_state[MAX_MODULE];
|
||||
void* minfo[MAX_MODULE];
|
||||
time_t prefetch_leeway;
|
||||
|
||||
struct module_env* env; /* unwrapped */
|
||||
struct mesh_state* mesh_info;
|
||||
|
||||
struct edns_option* edns_opts_front_in;
|
||||
struct edns_option* edns_opts_back_out;
|
||||
struct edns_option* edns_opts_back_in;
|
||||
struct edns_option* edns_opts_front_out;
|
||||
int no_cache_lookup;
|
||||
int no_cache_store;
|
||||
};
|
||||
|
||||
%constant int MODULE_COUNT = MAX_MODULE;
|
||||
@ -540,6 +685,25 @@ struct module_qstate {
|
||||
def __getitem__(self, index): return _unboundmodule._ext_state_get(self.obj, index)
|
||||
def __setitem__(self, index, value): _unboundmodule._ext_state_set(self.obj, index, value)
|
||||
def __len__(self): return _unboundmodule.MODULE_COUNT
|
||||
|
||||
class EdnsOptsListIter:
|
||||
def __init__(self, obj):
|
||||
self._current = obj
|
||||
self._temp = None
|
||||
def __iter__(self): return self
|
||||
def __next__(self):
|
||||
"""Python 3 compatibility"""
|
||||
return self._get_next()
|
||||
def next(self):
|
||||
"""Python 2 compatibility"""
|
||||
return self._get_next()
|
||||
def _get_next(self):
|
||||
if not edns_opt_list_is_empty(self._current):
|
||||
self._temp = self._current
|
||||
self._current = _p_p_edns_option_get_next(self._current)
|
||||
return _dereference_edns_option(self._temp)
|
||||
else:
|
||||
raise StopIteration
|
||||
%}
|
||||
|
||||
%inline %{
|
||||
@ -549,12 +713,42 @@ struct module_qstate {
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
void _ext_state_set(struct module_qstate* q, int idx, enum module_ext_state state) {
|
||||
if ((q != NULL) && (idx >= 0) && (idx < MAX_MODULE)) {
|
||||
q->ext_state[idx] = state;
|
||||
}
|
||||
}
|
||||
|
||||
int edns_opt_list_is_empty(struct edns_option** opt) {
|
||||
if (!opt || !(*opt)) return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct edns_option* _dereference_edns_option(struct edns_option** opt) {
|
||||
if (!opt) return NULL;
|
||||
return *opt;
|
||||
}
|
||||
|
||||
struct edns_option** _p_p_edns_option_get_next(struct edns_option** opt) {
|
||||
return &(*opt)->next;
|
||||
}
|
||||
|
||||
struct edns_option** _edns_opts_front_in_get(struct module_qstate* q) {
|
||||
return &q->edns_opts_front_in;
|
||||
}
|
||||
|
||||
struct edns_option** _edns_opts_back_out_get(struct module_qstate* q) {
|
||||
return &q->edns_opts_back_out;
|
||||
}
|
||||
|
||||
struct edns_option** _edns_opts_back_in_get(struct module_qstate* q) {
|
||||
return &q->edns_opts_back_in;
|
||||
}
|
||||
|
||||
struct edns_option** _edns_opts_front_out_get(struct module_qstate* q) {
|
||||
return &q->edns_opts_front_out;
|
||||
}
|
||||
%}
|
||||
|
||||
%extend module_qstate {
|
||||
@ -566,6 +760,32 @@ struct module_qstate {
|
||||
def __ext_state_get(self): return ExtState(self)
|
||||
__swig_getmethods__["ext_state"] = __ext_state_get
|
||||
if _newclass:ext_state = _swig_property(__ext_state_get)#, __ext_state_set)
|
||||
|
||||
def _edns_opts_front_in_iter(self): return EdnsOptsListIter(self.edns_opts_front_in)
|
||||
__swig_getmethods__["edns_opts_front_in_iter"] = _edns_opts_front_in_iter
|
||||
if _newclass:edns_opts_front_in_iter = _swig_property(_edns_opts_front_in_iter)
|
||||
def _edns_opts_back_out_iter(self): return EdnsOptsListIter(self.edns_opts_back_out)
|
||||
__swig_getmethods__["edns_opts_back_out_iter"] = _edns_opts_back_out_iter
|
||||
if _newclass:edns_opts_back_out_iter = _swig_property(_edns_opts_back_out_iter)
|
||||
def _edns_opts_back_in_iter(self): return EdnsOptsListIter(self.edns_opts_back_in)
|
||||
__swig_getmethods__["edns_opts_back_in_iter"] = _edns_opts_back_in_iter
|
||||
if _newclass:edns_opts_back_in_iter = _swig_property(_edns_opts_back_in_iter)
|
||||
def _edns_opts_front_out_iter(self): return EdnsOptsListIter(self.edns_opts_front_out)
|
||||
__swig_getmethods__["edns_opts_front_out_iter"] = _edns_opts_front_out_iter
|
||||
if _newclass:edns_opts_front_out_iter = _swig_property(_edns_opts_front_out_iter)
|
||||
|
||||
def _edns_opts_front_in(self): return _edns_opts_front_in_get(self)
|
||||
__swig_getmethods__["edns_opts_front_in"] = _edns_opts_front_in
|
||||
if _newclass:edns_opts_front_in = _swig_property(_edns_opts_front_in)
|
||||
def _edns_opts_back_out(self): return _edns_opts_back_out_get(self)
|
||||
__swig_getmethods__["edns_opts_back_out"] = _edns_opts_back_out
|
||||
if _newclass:edns_opts_back_out = _swig_property(_edns_opts_back_out)
|
||||
def _edns_opts_back_in(self): return _edns_opts_back_in_get(self)
|
||||
__swig_getmethods__["edns_opts_back_in"] = _edns_opts_back_in
|
||||
if _newclass:edns_opts_back_in = _swig_property(_edns_opts_back_in)
|
||||
def _edns_opts_front_out(self): return _edns_opts_front_out_get(self)
|
||||
__swig_getmethods__["edns_opts_front_out"] = _edns_opts_front_out
|
||||
if _newclass:edns_opts_front_out = _swig_property(_edns_opts_front_out)
|
||||
%}
|
||||
}
|
||||
|
||||
@ -1037,8 +1257,9 @@ struct delegpt* find_delegation(struct module_qstate* qstate, char *nm, size_t n
|
||||
/* ************************************************************************************ *
|
||||
Functions
|
||||
* ************************************************************************************ */
|
||||
|
||||
// Various debuging functions
|
||||
/******************************
|
||||
* Various debuging functions *
|
||||
******************************/
|
||||
void verbose(enum verbosity_value level, const char* format, ...);
|
||||
void log_info(const char* format, ...);
|
||||
void log_err(const char* format, ...);
|
||||
@ -1048,24 +1269,166 @@ void log_dns_msg(const char* str, struct query_info* qinfo, struct reply_info* r
|
||||
void log_query_info(enum verbosity_value v, const char* str, struct query_info* qinf);
|
||||
void regional_log_stats(struct regional *r);
|
||||
|
||||
// Free allocated memory from marked sources returning corresponding types
|
||||
/***************************************************************************
|
||||
* Free allocated memory from marked sources returning corresponding types *
|
||||
***************************************************************************/
|
||||
%typemap(newfree, noblock = 1) char * {
|
||||
free($1);
|
||||
}
|
||||
|
||||
// Mark as source returning newly allocated memory
|
||||
/***************************************************
|
||||
* Mark as source returning newly allocated memory *
|
||||
***************************************************/
|
||||
%newobject sldns_wire2str_type;
|
||||
%newobject sldns_wire2str_class;
|
||||
|
||||
// LDNS functions
|
||||
/******************
|
||||
* LDNS functions *
|
||||
******************/
|
||||
char *sldns_wire2str_type(const uint16_t atype);
|
||||
char *sldns_wire2str_class(const uint16_t aclass);
|
||||
|
||||
// Functions from pythonmod_utils
|
||||
/**********************************
|
||||
* Functions from pythonmod_utils *
|
||||
**********************************/
|
||||
int storeQueryInCache(struct module_qstate* qstate, struct query_info* qinfo, struct reply_info* msgrep, int is_referral);
|
||||
void invalidateQueryInCache(struct module_qstate* qstate, struct query_info* qinfo);
|
||||
|
||||
// Module conversion functions
|
||||
/*******************************
|
||||
* Module conversion functions *
|
||||
*******************************/
|
||||
const char* strextstate(enum module_ext_state s);
|
||||
const char* strmodulevent(enum module_ev e);
|
||||
|
||||
/**************************
|
||||
* Edns related functions *
|
||||
**************************/
|
||||
struct edns_option* edns_opt_list_find(struct edns_option* list, uint16_t code);
|
||||
int edns_register_option(uint16_t opt_code, int bypass_cache_stage,
|
||||
int no_aggregation, struct module_env* env);
|
||||
|
||||
%pythoncode %{
|
||||
def register_edns_option(env, code, bypass_cache_stage=False,
|
||||
no_aggregation=False):
|
||||
"""Wrapper function to provide keyword attributes."""
|
||||
return edns_register_option(code, bypass_cache_stage,
|
||||
no_aggregation, env)
|
||||
%}
|
||||
|
||||
/******************************
|
||||
* Callback related functions *
|
||||
******************************/
|
||||
/* typemap to check if argument is callable */
|
||||
%typemap(in) PyObject *py_cb {
|
||||
if (!PyCallable_Check($input)) {
|
||||
SWIG_exception_fail(SWIG_TypeError, "Need a callable object!");
|
||||
return NULL;
|
||||
}
|
||||
$1 = $input;
|
||||
}
|
||||
/* typemap to get content/size from a bytearray */
|
||||
%typemap(in) (size_t len, uint8_t* py_bytearray_data) {
|
||||
if (!PyByteArray_CheckExact($input)) {
|
||||
SWIG_exception_fail(SWIG_TypeError, "Expected bytearray!");
|
||||
return NULL;
|
||||
}
|
||||
$2 = PyByteArray_AsString($input);
|
||||
$1 = PyByteArray_Size($input);
|
||||
}
|
||||
|
||||
int edns_opt_list_remove(struct edns_option** list, uint16_t code);
|
||||
int edns_opt_list_append(struct edns_option** list, uint16_t code, size_t len,
|
||||
uint8_t* py_bytearray_data, struct regional* region);
|
||||
|
||||
%{
|
||||
/* This function is called by unbound in order to call the python
|
||||
* callback function. */
|
||||
int python_inplace_cb_reply_generic(struct query_info* qinfo,
|
||||
struct module_qstate* qstate, struct reply_info* rep, int rcode,
|
||||
struct edns_data* edns, struct edns_option** opt_list_out,
|
||||
struct regional* region, void* python_callback)
|
||||
{
|
||||
PyObject *func, *py_edns, *py_qstate, *py_opt_list_out, *py_qinfo;
|
||||
PyObject *py_rep, *py_region;
|
||||
PyObject *result;
|
||||
int res = 0;
|
||||
|
||||
func = (PyObject *) python_callback;
|
||||
PyGILState_STATE gstate = PyGILState_Ensure();
|
||||
py_edns = SWIG_NewPointerObj((void*) edns, SWIGTYPE_p_edns_data, 0);
|
||||
py_qstate = SWIG_NewPointerObj((void*) qstate,
|
||||
SWIGTYPE_p_module_qstate, 0);
|
||||
py_opt_list_out = SWIG_NewPointerObj((void*) opt_list_out,
|
||||
SWIGTYPE_p_p_edns_option, 0);
|
||||
py_qinfo = SWIG_NewPointerObj((void*) qinfo, SWIGTYPE_p_query_info, 0);
|
||||
py_rep = SWIG_NewPointerObj((void*) rep, SWIGTYPE_p_reply_info, 0);
|
||||
py_region = SWIG_NewPointerObj((void*) region, SWIGTYPE_p_regional, 0);
|
||||
result = PyObject_CallFunction(func, "OOOiOOO", py_qinfo, py_qstate,
|
||||
py_rep, rcode, py_edns, py_opt_list_out, py_region);
|
||||
Py_XDECREF(py_edns);
|
||||
Py_XDECREF(py_qstate);
|
||||
Py_XDECREF(py_opt_list_out);
|
||||
Py_XDECREF(py_qinfo);
|
||||
Py_XDECREF(py_rep);
|
||||
Py_XDECREF(py_region);
|
||||
if (result) {
|
||||
res = PyInt_AsLong(result);
|
||||
}
|
||||
Py_XDECREF(result);
|
||||
PyGILState_Release(gstate);
|
||||
return res;
|
||||
}
|
||||
|
||||
/* Swig implementations for Python */
|
||||
static int register_inplace_cb_reply(PyObject* py_cb,
|
||||
struct module_env* env)
|
||||
{
|
||||
int ret = inplace_cb_reply_register(
|
||||
python_inplace_cb_reply_generic, (void*) py_cb, env);
|
||||
if (ret) Py_INCREF(py_cb);
|
||||
return ret;
|
||||
}
|
||||
static int register_inplace_cb_reply_cache(PyObject* py_cb,
|
||||
struct module_env* env)
|
||||
{
|
||||
int ret = inplace_cb_reply_cache_register(
|
||||
python_inplace_cb_reply_generic, (void*) py_cb, env);
|
||||
if (ret) Py_INCREF(py_cb);
|
||||
return ret;
|
||||
}
|
||||
static int register_inplace_cb_reply_local(PyObject* py_cb,
|
||||
struct module_env* env)
|
||||
{
|
||||
int ret = inplace_cb_reply_local_register(
|
||||
python_inplace_cb_reply_generic, (void*) py_cb, env);
|
||||
if (ret) Py_INCREF(py_cb);
|
||||
return ret;
|
||||
}
|
||||
static int register_inplace_cb_reply_servfail(PyObject* py_cb,
|
||||
struct module_env* env)
|
||||
{
|
||||
int ret = inplace_cb_reply_servfail_register(
|
||||
python_inplace_cb_reply_generic, (void*) py_cb, env);
|
||||
if (ret) Py_INCREF(py_cb);
|
||||
return ret;
|
||||
}
|
||||
%}
|
||||
/* C declarations */
|
||||
int inplace_cb_reply_register(
|
||||
inplace_cb_reply_func_t* cb, void* cb_arg, struct module_env* env);
|
||||
int inplace_cb_reply_cache_register(
|
||||
inplace_cb_reply_func_t* cb, void* cb_arg, struct module_env* env);
|
||||
int inplace_cb_reply_local_register(
|
||||
inplace_cb_reply_func_t* cb, void* cb_arg, struct module_env* env);
|
||||
int inplace_cb_reply_servfail_register(
|
||||
inplace_cb_reply_func_t* cb, void* cb_arg, struct module_env* env);
|
||||
|
||||
/* Swig declarations */
|
||||
static int register_inplace_cb_reply(PyObject* py_cb,
|
||||
struct module_env* env);
|
||||
static int register_inplace_cb_reply_cache(PyObject* py_cb,
|
||||
struct module_env* env);
|
||||
static int register_inplace_cb_reply_local(PyObject* py_cb,
|
||||
struct module_env* env);
|
||||
static int register_inplace_cb_reply_servfail(PyObject* py_cb,
|
||||
struct module_env* env);
|
||||
|
@ -112,8 +112,10 @@ int pythonmod_init(struct module_env* env, int id)
|
||||
{
|
||||
/* Initialize module */
|
||||
FILE* script_py = NULL;
|
||||
PyObject* py_cfg, *res;
|
||||
PyObject* py_init_arg, *res;
|
||||
PyGILState_STATE gil;
|
||||
int init_standard = 1;
|
||||
|
||||
struct pythonmod_env* pe = (struct pythonmod_env*)calloc(1, sizeof(struct pythonmod_env));
|
||||
if (!pe)
|
||||
{
|
||||
@ -156,10 +158,10 @@ int pythonmod_init(struct module_env* env, int id)
|
||||
PyRun_SimpleString("import sys \n");
|
||||
PyRun_SimpleString("sys.path.append('.') \n");
|
||||
if(env->cfg->directory && env->cfg->directory[0]) {
|
||||
char wdir[1524];
|
||||
snprintf(wdir, sizeof(wdir), "sys.path.append('%s') \n",
|
||||
env->cfg->directory);
|
||||
PyRun_SimpleString(wdir);
|
||||
char wdir[1524];
|
||||
snprintf(wdir, sizeof(wdir), "sys.path.append('%s') \n",
|
||||
env->cfg->directory);
|
||||
PyRun_SimpleString(wdir);
|
||||
}
|
||||
PyRun_SimpleString("sys.path.append('"RUN_DIR"') \n");
|
||||
PyRun_SimpleString("sys.path.append('"SHARE_DIR"') \n");
|
||||
@ -198,11 +200,15 @@ int pythonmod_init(struct module_env* env, int id)
|
||||
|
||||
fclose(script_py);
|
||||
|
||||
if ((pe->func_init = PyDict_GetItemString(pe->dict, "init")) == NULL)
|
||||
if ((pe->func_init = PyDict_GetItemString(pe->dict, "init_standard")) == NULL)
|
||||
{
|
||||
log_err("pythonmod: function init is missing in %s", pe->fname);
|
||||
PyGILState_Release(gil);
|
||||
return 0;
|
||||
init_standard = 0;
|
||||
if ((pe->func_init = PyDict_GetItemString(pe->dict, "init")) == NULL)
|
||||
{
|
||||
log_err("pythonmod: function init is missing in %s", pe->fname);
|
||||
PyGILState_Release(gil);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
if ((pe->func_deinit = PyDict_GetItemString(pe->dict, "deinit")) == NULL)
|
||||
{
|
||||
@ -223,16 +229,28 @@ int pythonmod_init(struct module_env* env, int id)
|
||||
return 0;
|
||||
}
|
||||
|
||||
py_cfg = SWIG_NewPointerObj((void*) env->cfg, SWIGTYPE_p_config_file, 0);
|
||||
res = PyObject_CallFunction(pe->func_init, "iO", id, py_cfg);
|
||||
if (init_standard)
|
||||
{
|
||||
py_init_arg = SWIG_NewPointerObj((void*) env, SWIGTYPE_p_module_env, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
py_init_arg = SWIG_NewPointerObj((void*) env->cfg,
|
||||
SWIGTYPE_p_config_file, 0);
|
||||
}
|
||||
res = PyObject_CallFunction(pe->func_init, "iO", id, py_init_arg);
|
||||
if (PyErr_Occurred())
|
||||
{
|
||||
log_err("pythonmod: Exception occurred in function init");
|
||||
PyErr_Print();
|
||||
Py_XDECREF(res);
|
||||
Py_XDECREF(py_init_arg);
|
||||
PyGILState_Release(gil);
|
||||
return 0;
|
||||
}
|
||||
|
||||
Py_XDECREF(res);
|
||||
Py_XDECREF(py_cfg);
|
||||
Py_XDECREF(py_init_arg);
|
||||
PyGILState_Release(gil);
|
||||
|
||||
return 1;
|
||||
|
@ -55,14 +55,22 @@ int pythonmod_init(struct module_env* env, int id);
|
||||
void pythonmod_deinit(struct module_env* env, int id);
|
||||
|
||||
/** python module operate on a query */
|
||||
void pythonmod_operate(struct module_qstate* qstate, enum module_ev event, int id, struct outbound_entry* outbound);
|
||||
void pythonmod_operate(struct module_qstate* qstate, enum module_ev event,
|
||||
int id, struct outbound_entry* outbound);
|
||||
|
||||
/** python module */
|
||||
void pythonmod_inform_super(struct module_qstate* qstate, int id, struct module_qstate* super);
|
||||
void pythonmod_inform_super(struct module_qstate* qstate, int id,
|
||||
struct module_qstate* super);
|
||||
|
||||
/** python module cleanup query state */
|
||||
void pythonmod_clear(struct module_qstate* qstate, int id);
|
||||
|
||||
/** python module alloc size routine */
|
||||
size_t pythonmod_get_mem(struct module_env* env, int id);
|
||||
|
||||
/** Declared here for fptr_wlist access. The definition is in interface.i. */
|
||||
int python_inplace_cb_reply_generic(struct query_info* qinfo,
|
||||
struct module_qstate* qstate, struct reply_info* rep, int rcode,
|
||||
struct edns_data* edns, struct edns_option** opt_list_out,
|
||||
struct regional* region, void* python_callback);
|
||||
#endif /* PYTHONMOD_H */
|
||||
|
17
pythonmod/test-edns.conf
Normal file
17
pythonmod/test-edns.conf
Normal file
@ -0,0 +1,17 @@
|
||||
# Example configuration file for edns.py
|
||||
server:
|
||||
verbosity: 1
|
||||
interface: 0.0.0.0
|
||||
do-daemonize: no
|
||||
access-control: 0.0.0.0/0 allow
|
||||
chroot: ""
|
||||
username: ""
|
||||
directory: ""
|
||||
logfile: ""
|
||||
pidfile: "unbound.pid"
|
||||
module-config: "validator python iterator"
|
||||
|
||||
# Python config section
|
||||
python:
|
||||
# Script file to load
|
||||
python-script: "./examples/edns.py"
|
17
pythonmod/test-inplace_callbacks.py
Normal file
17
pythonmod/test-inplace_callbacks.py
Normal file
@ -0,0 +1,17 @@
|
||||
# Example configuration file for edns.py
|
||||
server:
|
||||
verbosity: 1
|
||||
interface: 0.0.0.0
|
||||
do-daemonize: no
|
||||
access-control: 0.0.0.0/0 allow
|
||||
chroot: ""
|
||||
username: ""
|
||||
directory: ""
|
||||
logfile: ""
|
||||
pidfile: "unbound.pid"
|
||||
module-config: "validator python iterator"
|
||||
|
||||
# Python config section
|
||||
python:
|
||||
# Script file to load
|
||||
python-script: "./examples/inplace_callbacks.py"
|
@ -1182,8 +1182,8 @@ void local_zones_print(struct local_zones* zones)
|
||||
|
||||
/** encode answer consisting of 1 rrset */
|
||||
static int
|
||||
local_encode(struct query_info* qinfo, struct edns_data* edns,
|
||||
sldns_buffer* buf, struct regional* temp,
|
||||
local_encode(struct query_info* qinfo, struct module_env* env,
|
||||
struct edns_data* edns, sldns_buffer* buf, struct regional* temp,
|
||||
struct ub_packed_rrset_key* rrset, int ansec, int rcode)
|
||||
{
|
||||
struct reply_info rep;
|
||||
@ -1202,15 +1202,15 @@ local_encode(struct query_info* qinfo, struct edns_data* edns,
|
||||
edns->udp_size = EDNS_ADVERTISED_SIZE;
|
||||
edns->ext_rcode = 0;
|
||||
edns->bits &= EDNS_DO;
|
||||
if(!edns_opt_inplace_reply(edns, temp) ||
|
||||
!reply_info_answer_encode(qinfo, &rep,
|
||||
if(!inplace_cb_reply_local_call(env, qinfo, NULL, &rep, rcode, edns, temp)
|
||||
|| !reply_info_answer_encode(qinfo, &rep,
|
||||
*(uint16_t*)sldns_buffer_begin(buf),
|
||||
sldns_buffer_read_u16_at(buf, 2),
|
||||
buf, 0, 0, temp, udpsize, edns,
|
||||
buf, 0, 0, temp, udpsize, edns,
|
||||
(int)(edns->bits&EDNS_DO), 0))
|
||||
error_encode(buf, (LDNS_RCODE_SERVFAIL|BIT_AA), qinfo,
|
||||
*(uint16_t*)sldns_buffer_begin(buf),
|
||||
sldns_buffer_read_u16_at(buf, 2), edns);
|
||||
sldns_buffer_read_u16_at(buf, 2), edns);
|
||||
return 1;
|
||||
}
|
||||
|
||||
@ -1318,11 +1318,11 @@ find_tag_datas(struct query_info* qinfo, struct config_strlist* list,
|
||||
|
||||
/** answer local data match */
|
||||
static int
|
||||
local_data_answer(struct local_zone* z, struct query_info* qinfo,
|
||||
struct edns_data* edns, sldns_buffer* buf, struct regional* temp,
|
||||
int labs, struct local_data** ldp, enum localzone_type lz_type,
|
||||
int tag, struct config_strlist** tag_datas, size_t tag_datas_size,
|
||||
char** tagname, int num_tags)
|
||||
local_data_answer(struct local_zone* z, struct module_env* env,
|
||||
struct query_info* qinfo, struct edns_data* edns, sldns_buffer* buf,
|
||||
struct regional* temp, int labs, struct local_data** ldp,
|
||||
enum localzone_type lz_type, int tag, struct config_strlist** tag_datas,
|
||||
size_t tag_datas_size, char** tagname, int num_tags)
|
||||
{
|
||||
struct local_data key;
|
||||
struct local_data* ld;
|
||||
@ -1348,7 +1348,7 @@ local_data_answer(struct local_zone* z, struct query_info* qinfo,
|
||||
* chain. */
|
||||
if(qinfo->local_alias)
|
||||
return 1;
|
||||
return local_encode(qinfo, edns, buf, temp,
|
||||
return local_encode(qinfo, env, edns, buf, temp,
|
||||
&r, 1, LDNS_RCODE_NOERROR);
|
||||
}
|
||||
}
|
||||
@ -1383,16 +1383,17 @@ local_data_answer(struct local_zone* z, struct query_info* qinfo,
|
||||
struct ub_packed_rrset_key r = *lr->rrset;
|
||||
r.rk.dname = qinfo->qname;
|
||||
r.rk.dname_len = qinfo->qname_len;
|
||||
return local_encode(qinfo, edns, buf, temp, &r, 1,
|
||||
return local_encode(qinfo, env, edns, buf, temp, &r, 1,
|
||||
LDNS_RCODE_NOERROR);
|
||||
}
|
||||
return local_encode(qinfo, edns, buf, temp, lr->rrset, 1,
|
||||
return local_encode(qinfo, env, edns, buf, temp, lr->rrset, 1,
|
||||
LDNS_RCODE_NOERROR);
|
||||
}
|
||||
|
||||
/**
|
||||
* answer in case where no exact match is found
|
||||
* @param z: zone for query
|
||||
* @param env: module environment
|
||||
* @param qinfo: query
|
||||
* @param edns: edns from query
|
||||
* @param buf: buffer for answer.
|
||||
@ -1402,9 +1403,9 @@ local_data_answer(struct local_zone* z, struct query_info* qinfo,
|
||||
* @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, sldns_buffer* buf, struct regional* temp,
|
||||
struct local_data* ld, enum localzone_type lz_type)
|
||||
lz_zone_answer(struct local_zone* z, struct module_env* env,
|
||||
struct query_info* qinfo, struct edns_data* edns, sldns_buffer* buf,
|
||||
struct regional* temp, struct local_data* ld, enum localzone_type lz_type)
|
||||
{
|
||||
if(lz_type == local_zone_deny || lz_type == local_zone_inform_deny) {
|
||||
/** no reply at all, signal caller by clearing buffer. */
|
||||
@ -1430,7 +1431,7 @@ lz_zone_answer(struct local_zone* z, struct query_info* qinfo,
|
||||
int rcode = (ld || lz_type == local_zone_redirect)?
|
||||
LDNS_RCODE_NOERROR:LDNS_RCODE_NXDOMAIN;
|
||||
if(z->soa)
|
||||
return local_encode(qinfo, edns, buf, temp,
|
||||
return local_encode(qinfo, env, edns, buf, temp,
|
||||
z->soa, 0, rcode);
|
||||
error_encode(buf, (rcode|BIT_AA), qinfo,
|
||||
*(uint16_t*)sldns_buffer_begin(buf),
|
||||
@ -1448,7 +1449,7 @@ lz_zone_answer(struct local_zone* z, struct query_info* qinfo,
|
||||
if(ld && ld->rrsets) {
|
||||
int rcode = LDNS_RCODE_NOERROR;
|
||||
if(z->soa)
|
||||
return local_encode(qinfo, edns, buf, temp,
|
||||
return local_encode(qinfo, env, edns, buf, temp,
|
||||
z->soa, 0, rcode);
|
||||
error_encode(buf, (rcode|BIT_AA), qinfo,
|
||||
*(uint16_t*)sldns_buffer_begin(buf),
|
||||
@ -1521,10 +1522,10 @@ lz_type(uint8_t *taglist, size_t taglen, uint8_t *taglist2, size_t taglen2,
|
||||
}
|
||||
|
||||
int
|
||||
local_zones_answer(struct local_zones* zones, struct query_info* qinfo,
|
||||
struct edns_data* edns, sldns_buffer* buf, struct regional* temp,
|
||||
struct comm_reply* repinfo, uint8_t* taglist, size_t taglen,
|
||||
uint8_t* tagactions, size_t tagactionssize,
|
||||
local_zones_answer(struct local_zones* zones, struct module_env* env,
|
||||
struct query_info* qinfo, struct edns_data* edns, sldns_buffer* buf,
|
||||
struct regional* temp, struct comm_reply* repinfo, uint8_t* taglist,
|
||||
size_t taglen, uint8_t* tagactions, size_t tagactionssize,
|
||||
struct config_strlist** tag_datas, size_t tag_datas_size,
|
||||
char** tagname, int num_tags, struct view* view)
|
||||
{
|
||||
@ -1578,14 +1579,14 @@ local_zones_answer(struct local_zones* zones, struct query_info* qinfo,
|
||||
if(lzt != local_zone_always_refuse
|
||||
&& lzt != local_zone_always_transparent
|
||||
&& lzt != local_zone_always_nxdomain
|
||||
&& local_data_answer(z, qinfo, edns, buf, temp, labs, &ld, lzt,
|
||||
&& local_data_answer(z, env, qinfo, edns, buf, temp, labs, &ld, lzt,
|
||||
tag, tag_datas, tag_datas_size, tagname, num_tags)) {
|
||||
lock_rw_unlock(&z->lock);
|
||||
/* 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);
|
||||
r = lz_zone_answer(z, env, qinfo, edns, buf, temp, ld, lzt);
|
||||
lock_rw_unlock(&z->lock);
|
||||
return r && !qinfo->local_alias; /* see above */
|
||||
}
|
||||
|
@ -44,6 +44,7 @@
|
||||
#include "util/rbtree.h"
|
||||
#include "util/locks.h"
|
||||
#include "util/storage/dnstree.h"
|
||||
#include "util/module.h"
|
||||
#include "services/view.h"
|
||||
struct ub_packed_rrset_key;
|
||||
struct regional;
|
||||
@ -267,6 +268,7 @@ void local_zones_print(struct local_zones* zones);
|
||||
* Answer authoritatively for local zones.
|
||||
* Takes care of locking.
|
||||
* @param zones: the stored zones (shared, read only).
|
||||
* @param env: the module environment.
|
||||
* @param qinfo: query info (parsed).
|
||||
* @param edns: edns info (parsed).
|
||||
* @param buf: buffer with query ID and flags, also for reply.
|
||||
@ -293,10 +295,10 @@ void local_zones_print(struct local_zones* zones);
|
||||
* 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,
|
||||
struct comm_reply* repinfo, uint8_t* taglist, size_t taglen,
|
||||
uint8_t* tagactions, size_t tagactionssize,
|
||||
int local_zones_answer(struct local_zones* zones, struct module_env* env,
|
||||
struct query_info* qinfo, struct edns_data* edns, struct sldns_buffer* buf,
|
||||
struct regional* temp, struct comm_reply* repinfo, uint8_t* taglist,
|
||||
size_t taglen, uint8_t* tagactions, size_t tagactionssize,
|
||||
struct config_strlist** tag_datas, size_t tag_datas_size,
|
||||
char** tagname, int num_tags, struct view* view);
|
||||
|
||||
|
127
services/mesh.c
127
services/mesh.c
@ -56,6 +56,7 @@
|
||||
#include "util/alloc.h"
|
||||
#include "util/config_file.h"
|
||||
#include "sldns/sbuffer.h"
|
||||
#include "sldns/wire2str.h"
|
||||
#include "services/localzone.h"
|
||||
#include "util/data/dname.h"
|
||||
|
||||
@ -129,6 +130,11 @@ mesh_state_compare(const void* ap, const void* bp)
|
||||
struct mesh_state* a = (struct mesh_state*)ap;
|
||||
struct mesh_state* b = (struct mesh_state*)bp;
|
||||
|
||||
if(a->unique < b->unique)
|
||||
return -1;
|
||||
if(a->unique > b->unique)
|
||||
return 1;
|
||||
|
||||
if(a->s.is_priming && !b->s.is_priming)
|
||||
return -1;
|
||||
if(!a->s.is_priming && b->s.is_priming)
|
||||
@ -284,10 +290,13 @@ void mesh_new_client(struct mesh_area* mesh, struct query_info* qinfo,
|
||||
uint16_t qflags, struct edns_data* edns, struct comm_reply* rep,
|
||||
uint16_t qid)
|
||||
{
|
||||
struct mesh_state* s = mesh_area_find(mesh, qinfo, qflags&(BIT_RD|BIT_CD), 0, 0);
|
||||
struct mesh_state* s = NULL;
|
||||
int unique = edns_unique_mesh_state(edns->opt_list, mesh->env);
|
||||
int was_detached = 0;
|
||||
int was_noreply = 0;
|
||||
int added = 0;
|
||||
if(!unique)
|
||||
s = mesh_area_find(mesh, qinfo, qflags&(BIT_RD|BIT_CD), 0, 0);
|
||||
/* does this create a new reply state? */
|
||||
if(!s || s->list_select == mesh_no_list) {
|
||||
if(!mesh_make_new_space(mesh, rep->c->buffer)) {
|
||||
@ -317,13 +326,32 @@ void mesh_new_client(struct mesh_area* mesh, struct query_info* qinfo,
|
||||
s = mesh_state_create(mesh->env, qinfo, qflags&(BIT_RD|BIT_CD), 0, 0);
|
||||
if(!s) {
|
||||
log_err("mesh_state_create: out of memory; SERVFAIL");
|
||||
if(!edns_opt_inplace_reply(edns, mesh->env->scratch))
|
||||
edns->opt_list = NULL;
|
||||
if(!inplace_cb_reply_servfail_call(mesh->env, qinfo, NULL, NULL,
|
||||
LDNS_RCODE_SERVFAIL, edns, mesh->env->scratch))
|
||||
edns->opt_list = NULL;
|
||||
error_encode(rep->c->buffer, LDNS_RCODE_SERVFAIL,
|
||||
qinfo, qid, qflags, edns);
|
||||
comm_point_send_reply(rep);
|
||||
return;
|
||||
}
|
||||
if(unique)
|
||||
mesh_state_make_unique(s);
|
||||
/* copy the edns options we got from the front */
|
||||
if(edns->opt_list) {
|
||||
s->s.edns_opts_front_in = edns_opt_copy_region(edns->opt_list,
|
||||
s->s.region);
|
||||
if(!s->s.edns_opts_front_in) {
|
||||
log_err("mesh_state_create: out of memory; SERVFAIL");
|
||||
if(!inplace_cb_reply_servfail_call(mesh->env, qinfo, NULL,
|
||||
NULL, LDNS_RCODE_SERVFAIL, edns, mesh->env->scratch))
|
||||
edns->opt_list = NULL;
|
||||
error_encode(rep->c->buffer, LDNS_RCODE_SERVFAIL,
|
||||
qinfo, qid, qflags, edns);
|
||||
comm_point_send_reply(rep);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef UNBOUND_DEBUG
|
||||
n =
|
||||
#else
|
||||
@ -342,8 +370,9 @@ void mesh_new_client(struct mesh_area* mesh, struct query_info* qinfo,
|
||||
/* add reply to s */
|
||||
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;
|
||||
if(!inplace_cb_reply_servfail_call(mesh->env, qinfo, &s->s,
|
||||
NULL, LDNS_RCODE_SERVFAIL, edns, mesh->env->scratch))
|
||||
edns->opt_list = NULL;
|
||||
error_encode(rep->c->buffer, LDNS_RCODE_SERVFAIL,
|
||||
qinfo, qid, qflags, edns);
|
||||
comm_point_send_reply(rep);
|
||||
@ -382,10 +411,13 @@ mesh_new_callback(struct mesh_area* mesh, struct query_info* qinfo,
|
||||
uint16_t qflags, struct edns_data* edns, sldns_buffer* buf,
|
||||
uint16_t qid, mesh_cb_func_t cb, void* cb_arg)
|
||||
{
|
||||
struct mesh_state* s = mesh_area_find(mesh, qinfo, qflags&(BIT_RD|BIT_CD), 0, 0);
|
||||
struct mesh_state* s = NULL;
|
||||
int unique = edns_unique_mesh_state(edns->opt_list, mesh->env);
|
||||
int was_detached = 0;
|
||||
int was_noreply = 0;
|
||||
int added = 0;
|
||||
if(!unique)
|
||||
s = mesh_area_find(mesh, qinfo, qflags&(BIT_RD|BIT_CD), 0, 0);
|
||||
/* there are no limits on the number of callbacks */
|
||||
|
||||
/* see if it already exists, if not, create one */
|
||||
@ -397,6 +429,15 @@ mesh_new_callback(struct mesh_area* mesh, struct query_info* qinfo,
|
||||
if(!s) {
|
||||
return 0;
|
||||
}
|
||||
if(unique)
|
||||
mesh_state_make_unique(s);
|
||||
if(edns->opt_list) {
|
||||
s->s.edns_opts_front_in = edns_opt_copy_region(edns->opt_list,
|
||||
s->s.region);
|
||||
if(!s->s.edns_opts_front_in) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
#ifdef UNBOUND_DEBUG
|
||||
n =
|
||||
#else
|
||||
@ -435,7 +476,8 @@ mesh_new_callback(struct mesh_area* mesh, struct query_info* qinfo,
|
||||
void mesh_new_prefetch(struct mesh_area* mesh, struct query_info* qinfo,
|
||||
uint16_t qflags, time_t leeway)
|
||||
{
|
||||
struct mesh_state* s = mesh_area_find(mesh, qinfo, qflags&(BIT_RD|BIT_CD), 0, 0);
|
||||
struct mesh_state* s = mesh_area_find(mesh, qinfo, qflags&(BIT_RD|BIT_CD),
|
||||
0, 0);
|
||||
#ifdef UNBOUND_DEBUG
|
||||
struct rbnode_t* n;
|
||||
#endif
|
||||
@ -454,6 +496,7 @@ void mesh_new_prefetch(struct mesh_area* mesh, struct query_info* qinfo,
|
||||
mesh->stats_dropped ++;
|
||||
return;
|
||||
}
|
||||
|
||||
s = mesh_state_create(mesh->env, qinfo, qflags&(BIT_RD|BIT_CD), 0, 0);
|
||||
if(!s) {
|
||||
log_err("prefetch mesh_state_create: out of memory");
|
||||
@ -527,6 +570,7 @@ mesh_state_create(struct module_env* env, struct query_info* qinfo,
|
||||
rbtree_init(&mstate->super_set, &mesh_state_ref_compare);
|
||||
rbtree_init(&mstate->sub_set, &mesh_state_ref_compare);
|
||||
mstate->num_activated = 0;
|
||||
mstate->unique = NULL;
|
||||
/* init module qstate */
|
||||
mstate->s.qinfo.qtype = qinfo->qtype;
|
||||
mstate->s.qinfo.qclass = qinfo->qclass;
|
||||
@ -550,14 +594,34 @@ mesh_state_create(struct module_env* env, struct query_info* qinfo,
|
||||
mstate->s.env = env;
|
||||
mstate->s.mesh_info = mstate;
|
||||
mstate->s.prefetch_leeway = 0;
|
||||
mstate->s.no_cache_lookup = 0;
|
||||
mstate->s.no_cache_store = 0;
|
||||
/* init modules */
|
||||
for(i=0; i<env->mesh->mods.num; i++) {
|
||||
mstate->s.minfo[i] = NULL;
|
||||
mstate->s.ext_state[i] = module_state_initial;
|
||||
}
|
||||
/* init edns option lists */
|
||||
mstate->s.edns_opts_front_in = NULL;
|
||||
mstate->s.edns_opts_back_out = NULL;
|
||||
mstate->s.edns_opts_back_in = NULL;
|
||||
mstate->s.edns_opts_front_out = NULL;
|
||||
|
||||
return mstate;
|
||||
}
|
||||
|
||||
int
|
||||
mesh_state_is_unique(struct mesh_state* mstate)
|
||||
{
|
||||
return mstate->unique != NULL;
|
||||
}
|
||||
|
||||
void
|
||||
mesh_state_make_unique(struct mesh_state* mstate)
|
||||
{
|
||||
mstate->unique = mstate;
|
||||
}
|
||||
|
||||
void
|
||||
mesh_state_cleanup(struct mesh_state* mstate)
|
||||
{
|
||||
@ -692,8 +756,7 @@ int mesh_attach_sub(struct module_qstate* qstate, struct query_info* qinfo,
|
||||
{
|
||||
/* find it, if not, create it */
|
||||
struct mesh_area* mesh = qstate->env->mesh;
|
||||
struct mesh_state* sub = mesh_area_find(mesh, qinfo, qflags, prime,
|
||||
valrec);
|
||||
struct mesh_state* sub = mesh_area_find(mesh, qinfo, qflags, prime, valrec);
|
||||
int was_detached;
|
||||
if(mesh_detect_cycle_found(qstate, sub)) {
|
||||
verbose(VERB_ALGO, "attach failed, cycle detected");
|
||||
@ -704,8 +767,7 @@ int mesh_attach_sub(struct module_qstate* qstate, struct query_info* qinfo,
|
||||
struct rbnode_t* n;
|
||||
#endif
|
||||
/* create a new one */
|
||||
sub = mesh_state_create(qstate->env, qinfo, qflags, prime,
|
||||
valrec);
|
||||
sub = mesh_state_create(qstate->env, qinfo, qflags, prime, valrec);
|
||||
if(!sub) {
|
||||
log_err("mesh_attach_sub: out of memory");
|
||||
return 0;
|
||||
@ -807,6 +869,15 @@ mesh_do_callback(struct mesh_state* m, int rcode, struct reply_info* rep,
|
||||
}
|
||||
/* send the reply */
|
||||
if(rcode) {
|
||||
if(rcode == LDNS_RCODE_SERVFAIL) {
|
||||
if(!inplace_cb_reply_servfail_call(m->s.env, &m->s.qinfo, &m->s,
|
||||
rep, rcode, &r->edns, m->s.region))
|
||||
r->edns.opt_list = NULL;
|
||||
} else {
|
||||
if(!inplace_cb_reply_call(m->s.env, &m->s.qinfo, &m->s, rep, rcode,
|
||||
&r->edns, m->s.region))
|
||||
r->edns.opt_list = NULL;
|
||||
}
|
||||
fptr_ok(fptr_whitelist_mesh_cb(r->cb));
|
||||
(*r->cb)(r->cb_arg, rcode, r->buf, sec_status_unchecked, NULL);
|
||||
} else {
|
||||
@ -816,8 +887,10 @@ mesh_do_callback(struct mesh_state* m, int rcode, struct reply_info* rep,
|
||||
r->edns.udp_size = EDNS_ADVERTISED_SIZE;
|
||||
r->edns.ext_rcode = 0;
|
||||
r->edns.bits &= EDNS_DO;
|
||||
if(!edns_opt_inplace_reply(&r->edns, m->s.region) ||
|
||||
!reply_info_answer_encode(&m->s.qinfo, rep, r->qid,
|
||||
|
||||
if(!inplace_cb_reply_call(m->s.env, &m->s.qinfo, &m->s, rep,
|
||||
LDNS_RCODE_NOERROR, &r->edns, m->s.region) ||
|
||||
!reply_info_answer_encode(&m->s.qinfo, rep, r->qid,
|
||||
r->qflags, r->buf, 0, 1,
|
||||
m->s.env->scratch, udp_size, &r->edns,
|
||||
(int)(r->edns.bits & EDNS_DO), secure))
|
||||
@ -890,9 +963,16 @@ mesh_send_reply(struct mesh_state* m, int rcode, struct reply_info* rep,
|
||||
comm_point_send_reply(&r->query_reply);
|
||||
} else if(rcode) {
|
||||
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;
|
||||
if(rcode == LDNS_RCODE_SERVFAIL) {
|
||||
if(!inplace_cb_reply_servfail_call(m->s.env, &m->s.qinfo, &m->s,
|
||||
rep, rcode, &r->edns, m->s.region))
|
||||
r->edns.opt_list = NULL;
|
||||
} else {
|
||||
if(!inplace_cb_reply_call(m->s.env, &m->s.qinfo, &m->s, rep, rcode,
|
||||
&r->edns, m->s.region))
|
||||
r->edns.opt_list = NULL;
|
||||
}
|
||||
error_encode(r->query_reply.c->buffer, rcode, &m->s.qinfo,
|
||||
r->qid, r->qflags, &r->edns);
|
||||
comm_point_send_reply(&r->query_reply);
|
||||
@ -904,12 +984,16 @@ mesh_send_reply(struct mesh_state* m, int rcode, struct reply_info* rep,
|
||||
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,
|
||||
if(!inplace_cb_reply_call(m->s.env, &m->s.qinfo, &m->s, rep,
|
||||
LDNS_RCODE_NOERROR, &r->edns, m->s.region) ||
|
||||
!reply_info_answer_encode(&m->s.qinfo, rep, r->qid,
|
||||
r->qflags, r->query_reply.c->buffer, 0, 1,
|
||||
m->s.env->scratch, udp_size, &r->edns,
|
||||
(int)(r->edns.bits & EDNS_DO), secure))
|
||||
{
|
||||
if(!inplace_cb_reply_servfail_call(m->s.env, &m->s.qinfo, &m->s,
|
||||
rep, LDNS_RCODE_SERVFAIL, &r->edns, m->s.region))
|
||||
r->edns.opt_list = NULL;
|
||||
error_encode(r->query_reply.c->buffer,
|
||||
LDNS_RCODE_SERVFAIL, &m->s.qinfo, r->qid,
|
||||
r->qflags, &r->edns);
|
||||
@ -980,6 +1064,10 @@ struct mesh_state* mesh_area_find(struct mesh_area* mesh,
|
||||
key.s.is_valrec = valrec;
|
||||
key.s.qinfo = *qinfo;
|
||||
key.s.query_flags = qflags;
|
||||
/* We are searching for a similar mesh state when we DO want to
|
||||
* aggregate the state. Thus unique is set to NULL. (default when we
|
||||
* desire aggregation).*/
|
||||
key.unique = NULL;
|
||||
|
||||
result = (struct mesh_state*)rbtree_search(&mesh->all, &key);
|
||||
return result;
|
||||
@ -1282,8 +1370,9 @@ mesh_detect_cycle(struct module_qstate* qstate, struct query_info* qinfo,
|
||||
uint16_t flags, int prime, int valrec)
|
||||
{
|
||||
struct mesh_area* mesh = qstate->env->mesh;
|
||||
struct mesh_state* dep_m = mesh_area_find(mesh, qinfo, flags, prime,
|
||||
valrec);
|
||||
struct mesh_state* dep_m = NULL;
|
||||
if(!mesh_state_is_unique(qstate->mesh_info))
|
||||
dep_m = mesh_area_find(mesh, qinfo, flags, prime, valrec);
|
||||
return mesh_detect_cycle_found(qstate, dep_m);
|
||||
}
|
||||
|
||||
|
@ -180,6 +180,8 @@ struct mesh_state {
|
||||
/** if this state is in the forever list, jostle list, or neither */
|
||||
enum mesh_list_select { mesh_no_list, mesh_forever_list,
|
||||
mesh_jostle_list } list_select;
|
||||
/** pointer to this state for uniqueness or NULL */
|
||||
struct mesh_state* unique;
|
||||
|
||||
/** true if replies have been sent out (at end for alignment) */
|
||||
uint8_t replies_sent;
|
||||
@ -416,6 +418,21 @@ void mesh_state_delete(struct module_qstate* qstate);
|
||||
struct mesh_state* mesh_state_create(struct module_env* env,
|
||||
struct query_info* qinfo, uint16_t qflags, int prime, int valrec);
|
||||
|
||||
/**
|
||||
* Check if the mesh state is unique.
|
||||
* A unique mesh state uses it's unique member to point to itself, else NULL.
|
||||
* @param mstate: mesh state to check.
|
||||
* @return true if the mesh state is unique, false otherwise.
|
||||
*/
|
||||
int mesh_state_is_unique(struct mesh_state* mstate);
|
||||
|
||||
/**
|
||||
* Make a mesh state unique.
|
||||
* A unique mesh state uses it's unique member to point to itself.
|
||||
* @param mstate: mesh state to check.
|
||||
*/
|
||||
void mesh_state_make_unique(struct mesh_state* mstate);
|
||||
|
||||
/**
|
||||
* Cleanup a mesh state and its query state. Does not do rbtree or
|
||||
* reference cleanup.
|
||||
|
@ -1986,17 +1986,22 @@ serviced_udp_callback(struct comm_point* c, void* arg, int error,
|
||||
|
||||
struct serviced_query*
|
||||
outnet_serviced_query(struct outside_network* outnet,
|
||||
uint8_t* qname, size_t qnamelen, uint16_t qtype, uint16_t qclass,
|
||||
uint16_t flags, int dnssec, int want_dnssec, int nocaps,
|
||||
int tcp_upstream, int ssl_upstream, struct edns_option* opt_list,
|
||||
struct query_info* qinfo, uint16_t flags, int dnssec, int want_dnssec,
|
||||
int nocaps, int tcp_upstream, int ssl_upstream,
|
||||
struct sockaddr_storage* addr, socklen_t addrlen, uint8_t* zone,
|
||||
size_t zonelen, comm_point_callback_t* callback, void* callback_arg,
|
||||
sldns_buffer* buff)
|
||||
size_t zonelen, struct module_qstate* qstate,
|
||||
comm_point_callback_t* callback, void* callback_arg, sldns_buffer* buff,
|
||||
struct module_env* env)
|
||||
{
|
||||
struct serviced_query* sq;
|
||||
struct service_callback* cb;
|
||||
serviced_gen_query(buff, qname, qnamelen, qtype, qclass, flags);
|
||||
sq = lookup_serviced(outnet, buff, dnssec, addr, addrlen, opt_list);
|
||||
if(!inplace_cb_query_call(env, qinfo, flags, addr, addrlen, zone, zonelen,
|
||||
qstate, qstate->region))
|
||||
return NULL;
|
||||
serviced_gen_query(buff, qinfo->qname, qinfo->qname_len, qinfo->qtype,
|
||||
qinfo->qclass, flags);
|
||||
sq = lookup_serviced(outnet, buff, dnssec, addr, addrlen,
|
||||
qstate->edns_opts_back_out);
|
||||
/* duplicate entries are included in the callback list, because
|
||||
* there is a counterpart registration by our caller that needs to
|
||||
* be doubly-removed (with callbacks perhaps). */
|
||||
@ -2006,7 +2011,7 @@ outnet_serviced_query(struct outside_network* outnet,
|
||||
/* make new serviced query entry */
|
||||
sq = serviced_create(outnet, buff, dnssec, want_dnssec, nocaps,
|
||||
tcp_upstream, ssl_upstream, addr, addrlen, zone,
|
||||
zonelen, (int)qtype, opt_list);
|
||||
zonelen, (int)qinfo->qtype, qstate->edns_opts_back_out);
|
||||
if(!sq) {
|
||||
free(cb);
|
||||
return NULL;
|
||||
|
@ -59,6 +59,9 @@ struct sldns_buffer;
|
||||
struct serviced_query;
|
||||
struct dt_env;
|
||||
struct edns_option;
|
||||
struct module_env;
|
||||
struct module_qstate;
|
||||
struct query_info;
|
||||
|
||||
/**
|
||||
* Send queries to outside servers and wait for answers from servers.
|
||||
@ -471,10 +474,7 @@ void pending_delete(struct outside_network* outnet, struct pending* p);
|
||||
* Perform a serviced query to the authoritative servers.
|
||||
* Duplicate efforts are detected, and EDNS, TCP and UDP retry is performed.
|
||||
* @param outnet: outside network, with rbtree of serviced queries.
|
||||
* @param qname: what qname to query.
|
||||
* @param qnamelen: length of qname in octets including 0 root label.
|
||||
* @param qtype: rrset type to query (host format)
|
||||
* @param qclass: query class. (host format)
|
||||
* @param qinfo: query info.
|
||||
* @param flags: flags u16 (host format), includes opcode, CD bit.
|
||||
* @param dnssec: if set, DO bit is set in EDNS queries.
|
||||
* If the value includes BIT_CD, CD bit is set when in EDNS queries.
|
||||
@ -484,27 +484,28 @@ void pending_delete(struct outside_network* outnet, struct pending* p);
|
||||
* @param nocaps: ignore use_caps_for_id and use unperturbed qname.
|
||||
* @param tcp_upstream: use TCP for upstream queries.
|
||||
* @param ssl_upstream: use SSL for upstream queries.
|
||||
* @param opt_list: pass edns option list (deep copied into serviced query)
|
||||
* these options are set on the outgoing packets.
|
||||
* @param callback: callback function.
|
||||
* @param callback_arg: user argument to callback function.
|
||||
* @param addr: to which server to send the query.
|
||||
* @param addrlen: length of addr.
|
||||
* @param zone: name of the zone of the delegation point. wireformat dname.
|
||||
This is the delegation point name for which the server is deemed
|
||||
authoritative.
|
||||
* @param zonelen: length of zone.
|
||||
* @param qstate: module qstate. Mainly for inspecting the available
|
||||
* edns_opts_lists.
|
||||
* @param callback: callback function.
|
||||
* @param callback_arg: user argument to callback function.
|
||||
* @param buff: scratch buffer to create query contents in. Empty on exit.
|
||||
* @param env: the module environment.
|
||||
* @return 0 on error, or pointer to serviced query that is used to answer
|
||||
* this serviced query may be shared with other callbacks as well.
|
||||
*/
|
||||
struct serviced_query* outnet_serviced_query(struct outside_network* outnet,
|
||||
uint8_t* qname, size_t qnamelen, uint16_t qtype, uint16_t qclass,
|
||||
uint16_t flags, int dnssec, int want_dnssec, int nocaps,
|
||||
int tcp_upstream, int ssl_upstream, struct edns_option* opt_list,
|
||||
struct query_info* qinfo, uint16_t flags, int dnssec, int want_dnssec,
|
||||
int nocaps, int tcp_upstream, int ssl_upstream,
|
||||
struct sockaddr_storage* addr, socklen_t addrlen, uint8_t* zone,
|
||||
size_t zonelen, comm_point_callback_t* callback, void* callback_arg,
|
||||
struct sldns_buffer* buff);
|
||||
size_t zonelen, struct module_qstate* qstate,
|
||||
comm_point_callback_t* callback, void* callback_arg,
|
||||
struct sldns_buffer* buff, struct module_env* env);
|
||||
|
||||
/**
|
||||
* Remove service query callback.
|
||||
|
@ -118,12 +118,15 @@ check_mod(struct config_file* cfg, struct module_func_block* fb)
|
||||
env.scratch_buffer = sldns_buffer_new(BUFSIZ);
|
||||
if(!env.scratch || !env.scratch_buffer)
|
||||
fatal_exit("out of memory");
|
||||
if(!edns_known_options_init(&env))
|
||||
fatal_exit("out of memory");
|
||||
if(!(*fb->init)(&env, 0)) {
|
||||
fatal_exit("bad config for %s module", fb->name);
|
||||
}
|
||||
(*fb->deinit)(&env, 0);
|
||||
sldns_buffer_free(env.scratch_buffer);
|
||||
regional_destroy(env.scratch);
|
||||
edns_known_options_delete(&env);
|
||||
}
|
||||
|
||||
/** check localzones */
|
||||
|
@ -99,12 +99,10 @@ void worker_sighandler(int ATTR_UNUSED(sig), void* ATTR_UNUSED(arg))
|
||||
log_assert(0);
|
||||
}
|
||||
|
||||
struct outbound_entry* worker_send_query(uint8_t* ATTR_UNUSED(qname),
|
||||
size_t ATTR_UNUSED(qnamelen), uint16_t ATTR_UNUSED(qtype),
|
||||
uint16_t ATTR_UNUSED(qclass), uint16_t ATTR_UNUSED(flags),
|
||||
struct outbound_entry* worker_send_query(
|
||||
struct query_info* ATTR_UNUSED(qinfo), uint16_t ATTR_UNUSED(flags),
|
||||
int ATTR_UNUSED(dnssec), int ATTR_UNUSED(want_dnssec),
|
||||
int ATTR_UNUSED(nocaps), struct edns_option* ATTR_UNUSED(opt_list),
|
||||
struct sockaddr_storage* ATTR_UNUSED(addr),
|
||||
int ATTR_UNUSED(nocaps), struct sockaddr_storage* ATTR_UNUSED(addr),
|
||||
socklen_t ATTR_UNUSED(addrlen), uint8_t* ATTR_UNUSED(zone),
|
||||
size_t ATTR_UNUSED(zonelen), int ATTR_UNUSED(ssl_upstream),
|
||||
struct module_qstate* ATTR_UNUSED(q))
|
||||
@ -133,12 +131,10 @@ worker_alloc_cleanup(void* ATTR_UNUSED(arg))
|
||||
log_assert(0);
|
||||
}
|
||||
|
||||
struct outbound_entry* libworker_send_query(uint8_t* ATTR_UNUSED(qname),
|
||||
size_t ATTR_UNUSED(qnamelen), uint16_t ATTR_UNUSED(qtype),
|
||||
uint16_t ATTR_UNUSED(qclass), uint16_t ATTR_UNUSED(flags),
|
||||
struct outbound_entry* libworker_send_query(
|
||||
struct query_info* ATTR_UNUSED(qinfo), uint16_t ATTR_UNUSED(flags),
|
||||
int ATTR_UNUSED(dnssec), int ATTR_UNUSED(want_dnssec),
|
||||
int ATTR_UNUSED(nocaps), struct edns_option* ATTR_UNUSED(opt_list),
|
||||
struct sockaddr_storage* ATTR_UNUSED(addr),
|
||||
int ATTR_UNUSED(nocaps), struct sockaddr_storage* ATTR_UNUSED(addr),
|
||||
socklen_t ATTR_UNUSED(addrlen), uint8_t* ATTR_UNUSED(zone),
|
||||
size_t ATTR_UNUSED(zonelen), int ATTR_UNUSED(ssl_upstream),
|
||||
struct module_qstate* ATTR_UNUSED(q))
|
||||
|
@ -1036,13 +1036,13 @@ pending_tcp_query(struct serviced_query* sq, sldns_buffer* packet,
|
||||
}
|
||||
|
||||
struct serviced_query* outnet_serviced_query(struct outside_network* outnet,
|
||||
uint8_t* qname, size_t qnamelen, uint16_t qtype, uint16_t qclass,
|
||||
uint16_t flags, int dnssec, int ATTR_UNUSED(want_dnssec),
|
||||
int ATTR_UNUSED(nocaps), int ATTR_UNUSED(tcp_upstream),
|
||||
int ATTR_UNUSED(ssl_upstream), struct edns_option* opt_list,
|
||||
struct query_info* qinfo, uint16_t flags, int dnssec,
|
||||
int ATTR_UNUSED(want_dnssec), int ATTR_UNUSED(nocaps),
|
||||
int ATTR_UNUSED(tcp_upstream), int ATTR_UNUSED(ssl_upstream),
|
||||
struct sockaddr_storage* addr, socklen_t addrlen, uint8_t* zone,
|
||||
size_t zonelen, comm_point_callback_t* callback, void* callback_arg,
|
||||
sldns_buffer* ATTR_UNUSED(buff))
|
||||
size_t zonelen, struct module_qstate* qstate,
|
||||
comm_point_callback_t* callback, void* callback_arg,
|
||||
sldns_buffer* ATTR_UNUSED(buff), struct module_env* ATTR_UNUSED(env))
|
||||
{
|
||||
struct replay_runtime* runtime = (struct replay_runtime*)outnet->base;
|
||||
struct fake_pending* pend = (struct fake_pending*)calloc(1,
|
||||
@ -1050,7 +1050,7 @@ struct serviced_query* outnet_serviced_query(struct outside_network* outnet,
|
||||
char z[256];
|
||||
log_assert(pend);
|
||||
log_nametypeclass(VERB_OPS, "pending serviced query",
|
||||
qname, qtype, qclass);
|
||||
qinfo->qname, qinfo->qtype, qinfo->qclass);
|
||||
dname_str(zone, z);
|
||||
verbose(VERB_OPS, "pending serviced query zone %s flags%s%s%s%s",
|
||||
z, (flags&BIT_RD)?" RD":"", (flags&BIT_CD)?" CD":"",
|
||||
@ -1065,9 +1065,9 @@ struct serviced_query* outnet_serviced_query(struct outside_network* outnet,
|
||||
sldns_buffer_write_u16(pend->buffer, 0); /* ancount */
|
||||
sldns_buffer_write_u16(pend->buffer, 0); /* nscount */
|
||||
sldns_buffer_write_u16(pend->buffer, 0); /* arcount */
|
||||
sldns_buffer_write(pend->buffer, qname, qnamelen);
|
||||
sldns_buffer_write_u16(pend->buffer, qtype);
|
||||
sldns_buffer_write_u16(pend->buffer, qclass);
|
||||
sldns_buffer_write(pend->buffer, qinfo->qname, qinfo->qname_len);
|
||||
sldns_buffer_write_u16(pend->buffer, qinfo->qtype);
|
||||
sldns_buffer_write_u16(pend->buffer, qinfo->qclass);
|
||||
sldns_buffer_flip(pend->buffer);
|
||||
if(1) {
|
||||
/* add edns */
|
||||
@ -1077,7 +1077,7 @@ struct serviced_query* outnet_serviced_query(struct outside_network* outnet,
|
||||
edns.edns_version = EDNS_ADVERTISED_VERSION;
|
||||
edns.udp_size = EDNS_ADVERTISED_SIZE;
|
||||
edns.bits = 0;
|
||||
edns.opt_list = opt_list;
|
||||
edns.opt_list = qstate->edns_opts_back_out;
|
||||
if(dnssec)
|
||||
edns.bits = EDNS_DO;
|
||||
attach_edns_record(pend->buffer, &edns);
|
||||
@ -1086,7 +1086,7 @@ struct serviced_query* outnet_serviced_query(struct outside_network* outnet,
|
||||
pend->addrlen = addrlen;
|
||||
pend->zone = memdup(zone, zonelen);
|
||||
pend->zonelen = zonelen;
|
||||
pend->qtype = (int)qtype;
|
||||
pend->qtype = (int)qinfo->qtype;
|
||||
log_assert(pend->zone);
|
||||
pend->callback = callback;
|
||||
pend->cb_arg = callback_arg;
|
||||
|
@ -1072,3 +1072,22 @@ parse_edns_from_pkt(sldns_buffer* pkt, struct edns_data* edns,
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
log_edns_opt_list(enum verbosity_value level, const char* info_str,
|
||||
struct edns_option* list)
|
||||
{
|
||||
if(verbosity >= level && list) {
|
||||
char str[128], *s;
|
||||
size_t slen;
|
||||
verbose(level, "%s", info_str);
|
||||
while(list) {
|
||||
s = str;
|
||||
slen = sizeof(str);
|
||||
(void)sldns_wire2str_edns_option_print(&s, &slen, list->opt_code,
|
||||
list->opt_data, list->opt_len);
|
||||
verbose(level, " %s", str);
|
||||
list = list->next;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -322,4 +322,13 @@ struct rrset_parse* msgparse_hashtable_lookup(struct msg_parse* msg,
|
||||
*/
|
||||
void msgparse_bucket_remove(struct msg_parse* msg, struct rrset_parse* rrset);
|
||||
|
||||
/**
|
||||
* Log the edns options in the edns option list.
|
||||
* @param level: the verbosity level.
|
||||
* @param info_str: the informational string to be printed before the options.
|
||||
* @param list: the edns option list.
|
||||
*/
|
||||
void log_edns_opt_list(enum verbosity_value level, const char* info_str,
|
||||
struct edns_option* list);
|
||||
|
||||
#endif /* UTIL_DATA_MSGPARSE_H */
|
||||
|
@ -52,6 +52,8 @@
|
||||
#include "util/data/msgencode.h"
|
||||
#include "sldns/sbuffer.h"
|
||||
#include "sldns/wire2str.h"
|
||||
#include "util/module.h"
|
||||
#include "util/fptr_wlist.h"
|
||||
|
||||
/** MAX TTL default for messages and rrsets */
|
||||
time_t MAX_TTL = 3600 * 24 * 10; /* ten days */
|
||||
@ -874,9 +876,12 @@ int edns_opt_append(struct edns_data* edns, struct regional* region,
|
||||
opt->next = NULL;
|
||||
opt->opt_code = code;
|
||||
opt->opt_len = len;
|
||||
opt->opt_data = regional_alloc_init(region, data, len);
|
||||
if(!opt->opt_data)
|
||||
return 0;
|
||||
opt->opt_data = NULL;
|
||||
if(len > 0) {
|
||||
opt->opt_data = regional_alloc_init(region, data, len);
|
||||
if(!opt->opt_data)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* append at end of list */
|
||||
prevp = &edns->opt_list;
|
||||
@ -886,13 +891,138 @@ int edns_opt_append(struct edns_data* edns, struct regional* region,
|
||||
return 1;
|
||||
}
|
||||
|
||||
int edns_opt_inplace_reply(struct edns_data* edns, struct regional* region)
|
||||
int edns_opt_list_append(struct edns_option** list, uint16_t code, size_t len,
|
||||
uint8_t* data, struct regional* region)
|
||||
{
|
||||
(void)region;
|
||||
/* remove all edns options from the reply, because only the
|
||||
* options that we understand should be in the reply
|
||||
* (sec 6.1.2 RFC 6891) */
|
||||
edns->opt_list = NULL;
|
||||
struct edns_option** prevp;
|
||||
struct edns_option* opt;
|
||||
|
||||
/* allocate new element */
|
||||
opt = (struct edns_option*)regional_alloc(region, sizeof(*opt));
|
||||
if(!opt)
|
||||
return 0;
|
||||
opt->next = NULL;
|
||||
opt->opt_code = code;
|
||||
opt->opt_len = len;
|
||||
opt->opt_data = NULL;
|
||||
if(len > 0) {
|
||||
opt->opt_data = regional_alloc_init(region, data, len);
|
||||
if(!opt->opt_data)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* append at end of list */
|
||||
prevp = list;
|
||||
while(*prevp != NULL) {
|
||||
prevp = &((*prevp)->next);
|
||||
}
|
||||
*prevp = opt;
|
||||
return 1;
|
||||
}
|
||||
|
||||
int edns_opt_list_remove(struct edns_option** list, uint16_t code)
|
||||
{
|
||||
/* The list should already be allocated in a region. Freeing the
|
||||
* allocated space in a region is not possible. We just unlink the
|
||||
* required elements and they will be freed together with the region. */
|
||||
|
||||
struct edns_option* prev;
|
||||
struct edns_option* curr;
|
||||
if(!list || !(*list)) return 0;
|
||||
|
||||
/* Unlink and repoint if the element(s) are first in list */
|
||||
while(list && *list && (*list)->opt_code == code) {
|
||||
*list = (*list)->next;
|
||||
}
|
||||
|
||||
if(!list || !(*list)) return 1;
|
||||
/* Unlink elements and reattach the list */
|
||||
prev = *list;
|
||||
curr = (*list)->next;
|
||||
while(curr != NULL) {
|
||||
if(curr->opt_code == code) {
|
||||
prev->next = curr->next;
|
||||
curr = curr->next;
|
||||
} else {
|
||||
prev = curr;
|
||||
curr = curr->next;
|
||||
}
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int inplace_cb_reply_call_generic(
|
||||
struct inplace_cb_reply* callback_list, enum inplace_cb_list_type type,
|
||||
struct query_info* qinfo, struct module_qstate* qstate,
|
||||
struct reply_info* rep, int rcode, struct edns_data* edns,
|
||||
struct regional* region)
|
||||
{
|
||||
struct inplace_cb_reply* cb;
|
||||
struct edns_option* opt_list_out = NULL;
|
||||
if(qstate)
|
||||
opt_list_out = qstate->edns_opts_front_out;
|
||||
for(cb=callback_list; cb; cb=cb->next) {
|
||||
fptr_ok(fptr_whitelist_inplace_cb_reply_generic(cb->cb, type));
|
||||
(void)(*cb->cb)(qinfo, qstate, rep, rcode, edns, &opt_list_out, region,
|
||||
cb->cb_arg);
|
||||
}
|
||||
edns->opt_list = opt_list_out;
|
||||
return 1;
|
||||
}
|
||||
|
||||
int inplace_cb_reply_call(struct module_env* env, struct query_info* qinfo,
|
||||
struct module_qstate* qstate, struct reply_info* rep, int rcode,
|
||||
struct edns_data* edns, struct regional* region)
|
||||
{
|
||||
return inplace_cb_reply_call_generic(
|
||||
env->inplace_cb_lists[inplace_cb_reply], inplace_cb_reply, qinfo,
|
||||
qstate, rep, rcode, edns, region);
|
||||
}
|
||||
|
||||
int inplace_cb_reply_cache_call(struct module_env* env,
|
||||
struct query_info* qinfo, struct module_qstate* qstate,
|
||||
struct reply_info* rep, int rcode, struct edns_data* edns,
|
||||
struct regional* region)
|
||||
{
|
||||
return inplace_cb_reply_call_generic(
|
||||
env->inplace_cb_lists[inplace_cb_reply_cache], inplace_cb_reply_cache,
|
||||
qinfo, qstate, rep, rcode, edns, region);
|
||||
}
|
||||
|
||||
int inplace_cb_reply_local_call(struct module_env* env,
|
||||
struct query_info* qinfo, struct module_qstate* qstate,
|
||||
struct reply_info* rep, int rcode, struct edns_data* edns,
|
||||
struct regional* region)
|
||||
{
|
||||
return inplace_cb_reply_call_generic(
|
||||
env->inplace_cb_lists[inplace_cb_reply_local], inplace_cb_reply_local,
|
||||
qinfo, qstate, rep, rcode, edns, region);
|
||||
}
|
||||
|
||||
int inplace_cb_reply_servfail_call(struct module_env* env,
|
||||
struct query_info* qinfo, struct module_qstate* qstate,
|
||||
struct reply_info* rep, int rcode, struct edns_data* edns,
|
||||
struct regional* region)
|
||||
{
|
||||
/* We are going to servfail. Remove any potential edns options. */
|
||||
if(qstate)
|
||||
qstate->edns_opts_front_out = NULL;
|
||||
return inplace_cb_reply_call_generic(
|
||||
env->inplace_cb_lists[inplace_cb_reply_servfail],
|
||||
inplace_cb_reply_servfail, qinfo, qstate, rep, rcode, edns, region);
|
||||
}
|
||||
|
||||
int inplace_cb_query_call(struct module_env* env, struct query_info* qinfo,
|
||||
uint16_t flags, struct sockaddr_storage* addr, socklen_t addrlen,
|
||||
uint8_t* zone, size_t zonelen, struct module_qstate* qstate,
|
||||
struct regional* region)
|
||||
{
|
||||
struct inplace_cb_query* cb = env->inplace_cb_lists[inplace_cb_query];
|
||||
for(; cb; cb=cb->next) {
|
||||
fptr_ok(fptr_whitelist_inplace_cb_query(cb->cb));
|
||||
(void)(*cb->cb)(qinfo, flags, qstate, addr, addrlen, zone, zonelen,
|
||||
region, cb->cb_arg);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
@ -1003,7 +1133,7 @@ struct edns_option* edns_opt_copy_alloc(struct edns_option* list)
|
||||
return result;
|
||||
}
|
||||
|
||||
struct edns_option* edns_opt_find(struct edns_option* list, uint16_t code)
|
||||
struct edns_option* edns_opt_list_find(struct edns_option* list, uint16_t code)
|
||||
{
|
||||
struct edns_option* p;
|
||||
for(p=list; p; p=p->next) {
|
||||
|
@ -49,6 +49,11 @@ struct alloc_cache;
|
||||
struct iovec;
|
||||
struct regional;
|
||||
struct edns_data;
|
||||
struct edns_option;
|
||||
struct inplace_cb_reply;
|
||||
struct inplace_cb_query;
|
||||
struct module_qstate;
|
||||
struct module_env;
|
||||
struct msg_parse;
|
||||
struct rrset_parse;
|
||||
struct local_rrset;
|
||||
@ -457,29 +462,133 @@ void log_query_info(enum verbosity_value v, const char* str,
|
||||
|
||||
/**
|
||||
* Append edns option to edns data structure
|
||||
* @param edns: the edns data structure to append the edns option to.
|
||||
* @param region: region to allocate the new edns option.
|
||||
* @param code: the edns option's code.
|
||||
* @param len: the edns option's length.
|
||||
* @param data: the edns option's data.
|
||||
* @return false on failure.
|
||||
*/
|
||||
int edns_opt_append(struct edns_data* edns, struct regional* region,
|
||||
uint16_t code, size_t len, uint8_t* data);
|
||||
|
||||
/**
|
||||
* Append edns option to edns option list
|
||||
* @param list: the edns option list to append the edns option to.
|
||||
* @param code: the edns option's code.
|
||||
* @param len: the edns option's length.
|
||||
* @param data: the edns option's data.
|
||||
* @param region: region to allocate the new edns option.
|
||||
* @return false on failure.
|
||||
*/
|
||||
int edns_opt_list_append(struct edns_option** list, uint16_t code, size_t len,
|
||||
uint8_t* data, struct regional* region);
|
||||
|
||||
/**
|
||||
* Remove any option found on the edns option list that matches the code.
|
||||
* @param list: the list of edns options.
|
||||
* @param code: the opt code to remove.
|
||||
* @return true when at least one edns option was removed, false otherwise.
|
||||
*/
|
||||
int edns_opt_list_remove(struct edns_option** list, uint16_t code);
|
||||
|
||||
/**
|
||||
* Find edns option in edns list
|
||||
* @param list: list of edns options (eg. edns.opt_list)
|
||||
* @param code: opt code to find.
|
||||
* @return NULL or the edns_option element.
|
||||
*/
|
||||
struct edns_option* edns_opt_find(struct edns_option* list, uint16_t code);
|
||||
struct edns_option* edns_opt_list_find(struct edns_option* list, uint16_t code);
|
||||
|
||||
/**
|
||||
* Transform edns data structure from query structure into reply structure.
|
||||
* In place transform, for errors and cache replies.
|
||||
* @param edns: on input contains the edns from the query. On output contains
|
||||
* the edns for the answer. Add new options to the opt_list to put them
|
||||
* in the answer (allocated in the region, with edns_opt_append).
|
||||
* @param region: to allocate stuff in.
|
||||
* @return false on failure (servfail to client, or for some error encodings,
|
||||
* no EDNS options in the answer).
|
||||
* Call the registered functions in the inplace_cb_reply linked list.
|
||||
* This function is going to get called while answering with a resolved query.
|
||||
* @param env: module environment.
|
||||
* @param qinfo: query info.
|
||||
* @param qstate: module qstate.
|
||||
* @param rep: Reply info. Could be NULL.
|
||||
* @param rcode: return code.
|
||||
* @param edns: edns data of the reply.
|
||||
* @param region: region to store data.
|
||||
* @return false on failure (a callback function returned an error).
|
||||
*/
|
||||
int edns_opt_inplace_reply(struct edns_data* edns, struct regional* region);
|
||||
int inplace_cb_reply_call(struct module_env* env, struct query_info* qinfo,
|
||||
struct module_qstate* qstate, struct reply_info* rep, int rcode,
|
||||
struct edns_data* edns, struct regional* region);
|
||||
|
||||
/**
|
||||
* Call the registered functions in the inplace_cb_reply_cache linked list.
|
||||
* This function is going to get called while answering from cache.
|
||||
* @param env: module environment.
|
||||
* @param qinfo: query info.
|
||||
* @param qstate: module qstate. NULL when replying from cache.
|
||||
* @param rep: Reply info.
|
||||
* @param rcode: return code.
|
||||
* @param edns: edns data of the reply. Edns input can be found here.
|
||||
* @param region: region to store data.
|
||||
* @return false on failure (a callback function returned an error).
|
||||
*/
|
||||
int inplace_cb_reply_cache_call(struct module_env* env,
|
||||
struct query_info* qinfo, struct module_qstate* qstate,
|
||||
struct reply_info* rep, int rcode, struct edns_data* edns,
|
||||
struct regional* region);
|
||||
|
||||
/**
|
||||
* Call the registered functions in the inplace_cb_reply_local linked list.
|
||||
* This function is going to get called while answering with local data.
|
||||
* @param env: module environment.
|
||||
* @param qinfo: query info.
|
||||
* @param qstate: module qstate. NULL when replying from cache.
|
||||
* @param rep: Reply info.
|
||||
* @param rcode: return code.
|
||||
* @param edns: edns data of the reply. Edns input can be found here.
|
||||
* @param region: region to store data.
|
||||
* @return false on failure (a callback function returned an error).
|
||||
*/
|
||||
int inplace_cb_reply_local_call(struct module_env* env,
|
||||
struct query_info* qinfo, struct module_qstate* qstate,
|
||||
struct reply_info* rep, int rcode, struct edns_data* edns,
|
||||
struct regional* region);
|
||||
|
||||
/**
|
||||
* Call the registered functions in the inplace_cb_reply linked list.
|
||||
* This function is going to get called while answering with a servfail.
|
||||
* @param env: module environment.
|
||||
* @param qinfo: query info.
|
||||
* @param qstate: module qstate. Contains the edns option lists. Could be NULL.
|
||||
* @param rep: Reply info. NULL when servfail.
|
||||
* @param rcode: return code. LDNS_RCODE_SERVFAIL.
|
||||
* @param edns: edns data of the reply. Edns input can be found here if qstate
|
||||
* is NULL.
|
||||
* @param region: region to store data.
|
||||
* @return false on failure (a callback function returned an error).
|
||||
*/
|
||||
int inplace_cb_reply_servfail_call(struct module_env* env,
|
||||
struct query_info* qinfo, struct module_qstate* qstate,
|
||||
struct reply_info* rep, int rcode, struct edns_data* edns,
|
||||
struct regional* region);
|
||||
|
||||
/**
|
||||
* Call the registered functions in the inplace_cb_query linked list.
|
||||
* This function is going to get called just before sending a query to a
|
||||
* nameserver.
|
||||
* @param env: module environment.
|
||||
* @param qinfo: query info.
|
||||
* @param flags: flags of the query.
|
||||
* @param addr: to which server to send the query.
|
||||
* @param addrlen: length of addr.
|
||||
* @param zone: name of the zone of the delegation point. wireformat dname.
|
||||
* This is the delegation point name for which the server is deemed
|
||||
* authoritative.
|
||||
* @param zonelen: length of zone.
|
||||
* @param qstate: module qstate.
|
||||
* @param region: region to store data.
|
||||
* @return false on failure (a callback function returned an error).
|
||||
*/
|
||||
int inplace_cb_query_call(struct module_env* env, struct query_info* qinfo,
|
||||
uint16_t flags, struct sockaddr_storage* addr, socklen_t addrlen,
|
||||
uint8_t* zone, size_t zonelen, struct module_qstate* qstate,
|
||||
struct regional* region);
|
||||
|
||||
/**
|
||||
* Copy edns option list allocated to the new region
|
||||
|
@ -267,11 +267,9 @@ fptr_whitelist_hash_markdelfunc(lruhash_markdelfunc_t fptr)
|
||||
/** whitelist env->send_query callbacks */
|
||||
int
|
||||
fptr_whitelist_modenv_send_query(struct outbound_entry* (*fptr)(
|
||||
uint8_t* qname, size_t qnamelen, uint16_t qtype, uint16_t qclass,
|
||||
uint16_t flags, int dnssec, int want_dnssec, int nocaps,
|
||||
struct edns_option* opt_list, struct sockaddr_storage* addr,
|
||||
socklen_t addrlen, uint8_t* zone, size_t zonelen, int ssl_upstream,
|
||||
struct module_qstate* q))
|
||||
struct query_info* qinfo, uint16_t flags, int dnssec, int want_dnssec,
|
||||
int nocaps, struct sockaddr_storage* addr, socklen_t addrlen,
|
||||
uint8_t* zone, size_t zonelen, int ssl_upstream, struct module_qstate* q))
|
||||
{
|
||||
if(fptr == &worker_send_query) return 1;
|
||||
else if(fptr == &libworker_send_query) return 1;
|
||||
@ -434,3 +432,31 @@ int fptr_whitelist_print_func(void (*fptr)(char*,void*))
|
||||
else if(fptr == &remote_get_opt_ssl) return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int fptr_whitelist_inplace_cb_reply_generic(inplace_cb_reply_func_t* fptr,
|
||||
enum inplace_cb_list_type type)
|
||||
{
|
||||
if(type == inplace_cb_reply) {
|
||||
#ifdef WITH_PYTHONMODULE
|
||||
if(fptr == &python_inplace_cb_reply_generic) return 1;
|
||||
#endif
|
||||
} else if(type == inplace_cb_reply_cache) {
|
||||
#ifdef WITH_PYTHONMODULE
|
||||
if(fptr == &python_inplace_cb_reply_generic) return 1;
|
||||
#endif
|
||||
} else if(type == inplace_cb_reply_local) {
|
||||
#ifdef WITH_PYTHONMODULE
|
||||
if(fptr == &python_inplace_cb_reply_generic) return 1;
|
||||
#endif
|
||||
} else if(type == inplace_cb_reply_servfail) {
|
||||
#ifdef WITH_PYTHONMODULE
|
||||
if(fptr == &python_inplace_cb_reply_generic) return 1;
|
||||
#endif
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int fptr_whitelist_inplace_cb_query(inplace_cb_query_func_t* fptr)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
@ -210,11 +210,9 @@ int fptr_whitelist_hash_markdelfunc(lruhash_markdelfunc_t fptr);
|
||||
* @return false if not in whitelist.
|
||||
*/
|
||||
int fptr_whitelist_modenv_send_query(struct outbound_entry* (*fptr)(
|
||||
uint8_t* qname, size_t qnamelen, uint16_t qtype, uint16_t qclass,
|
||||
uint16_t flags, int dnssec, int want_dnssec, int nocaps,
|
||||
struct edns_option*, struct sockaddr_storage* addr, socklen_t addrlen,
|
||||
uint8_t* zone, size_t zonelen, int ssl_upstream,
|
||||
struct module_qstate* q));
|
||||
struct query_info* qinfo, uint16_t flags, int dnssec, int want_dnssec,
|
||||
int nocaps, struct sockaddr_storage* addr, socklen_t addrlen,
|
||||
uint8_t* zone, size_t zonelen, int ssl_upstream, struct module_qstate* q));
|
||||
|
||||
/**
|
||||
* Check function pointer whitelist for module_env detach_subs callback values.
|
||||
@ -335,6 +333,24 @@ int fptr_whitelist_mesh_cb(mesh_cb_func_t fptr);
|
||||
*/
|
||||
int fptr_whitelist_print_func(void (*fptr)(char*,void*));
|
||||
|
||||
/**
|
||||
* Check function pointer whitelist for inplace_cb_reply,
|
||||
* inplace_cb_reply_cache, inplace_cb_reply_local and inplace_cb_reply_servfail
|
||||
* func values.
|
||||
* @param fptr: function pointer to check.
|
||||
* @param type: the type of the callback function.
|
||||
* @return false if not in whitelist.
|
||||
*/
|
||||
int fptr_whitelist_inplace_cb_reply_generic(inplace_cb_reply_func_t* fptr,
|
||||
enum inplace_cb_list_type type);
|
||||
|
||||
/**
|
||||
* Check function pointer whitelist for inplace_cb_query func values.
|
||||
* @param fptr: function pointer to check.
|
||||
* @return false if not in whitelist.
|
||||
*/
|
||||
int fptr_whitelist_inplace_cb_query(inplace_cb_query_func_t* fptr);
|
||||
|
||||
/** Due to module breakage by fptr wlist, these test app declarations
|
||||
* are presented here */
|
||||
/**
|
||||
|
254
util/module.c
254
util/module.c
@ -39,6 +39,7 @@
|
||||
|
||||
#include "config.h"
|
||||
#include "util/module.h"
|
||||
#include "sldns/wire2str.h"
|
||||
|
||||
const char*
|
||||
strextstate(enum module_ext_state s)
|
||||
@ -69,3 +70,256 @@ strmodulevent(enum module_ev e)
|
||||
}
|
||||
return "bad_event_value";
|
||||
}
|
||||
|
||||
int
|
||||
edns_known_options_init(struct module_env* env)
|
||||
{
|
||||
env->edns_known_options_num = 0;
|
||||
env->edns_known_options = (struct edns_known_option*)calloc(
|
||||
MAX_KNOWN_EDNS_OPTS, sizeof(struct edns_known_option));
|
||||
if(!env->edns_known_options) return 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
void
|
||||
edns_known_options_delete(struct module_env* env)
|
||||
{
|
||||
free(env->edns_known_options);
|
||||
env->edns_known_options = NULL;
|
||||
env->edns_known_options_num = 0;
|
||||
}
|
||||
|
||||
int
|
||||
edns_register_option(uint16_t opt_code, int bypass_cache_stage,
|
||||
int no_aggregation, struct module_env* env)
|
||||
{
|
||||
int i;
|
||||
if(env->worker) {
|
||||
log_err("invalid edns registration: "
|
||||
"trying to register option after module init phase");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checking if we are full first is faster but it does not provide
|
||||
* the option to change the flags when the array is full.
|
||||
* It only impacts unbound initialization, leave it for now.
|
||||
*/
|
||||
/* Check if the option is already registered. */
|
||||
for(i=0; i<env->edns_known_options_num; i++)
|
||||
if(env->edns_known_options[i].opt_code == opt_code)
|
||||
break;
|
||||
/* If it is not yet registered check if we have space to add a new one. */
|
||||
if(i == env->edns_known_options_num) {
|
||||
if(env->edns_known_options_num >= MAX_KNOWN_EDNS_OPTS) {
|
||||
log_err("invalid edns registration: maximum options reached");
|
||||
return 0;
|
||||
}
|
||||
env->edns_known_options_num++;
|
||||
}
|
||||
env->edns_known_options[i].opt_code = opt_code;
|
||||
env->edns_known_options[i].bypass_cache_stage = bypass_cache_stage;
|
||||
env->edns_known_options[i].no_aggregation = no_aggregation;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int
|
||||
inplace_cb_reply_register_generic(inplace_cb_reply_func_t* cb,
|
||||
enum inplace_cb_list_type type, void* cb_arg, struct module_env* env)
|
||||
{
|
||||
struct inplace_cb_reply* callback;
|
||||
struct inplace_cb_reply** prevp;
|
||||
if(env->worker) {
|
||||
log_err("invalid edns callback registration: "
|
||||
"trying to register callback after module init phase");
|
||||
return 0;
|
||||
}
|
||||
|
||||
callback = (struct inplace_cb_reply*)calloc(1, sizeof(*callback));
|
||||
if(callback == NULL) {
|
||||
log_err("out of memory during edns callback registration.");
|
||||
return 0;
|
||||
}
|
||||
callback->next = NULL;
|
||||
callback->cb = cb;
|
||||
callback->cb_arg = cb_arg;
|
||||
|
||||
prevp = (struct inplace_cb_reply**) &env->inplace_cb_lists[type];
|
||||
/* append at end of list */
|
||||
while(*prevp != NULL)
|
||||
prevp = &((*prevp)->next);
|
||||
*prevp = callback;
|
||||
return 1;
|
||||
}
|
||||
|
||||
int
|
||||
inplace_cb_reply_register(inplace_cb_reply_func_t* cb, void* cb_arg,
|
||||
struct module_env* env)
|
||||
{
|
||||
return inplace_cb_reply_register_generic(cb, inplace_cb_reply, cb_arg,
|
||||
env);
|
||||
}
|
||||
|
||||
int
|
||||
inplace_cb_reply_cache_register(inplace_cb_reply_func_t* cb, void* cb_arg,
|
||||
struct module_env* env)
|
||||
{
|
||||
return inplace_cb_reply_register_generic(cb, inplace_cb_reply_cache,
|
||||
cb_arg, env);
|
||||
}
|
||||
|
||||
int
|
||||
inplace_cb_reply_local_register(inplace_cb_reply_func_t* cb, void* cb_arg,
|
||||
struct module_env* env)
|
||||
{
|
||||
return inplace_cb_reply_register_generic(cb, inplace_cb_reply_local,
|
||||
cb_arg, env);
|
||||
}
|
||||
|
||||
int
|
||||
inplace_cb_reply_servfail_register(inplace_cb_reply_func_t* cb, void* cb_arg,
|
||||
struct module_env* env)
|
||||
{
|
||||
return inplace_cb_reply_register_generic(cb, inplace_cb_reply_servfail,
|
||||
cb_arg, env);
|
||||
}
|
||||
|
||||
static void
|
||||
inplace_cb_reply_delete_generic(struct module_env* env,
|
||||
enum inplace_cb_list_type type)
|
||||
{
|
||||
struct inplace_cb_reply* curr = env->inplace_cb_lists[type];
|
||||
struct inplace_cb_reply* tmp;
|
||||
/* delete list */
|
||||
while(curr) {
|
||||
tmp = curr->next;
|
||||
free(curr);
|
||||
curr = tmp;
|
||||
}
|
||||
/* update head pointer */
|
||||
env->inplace_cb_lists[type] = NULL;
|
||||
}
|
||||
|
||||
void inplace_cb_reply_delete(struct module_env* env)
|
||||
{
|
||||
inplace_cb_reply_delete_generic(env, inplace_cb_reply);
|
||||
}
|
||||
|
||||
void inplace_cb_reply_cache_delete(struct module_env* env)
|
||||
{
|
||||
inplace_cb_reply_delete_generic(env, inplace_cb_reply_cache);
|
||||
}
|
||||
|
||||
void inplace_cb_reply_servfail_delete(struct module_env* env)
|
||||
{
|
||||
inplace_cb_reply_delete_generic(env, inplace_cb_reply_servfail);
|
||||
}
|
||||
|
||||
int
|
||||
inplace_cb_query_register(inplace_cb_query_func_t* cb, void* cb_arg,
|
||||
struct module_env* env)
|
||||
{
|
||||
struct inplace_cb_query* callback;
|
||||
struct inplace_cb_query** prevp;
|
||||
if(env->worker) {
|
||||
log_err("invalid edns callback registration: "
|
||||
"trying to register callback after module init phase");
|
||||
return 0;
|
||||
}
|
||||
|
||||
callback = (struct inplace_cb_query*)calloc(1, sizeof(*callback));
|
||||
if(callback == NULL) {
|
||||
log_err("out of memory during edns callback registration.");
|
||||
return 0;
|
||||
}
|
||||
callback->next = NULL;
|
||||
callback->cb = cb;
|
||||
callback->cb_arg = cb_arg;
|
||||
|
||||
prevp = (struct inplace_cb_query**)
|
||||
&env->inplace_cb_lists[inplace_cb_query];
|
||||
/* append at end of list */
|
||||
while(*prevp != NULL)
|
||||
prevp = &((*prevp)->next);
|
||||
*prevp = callback;
|
||||
return 1;
|
||||
}
|
||||
|
||||
void
|
||||
inplace_cb_query_delete(struct module_env* env)
|
||||
{
|
||||
struct inplace_cb_query* curr = env->inplace_cb_lists[inplace_cb_query];
|
||||
struct inplace_cb_query* tmp;
|
||||
/* delete list */
|
||||
while(curr) {
|
||||
tmp = curr->next;
|
||||
free(curr);
|
||||
curr = tmp;
|
||||
}
|
||||
/* update head pointer */
|
||||
env->inplace_cb_lists[inplace_cb_query] = NULL;
|
||||
}
|
||||
|
||||
void
|
||||
inplace_cb_lists_delete(struct module_env* env)
|
||||
{
|
||||
inplace_cb_reply_delete(env);
|
||||
inplace_cb_reply_cache_delete(env);
|
||||
inplace_cb_reply_servfail_delete(env);
|
||||
inplace_cb_query_delete(env);
|
||||
}
|
||||
|
||||
struct edns_known_option*
|
||||
edns_option_is_known(uint16_t opt_code, struct module_env* env)
|
||||
{
|
||||
int i;
|
||||
for(i=0; i<env->edns_known_options_num; i++)
|
||||
if(env->edns_known_options[i].opt_code == opt_code)
|
||||
return env->edns_known_options + i;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int
|
||||
edns_bypass_cache_stage(struct edns_option* list, struct module_env* env)
|
||||
{
|
||||
int i;
|
||||
for(; list; list=list->next)
|
||||
for(i=0; i<env->edns_known_options_num; i++)
|
||||
if(env->edns_known_options[i].opt_code == list->opt_code &&
|
||||
env->edns_known_options[i].bypass_cache_stage == 1)
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
edns_unique_mesh_state(struct edns_option* list, struct module_env* env)
|
||||
{
|
||||
int i;
|
||||
for(; list; list=list->next)
|
||||
for(i=0; i<env->edns_known_options_num; i++)
|
||||
if(env->edns_known_options[i].opt_code == list->opt_code &&
|
||||
env->edns_known_options[i].no_aggregation == 1)
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
log_edns_known_options(enum verbosity_value level, struct module_env* env)
|
||||
{
|
||||
int i;
|
||||
char str[32], *s;
|
||||
size_t slen;
|
||||
if(env->edns_known_options_num > 0 && verbosity >= level) {
|
||||
verbose(level, "EDNS known options:");
|
||||
verbose(level, " Code: Bypass_cache_stage: Aggregate_mesh:");
|
||||
for(i=0; i<env->edns_known_options_num; i++) {
|
||||
s = str;
|
||||
slen = sizeof(str);
|
||||
(void)sldns_wire2str_edns_option_code_print(&s, &slen,
|
||||
env->edns_known_options[i].opt_code);
|
||||
verbose(level, " %-8.8s %-19s %-15s", str,
|
||||
env->edns_known_options[i].bypass_cache_stage?"YES":"NO",
|
||||
env->edns_known_options[i].no_aggregation?"NO":"YES");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
297
util/module.h
297
util/module.h
@ -178,6 +178,115 @@ struct iter_hints;
|
||||
/** Maximum number of modules in operation */
|
||||
#define MAX_MODULE 5
|
||||
|
||||
/** Maximum number of known edns options */
|
||||
#define MAX_KNOWN_EDNS_OPTS 256
|
||||
|
||||
enum inplace_cb_list_type {
|
||||
/* Inplace callbacks for when a resolved reply is ready to be sent to the
|
||||
* front.*/
|
||||
inplace_cb_reply = 0,
|
||||
/* Inplace callbacks for when a reply is given from the cache. */
|
||||
inplace_cb_reply_cache,
|
||||
/* Inplace callbacks for when a reply is given with local data
|
||||
* (or Chaos reply). */
|
||||
inplace_cb_reply_local,
|
||||
/* Inplace callbacks for when the reply is servfail. */
|
||||
inplace_cb_reply_servfail,
|
||||
/* Inplace callbacks for when a query is ready to be sent to the back.*/
|
||||
inplace_cb_query,
|
||||
/* Total number of types. Used for array initialization.
|
||||
* Should always be last. */
|
||||
inplace_cb_types_total
|
||||
};
|
||||
|
||||
|
||||
/** Known edns option. Can be populated during modules' init. */
|
||||
struct edns_known_option {
|
||||
/** type of this edns option */
|
||||
uint16_t opt_code;
|
||||
/** whether the option needs to bypass the cache stage */
|
||||
int bypass_cache_stage;
|
||||
/** whether the option needs mesh aggregation */
|
||||
int no_aggregation;
|
||||
};
|
||||
|
||||
/**
|
||||
* Inplace callback function called before replying.
|
||||
* Called as func(edns, qstate, opt_list_out, qinfo, reply_info, rcode,
|
||||
* region, python_callback)
|
||||
* Where:
|
||||
* qinfo: the query info.
|
||||
* qstate: the module state. NULL when calling before the query reaches the
|
||||
* mesh states.
|
||||
* rep: reply_info. Could be NULL.
|
||||
* rcode: the return code.
|
||||
* edns: the edns_data of the reply. When qstate is NULL, it is also used as
|
||||
* the edns input.
|
||||
* opt_list_out: the edns options list for the reply.
|
||||
* region: region to store data.
|
||||
* python_callback: only used for registering a python callback function.
|
||||
*/
|
||||
typedef int inplace_cb_reply_func_t(struct query_info* qinfo,
|
||||
struct module_qstate* qstate, struct reply_info* rep, int rcode,
|
||||
struct edns_data* edns, struct edns_option** opt_list_out,
|
||||
struct regional* region, void* python_callback);
|
||||
|
||||
/**
|
||||
* Inplace callback list of registered routines to be called before replying
|
||||
* with a resolved query.
|
||||
*/
|
||||
struct inplace_cb_reply {
|
||||
/** next in list */
|
||||
struct inplace_cb_reply* next;
|
||||
/**
|
||||
* Inplace callback routine for cache stage response.
|
||||
* called as cb(qinfo, qstate, qinfo, reply_info, rcode, edns,
|
||||
* opt_list_out, region, python_callback);
|
||||
* python_callback is only used for registering a python callback function.
|
||||
*/
|
||||
inplace_cb_reply_func_t* cb;
|
||||
void* cb_arg;
|
||||
};
|
||||
|
||||
/**
|
||||
* Inplace callback function called before sending the query to a nameserver.
|
||||
* Called as func(qinfo, flags, qstate, addr, addrlen, zone, zonelen, region,
|
||||
* python_callback)
|
||||
* Where:
|
||||
* qinfo: query info.
|
||||
* flags: flags of the query.
|
||||
* qstate: query state.
|
||||
* addr: to which server to send the query.
|
||||
* addrlen: length of addr.
|
||||
* zone: name of the zone of the delegation point. wireformat dname.
|
||||
* This is the delegation point name for which the server is deemed
|
||||
* authoritative.
|
||||
* zonelen: length of zone.
|
||||
* region: region to store data.
|
||||
* python_callback: only used for registering a python callback function.
|
||||
*/
|
||||
typedef int inplace_cb_query_func_t(struct query_info* qinfo, uint16_t flags,
|
||||
struct module_qstate* qstate, struct sockaddr_storage* addr,
|
||||
socklen_t addrlen, uint8_t* zone, size_t zonelen, struct regional* region,
|
||||
void* python_callback);
|
||||
|
||||
/**
|
||||
* Inplace callback list of registered routines to be called before quering a
|
||||
* nameserver.
|
||||
*/
|
||||
struct inplace_cb_query {
|
||||
/** next in list */
|
||||
struct inplace_cb_query* next;
|
||||
/**
|
||||
* Inplace callback routine for cache stage response.
|
||||
* called as cb(qinfo, flags, qstate, addr, addrlen, zone, zonelen,
|
||||
* region, python_callback);
|
||||
* python_callback is only used for registering a python callback function.
|
||||
*/
|
||||
inplace_cb_query_func_t* cb;
|
||||
void* cb_arg;
|
||||
};
|
||||
|
||||
/**
|
||||
* Module environment.
|
||||
* Services and data provided to the module.
|
||||
@ -202,10 +311,7 @@ struct module_env {
|
||||
* will cause operate() to be called with event timeout or reply.
|
||||
* The time until a timeout is calculated from roundtrip timing,
|
||||
* several UDP retries are attempted.
|
||||
* @param qname: query name. (host order)
|
||||
* @param qnamelen: length in bytes of qname, including trailing 0.
|
||||
* @param qtype: query type. (host order)
|
||||
* @param qclass: query class. (host order)
|
||||
* @param qinfo: query info.
|
||||
* @param flags: host order flags word, with opcode and CD bit.
|
||||
* @param dnssec: if set, EDNS record will have bits set.
|
||||
* If EDNS_DO bit is set, DO bit is set in EDNS records.
|
||||
@ -214,8 +320,6 @@ struct module_env {
|
||||
* EDNS, the answer is likely to be useless for this domain.
|
||||
* @param nocaps: do not use caps_for_id, use the qname as given.
|
||||
* (ignored if caps_for_id is disabled).
|
||||
* @param opt_list: set these EDNS options on the outgoing packet.
|
||||
* or NULL if none (the list is deep-copied).
|
||||
* @param addr: where to.
|
||||
* @param addrlen: length of addr.
|
||||
* @param zone: delegation point name.
|
||||
@ -227,9 +331,8 @@ struct module_env {
|
||||
* This outbound_entry will be used on later module invocations
|
||||
* that involve this query (timeout, error or reply).
|
||||
*/
|
||||
struct outbound_entry* (*send_query)(uint8_t* qname, size_t qnamelen,
|
||||
uint16_t qtype, uint16_t qclass, uint16_t flags, int dnssec,
|
||||
int want_dnssec, int nocaps, struct edns_option* opt_list,
|
||||
struct outbound_entry* (*send_query)(struct query_info* qinfo,
|
||||
uint16_t flags, int dnssec, int want_dnssec, int nocaps,
|
||||
struct sockaddr_storage* addr, socklen_t addrlen,
|
||||
uint8_t* zone, size_t zonelen, int ssl_upstream,
|
||||
struct module_qstate* q);
|
||||
@ -337,6 +440,17 @@ struct module_env {
|
||||
struct iter_hints* hints;
|
||||
/** module specific data. indexed by module id. */
|
||||
void* modinfo[MAX_MODULE];
|
||||
|
||||
/* Shared linked list of inplace callback functions */
|
||||
void* inplace_cb_lists[inplace_cb_types_total];
|
||||
|
||||
/**
|
||||
* Shared array of known edns options (size MAX_KNOWN_EDNS_OPTS).
|
||||
* Filled by edns literate modules during init.
|
||||
*/
|
||||
struct edns_known_option* edns_known_options;
|
||||
/* Number of known edns options */
|
||||
size_t edns_known_options_num;
|
||||
};
|
||||
|
||||
/**
|
||||
@ -435,6 +549,19 @@ struct module_qstate {
|
||||
struct mesh_state* mesh_info;
|
||||
/** how many seconds before expiry is this prefetched (0 if not) */
|
||||
time_t prefetch_leeway;
|
||||
|
||||
/** incoming edns options from the front end */
|
||||
struct edns_option* edns_opts_front_in;
|
||||
/** outgoing edns options to the back end */
|
||||
struct edns_option* edns_opts_back_out;
|
||||
/** incoming edns options from the back end */
|
||||
struct edns_option* edns_opts_back_in;
|
||||
/** outgoing edns options to the front end */
|
||||
struct edns_option* edns_opts_front_out;
|
||||
/** whether modules should answer from the cache */
|
||||
int no_cache_lookup;
|
||||
/** whether modules should store answer in the cache */
|
||||
int no_cache_store;
|
||||
};
|
||||
|
||||
/**
|
||||
@ -524,4 +651,156 @@ const char* strextstate(enum module_ext_state s);
|
||||
*/
|
||||
const char* strmodulevent(enum module_ev e);
|
||||
|
||||
/**
|
||||
* Initialize the edns known options by allocating the required space.
|
||||
* @param env: the module environment.
|
||||
* @return false on failure (no memory).
|
||||
*/
|
||||
int edns_known_options_init(struct module_env* env);
|
||||
|
||||
/**
|
||||
* Free the allocated space for the known edns options.
|
||||
* @param env: the module environment.
|
||||
*/
|
||||
void edns_known_options_delete(struct module_env* env);
|
||||
|
||||
/**
|
||||
* Register a known edns option. Overwrite the flags if it is already
|
||||
* registered. Used before creating workers to register known edns options.
|
||||
* @param opt_code: the edns option code.
|
||||
* @param bypass_cache_stage: whether the option interacts with the cache.
|
||||
* @param no_aggregation: whether the option implies more specific
|
||||
* aggregation.
|
||||
* @param env: the module environment.
|
||||
* @return true on success, false on failure (registering more options than
|
||||
* allowed or trying to register after the environment is copied to the
|
||||
* threads.)
|
||||
*/
|
||||
int edns_register_option(uint16_t opt_code, int bypass_cache_stage,
|
||||
int no_aggregation, struct module_env* env);
|
||||
|
||||
/**
|
||||
* Register an inplace callback function called before replying with a resolved
|
||||
* query.
|
||||
* @param cb: pointer to the callback function.
|
||||
* @param cb_arg: optional argument for the callback function.
|
||||
* @param env: the module environment.
|
||||
* @return true on success, false on failure (out of memory or trying to
|
||||
* register after the environment is copied to the threads.)
|
||||
*/
|
||||
int inplace_cb_reply_register(inplace_cb_reply_func_t* cb, void* cb_arg,
|
||||
struct module_env* env);
|
||||
|
||||
/**
|
||||
* Register an inplace callback function called before replying from the cache.
|
||||
* @param cb: pointer to the callback function.
|
||||
* @param cb_arg: optional argument for the callback function.
|
||||
* @param env: the module environment.
|
||||
* @return true on success, false on failure (out of memory or trying to
|
||||
* register after the environment is copied to the threads.)
|
||||
*/
|
||||
int inplace_cb_reply_cache_register(inplace_cb_reply_func_t* cb, void* cb_arg,
|
||||
struct module_env* env);
|
||||
|
||||
/**
|
||||
* Register an inplace callback function called before replying with local
|
||||
* data or Chaos reply.
|
||||
* @param cb: pointer to the callback function.
|
||||
* @param cb_arg: optional argument for the callback function.
|
||||
* @param env: the module environment.
|
||||
* @return true on success, false on failure (out of memory or trying to
|
||||
* register after the environment is copied to the threads.)
|
||||
*/
|
||||
int inplace_cb_reply_local_register(inplace_cb_reply_func_t* cb, void* cb_arg,
|
||||
struct module_env* env);
|
||||
|
||||
/**
|
||||
* Register an inplace callback function called before replying with servfail.
|
||||
* @param cb: pointer to the callback function.
|
||||
* @param cb_arg: optional argument for the callback function.
|
||||
* @param env: the module environment.
|
||||
* @return true on success, false on failure (out of memory or trying to
|
||||
* register after the environment is copied to the threads.)
|
||||
*/
|
||||
int inplace_cb_reply_servfail_register(inplace_cb_reply_func_t* cb,
|
||||
void* cb_arg, struct module_env* env);
|
||||
|
||||
/**
|
||||
* Delete the inplace_cb_reply callback linked list.
|
||||
* @param env: the module environment.
|
||||
*/
|
||||
void inplace_cb_reply_delete(struct module_env* env);
|
||||
|
||||
/**
|
||||
* Delete the inplace_cb_reply_cache callback linked list.
|
||||
* @param env: the module environment.
|
||||
*/
|
||||
void inplace_cb_reply_cache_delete(struct module_env* env);
|
||||
|
||||
/**
|
||||
* Delete the inplace_cb_reply_servfail callback linked list.
|
||||
* @param env: the module environment.
|
||||
*/
|
||||
void inplace_cb_reply_servfail_delete(struct module_env* env);
|
||||
|
||||
/**
|
||||
* Register an inplace callback function called before quering a nameserver.
|
||||
* @param cb: pointer to the callback function.
|
||||
* @param cb_arg: optional argument for the callback function.
|
||||
* @param env: the module environment.
|
||||
* @return true on success, false on failure (out of memory or trying to
|
||||
* register after the environment is copied to the threads.)
|
||||
*/
|
||||
int inplace_cb_query_register(inplace_cb_query_func_t* cb, void* cb_arg,
|
||||
struct module_env* env);
|
||||
|
||||
/**
|
||||
* Delete the inplace_cb_query callback linked list.
|
||||
* @param env: the module environment.
|
||||
*/
|
||||
void inplace_cb_query_delete(struct module_env* env);
|
||||
|
||||
/**
|
||||
* Delete all the inplace callback linked lists.
|
||||
* @param env: the module environment.
|
||||
*/
|
||||
void inplace_cb_lists_delete(struct module_env* env);
|
||||
|
||||
/**
|
||||
* Check if an edns option is known.
|
||||
* @param opt_code: the edns option code.
|
||||
* @param env: the module environment.
|
||||
* @return pointer to registered option if the edns option is known,
|
||||
* NULL otherwise.
|
||||
*/
|
||||
struct edns_known_option* edns_option_is_known(uint16_t opt_code,
|
||||
struct module_env* env);
|
||||
|
||||
/**
|
||||
* Check if an edns option needs to bypass the reply from cache stage.
|
||||
* @param list: the edns options.
|
||||
* @param env: the module environment.
|
||||
* @return true if an edns option needs to bypass the cache stage,
|
||||
* false otherwise.
|
||||
*/
|
||||
int edns_bypass_cache_stage(struct edns_option* list,
|
||||
struct module_env* env);
|
||||
|
||||
/**
|
||||
* Check if an edns option needs a unique mesh state.
|
||||
* @param list: the edns options.
|
||||
* @param env: the module environment.
|
||||
* @return true if an edns option needs a unique mesh state,
|
||||
* false otherwise.
|
||||
*/
|
||||
int edns_unique_mesh_state(struct edns_option* list, struct module_env* env);
|
||||
|
||||
/**
|
||||
* Log the known edns options.
|
||||
* @param level: the desired verbosity level.
|
||||
* @param env: the module environment.
|
||||
*/
|
||||
void log_edns_known_options(enum verbosity_value level,
|
||||
struct module_env* env);
|
||||
|
||||
#endif /* UTIL_MODULE_H */
|
||||
|
@ -181,6 +181,7 @@ val_init(struct module_env* env, int id)
|
||||
log_err("validator: could not apply configuration settings.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
@ -2088,7 +2089,7 @@ processFinished(struct module_qstate* qstate, struct val_qstate* vq,
|
||||
}
|
||||
|
||||
/* store results in cache */
|
||||
if(qstate->query_flags&BIT_RD) {
|
||||
if(!qstate->no_cache_store && qstate->query_flags&BIT_RD) {
|
||||
/* if secure, this will override cache anyway, no need
|
||||
* to check if from parentNS */
|
||||
if(!dns_cache_store(qstate->env, &vq->orig_msg->qinfo,
|
||||
@ -2281,6 +2282,7 @@ val_operate(struct module_qstate* qstate, enum module_ev event, int id,
|
||||
(void)outbound;
|
||||
if(event == module_event_new ||
|
||||
(event == module_event_pass && vq == NULL)) {
|
||||
|
||||
/* pass request to next module, to get it */
|
||||
verbose(VERB_ALGO, "validator: pass to next module");
|
||||
qstate->ext_state[id] = module_wait_module;
|
||||
@ -2289,6 +2291,7 @@ val_operate(struct module_qstate* qstate, enum module_ev event, int id,
|
||||
if(event == module_event_moddone) {
|
||||
/* check if validation is needed */
|
||||
verbose(VERB_ALGO, "validator: nextmodule returned");
|
||||
|
||||
if(!needs_validation(qstate, qstate->return_rcode,
|
||||
qstate->return_msg)) {
|
||||
/* no need to validate this */
|
||||
|
Loading…
Reference in New Issue
Block a user