]> git.ipfire.org Git - thirdparty/unbound.git/commitdiff
- Added generic EDNS code for registering known EDNS option codes,
authorGeorge Thessalonikefs <george@nlnetlabs.nl>
Tue, 6 Dec 2016 13:42:51 +0000 (13:42 +0000)
committerGeorge Thessalonikefs <george@nlnetlabs.nl>
Tue, 6 Dec 2016 13:42:51 +0000 (13:42 +0000)
  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

59 files changed:
cachedb/cachedb.c
daemon/daemon.c
daemon/worker.c
daemon/worker.h
dns64/dns64.c
doc/Changelog
iterator/iterator.c
libunbound/context.c
libunbound/libunbound.c
libunbound/libworker.c
libunbound/libworker.h
libunbound/python/doc/conf.py
libunbound/python/doc/examples/example1a.rst
libunbound/python/doc/examples/example1b.rst
libunbound/python/doc/examples/example2.rst
libunbound/python/doc/examples/example3.rst
libunbound/python/doc/examples/example4.rst
libunbound/python/doc/examples/example5.rst
libunbound/python/doc/examples/example6.rst
libunbound/python/doc/examples/example7.rst
libunbound/python/doc/examples/example8.rst
libunbound/python/doc/examples/index.rst
libunbound/python/doc/install.rst
libunbound/python/doc/intro.rst
libunbound/worker.h
pythonmod/doc/conf.py
pythonmod/doc/examples/example2.rst
pythonmod/doc/examples/example4.rst
pythonmod/doc/examples/example5.rst [new file with mode: 0644]
pythonmod/doc/examples/example6.rst [new file with mode: 0644]
pythonmod/doc/examples/index.rst
pythonmod/doc/install.rst
pythonmod/doc/modules/functions.rst
pythonmod/doc/modules/struct.rst
pythonmod/examples/edns.py [new file with mode: 0644]
pythonmod/examples/inplace_callbacks.py [new file with mode: 0644]
pythonmod/interface.i
pythonmod/pythonmod.c
pythonmod/pythonmod.h
pythonmod/test-edns.conf [new file with mode: 0644]
pythonmod/test-inplace_callbacks.py [new file with mode: 0644]
services/localzone.c
services/localzone.h
services/mesh.c
services/mesh.h
services/outside_network.c
services/outside_network.h
smallapp/unbound-checkconf.c
smallapp/worker_cb.c
testcode/fake_event.c
util/data/msgparse.c
util/data/msgparse.h
util/data/msgreply.c
util/data/msgreply.h
util/fptr_wlist.c
util/fptr_wlist.h
util/module.c
util/module.h
validator/validator.c

index cbec59df3c4ab92a2b9624a0f486209a5c128fb6..a326d6ef8dbca209a7e7ecb95b687e94bdcb9095 100644 (file)
@@ -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;
index 53a32dcf8a9f13fcf35d08bbdf0699545a89867c..88c695be31637129dd9c4ccdf0bb19987b583fd9 100644 (file)
@@ -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);
index 94f636b657466b212e3d1931bc016b7eed0940df..486cf4afe0e0afa42504b1acc70d44c29934687d 100644 (file)
@@ -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;
index 63613430b0544a589d400b3c1b4ce433e4d8223d..d6c87c80739ce88df9fdfbf4e5c59966ce43bf03 100644 (file)
@@ -61,6 +61,7 @@ struct ub_randstate;
 struct regional;
 struct tube;
 struct daemon_remote;
+struct query_info;
 
 /** worker commands */
 enum worker_commands {
index 5fa2096b8c5e585a6a51017e40ef917954ee16fc..befec864d6ab976105ee11b383999ae03c002e9b 100644 (file)
@@ -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");
 }
 
index 305dec4e983dc4a9444f8b78abb29b3d775e342e..96008eebaccb5ab646562717e6ff0da03a3ed60d 100644 (file)
@@ -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.
index dfe4fa6f2fa4cda674e1347915fe4efb2bb55d24..f60893323a10e089fd806a7194d25d3a211b2639 100644 (file)
@@ -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 */
-       }
-       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);
+       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);
+       }
        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;
 
index 4469b5bb4eb227581b522dc6434634c57401416b..94a2472ae5313a495b5a84fdaf5d8eededa2e1f5 100644 (file)
@@ -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;
index 69ccb08f45cb3516d08c9369b754d59bf2ad9876..aaaaec08ce9ee2b6d74dcb66150cf53d9c765ae2 100644 (file)
@@ -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);
index b00b96647eb88449f1652b9ffbeb45b1464c0a84..c90101956d5173741b6097dcbbbf31e3f35c12a8 100644 (file)
@@ -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;
index e8c29328038d81b265d0b4eff3c7d4b6d72e98cd..b546e89f2ca56e5aea97f83c5830da2da564850d 100644 (file)
@@ -59,6 +59,7 @@ struct regional;
 struct tube;
 struct sldns_buffer;
 struct ub_event_base;
+struct query_info;
 
 /** 
  * The library-worker status structure
index 97fca2125f83e205345c7d72f6cd1140523709dc..1766036b9a1f118b4de7a882d3f81a8e2826fbcf 100644 (file)
@@ -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".
index 3c81547f294874c24be9036f2fb33b858445da83..f46cb92f47e82b672f5b245ec28dbe980c5f031a 100644 (file)
@@ -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)
-
-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. 
-
-If only domain name is given, the :meth:`unbound.ub_ctx.resolve` looks for A records in IN class.
+    #!/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)
+
+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.
index ea1e6f57de65a137df92bba2ff3ccbf2ecaf39ef..1adae2cb1298bfc03647dcee347c08a47beb1656 100644 (file)
@@ -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)
-
-In order to simplify the python code, unbound module contains function which reverses the hostname components. 
-This function is defined as follows::
+    #!/usr/bin/python
+    import unbound
 
-       def reverse(domain):
-               return '.'.join([a for a in domain.split(".")][::-1])
+    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])
index c009ec1f5ae7442209a153ec0cda55a05b6ed141..a2bf2cbf51998010673efcf8d5109cbc5530a7fe 100644 (file)
@@ -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
-
-               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()
+    #!/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
+
+        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()
index 91360335c63a9a82e40f9d793b5e21c2927df1f3..b0626b55f9c194d0675d2a02e1f5c9e662076c26 100644 (file)
@@ -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``.
index 996ef4ede9247efe60649c3b845ed3e81aba9957..3b43eb85f93922bc46267a812caf1b2280f4446c 100644 (file)
@@ -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`_.
 
index 0a31d9a57e76c76eab07db48480df33fc3b7d419..9262014bb7fd6347bab343529929958d0bc98d42 100644 (file)
@@ -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).
index 478e139092774798bf7df5ef54d9fd0725ee7fee..6fde8b25fd8d9a2a061fb7825411f6f82a10c541 100644 (file)
@@ -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
index d4050215e435a9ac67423e72d9ecf03cfd379a47..2f48c8f0f26fd7d1996edabcdec15334bb8ce126 100644 (file)
@@ -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
index 8cdfcdc0a904bb75983528c9af8aa267afd5e111..16c14047576bc7e2eb9260a0690e8cffab147dbe 100644 (file)
@@ -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.
index c2c9cf45711702998db81f67bd32fb117dc6c0d1..283261652dc0e55387f83fa14cf527533c15558f 100644 (file)
@@ -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*
index a073a5c6603f8cd5d414ed5326668444ac858584..bb3118984de355a5f9e5392d4ef61a7fe061bfc8 100644 (file)
@@ -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.
index f751f54c07be4719b8f564e07dbdfc12d602babd..e490d2c6f5085b75916b632ff435950569b3eb8d 100644 (file)
@@ -1,39 +1,58 @@
 Introduction
-===================================
-
-**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
-
-**pyUnbound**
-
-       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.
-
-**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
+============
+
+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
+
+pyUnbound
+---------
+
+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.
+
+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
index 5d1c9ab92ff212630bad5ecfd247d3f831985143..88e1cf799d474a25897fbcf995602e564f9962be 100644 (file)
@@ -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);
 
index bc7a5aba68d560ee677b271f6859289bb4988855..7fcfe2d0508c58f2b19357ca67578824d6691027 100644 (file)
@@ -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".
index f00fcc239609809ddce5035219ce53bbd723c3fe..4ba9239a003fee79d208244ec17466102969eb42 100644 (file)
@@ -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
-
-As we handle (override) in python module only queries ending with "localdomain.", the unboud can still resolve host names.
+    ;; 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
+
+As we handle (override) in the python module only queries ending with
+``localdomain.``, unboud can still resolve host names.
index b665351e84ecd44aec74afb821cfe2351803412b..3382109903aa26edc29e8e3043cc2c752ebf5cfc 100644 (file)
@@ -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
-
-Proof that the unbound still works as resolver.
+    ; <<>> 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
+
+    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
 --------------------
diff --git a/pythonmod/doc/examples/example5.rst b/pythonmod/doc/examples/example5.rst
new file mode 100644 (file)
index 0000000..058fc33
--- /dev/null
@@ -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
diff --git a/pythonmod/doc/examples/example6.rst b/pythonmod/doc/examples/example6.rst
new file mode 100644 (file)
index 0000000..ce89aab
--- /dev/null
@@ -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
index 6c50225810efbb3b96b3087bd6fa29916e4e9bc2..93d9b8e1e1331691fe682b47d34f993959d4c202 100644 (file)
@@ -1,15 +1,16 @@
 .. _Tutorials:
 
-==============================
-Tutorials
-==============================
+Examples
+========
 
-Here you can find several tutorials which clarify the usage and capabilities of Unbound scriptable interface. 
+Here you can find several tutorials which clarify the usage and capabilities of
+the Unbound scriptable interface. 
 
-`Tutorials`
+Tutorials
+---------
 
 .. toctree::
-       :maxdepth: 2
-       :glob:
+    :maxdepth: 2
+    :glob:
 
-       example*
+    example*
index 991e2b4becf901671e67bb9180ceebda6c2efc33..b8d0b9fa60d14b19f2bc345cc91c57b47de0252e 100644 (file)
@@ -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.
index 45a469fec04ee50ee776dafb077e91ae5e3bdcf0..627d44922477a77a444c00bca166698cd1d8b4d1 100644 (file)
@@ -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.
index 669f36d91ea251649fc6519affcaa6c41e6ce276..3af5d8a48c01a1ba977dfd29380d214ec3049718 100644 (file)
@@ -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
 --------------------
 
diff --git a/pythonmod/examples/edns.py b/pythonmod/examples/edns.py
new file mode 100644 (file)
index 0000000..3fae1c6
--- /dev/null
@@ -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
diff --git a/pythonmod/examples/inplace_callbacks.py b/pythonmod/examples/inplace_callbacks.py
new file mode 100644 (file)
index 0000000..e87614a
--- /dev/null
@@ -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
index 4b20c6ec135c9cdd6533797f955d2a6abea1971e..89d138486606e1691c3d036e9bbbe1ce2f3ad115 100644 (file)
@@ -1,7 +1,6 @@
 /*
  * interface.i: unbound python module
  */
-
 %module unboundmodule
 %{
 /**
    #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);
index 6c5e6392df4be0bae0467205c0a5806c920f0ff9..92e09dcacfaf8449b4df7f17ef638d1a18524214 100644 (file)
@@ -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;
index b108cf9234c141fd9d26282f5957b0fc650b8337..2386882de9c24a5f4d5844f4ee53f40ad8a21bc5 100644 (file)
@@ -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 */
diff --git a/pythonmod/test-edns.conf b/pythonmod/test-edns.conf
new file mode 100644 (file)
index 0000000..440947f
--- /dev/null
@@ -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"
diff --git a/pythonmod/test-inplace_callbacks.py b/pythonmod/test-inplace_callbacks.py
new file mode 100644 (file)
index 0000000..d7081fa
--- /dev/null
@@ -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"
index 5822c810facdfeb7ecd6ae19fdd84ba8defd3c33..0ea74d8568739a3eeabf713af1b25c031a2e78d9 100644 (file)
@@ -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 */
 }
index c202d8cbf8d6f60d1ac19fcaa128d75365062ca2..6db9b3dd97db2ca5bd6e9ac1282cb0b0bc31d847 100644 (file)
@@ -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);
 
index 52de1236654d6ef6b2ddc9af21a0d73de6685433..83a01ede82af17a323b75346329cd50994d32648 100644 (file)
@@ -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);
 }
 
index 612cbb7f9ca0074def47ed9d36b00531d0de0956..7dd62ef19b627e9c2f74c9e423df963f490cef2c 100644 (file)
@@ -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.
index 6aaa7962f5ad92232e17437db50bdda0a150c4d0..eba0195207001604ac19da0f76579f2299fe5dc1 100644 (file)
@@ -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;
index d6a448c0b68bb9f842c3439f081a452b2d45b389..f006b04cb734ddbaad399d2b992921b497d1094a 100644 (file)
@@ -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.
index 6670d87dc255aac549442de612f4331e26ba3b77..eebc0e76ee8ac843eab6ed93127bf275644730ba 100644 (file)
@@ -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 */
index efeef37cd0d2de08322730ed66dd03456a09ca98..e88e8c8d754b2f9367e922e2f7a09c20802b2208 100644 (file)
@@ -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))
index 7e0d075cd0fa0afd66de2eb75c2e2c578f8e4fed..bc6588617f055515dbf1f638a4f2df14c4820e1b 100644 (file)
@@ -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;
index 1d565c1ea280d818f7e194f5f276647617292211..3774054f570008bbf54d8e344fdfe5b1fa558996 100644 (file)
@@ -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;
+               }
+       }
+}
index cae988ff9950f6326a907f56e8de50b9553923bb..594517b2db11494f11d4bbe032b6fe1eb028d3c3 100644 (file)
@@ -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 */
index 1f02eac331d87cb3c084b6a97a9e748df8b4cc3a..2caee7789fde317937f4d2ff59f13304e2381082 100644 (file)
@@ -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)
+{
+       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)
 {
-       (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 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) {
index 729e35573753fa863ece9baf1bf1725b98f2ee4e..cc02161333778d50a42dd12fc96cd6ba11ac750e 100644 (file)
@@ -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).
- */
-int edns_opt_inplace_reply(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 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 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
index 581940d89262ca3b04bb7bc4ccc125a9b6f9ebf0..0d1cae40fd7824c83c501634593d41340f7c771b 100644 (file)
@@ -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;
+}
index aba5f8f92b6d4b17c7f6acd522e64ea230ff58d2..57eec99a8df79c1b1b5cd9653a5708093a3e958b 100644 (file)
@@ -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 */
 /** 
index 09e276c301526f5c7a61882f37f1576c9779b0ea..9c3651dd671a4599bf2b2657258ac1ee9efa2fa5 100644 (file)
@@ -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");
+               }
+       }
+}
index 97dc4c7175667d440ceabc69724ac5a6286a2802..0e27e6a56d718a667559184c64a6aca867877f18 100644 (file)
@@ -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 */
index 362cd2a39adc42959b367d6d7f16f23054fe0e3c..676dcdfe4d8bf21744d9ff151c1d57007dae4202 100644 (file)
@@ -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 */