mirror of
https://github.com/NLnetLabs/unbound.git
synced 2025-12-20 23:00:56 -05:00
- 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:
parent
dad3e92df7
commit
45022b6add
16 changed files with 2763 additions and 1686 deletions
|
|
@ -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
864
dns64/dns64.c
Normal 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
71
dns64/dns64.h
Normal 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 */
|
||||||
|
|
@ -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
26
doc/README.DNS64
Normal 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
|
||||||
|
|
||||||
|
|
@ -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.
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
2380
util/configlexer.c
2380
util/configlexer.c
File diff suppressed because it is too large
Load diff
|
|
@ -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 */
|
||||||
|
|
|
||||||
1034
util/configparser.c
1034
util/configparser.c
File diff suppressed because it is too large
Load diff
|
|
@ -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 */
|
||||||
|
|
|
||||||
|
|
@ -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));
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
|
||||||
|
|
@ -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.
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue