This is the patch from http://ecdysis.viagenie.ca/, updated for
unbound-1.4.9. Needed for nat64 in pf being tested now, but also useful
if you're running nat64 on another box.

ok?

Index: Makefile
===================================================================
RCS file: /cvs/ports/net/unbound/Makefile,v
retrieving revision 1.28
diff -u -p -r1.28 Makefile
--- Makefile    24 Mar 2011 20:30:42 -0000      1.28
+++ Makefile    13 May 2011 22:14:35 -0000
@@ -3,6 +3,7 @@
 COMMENT=       validating DNS resolver
 
 DISTNAME=      unbound-1.4.9
+REVISION=      0
 CATEGORIES=    net
 
 MASTER_SITES=  http://www.unbound.net/downloads/
@@ -36,7 +37,16 @@ CONFIGURE_ARGS+=     --with-ssl=/usr \
                        --with-pidfile=/var/unbound/var/run/unbound.pid \
                        --with-conf-file=/var/unbound/etc/unbound.conf \
                        --with-username=_unbound
+
+FLAVORS=       dns64
+FLAVOR?=
+
 USE_GROFF =    Yes
+
+.if ${FLAVOR:L:Mdns64}
+post-patch:
+         patch -p0 -d ${WRKSRC} < ${FILESDIR}/ecdysis-unbound.patch
+.endif
 
 post-install:
        ${INSTALL_DATA_DIR} ${PREFIX}/share/examples/unbound
--- /dev/null   Sat May 14 07:14:55 2011
+++ files/ecdysis-unbound.patch Sat May 14 06:12:54 2011
@@ -0,0 +1,1159 @@
+--- Makefile.in.orig   Sat May 14 06:00:54 2011
++++ Makefile.in        Sat May 14 06:01:09 2011
+@@ -92,7 +92,8 @@ endif
+ COMMON_SRC=$(patsubst $(srcdir)/%,%, $(wildcard $(srcdir)/services/*.c \
+       $(srcdir)/services/cache/*.c $(srcdir)/util/*.c \
+       $(srcdir)/util/data/*.c $(srcdir)/util/storage/*.c \
+-      $(srcdir)/iterator/*.c $(srcdir)/validator/*.c $(PYTHONMOD_SRC))) \
++      $(srcdir)/dns64/*.c $(srcdir)/iterator/*.c $(srcdir)/validator/*.c \
++      $(PYTHONMOD_SRC))) \
+       util/configparser.c util/configlexer.c $(CHECKLOCK_SRC)
+ COMMON_OBJ=$(addprefix $(BUILD),$(COMMON_SRC:.c=.lo))
+ COMPAT_SRC=$(addprefix compat/,$(LIBOBJS:.o=.c))
+--- /dev/null  Sat May 14 06:12:54 2011
++++ dns64/dns64.c      Sat May 14 06:01:09 2011
+@@ -0,0 +1,865 @@
++/*
++ * 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.
++ *
++ * It will be change when/if IANA assigns a well-known prefix for DNS64.
++ */
++static const char DEFAULT_DNS64_PREFIX[] = "DEAD:BEEF::/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))
++              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;
++}
+--- /dev/null  Sat May 14 06:12:54 2011
++++ dns64/dns64.h      Sat May 14 06:01:09 2011
+@@ -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();
++
++/** 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 */
+--- /dev/null  Sat May 14 06:12:54 2011
++++ doc/README.DNS64   Sat May 14 06:01:09 2011
+@@ -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
++
+--- doc/example.conf.in.orig   Sat May 14 06:00:54 2011
++++ doc/example.conf.in        Sat May 14 06:01:09 2011
+@@ -41,7 +41,7 @@ server:
+ 
+       # enable this feature to copy the source address of queries to reply.
+       # Socket options are not supported on all platforms. experimental. 
+-      # interface-automatic: no
++      interface-automatic: yes
+ 
+       # port to answer queries from
+       # port: 53
+@@ -166,6 +166,8 @@ server:
+       # access-control: ::0/0 refuse
+       # access-control: ::1 allow
+       # access-control: ::ffff:127.0.0.1 allow
++      access-control: ::0/0 allow
++        access-control: 0.0.0.0/0 allow
+ 
+       # if given, a chroot(2) is done to the given directory.
+       # i.e. you can chroot to the working directory, for example,
+@@ -310,8 +312,8 @@ server:
+       # prefetch-key: no
+ 
+       # module configuration of the server. A string with identifiers
+-      # separated by spaces. "iterator" or "validator iterator"
+-      # module-config: "validator iterator"
++      # separated by spaces. Syntax: [dns64] [validator] iterator
++      module-config: "dns64 iterator"
+ 
+       # File with trusted keys, kept uptodate using RFC5011 probes,
+       # initial file like trust-anchor-file, then it stores metadata.
+@@ -447,6 +449,9 @@ server:
+       # You can also add PTR records using local-data directly, but then
+       # you need to do the reverse notation yourself.
+       # local-data-ptr: "192.0.2.3 www.example.com"
++
++      # DNS64 prefix. Must be specified when DNS64 is in use.
++      dns64-prefix: 64:ff9b::0/96
+ 
+ # Python config section. To enable:
+ # o use --with-pythonmodule to configure before compiling.
+--- services/modstack.c.orig   Sat Mar 13 00:17:48 2010
++++ services/modstack.c        Sat May 14 06:01:09 2011
+@@ -43,6 +43,7 @@
+ #include "services/modstack.h"
+ #include "util/module.h"
+ #include "util/fptr_wlist.h"
++#include "dns64/dns64.h"
+ #include "iterator/iterator.h"
+ #include "validator/validator.h"
+ 
+@@ -116,6 +117,7 @@ module_list_avail(void)
+ {
+         /* these are the modules available */
+         static const char* names[] = {
++              "dns64",
+ #ifdef WITH_PYTHONMODULE
+               "python", 
+ #endif
+@@ -133,6 +135,7 @@ static fbgetfunctype*
+ module_funcs_avail(void)
+ {
+         static struct module_func_block* (*fb[])(void) = {
++              &dns64_get_funcblock,
+ #ifdef WITH_PYTHONMODULE
+               &pythonmod_get_funcblock, 
+ #endif
+--- util/config_file.h.orig    Thu Nov 18 17:49:15 2010
++++ util/config_file.h Sat May 14 06:01:09 2011
+@@ -279,6 +279,12 @@ struct config_file {
+ 
+       /** daemonize, i.e. fork into the background. */
+       int do_daemonize;
++
++      /** DNS64 prefix */
++      char* dns64_prefix;
++
++      /** Synthetize all AAAA record despite the presence of an authoritative 
one */
++      int dns64_synthall;
+ };
+ 
+ /**
+--- util/configlexer.lex.orig  Thu Nov 18 17:49:15 2010
++++ util/configlexer.lex       Sat May 14 06:01:09 2011
+@@ -232,6 +232,8 @@ control-cert-file{COLON}   { YDVAR(1, VAR_CONTROL_CERT_F
+ python-script{COLON}          { YDVAR(1, VAR_PYTHON_SCRIPT) }
+ python{COLON}                 { YDVAR(0, VAR_PYTHON) }
+ domain-insecure{COLON}                { YDVAR(1, VAR_DOMAIN_INSECURE) }
++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++; 
}
+ 
+       /* Quoted strings. Strip leading and ending quotes */
+--- util/data/msgreply.c.orig  Fri Aug 20 22:30:41 2010
++++ util/data/msgreply.c       Sat May 14 06:01:09 2011
+@@ -77,7 +77,7 @@ parse_create_qinfo(ldns_buffer* pkt, struct msg_parse*
+ }
+ 
+ /** constructor for replyinfo */
+-static struct reply_info*
++struct reply_info*
+ construct_reply_info_base(struct regional* region, uint16_t flags, size_t qd,
+       uint32_t ttl, uint32_t prettl, size_t an, size_t ns, size_t ar, 
+       size_t total, enum sec_status sec)
+--- util/data/msgreply.h.orig  Fri Aug 20 22:30:41 2010
++++ util/data/msgreply.h       Sat May 14 06:01:09 2011
+@@ -191,6 +191,11 @@ struct msgreply_entry {
+       struct lruhash_entry entry;
+ };
+ 
++struct reply_info*
++construct_reply_info_base(struct regional* region, uint16_t flags, size_t qd,
++              uint32_t ttl, uint32_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. 
+  * initialises the (prealloced) queryinfo structure as well.
+--- util/fptr_wlist.c.orig     Fri Aug 20 22:30:41 2010
++++ util/fptr_wlist.c  Sat May 14 06:01:09 2011
+@@ -53,6 +53,7 @@
+ #include "services/localzone.h"
+ #include "services/cache/infra.h"
+ #include "services/cache/rrset.h"
++#include "dns64/dns64.h"
+ #include "iterator/iterator.h"
+ #include "iterator/iter_fwd.h"
+ #include "validator/validator.h"
+@@ -296,6 +297,7 @@ fptr_whitelist_mod_init(int (*fptr)(struct module_env*
+ {
+       if(fptr == &iter_init) return 1;
+       else if(fptr == &val_init) return 1;
++      else if(fptr == &dns64_init) return 1;
+ #ifdef WITH_PYTHONMODULE
+       else if(fptr == &pythonmod_init) return 1;
+ #endif
+@@ -307,6 +309,7 @@ fptr_whitelist_mod_deinit(void (*fptr)(struct module_e
+ {
+       if(fptr == &iter_deinit) return 1;
+       else if(fptr == &val_deinit) return 1;
++      else if(fptr == &dns64_deinit) return 1;
+ #ifdef WITH_PYTHONMODULE
+       else if(fptr == &pythonmod_deinit) return 1;
+ #endif
+@@ -319,6 +322,7 @@ fptr_whitelist_mod_operate(void (*fptr)(struct module_
+ {
+       if(fptr == &iter_operate) return 1;
+       else if(fptr == &val_operate) return 1;
++      else if(fptr == &dns64_operate) return 1;
+ #ifdef WITH_PYTHONMODULE
+       else if(fptr == &pythonmod_operate) return 1;
+ #endif
+@@ -331,6 +335,7 @@ fptr_whitelist_mod_inform_super(void (*fptr)(
+ {
+       if(fptr == &iter_inform_super) return 1;
+       else if(fptr == &val_inform_super) return 1;
++      else if(fptr == &dns64_inform_super) return 1;
+ #ifdef WITH_PYTHONMODULE
+       else if(fptr == &pythonmod_inform_super) return 1;
+ #endif
+@@ -343,6 +348,7 @@ fptr_whitelist_mod_clear(void (*fptr)(struct module_qs
+ {
+       if(fptr == &iter_clear) return 1;
+       else if(fptr == &val_clear) return 1;
++      else if(fptr == &dns64_clear) return 1;
+ #ifdef WITH_PYTHONMODULE
+       else if(fptr == &pythonmod_clear) return 1;
+ #endif
+@@ -354,6 +360,7 @@ fptr_whitelist_mod_get_mem(size_t (*fptr)(struct modul
+ {
+       if(fptr == &iter_get_mem) return 1;
+       else if(fptr == &val_get_mem) return 1;
++      else if(fptr == &dns64_get_mem) return 1;
+ #ifdef WITH_PYTHONMODULE
+       else if(fptr == &pythonmod_get_mem) return 1;
+ #endif

Reply via email to