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