- DNS64 from Viagenie (BSD Licensed), written by Simon Perrault.

Initial commit of the patch from the FreeBSD base (with its fixes).
  This adds a module (for module-config in unbound.conf) dns64 that
  performs DNS64 processing, see README.DNS64.


git-svn-id: file:///svn/unbound/trunk@3198 be551aaa-1e26-0410-a405-d3ace91eadb9
This commit is contained in:
Wouter Wijngaards 2014-07-31 07:15:33 +00:00
parent dad3e92df7
commit 45022b6add
16 changed files with 2763 additions and 1686 deletions

View file

@ -106,7 +106,7 @@ util/winsock_event.c validator/autotrust.c validator/val_anchor.c \
validator/validator.c validator/val_kcache.c validator/val_kentry.c \ validator/validator.c validator/val_kcache.c validator/val_kentry.c \
validator/val_neg.c validator/val_nsec3.c validator/val_nsec.c \ validator/val_neg.c validator/val_nsec3.c validator/val_nsec.c \
validator/val_secalgo.c validator/val_sigcrypt.c \ validator/val_secalgo.c validator/val_sigcrypt.c \
validator/val_utils.c $(CHECKLOCK_SRC) validator/val_utils.c dns64/dns64.c $(CHECKLOCK_SRC)
COMMON_OBJ_WITHOUT_NETCALL=dns.lo infra.lo rrset.lo dname.lo msgencode.lo \ COMMON_OBJ_WITHOUT_NETCALL=dns.lo infra.lo rrset.lo dname.lo msgencode.lo \
msgparse.lo msgreply.lo packed_rrset.lo iterator.lo iter_delegpt.lo \ msgparse.lo msgreply.lo packed_rrset.lo iterator.lo iter_delegpt.lo \
iter_donotq.lo iter_fwd.lo iter_hints.lo iter_priv.lo iter_resptype.lo \ iter_donotq.lo iter_fwd.lo iter_hints.lo iter_priv.lo iter_resptype.lo \
@ -116,7 +116,8 @@ fptr_wlist.lo locks.lo log.lo mini_event.lo module.lo net_help.lo \
random.lo rbtree.lo regional.lo rtt.lo dnstree.lo lookup3.lo lruhash.lo \ random.lo rbtree.lo regional.lo rtt.lo dnstree.lo lookup3.lo lruhash.lo \
slabhash.lo timehist.lo tube.lo winsock_event.lo autotrust.lo val_anchor.lo \ slabhash.lo timehist.lo tube.lo winsock_event.lo autotrust.lo val_anchor.lo \
validator.lo val_kcache.lo val_kentry.lo val_neg.lo val_nsec3.lo val_nsec.lo \ validator.lo val_kcache.lo val_kentry.lo val_neg.lo val_nsec3.lo val_nsec.lo \
val_secalgo.lo val_sigcrypt.lo val_utils.lo $(PYTHONMOD_OBJ) $(CHECKLOCK_OBJ) val_secalgo.lo val_sigcrypt.lo val_utils.lo dns64.lo \
$(PYTHONMOD_OBJ) $(CHECKLOCK_OBJ)
COMMON_OBJ=$(COMMON_OBJ_WITHOUT_NETCALL) netevent.lo listen_dnsport.lo \ COMMON_OBJ=$(COMMON_OBJ_WITHOUT_NETCALL) netevent.lo listen_dnsport.lo \
outside_network.lo outside_network.lo
# set to $COMMON_OBJ or to "" if --enableallsymbols # set to $COMMON_OBJ or to "" if --enableallsymbols
@ -1179,3 +1180,4 @@ arc4random.lo arc4random.o: $(srcdir)/compat/arc4random.c $(srcdir)/compat/chach
arc4random_uniform.lo arc4random_uniform.o: $(srcdir)/compat/arc4random_uniform.c $(srcdir)/compat/chacha_private.h arc4random_uniform.lo arc4random_uniform.o: $(srcdir)/compat/arc4random_uniform.c $(srcdir)/compat/chacha_private.h
arc4_lock.lo arc4_lock.o: $(srcdir)/compat/arc4_lock.c arc4_lock.lo arc4_lock.o: $(srcdir)/compat/arc4_lock.c
sha512.lo sha512.o: $(srcdir)/compat/sha512.c sha512.lo sha512.o: $(srcdir)/compat/sha512.c
dns64.lo: $(srcdir)/dns64/dns64.c config.h $(srcdir)/util/module.h $(srcdir)/services/cache/dns.h $(srcdir)/services/cache/rrset.h $(srcdir)/util/config_file.h $(srcdir)/util/data/msgreply.h $(srcdir)/util/fptr_wlist.h $(srcdir)/util/net_help.h $(srcdir)/util/regional.h

864
dns64/dns64.c Normal file
View file

@ -0,0 +1,864 @@
/*
* iterator/iterator.h - DNS64 module
*
* Copyright (c) 2009, Viagénie. All rights reserved.
*
* 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 Viagénie 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.
*/
/**
* \file
*
* This file contains a module that performs DNS64 query processing.
*/
#include <config.h>
#include "dns64/dns64.h"
#include "services/cache/dns.h"
#include "services/cache/rrset.h"
#include "util/config_file.h"
#include "util/data/msgreply.h"
#include "util/fptr_wlist.h"
#include "util/net_help.h"
#include "util/regional.h"
/******************************************************************************
* *
* STATIC CONSTANTS *
* *
******************************************************************************/
/**
* This is the default DNS64 prefix that is used whent he dns64 module is listed
* in module-config but when the dns64-prefix variable is not present.
*/
static const char DEFAULT_DNS64_PREFIX[] = "64:ff9b::/96";
/**
* Maximum length of a domain name in a PTR query in the .in-addr.arpa tree.
*/
static const int MAX_PTR_QNAME_IPV4 = 30;
/**
* Per-query module-specific state. This is usually a dynamically-allocated
* structure, but in our case we only need to store one variable describing the
* state the query is in. So we repurpose the minfo pointer by storing an
* integer in there.
*/
enum dns64_qstate {
DNS64_INTERNAL_QUERY, /**< Internally-generated query, no DNS64
processing. */
DNS64_NEW_QUERY, /**< Query for which we're the first module in
line. */
DNS64_SUBQUERY_FINISHED /**< Query for which we generated a sub-query, and
for which this sub-query is finished. */
};
/******************************************************************************
* *
* STRUCTURES *
* *
******************************************************************************/
/**
* This structure contains module configuration information. One instance of
* this structure exists per instance of the module. Normally there is only one
* instance of the module.
*/
struct dns64_env {
/**
* DNS64 prefix address. We're using a full sockaddr instead of just an
* in6_addr because we can reuse Unbound's generic string parsing functions.
* It will always contain a sockaddr_in6, and only the sin6_addr member will
* ever be used.
*/
struct sockaddr_storage prefix_addr;
/**
* This is always sizeof(sockaddr_in6).
*/
socklen_t prefix_addrlen;
/**
* This is the CIDR length of the prefix. It needs to be between 0 and 96.
*/
int prefix_net;
};
/******************************************************************************
* *
* UTILITY FUNCTIONS *
* *
******************************************************************************/
/**
* Generic macro for swapping two variables.
*
* \param t Type of the variables. (e.g. int)
* \param a First variable.
* \param b Second variable.
*
* \warning Do not attempt something foolish such as swap(int,a++,b++)!
*/
#define swap(t,a,b) do {t x = a; a = b; b = x;} while(0)
/**
* Reverses a string.
*
* \param begin Points to the first character of the string.
* \param end Points one past the last character of the string.
*/
static void
reverse(char* begin, char* end)
{
while ( begin < --end ) {
swap(char, *begin, *end);
++begin;
}
}
/**
* Convert an unsigned integer to a string. The point of this function is that
* of being faster than sprintf().
*
* \param n The number to be converted.
* \param s The result will be written here. Must be large enough, be careful!
*
* \return The number of characters written.
*/
static int
uitoa(unsigned n, char* s)
{
char* ss = s;
do {
*ss++ = '0' + n % 10;
} while (n /= 10);
reverse(s, ss);
return ss - s;
}
/**
* Extract an IPv4 address embedded in the IPv6 address \a ipv6 at offset \a
* offset (in bits). Note that bits are not necessarily aligned on bytes so we
* need to be careful.
*
* \param ipv6 IPv6 address represented as a 128-bit array in big-endian
* order.
* \param offset Index of the MSB of the IPv4 address embedded in the IPv6
* address.
*/
static uint32_t
extract_ipv4(const uint8_t ipv6[16], const int offset)
{
uint32_t ipv4 = (uint32_t)ipv6[offset/8+0] << (24 + (offset%8))
| (uint32_t)ipv6[offset/8+1] << (16 + (offset%8))
| (uint32_t)ipv6[offset/8+2] << ( 8 + (offset%8))
| (uint32_t)ipv6[offset/8+3] << ( 0 + (offset%8));
if (offset/8+4 < 16)
ipv4 |= (uint32_t)ipv6[offset/8+4] >> (8 - offset%8);
return ipv4;
}
/**
* Builds the PTR query name corresponding to an IPv4 address. For example,
* given the number 3,464,175,361, this will build the string
* "\03206\03123\0231\011\07in-addr\04arpa".
*
* \param ipv4 IPv4 address represented as an unsigned 32-bit number.
* \param ptr The result will be written here. Must be large enough, be
* careful!
*
* \return The number of characters written.
*/
static int
ipv4_to_ptr(uint32_t ipv4, char ptr[MAX_PTR_QNAME_IPV4])
{
static const char IPV4_PTR_SUFFIX[] = "\07in-addr\04arpa";
int i;
char* c = ptr;
for (i = 0; i < 4; ++i) {
*c = uitoa(ipv4 % 256, c + 1);
c += *c + 1;
ipv4 /= 256;
}
strcpy(c, IPV4_PTR_SUFFIX);
return c + sizeof(IPV4_PTR_SUFFIX) - ptr;
}
/**
* Converts an IPv6-related domain name string from a PTR query into an IPv6
* address represented as a 128-bit array.
*
* \param ptr The domain name. (e.g. "\011[...]\010\012\016\012\03ip6\04arpa")
* \param ipv6 The result will be written here, in network byte order.
*
* \return 1 on success, 0 on failure.
*/
static int
ptr_to_ipv6(const char* ptr, uint8_t ipv6[16])
{
int i;
for (i = 0; i < 64; i++) {
int x;
if (ptr[i++] != 1)
return 0;
if (ptr[i] >= '0' && ptr[i] <= '9') {
x = ptr[i] - '0';
} else if (ptr[i] >= 'a' && ptr[i] <= 'f') {
x = ptr[i] - 'a' + 10;
} else if (ptr[i] >= 'A' && ptr[i] <= 'F') {
x = ptr[i] - 'A' + 10;
} else {
return 0;
}
ipv6[15-i/4] |= x << (2 * ((i-1) % 4));
}
return 1;
}
/**
* Synthesize an IPv6 address based on an IPv4 address and the DNS64 prefix.
*
* \param prefix_addr DNS64 prefix address.
* \param prefix_net CIDR length of the DNS64 prefix. Must be between 0 and 96.
* \param a IPv4 address.
* \param aaaa IPv6 address. The result will be written here.
*/
static void
synthesize_aaaa(const uint8_t prefix_addr[16], int prefix_net,
const uint8_t a[4], uint8_t aaaa[16])
{
memcpy(aaaa, prefix_addr, 16);
aaaa[prefix_net/8+0] |= a[0] >> (0+prefix_net%8);
aaaa[prefix_net/8+1] |= a[0] << (8-prefix_net%8);
aaaa[prefix_net/8+1] |= a[1] >> (0+prefix_net%8);
aaaa[prefix_net/8+2] |= a[1] << (8-prefix_net%8);
aaaa[prefix_net/8+2] |= a[2] >> (0+prefix_net%8);
aaaa[prefix_net/8+3] |= a[2] << (8-prefix_net%8);
aaaa[prefix_net/8+3] |= a[3] >> (0+prefix_net%8);
if (prefix_net/8/4 < 16) /* <-- my beautiful symmetry is destroyed! */
aaaa[prefix_net/8+4] |= a[3] << (8-prefix_net%8);
}
/******************************************************************************
* *
* DNS64 MODULE FUNCTIONS *
* *
******************************************************************************/
/**
* This function applies the configuration found in the parsed configuration
* file \a cfg to this instance of the dns64 module. Currently only the DNS64
* prefix (a.k.a. Pref64) is configurable.
*
* \param dns64_env Module-specific global parameters.
* \param cfg Parsed configuration file.
*/
static int
dns64_apply_cfg(struct dns64_env* dns64_env, struct config_file* cfg)
{
verbose(VERB_ALGO, "dns64-prefix: %s", cfg->dns64_prefix);
if (!netblockstrtoaddr(cfg->dns64_prefix ? cfg->dns64_prefix :
DEFAULT_DNS64_PREFIX, 0, &dns64_env->prefix_addr,
&dns64_env->prefix_addrlen, &dns64_env->prefix_net)) {
log_err("cannot parse dns64-prefix netblock: %s", cfg->dns64_prefix);
return 0;
}
if (!addr_is_ip6(&dns64_env->prefix_addr, dns64_env->prefix_addrlen)) {
log_err("dns64_prefix is not IPv6: %s", cfg->dns64_prefix);
return 0;
}
if (dns64_env->prefix_net < 0 || dns64_env->prefix_net > 96) {
log_err("dns64-prefix length it not between 0 and 96: %s",
cfg->dns64_prefix);
return 0;
}
return 1;
}
/**
* Initializes this instance of the dns64 module.
*
* \param env Global state of all module instances.
* \param id This instance's ID number.
*/
int
dns64_init(struct module_env* env, int id)
{
struct dns64_env* dns64_env =
(struct dns64_env*)calloc(1, sizeof(struct dns64_env));
if (!dns64_env) {
log_err("malloc failure");
return 0;
}
env->modinfo[id] = (void*)dns64_env;
if (!dns64_apply_cfg(dns64_env, env->cfg)) {
log_err("dns64: could not apply configuration settings.");
return 0;
}
return 1;
}
/**
* Deinitializes this instance of the dns64 module.
*
* \param env Global state of all module instances.
* \param id This instance's ID number.
*/
void
dns64_deinit(struct module_env* env, int id)
{
if (!env)
return;
free(env->modinfo[id]);
env->modinfo[id] = NULL;
}
/**
* Handle PTR queries for IPv6 addresses. If the address belongs to the DNS64
* prefix, we must do a PTR query for the corresponding IPv4 address instead.
*
* \param qstate Query state structure.
* \param id This module instance's ID number.
*
* \return The new state of the query.
*/
static enum module_ext_state
handle_ipv6_ptr(struct module_qstate* qstate, int id)
{
struct dns64_env* dns64_env = (struct dns64_env*)qstate->env->modinfo[id];
struct module_qstate* subq = NULL;
struct query_info qinfo;
struct sockaddr_in6 sin6;
/* Convert the PTR query string to an IPv6 address. */
memset(&sin6, 0, sizeof(sin6));
sin6.sin6_family = AF_INET6;
if (!ptr_to_ipv6((char*)qstate->qinfo.qname, sin6.sin6_addr.s6_addr))
return module_wait_module; /* Let other module handle this. */
/*
* If this IPv6 address is not part of our DNS64 prefix, then we don't need
* to do anything. Let another module handle the query.
*/
if (addr_in_common((struct sockaddr_storage*)&sin6, 128,
&dns64_env->prefix_addr, dns64_env->prefix_net,
sizeof(sin6)) != dns64_env->prefix_net)
return module_wait_module;
verbose(VERB_ALGO, "dns64: rewrite PTR record");
/*
* Create a new PTR query info for the domain name corresponding to the IPv4
* address corresponding to the IPv6 address corresponding to the original
* PTR query domain name.
*/
qinfo = qstate->qinfo;
if (!(qinfo.qname = regional_alloc(qstate->region, MAX_PTR_QNAME_IPV4)))
return module_error;
qinfo.qname_len = ipv4_to_ptr(extract_ipv4(sin6.sin6_addr.s6_addr,
dns64_env->prefix_net), (char*)qinfo.qname);
/* Create the new sub-query. */
fptr_ok(fptr_whitelist_modenv_attach_sub(qstate->env->attach_sub));
if(!(*qstate->env->attach_sub)(qstate, &qinfo, qstate->query_flags, 0,
&subq))
return module_error;
if (subq) {
subq->curmod = id;
subq->ext_state[id] = module_state_initial;
subq->minfo[id] = NULL;
}
return module_wait_subquery;
}
/** allocate (special) rrset keys, return 0 on error */
static int
repinfo_alloc_rrset_keys(struct reply_info* rep,
struct regional* region)
{
size_t i;
for(i=0; i<rep->rrset_count; i++) {
if(region) {
rep->rrsets[i] = (struct ub_packed_rrset_key*)
regional_alloc(region,
sizeof(struct ub_packed_rrset_key));
if(rep->rrsets[i]) {
memset(rep->rrsets[i], 0,
sizeof(struct ub_packed_rrset_key));
rep->rrsets[i]->entry.key = rep->rrsets[i];
}
}
else return 0;/* rep->rrsets[i] = alloc_special_obtain(alloc);*/
if(!rep->rrsets[i])
return 0;
rep->rrsets[i]->entry.data = NULL;
}
return 1;
}
static enum module_ext_state
generate_type_A_query(struct module_qstate* qstate, int id)
{
struct module_qstate* subq = NULL;
struct query_info qinfo;
verbose(VERB_ALGO, "dns64: query A record");
/* Create a new query info. */
qinfo = qstate->qinfo;
qinfo.qtype = LDNS_RR_TYPE_A;
/* Start the sub-query. */
fptr_ok(fptr_whitelist_modenv_attach_sub(qstate->env->attach_sub));
if(!(*qstate->env->attach_sub)(qstate, &qinfo, qstate->query_flags, 0,
&subq))
{
verbose(VERB_ALGO, "dns64: sub-query creation failed");
return module_error;
}
if (subq) {
subq->curmod = id;
subq->ext_state[id] = module_state_initial;
subq->minfo[id] = NULL;
}
return module_wait_subquery;
}
/**
* Handles the "pass" event for a query. This event is received when a new query
* is received by this module. The query may have been generated internally by
* another module, in which case we don't want to do any special processing
* (this is an interesting discussion topic), or it may be brand new, e.g.
* received over a socket, in which case we do want to apply DNS64 processing.
*
* \param qstate A structure representing the state of the query that has just
* received the "pass" event.
* \param id This module's instance ID.
*
* \return The new state of the query.
*/
static enum module_ext_state
handle_event_pass(struct module_qstate* qstate, int id)
{
if ((uintptr_t)qstate->minfo[id] == DNS64_NEW_QUERY
&& qstate->qinfo.qtype == LDNS_RR_TYPE_PTR
&& qstate->qinfo.qname_len == 74
&& !strcmp((char*)&qstate->qinfo.qname[64], "\03ip6\04arpa"))
/* Handle PTR queries for IPv6 addresses. */
return handle_ipv6_ptr(qstate, id);
if (qstate->env->cfg->dns64_synthall &&
(uintptr_t)qstate->minfo[id] == DNS64_NEW_QUERY
&& qstate->qinfo.qtype == LDNS_RR_TYPE_AAAA)
return generate_type_A_query(qstate, id);
/* We are finished when our sub-query is finished. */
if ((uintptr_t)qstate->minfo[id] == DNS64_SUBQUERY_FINISHED)
return module_finished;
/* Otherwise, pass request to next module. */
verbose(VERB_ALGO, "dns64: pass to next module");
return module_wait_module;
}
/**
* Handles the "done" event for a query. We need to analyze the response and
* maybe issue a new sub-query for the A record.
*
* \param qstate A structure representing the state of the query that has just
* received the "pass" event.
* \param id This module's instance ID.
*
* \return The new state of the query.
*/
static enum module_ext_state
handle_event_moddone(struct module_qstate* qstate, int id)
{
/*
* In many cases we have nothing special to do. From most to least common:
*
* - An internal query.
* - A query for a record type other than AAAA.
* - An AAAA query for which an error was returned.
* - A successful AAAA query with an answer.
*/
if (qstate->minfo[id] == DNS64_INTERNAL_QUERY
|| qstate->qinfo.qtype != LDNS_RR_TYPE_AAAA
|| qstate->return_rcode != LDNS_RCODE_NOERROR
|| (qstate->return_msg &&
qstate->return_msg->rep &&
reply_find_answer_rrset(&qstate->qinfo,
qstate->return_msg->rep)))
return module_finished;
return generate_type_A_query(qstate, id);
}
/**
* This is the module's main() function. It gets called each time a query
* receives an event which we may need to handle. We respond by updating the
* state of the query.
*
* \param qstate Structure containing the state of the query.
* \param event Event that has just been received.
* \param id This module's instance ID.
* \param outbound State of a DNS query on an authoritative server. We never do
* our own queries ourselves (other modules do it for us), so
* this is unused.
*/
void
dns64_operate(struct module_qstate* qstate, enum module_ev event, int id,
struct outbound_entry* ATTR_UNUSED(outbound))
{
verbose(VERB_QUERY, "dns64[module %d] operate: extstate:%s event:%s",
id, strextstate(qstate->ext_state[id]),
strmodulevent(event));
log_query_info(VERB_QUERY, "dns64 operate: query", &qstate->qinfo);
switch(event) {
case module_event_new:
/* Tag this query as being new and fall through. */
qstate->minfo[id] = (void*)DNS64_NEW_QUERY;
case module_event_pass:
qstate->ext_state[id] = handle_event_pass(qstate, id);
break;
case module_event_moddone:
qstate->ext_state[id] = handle_event_moddone(qstate, id);
break;
default:
qstate->ext_state[id] = module_finished;
break;
}
}
static void
dns64_synth_aaaa_data(const struct ub_packed_rrset_key* fk,
const struct packed_rrset_data* fd,
struct ub_packed_rrset_key *dk,
struct packed_rrset_data **dd_out, struct regional *region,
struct dns64_env* dns64_env )
{
struct packed_rrset_data *dd;
size_t i;
/*
* Create synthesized AAAA RR set data. We need to allocated extra memory
* for the RRs themselves. Each RR has a length, TTL, pointer to wireformat
* data, 2 bytes of data length, and 16 bytes of IPv6 address.
*/
if (!(dd = *dd_out = regional_alloc(region,
sizeof(struct packed_rrset_data)
+ fd->count * (sizeof(size_t) + sizeof(uint32_t) +
sizeof(uint8_t*) + 2 + 16)))) {
log_err("out of memory");
return;
}
/* Copy attributes from A RR set. */
dd->ttl = fd->ttl;
dd->count = fd->count;
dd->rrsig_count = 0;
dd->trust = fd->trust;
dd->security = fd->security;
/*
* Synthesize AAAA records. Adjust pointers in structure.
*/
dd->rr_len =
(size_t*)((uint8_t*)dd + sizeof(struct packed_rrset_data));
dd->rr_data = (uint8_t**)&dd->rr_len[dd->count];
dd->rr_ttl = (uint32_t*)&dd->rr_data[dd->count];
for(i = 0; i < fd->count; ++i) {
if (fd->rr_len[i] != 6 || fd->rr_data[i][0] != 0
|| fd->rr_data[i][1] != 4)
return;
dd->rr_len[i] = 18;
dd->rr_data[i] =
(uint8_t*)&dd->rr_ttl[dd->count] + 18*i;
dd->rr_data[i][0] = 0;
dd->rr_data[i][1] = 16;
synthesize_aaaa(
((struct sockaddr_in6*)&dns64_env->prefix_addr)->sin6_addr.s6_addr,
dns64_env->prefix_net, &fd->rr_data[i][2],
&dd->rr_data[i][2] );
dd->rr_ttl[i] = fd->rr_ttl[i];
}
/*
* Create synthesized AAAA RR set key. This is mostly just bookkeeping,
* nothing interesting here.
*/
if(!dk) {
log_err("no key");
return;
}
dk->rk.dname = (uint8_t*)regional_alloc_init(region,
fk->rk.dname, fk->rk.dname_len);
if(!dk->rk.dname) {
log_err("out of memory");
return;
}
dk->rk.type = htons(LDNS_RR_TYPE_AAAA);
memset(&dk->entry, 0, sizeof(dk->entry));
dk->entry.key = dk;
dk->entry.hash = rrset_key_hash(&dk->rk);
dk->entry.data = dd;
}
/**
* Synthesize an AAAA RR set from an A sub-query's answer and add it to the
* original empty response.
*
* \param id This module's instance ID.
* \param answer The answer RR set located in the sub-query's response.
* \param super Original AAAA query.
* \param qstate A query.
*/
static void
dns64_adjust_a(int id, struct module_qstate* super, struct module_qstate* qstate)
{
struct dns64_env* dns64_env = (struct dns64_env*)super->env->modinfo[id];
struct reply_info *rep, *cp;
size_t i, s;
struct packed_rrset_data* fd, *dd;
struct ub_packed_rrset_key* fk, *dk;
verbose(VERB_ALGO, "converting A answers to AAAA answers");
log_assert(super->region);
log_assert(qstate->return_msg);
log_assert(qstate->return_msg->rep);
log_assert(qstate->region);
/* If dns64-synthall is enabled, return_msg is not initialized */
if(!super->return_msg) {
super->return_msg = (struct dns_msg*)regional_alloc(
super->region, sizeof(struct dns_msg));
if(!super->return_msg)
return;
memset(super->return_msg, 0, sizeof(*super->return_msg));
super->return_msg->qinfo = super->qinfo;
}
rep = qstate->return_msg->rep;
/*
* Build the actual reply.
*/
cp = construct_reply_info_base(qstate->region, rep->flags, rep->qdcount,
rep->ttl, rep->prefetch_ttl, rep->an_numrrsets, rep->ns_numrrsets,
rep->ar_numrrsets, rep->rrset_count, rep->security);
if(!cp)
return;
/* allocate ub_key structures special or not */
if(!repinfo_alloc_rrset_keys(cp, qstate->region)) {
return;
}
/* copy everything and replace A by AAAA */
for(i=0; i<cp->rrset_count; i++) {
fk = rep->rrsets[i];
dk = cp->rrsets[i];
fd = (struct packed_rrset_data*)fk->entry.data;
dk->entry.hash = fk->entry.hash;
dk->rk = fk->rk;
dk->id = fk->id;
if(i<rep->an_numrrsets && fk->rk.type == htons(LDNS_RR_TYPE_A)) {
dns64_synth_aaaa_data(fk, fd, dk, &dd, super->region, dns64_env);
/* Delete negative AAAA record from cache stored by
* the iterator module */
rrset_cache_remove(super->env->rrset_cache, dk->rk.dname,
dk->rk.dname_len, LDNS_RR_TYPE_AAAA,
LDNS_RR_CLASS_IN, 0);
} else {
dk->rk.dname = (uint8_t*)regional_alloc_init(qstate->region,
fk->rk.dname, fk->rk.dname_len);
if(!dk->rk.dname)
return;
s = packed_rrset_sizeof(fd);
dd = (struct packed_rrset_data*)regional_alloc_init(
qstate->region, fd, s);
if(!dd)
return;
}
packed_rrset_ptr_fixup(dd);
dk->entry.data = (void*)dd;
}
/* Commit changes. */
super->return_msg->rep = cp;
}
/**
* Generate a response for the original IPv6 PTR query based on an IPv4 PTR
* sub-query's response.
*
* \param qstate IPv4 PTR sub-query.
* \param super Original IPv6 PTR query.
*/
static void
dns64_adjust_ptr(struct module_qstate* qstate, struct module_qstate* super)
{
struct ub_packed_rrset_key* answer;
verbose(VERB_ALGO, "adjusting PTR reply");
/* Copy the sub-query's reply to the parent. */
if (!(super->return_msg = (struct dns_msg*)regional_alloc(super->region,
sizeof(struct dns_msg))))
return;
super->return_msg->qinfo = super->qinfo;
super->return_msg->rep = reply_info_copy(qstate->return_msg->rep, NULL,
super->region);
/*
* Adjust the domain name of the answer RR set so that it matches the
* initial query's domain name.
*/
answer = reply_find_answer_rrset(&qstate->qinfo, super->return_msg->rep);
log_assert(answer);
answer->rk.dname = super->qinfo.qname;
answer->rk.dname_len = super->qinfo.qname_len;
}
/**
* This function is called when a sub-query finishes to inform the parent query.
*
* We issue two kinds of sub-queries: PTR and A.
*
* \param qstate State of the sub-query.
* \param id This module's instance ID.
* \param super State of the super-query.
*/
void
dns64_inform_super(struct module_qstate* qstate, int id,
struct module_qstate* super)
{
log_query_info(VERB_ALGO, "dns64: inform_super, sub is",
&qstate->qinfo);
log_query_info(VERB_ALGO, "super is", &super->qinfo);
/*
* Signal that the sub-query is finished, no matter whether we are
* successful or not. This lets the state machine terminate.
*/
super->minfo[id] = (void*)DNS64_SUBQUERY_FINISHED;
/* If there is no successful answer, we're done. */
if (qstate->return_rcode != LDNS_RCODE_NOERROR
|| !qstate->return_msg
|| !qstate->return_msg->rep
|| !reply_find_answer_rrset(&qstate->qinfo,
qstate->return_msg->rep))
return;
/* Generate a response suitable for the original query. */
if (qstate->qinfo.qtype == LDNS_RR_TYPE_A) {
dns64_adjust_a(id, super, qstate);
} else {
log_assert(qstate->qinfo.qtype == LDNS_RR_TYPE_PTR);
dns64_adjust_ptr(qstate, super);
}
/* Store the generated response in cache. */
if (!dns_cache_store(super->env, &super->qinfo, super->return_msg->rep,
0, 0, 0, NULL))
log_err("out of memory");
}
/**
* Clear module-specific data from query state. Since we do not allocate memory,
* it's just a matter of setting a pointer to NULL.
*
* \param qstate Query state.
* \param id This module's instance ID.
*/
void
dns64_clear(struct module_qstate* qstate, int id)
{
qstate->minfo[id] = NULL;
}
/**
* Returns the amount of global memory that this module uses, not including
* per-query data.
*
* \param env Module environment.
* \param id This module's instance ID.
*/
size_t
dns64_get_mem(struct module_env* env, int id)
{
struct dns64_env* dns64_env = (struct dns64_env*)env->modinfo[id];
if (!dns64_env)
return 0;
return sizeof(*dns64_env);
}
/**
* The dns64 function block.
*/
static struct module_func_block dns64_block = {
"dns64",
&dns64_init, &dns64_deinit, &dns64_operate, &dns64_inform_super,
&dns64_clear, &dns64_get_mem
};
/**
* Function for returning the above function block.
*/
struct module_func_block *
dns64_get_funcblock()
{
return &dns64_block;
}

71
dns64/dns64.h Normal file
View file

@ -0,0 +1,71 @@
/*
* dns64/dns64.h - DNS64 module
*
* Copyright (c) 2009, Viagénie. All rights reserved.
*
* 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 NLNET LABS 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.
*/
/**
* \file
*
* This file contains a module that performs DNS64 query processing.
*/
#ifndef DNS64_DNS64_H
#define DNS64_DNS64_H
#include "util/module.h"
/**
* Get the dns64 function block.
* @return: function block with function pointers to dns64 methods.
*/
struct module_func_block *dns64_get_funcblock(void);
/** dns64 init */
int dns64_init(struct module_env* env, int id);
/** dns64 deinit */
void dns64_deinit(struct module_env* env, int id);
/** dns64 operate on a query */
void dns64_operate(struct module_qstate* qstate, enum module_ev event, int id,
struct outbound_entry* outbound);
void dns64_inform_super(struct module_qstate* qstate, int id,
struct module_qstate* super);
/** dns64 cleanup query state */
void dns64_clear(struct module_qstate* qstate, int id);
/** dns64 alloc size routine */
size_t dns64_get_mem(struct module_env* env, int id);
#endif /* DNS64_DNS64_H */

View file

@ -1,3 +1,9 @@
31 July 2014: Wouter
- DNS64 from Viagenie (BSD Licensed), written by Simon Perrault.
Initial commit of the patch from the FreeBSD base (with its fixes).
This adds a module (for module-config in unbound.conf) dns64 that
performs DNS64 processing, see README.DNS64.
29 July 2014: Wouter 29 July 2014: Wouter
- Patch from Dag-Erling Smorgrav that implements feature, unbound -dd - Patch from Dag-Erling Smorgrav that implements feature, unbound -dd
does not fork in the background and also logs to stderr. does not fork in the background and also logs to stderr.

26
doc/README.DNS64 Normal file
View file

@ -0,0 +1,26 @@
To enable DNS64 functionality in Unbound, two directives in unbound.conf must
be edited:
1. The "module-config" directive must start with "dns64". For example:
module-config: "dns64 validator iterator"
If you're not using DNSSEC then you may remove "validator".
2. The "dns64-prefix" directive indicates your DNS64 prefix. For example:
dns64-prefix: 64:FF9B::/96
The prefix must be a /96 or shorter.
To test that things are working right, perform a query against Unbound for a
domain name for which no AAAA record exists. You should see a AAAA record in
the answer section. The corresponding IPv6 address will be inside the DNS64
prefix. For example:
$ unbound -c unbound.conf
$ dig @localhost jazz-v4.viagenie.ca aaaa
[...]
;; ANSWER SECTION:
jazz-v4.viagenie.ca. 86400 IN AAAA 64:ff9b::ce7b:1f02

View file

@ -331,8 +331,8 @@ server:
# minimal-responses: no # minimal-responses: no
# module configuration of the server. A string with identifiers # module configuration of the server. A string with identifiers
# separated by spaces. "iterator" or "validator iterator" # separated by spaces. Syntax: [dns64] [validator] iterator
# module-config: "validator iterator" # module-config: "dns64 iterator"
# File with trusted keys, kept uptodate using RFC5011 probes, # File with trusted keys, kept uptodate using RFC5011 probes,
# initial file like trust-anchor-file, then it stores metadata. # initial file like trust-anchor-file, then it stores metadata.
@ -534,6 +534,9 @@ server:
# Default is no. Can be turned on and off with unbound-control. # Default is no. Can be turned on and off with unbound-control.
# ssl-upstream: no # ssl-upstream: no
# DNS64 prefix. Must be specified when DNS64 is in use.
# dns64-prefix: 64:ff9b::0/96
# Python config section. To enable: # Python config section. To enable:
# o use --with-pythonmodule to configure before compiling. # o use --with-pythonmodule to configure before compiling.
# o list python in the module-config string (above) to enable. # o list python in the module-config string (above) to enable.

View file

@ -43,6 +43,7 @@
#include "services/modstack.h" #include "services/modstack.h"
#include "util/module.h" #include "util/module.h"
#include "util/fptr_wlist.h" #include "util/fptr_wlist.h"
#include "dns64/dns64.h"
#include "iterator/iterator.h" #include "iterator/iterator.h"
#include "validator/validator.h" #include "validator/validator.h"
@ -116,6 +117,7 @@ module_list_avail(void)
{ {
/* these are the modules available */ /* these are the modules available */
static const char* names[] = { static const char* names[] = {
"dns64",
#ifdef WITH_PYTHONMODULE #ifdef WITH_PYTHONMODULE
"python", "python",
#endif #endif
@ -133,6 +135,7 @@ static fbgetfunctype*
module_funcs_avail(void) module_funcs_avail(void)
{ {
static struct module_func_block* (*fb[])(void) = { static struct module_func_block* (*fb[])(void) = {
&dns64_get_funcblock,
#ifdef WITH_PYTHONMODULE #ifdef WITH_PYTHONMODULE
&pythonmod_get_funcblock, &pythonmod_get_funcblock,
#endif #endif

View file

@ -305,6 +305,12 @@ struct config_file {
/* maximum UDP response size */ /* maximum UDP response size */
size_t max_udp_size; size_t max_udp_size;
/* DNS64 prefix */
char* dns64_prefix;
/* Synthetize all AAAA record despite the presence of an authoritative one */
int dns64_synthall;
}; };
/** /**

File diff suppressed because it is too large Load diff

View file

@ -8,6 +8,8 @@
* *
*/ */
#include "config.h"
#include <ctype.h> #include <ctype.h>
#include <string.h> #include <string.h>
#include <strings.h> #include <strings.h>
@ -326,6 +328,8 @@ domain-insecure{COLON} { YDVAR(1, VAR_DOMAIN_INSECURE) }
minimal-responses{COLON} { YDVAR(1, VAR_MINIMAL_RESPONSES) } minimal-responses{COLON} { YDVAR(1, VAR_MINIMAL_RESPONSES) }
rrset-roundrobin{COLON} { YDVAR(1, VAR_RRSET_ROUNDROBIN) } rrset-roundrobin{COLON} { YDVAR(1, VAR_RRSET_ROUNDROBIN) }
max-udp-size{COLON} { YDVAR(1, VAR_MAX_UDP_SIZE) } max-udp-size{COLON} { YDVAR(1, VAR_MAX_UDP_SIZE) }
dns64-prefix{COLON} { YDVAR(1, VAR_DNS64_PREFIX) }
dns64-synthall{COLON} { YDVAR(1, VAR_DNS64_SYNTHALL) }
<INITIAL,val>{NEWLINE} { LEXOUT(("NL\n")); cfg_parser->line++; } <INITIAL,val>{NEWLINE} { LEXOUT(("NL\n")); cfg_parser->line++; }
/* Quoted strings. Strip leading and ending quotes */ /* Quoted strings. Strip leading and ending quotes */

File diff suppressed because it is too large Load diff

View file

@ -176,7 +176,9 @@ extern int yydebug;
VAR_RRSET_ROUNDROBIN = 385, VAR_RRSET_ROUNDROBIN = 385,
VAR_MAX_UDP_SIZE = 386, VAR_MAX_UDP_SIZE = 386,
VAR_DELAY_CLOSE = 387, VAR_DELAY_CLOSE = 387,
VAR_UNBLOCK_LAN_ZONES = 388 VAR_UNBLOCK_LAN_ZONES = 388,
VAR_DNS64_PREFIX = 389,
VAR_DNS64_SYNTHALL = 390
}; };
#endif #endif
/* Tokens. */ /* Tokens. */
@ -311,6 +313,8 @@ extern int yydebug;
#define VAR_MAX_UDP_SIZE 386 #define VAR_MAX_UDP_SIZE 386
#define VAR_DELAY_CLOSE 387 #define VAR_DELAY_CLOSE 387
#define VAR_UNBLOCK_LAN_ZONES 388 #define VAR_UNBLOCK_LAN_ZONES 388
#define VAR_DNS64_PREFIX 389
#define VAR_DNS64_SYNTHALL 390
@ -324,7 +328,7 @@ typedef union YYSTYPE
/* Line 2049 of yacc.c */ /* Line 2049 of yacc.c */
#line 328 "util/configparser.h" #line 332 "util/configparser.h"
} YYSTYPE; } YYSTYPE;
# define YYSTYPE_IS_TRIVIAL 1 # define YYSTYPE_IS_TRIVIAL 1
# define yystype YYSTYPE /* obsolescent; will be withdrawn */ # define yystype YYSTYPE /* obsolescent; will be withdrawn */

View file

@ -106,6 +106,7 @@ extern struct config_parser_state* cfg_parser;
%token VAR_SSL_SERVICE_KEY VAR_SSL_SERVICE_PEM VAR_SSL_PORT VAR_FORWARD_FIRST %token VAR_SSL_SERVICE_KEY VAR_SSL_SERVICE_PEM VAR_SSL_PORT VAR_FORWARD_FIRST
%token VAR_STUB_FIRST VAR_MINIMAL_RESPONSES VAR_RRSET_ROUNDROBIN %token VAR_STUB_FIRST VAR_MINIMAL_RESPONSES VAR_RRSET_ROUNDROBIN
%token VAR_MAX_UDP_SIZE VAR_DELAY_CLOSE VAR_UNBLOCK_LAN_ZONES %token VAR_MAX_UDP_SIZE VAR_DELAY_CLOSE VAR_UNBLOCK_LAN_ZONES
%token VAR_DNS64_PREFIX VAR_DNS64_SYNTHALL
%% %%
toplevelvars: /* empty */ | toplevelvars toplevelvar ; toplevelvars: /* empty */ | toplevelvars toplevelvar ;
@ -163,7 +164,8 @@ content_server: server_num_threads | server_verbosity | server_port |
server_log_queries | server_tcp_upstream | server_ssl_upstream | server_log_queries | server_tcp_upstream | server_ssl_upstream |
server_ssl_service_key | server_ssl_service_pem | server_ssl_port | server_ssl_service_key | server_ssl_service_pem | server_ssl_port |
server_minimal_responses | server_rrset_roundrobin | server_max_udp_size | server_minimal_responses | server_rrset_roundrobin | server_max_udp_size |
server_so_reuseport | server_delay_close | server_unblock_lan_zones server_so_reuseport | server_delay_close | server_unblock_lan_zones |
server_dns64_prefix | server_dns64_synthall
; ;
stubstart: VAR_STUB_ZONE stubstart: VAR_STUB_ZONE
{ {
@ -1158,6 +1160,22 @@ server_max_udp_size: VAR_MAX_UDP_SIZE STRING_ARG
free($2); free($2);
} }
; ;
server_dns64_prefix: VAR_DNS64_PREFIX STRING_ARG
{
OUTYY(("P(dns64_prefix:%s)\n", $2));
free(cfg_parser->cfg->dns64_prefix);
cfg_parser->cfg->dns64_prefix = $2;
}
;
server_dns64_synthall: VAR_DNS64_SYNTHALL STRING_ARG
{
OUTYY(("P(server_dns64_synthall:%s)\n", $2));
if(strcmp($2, "yes") != 0 && strcmp($2, "no") != 0)
yyerror("expected yes or no.");
else cfg_parser->cfg->dns64_synthall = (strcmp($2, "yes")==0);
free($2);
}
;
stub_name: VAR_NAME STRING_ARG stub_name: VAR_NAME STRING_ARG
{ {
OUTYY(("P(name:%s)\n", $2)); OUTYY(("P(name:%s)\n", $2));

View file

@ -78,7 +78,7 @@ parse_create_qinfo(sldns_buffer* pkt, struct msg_parse* msg,
} }
/** constructor for replyinfo */ /** constructor for replyinfo */
static struct reply_info* struct reply_info*
construct_reply_info_base(struct regional* region, uint16_t flags, size_t qd, construct_reply_info_base(struct regional* region, uint16_t flags, size_t qd,
time_t ttl, time_t prettl, size_t an, size_t ns, size_t ar, time_t ttl, time_t prettl, size_t an, size_t ns, size_t ar,
size_t total, enum sec_status sec) size_t total, enum sec_status sec)

View file

@ -192,6 +192,11 @@ struct msgreply_entry {
struct lruhash_entry entry; struct lruhash_entry entry;
}; };
struct reply_info*
construct_reply_info_base(struct regional* region, uint16_t flags, size_t qd,
time_t ttl, time_t prettl, size_t an, size_t ns, size_t ar,
size_t total, enum sec_status sec);
/** /**
* Parse wire query into a queryinfo structure, return 0 on parse error. * Parse wire query into a queryinfo structure, return 0 on parse error.
* initialises the (prealloced) queryinfo structure as well. * initialises the (prealloced) queryinfo structure as well.

View file

@ -51,6 +51,7 @@
#include "services/localzone.h" #include "services/localzone.h"
#include "services/cache/infra.h" #include "services/cache/infra.h"
#include "services/cache/rrset.h" #include "services/cache/rrset.h"
#include "dns64/dns64.h"
#include "iterator/iterator.h" #include "iterator/iterator.h"
#include "iterator/iter_fwd.h" #include "iterator/iter_fwd.h"
#include "validator/validator.h" #include "validator/validator.h"
@ -306,6 +307,7 @@ fptr_whitelist_mod_init(int (*fptr)(struct module_env* env, int id))
{ {
if(fptr == &iter_init) return 1; if(fptr == &iter_init) return 1;
else if(fptr == &val_init) return 1; else if(fptr == &val_init) return 1;
else if(fptr == &dns64_init) return 1;
#ifdef WITH_PYTHONMODULE #ifdef WITH_PYTHONMODULE
else if(fptr == &pythonmod_init) return 1; else if(fptr == &pythonmod_init) return 1;
#endif #endif
@ -317,6 +319,7 @@ fptr_whitelist_mod_deinit(void (*fptr)(struct module_env* env, int id))
{ {
if(fptr == &iter_deinit) return 1; if(fptr == &iter_deinit) return 1;
else if(fptr == &val_deinit) return 1; else if(fptr == &val_deinit) return 1;
else if(fptr == &dns64_deinit) return 1;
#ifdef WITH_PYTHONMODULE #ifdef WITH_PYTHONMODULE
else if(fptr == &pythonmod_deinit) return 1; else if(fptr == &pythonmod_deinit) return 1;
#endif #endif
@ -329,6 +332,7 @@ fptr_whitelist_mod_operate(void (*fptr)(struct module_qstate* qstate,
{ {
if(fptr == &iter_operate) return 1; if(fptr == &iter_operate) return 1;
else if(fptr == &val_operate) return 1; else if(fptr == &val_operate) return 1;
else if(fptr == &dns64_operate) return 1;
#ifdef WITH_PYTHONMODULE #ifdef WITH_PYTHONMODULE
else if(fptr == &pythonmod_operate) return 1; else if(fptr == &pythonmod_operate) return 1;
#endif #endif
@ -341,6 +345,7 @@ fptr_whitelist_mod_inform_super(void (*fptr)(
{ {
if(fptr == &iter_inform_super) return 1; if(fptr == &iter_inform_super) return 1;
else if(fptr == &val_inform_super) return 1; else if(fptr == &val_inform_super) return 1;
else if(fptr == &dns64_inform_super) return 1;
#ifdef WITH_PYTHONMODULE #ifdef WITH_PYTHONMODULE
else if(fptr == &pythonmod_inform_super) return 1; else if(fptr == &pythonmod_inform_super) return 1;
#endif #endif
@ -353,6 +358,7 @@ fptr_whitelist_mod_clear(void (*fptr)(struct module_qstate* qstate,
{ {
if(fptr == &iter_clear) return 1; if(fptr == &iter_clear) return 1;
else if(fptr == &val_clear) return 1; else if(fptr == &val_clear) return 1;
else if(fptr == &dns64_clear) return 1;
#ifdef WITH_PYTHONMODULE #ifdef WITH_PYTHONMODULE
else if(fptr == &pythonmod_clear) return 1; else if(fptr == &pythonmod_clear) return 1;
#endif #endif
@ -364,6 +370,7 @@ fptr_whitelist_mod_get_mem(size_t (*fptr)(struct module_env* env, int id))
{ {
if(fptr == &iter_get_mem) return 1; if(fptr == &iter_get_mem) return 1;
else if(fptr == &val_get_mem) return 1; else if(fptr == &val_get_mem) return 1;
else if(fptr == &dns64_get_mem) return 1;
#ifdef WITH_PYTHONMODULE #ifdef WITH_PYTHONMODULE
else if(fptr == &pythonmod_get_mem) return 1; else if(fptr == &pythonmod_get_mem) return 1;
#endif #endif