Download | Plain Text | Line Numbers


--- unbound-1.5.10.orig/Makefile.in	2017-11-09 22:58:08.476154201 +0100
+++ unbound-1.5.10/Makefile.in	2017-11-09 23:03:40.370372541 +0100
@@ -112,7 +112,7 @@
 validator/autotrust.c validator/val_anchor.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_secalgo.c \
-validator/val_sigcrypt.c validator/val_utils.c dns64/dns64.c cachedb/cachedb.c $(CHECKLOCK_SRC) \
+validator/val_sigcrypt.c validator/val_utils.c dns64/dns64.c cachedb/cachedb.c rewrite/rewrite.c $(CHECKLOCK_SRC) \
 $(DNSTAP_SRC)
 COMMON_OBJ_WITHOUT_NETCALL=dns.lo infra.lo rrset.lo dname.lo msgencode.lo \
 as112.lo msgparse.lo msgreply.lo packed_rrset.lo iterator.lo iter_delegpt.lo \
@@ -123,7 +123,7 @@
 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 \
 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 dns64.lo cachedb.lo \
+val_secalgo.lo val_sigcrypt.lo val_utils.lo dns64.lo cachedb.lo rewrite.lo \
 $(PYTHONMOD_OBJ) $(CHECKLOCK_OBJ) $(DNSTAP_OBJ)
 COMMON_OBJ_WITHOUT_UB_EVENT=$(COMMON_OBJ_WITHOUT_NETCALL) netevent.lo listen_dnsport.lo \
 outside_network.lo
@@ -723,7 +723,7 @@
  $(srcdir)/sldns/pkthdr.h $(srcdir)/sldns/rrdef.h $(srcdir)/util/fptr_wlist.h $(srcdir)/util/netevent.h \
  $(srcdir)/util/tube.h $(srcdir)/services/mesh.h $(srcdir)/util/rbtree.h $(srcdir)/dns64/dns64.h \
  $(srcdir)/iterator/iterator.h $(srcdir)/services/outbound_list.h $(srcdir)/validator/validator.h \
- $(srcdir)/validator/val_utils.h $(PYTHONMOD_HEADER) $(srcdir)/cachedb/cachedb.h
+ $(srcdir)/validator/val_utils.h $(PYTHONMOD_HEADER) $(srcdir)/cachedb/cachedb.h $(srcdir)/rewrite/rewrite.h
 outbound_list.lo outbound_list.o: $(srcdir)/services/outbound_list.c config.h \
  $(srcdir)/services/outbound_list.h $(srcdir)/services/outside_network.h $(srcdir)/util/rbtree.h \
  $(srcdir)/util/netevent.h 
@@ -768,7 +768,7 @@
  $(srcdir)/validator/val_neg.h $(srcdir)/validator/autotrust.h $(srcdir)/libunbound/libworker.h \
  $(srcdir)/libunbound/context.h $(srcdir)/util/alloc.h $(srcdir)/libunbound/unbound.h \
  $(srcdir)/libunbound/worker.h $(srcdir)/sldns/sbuffer.h $(srcdir)/util/config_file.h \
- $(PYTHONMOD_HEADER) $(srcdir)/cachedb/cachedb.h
+ $(PYTHONMOD_HEADER) $(srcdir)/cachedb/cachedb.h $(srcdir)/rewrite/rewrite.h
 locks.lo locks.o: $(srcdir)/util/locks.c config.h $(srcdir)/util/locks.h $(srcdir)/util/log.h
 log.lo log.o: $(srcdir)/util/log.c config.h $(srcdir)/util/log.h $(srcdir)/util/locks.h $(srcdir)/sldns/sbuffer.h
 mini_event.lo mini_event.o: $(srcdir)/util/mini_event.c config.h $(srcdir)/util/mini_event.h $(srcdir)/util/rbtree.h \
@@ -910,6 +910,12 @@
  $(srcdir)/util/rbtree.h $(srcdir)/validator/val_secalgo.h $(srcdir)/iterator/iter_utils.h \
  $(srcdir)/iterator/iter_resptype.h $(srcdir)/sldns/parseutil.h $(srcdir)/sldns/wire2str.h \
  $(srcdir)/sldns/sbuffer.h
+rewrite.lo rewrite.o: $(srcdir)/rewrite/rewrite.c config.h $(srcdir)/rewrite/rewrite.h $(srcdir)/util/module.h \
+ $(srcdir)/util/storage/lruhash.h $(srcdir)/util/locks.h $(srcdir)/util/log.h $(srcdir)/util/data/msgreply.h \
+ $(srcdir)/util/data/packed_rrset.h $(srcdir)/util/data/msgparse.h $(srcdir)/sldns/pkthdr.h \
+ $(srcdir)/sldns/rrdef.h $(srcdir)/sldns/str2wire.h $(srcdir)/services/cache/dns.h $(srcdir)/util/config_file.h \
+ $(srcdir)/util/fptr_wlist.h $(srcdir)/util/netevent.h $(srcdir)/util/tube.h $(srcdir)/services/mesh.h \
+ $(srcdir)/util/rbtree.h $(srcdir)/services/modstack.h $(srcdir)/util/regional.h $(srcdir)/util/data/dname.h
 checklocks.lo checklocks.o: $(srcdir)/testcode/checklocks.c config.h $(srcdir)/util/locks.h $(srcdir)/util/log.h \
  $(srcdir)/testcode/checklocks.h
 unitanchor.lo unitanchor.o: $(srcdir)/testcode/unitanchor.c config.h $(srcdir)/util/log.h $(srcdir)/util/data/dname.h \
--- unbound-1.5.10.orig/services/modstack.c	2017-11-09 22:58:08.479154167 +0100
+++ unbound-1.5.10/services/modstack.c	2017-11-09 23:04:36.188736720 +0100
@@ -53,6 +53,7 @@
 #ifdef USE_CACHEDB
 #include "cachedb/cachedb.h"
 #endif
+#include "rewrite/rewrite.h"
 
 /** count number of modules (words) in the string */
 static int
@@ -129,6 +130,7 @@
 #endif
 		"validator", 
 		"iterator", 
+		"rewrite",
 		NULL};
 	return names;
 }
@@ -150,6 +152,7 @@
 #endif
 		&val_get_funcblock, 
 		&iter_get_funcblock, 
+		&rewrite_get_funcblock,
 		NULL};
 	return fb;
 }
--- unbound-1.5.10.orig/util/config_file.c	2017-11-09 22:58:08.471154258 +0100
+++ unbound-1.5.10/util/config_file.c	2017-11-09 23:05:30.060123126 +0100
@@ -247,6 +247,7 @@
 	cfg->ratelimit_below_domain = NULL;
 	cfg->ratelimit_factor = 10;
 	cfg->qname_minimisation = 0;
+	cfg->rewrite_domain = NULL;
 	return cfg;
 error_exit:
 	config_delete(cfg); 
@@ -797,6 +798,7 @@
 	else O_LS3(opt, "local-zone-override", local_zone_overrides)
 	else O_LS3(opt, "access-control-tag-action", acl_tag_actions)
 	else O_LS3(opt, "access-control-tag-data", acl_tag_datas)
+	else O_LS2(opt, "rewrite-domain", rewrite_domain)
 	/* not here:
 	 * outgoing-permit, outgoing-avoid - have list of ports
 	 * local-zone - zones and nodefault variables
@@ -1063,6 +1065,7 @@
 	free(cfg->dnstap_version);
 	config_deldblstrlist(cfg->ratelimit_for_domain);
 	config_deldblstrlist(cfg->ratelimit_below_domain);
+	config_deldblstrlist(cfg->rewrite_domain);
 	free(cfg);
 }
 
--- unbound-1.5.10.orig/util/config_file.h	2017-11-09 22:58:08.472154246 +0100
+++ unbound-1.5.10/util/config_file.h	2017-11-09 23:05:49.042906923 +0100
@@ -396,6 +396,8 @@
 	int ratelimit_factor;
 	/** minimise outgoing QNAME and hide original QTYPE if possible */
 	int qname_minimisation;
+	/** from/to-pairs of domains to rewrite */
+	struct config_str2list* rewrite_domain;
 };
 
 /** from cfg username, after daemonise setup performed */
--- unbound-1.5.10.orig/util/configlexer.lex	2017-11-09 22:58:08.474154223 +0100
+++ unbound-1.5.10/util/configlexer.lex	2017-11-09 23:06:18.304573662 +0100
@@ -380,6 +380,7 @@
 ratelimit-for-domain{COLON}	{ YDVAR(2, VAR_RATELIMIT_FOR_DOMAIN) }
 ratelimit-below-domain{COLON}	{ YDVAR(2, VAR_RATELIMIT_BELOW_DOMAIN) }
 ratelimit-factor{COLON}		{ YDVAR(1, VAR_RATELIMIT_FACTOR) }
+rewrite-domain{COLON}		{ YDVAR(2, VAR_REWRITE_DOMAIN) }
 <INITIAL,val>{NEWLINE}		{ LEXOUT(("NL\n")); cfg_parser->line++; }
 
 	/* Quoted strings. Strip leading and ending quotes */
--- unbound-1.5.10.orig/util/configparser.y	2017-11-09 22:58:08.473154235 +0100
+++ unbound-1.5.10/util/configparser.y	2017-11-09 23:08:16.210230970 +0100
@@ -128,6 +128,7 @@
 %token VAR_QNAME_MINIMISATION VAR_IP_FREEBIND VAR_DEFINE_TAG VAR_LOCAL_ZONE_TAG
 %token VAR_ACCESS_CONTROL_TAG VAR_LOCAL_ZONE_OVERRIDE
 %token VAR_ACCESS_CONTROL_TAG_ACTION VAR_ACCESS_CONTROL_TAG_DATA
+%token VAR_REWRITE_DOMAIN
 
 %%
 toplevelvars: /* empty */ | toplevelvars toplevelvar ;
@@ -199,7 +200,7 @@
 	server_ip_freebind | server_define_tag | server_local_zone_tag |
 	server_disable_dnssec_lame_check | server_access_control_tag |
 	server_local_zone_override | server_access_control_tag_action |
-	server_access_control_tag_data
+	server_access_control_tag_data | server_rewrite_domain
 	;
 stubstart: VAR_STUB_ZONE
 	{
@@ -1497,6 +1498,15 @@
 		free($2);
 	}
 	;
+server_rewrite_domain: VAR_REWRITE_DOMAIN STRING_ARG STRING_ARG
+	{
+		OUTYY(("P(server_rewrite_domain:%s %s)\n", $2, $3));
+		if(!cfg_str2list_insert(&cfg_parser->cfg->
+			rewrite_domain, $2, $3))
+			fatal_exit("out of memory adding "
+				"rewrite-domain");
+	}
+	;
 stub_name: VAR_NAME STRING_ARG
 	{
 		OUTYY(("P(name:%s)\n", $2));
--- unbound-1.5.10.orig/util/fptr_wlist.c	2017-11-09 22:58:08.474154223 +0100
+++ unbound-1.5.10/util/fptr_wlist.c	2017-11-09 23:09:33.746348119 +0100
@@ -81,6 +81,7 @@
 #ifdef USE_CACHEDB
 #include "cachedb/cachedb.h"
 #endif
+#include "rewrite/rewrite.h"
 
 int 
 fptr_whitelist_comm_point(comm_point_callback_t *fptr)
@@ -321,6 +322,7 @@
 #ifdef USE_CACHEDB
 	else if(fptr == &cachedb_init) return 1;
 #endif
+	else if(fptr == &rewrite_init) return 1;
 	return 0;
 }
 
@@ -336,6 +338,7 @@
 #ifdef USE_CACHEDB
 	else if(fptr == &cachedb_deinit) return 1;
 #endif
+	else if(fptr == &rewrite_deinit) return 1;
 	return 0;
 }
 
@@ -352,6 +355,7 @@
 #ifdef USE_CACHEDB
 	else if(fptr == &cachedb_operate) return 1;
 #endif
+	else if(fptr == &rewrite_operate) return 1;
 	return 0;
 }
 
@@ -368,6 +372,7 @@
 #ifdef USE_CACHEDB
 	else if(fptr == &cachedb_inform_super) return 1;
 #endif
+	else if(fptr == &rewrite_inform_super) return 1;
 	return 0;
 }
 
@@ -384,6 +389,7 @@
 #ifdef USE_CACHEDB
 	else if(fptr == &cachedb_clear) return 1;
 #endif
+	else if(fptr == &rewrite_clear) return 1;
 	return 0;
 }
 
@@ -399,6 +405,7 @@
 #ifdef USE_CACHEDB
 	else if(fptr == &cachedb_get_mem) return 1;
 #endif
+	else if(fptr == &rewrite_get_mem) return 1;
 	return 0;
 }
 
--- /dev/null	1970-01-01 01:00:00.000000000 +0100
+++ unbound-1.5.10/rewrite/rewrite.c	2017-11-09 23:10:03.903004770 +0100
@@ -0,0 +1,446 @@
+/*
+ * Copyright (c) 2015, Manuel Mausz. 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.
+ */
+
+#include "config.h"
+#include "rewrite/rewrite.h"
+#include "sldns/str2wire.h"
+#include "services/cache/dns.h"
+#include "util/config_file.h"
+#include "util/fptr_wlist.h"
+#include "util/regional.h"
+#include "util/data/dname.h"
+
+/**
+ * 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 rewrite_domain {
+	uint8_t* dname;
+	size_t dname_len;
+	int labs;
+};
+
+struct rewrite_env {
+	struct rewrite_domain from;
+	struct rewrite_domain to;
+	struct rewrite_env *next;
+};
+
+enum rewrite_qstate {
+	REWRITE_IS_SUBQUERY = 1,
+};
+
+/**
+ * This function applies the configuration found in the parsed configuration
+ * file \a cfg to this instance of the rewrite module
+ *
+ * \param rewrite_env Module-specific global parameters.
+ * \param cfg         Parsed configuration file.
+ */
+static int
+rewrite_apply_cfg(struct rewrite_env** rewrite_env, struct config_file* cfg)
+{
+	struct rewrite_env** current = rewrite_env;
+	struct config_str2list* p;
+	*current = NULL;
+	for(p = cfg->rewrite_domain; p; p = p->next) {
+		struct rewrite_env* entry =
+			(struct rewrite_env*)calloc(1, sizeof(struct rewrite_env));
+		if (!(entry->from.dname = sldns_str2wire_dname(p->str, &entry->from.dname_len))) {
+			log_err("rewrite-domain: cannot parse source field: %s", p->str);
+			return 0;
+		}
+		entry->from.labs = dname_count_labels(entry->from.dname);
+		if (!(entry->to.dname = sldns_str2wire_dname(p->str2, &entry->to.dname_len))) {
+			log_err("rewrite-domain: cannot parse target field: %s", p->str2);
+			return 0;
+		}
+		entry->to.labs = dname_count_labels(entry->to.dname);
+		if (dname_subdomain_c(entry->to.dname, entry->from.dname)) {
+			log_err("rewrite-domain: target %s cannot be a sub-domain of source %s",
+			    p->str, p->str2);
+			return 0;
+		}
+		*current = entry;
+		current = &entry->next;
+		verbose(VERB_ALGO, "rewrite: added %s => %s", p->str, p->str2);
+	}
+	return 1;
+}
+
+/**
+ * Initializes this instance of the rewrite module.
+ *
+ * \param env Global state of all module instances.
+ * \param id  This instance's ID number.
+ */
+int
+rewrite_init(struct module_env* env, int id)
+{
+	struct rewrite_env* rewrite_env;
+	if (!rewrite_apply_cfg(&rewrite_env, env->cfg)) {
+		log_err("rewrite: could not apply configuration settings.");
+		return 0;
+	}
+	env->modinfo[id] = (void*)rewrite_env;
+	return 1;
+}
+
+/**
+ * Deinitializes this instance of the rewrite module.
+ *
+ * \param env Global state of all module instances.
+ * \param id  This instance's ID number.
+ */
+void
+rewrite_deinit(struct module_env* env, int id)
+{
+	if (!env)
+		return;
+
+	struct rewrite_env* cur = (struct rewrite_env*)env->modinfo[id];
+	struct rewrite_env* next;
+	while(cur) {
+		next = cur->next;
+		free(cur->from.dname);
+		free(cur->to.dname);
+		free(cur);
+		cur = next;
+	}
+
+	env->modinfo[id] = NULL;
+}
+
+static int
+dname_is_subdomain(uint8_t* dname, size_t dname_len,
+		struct rewrite_domain* dom)
+{
+	return (dname_len > dom->dname_len
+			&& dname_strict_subdomain(dname, dname_count_labels(dname),
+				dom->dname, dom->labs));
+}
+
+
+static struct rewrite_domain
+rewrite_dname(uint8_t* dname, size_t dname_len, struct rewrite_domain* from,
+		struct rewrite_domain* to, struct regional *region)
+{
+	struct rewrite_domain new = { NULL, 0 };
+	size_t sub_len = dname_len - from->dname_len;
+	new.dname_len  = sub_len + to->dname_len;
+
+	if (!(new.dname = regional_alloc(region, new.dname_len)))
+		return new;
+	memcpy(new.dname, dname, sub_len);
+	memcpy(new.dname + sub_len, to->dname, to->dname_len);
+
+	char buf[LDNS_MAX_DOMAINLEN+1];
+	dname_str((uint8_t*)new.dname, buf);
+	verbose(VERB_ALGO, "rewrite: name rewritten to %s", buf);
+
+	return new;
+}
+
+/**
+ * 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 do rewrite 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 (qstate->minfo[id]) {
+		/* we're either inside our sub-query ... */
+		if ((uintptr_t)qstate->minfo[id] == REWRITE_IS_SUBQUERY)
+			return module_wait_module;
+		/* ... or called again after the sub-query has finished */
+		else
+			return module_finished;
+	}
+
+	struct rewrite_env* rewrite_env;
+	for(rewrite_env = (struct rewrite_env*)qstate->env->modinfo[id];
+			rewrite_env; rewrite_env = rewrite_env->next) {
+		if (dname_is_subdomain(qstate->qinfo.qname, qstate->qinfo.qname_len,
+					&rewrite_env->from)) {
+			struct rewrite_domain new = rewrite_dname(
+					qstate->qinfo.qname, qstate->qinfo.qname_len,
+					&rewrite_env->from, &rewrite_env->to,
+					qstate->region);
+			if (!new.dname)
+				return module_error;
+
+			qstate->minfo[id] = (void*)rewrite_env;
+
+			/* start our sub query */
+			struct module_qstate* subq = NULL;
+			struct query_info qinfo = qstate->qinfo;
+			qinfo.qname     = new.dname;
+			qinfo.qname_len = new.dname_len;
+			fptr_ok(fptr_whitelist_modenv_attach_sub(qstate->env->attach_sub));
+			if (!(*qstate->env->attach_sub)(qstate, &qinfo,
+					qstate->query_flags, 0, 0, &subq))
+				return module_error;
+			if (subq) {
+				subq->curmod = id;
+				subq->ext_state[id] = module_state_initial;
+				subq->minfo[id] = (void*)REWRITE_IS_SUBQUERY;
+			}
+			return module_wait_subquery;
+		}
+	}
+
+	/* otherwise, pass request to next module. */
+	return module_wait_module;
+}
+
+/**
+ * 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
+rewrite_operate(struct module_qstate* qstate, enum module_ev event, int id,
+	struct outbound_entry* outbound)
+{
+	(void)outbound;
+	switch(event) {
+		case module_event_new:
+		case module_event_pass:
+			qstate->ext_state[id] = handle_event_pass(qstate, id);
+			break;
+		case module_event_moddone:
+		default:
+			qstate->ext_state[id] = module_finished;
+			break;
+	}
+}
+
+/** 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++) {
+		rep->rrsets[i] = (struct ub_packed_rrset_key*)regional_alloc_zero(
+				region, sizeof(struct ub_packed_rrset_key));
+		if (!rep->rrsets[i])
+			return 0;
+		rep->rrsets[i]->entry.key = rep->rrsets[i];
+		rep->rrsets[i]->entry.data = NULL;
+	}
+	return 1;
+}
+
+/** copy rrsets from replyinfo to dest replyinfo */
+static int
+repinfo_copy_rrsets(struct reply_info* dest, struct reply_info* from,
+		struct regional* region, struct rewrite_env* rewrite_env)
+{
+	size_t i, s;
+	struct packed_rrset_data* fd, *dd;
+	struct ub_packed_rrset_key* fk, *dk;
+
+	for(i = 0; i < dest->rrset_count; i++) {
+		fk = from->rrsets[i];
+		dk = dest->rrsets[i];
+		fd = (struct packed_rrset_data*)fk->entry.data;
+		dk->rk = fk->rk;
+		dk->id = fk->id;
+
+		if (dname_is_subdomain(fk->rk.dname, fk->rk.dname_len,
+					&rewrite_env->to)) {
+			struct rewrite_domain new = rewrite_dname(fk->rk.dname,
+					fk->rk.dname_len, &rewrite_env->to,
+					&rewrite_env->from, region);
+			if (!new.dname)
+				return 0;
+			dk->rk.dname     = new.dname;
+			dk->rk.dname_len = new.dname_len;
+			dk->entry.hash   = rrset_key_hash(&dk->rk);
+		}
+		else {
+			dk->entry.hash = fk->entry.hash;
+			dk->rk.dname   = (uint8_t*)regional_alloc_init(region,
+					fk->rk.dname, fk->rk.dname_len);
+			if(!dk->rk.dname)
+				return 0;
+		}
+		s = packed_rrset_sizeof(fd);
+		dd = (struct packed_rrset_data*)regional_alloc_init(region,
+				fd, s);
+		if (!dd)
+			return 0;
+		packed_rrset_ptr_fixup(dd);
+		dk->entry.data = (void*)dd;
+	}
+
+	return 1;
+}
+
+static int
+rewrite_copy_rrsets(struct module_qstate* from, int id,
+		struct module_qstate* dest)
+{
+	struct rewrite_env* rewrite_env = (struct rewrite_env*)dest->minfo[id];
+
+	/* build the actual reply */
+	struct reply_info* frep = from->return_msg->rep;
+	struct reply_info* drep = construct_reply_info_base(dest->region,
+			frep->flags, frep->qdcount, frep->ttl,
+			frep->prefetch_ttl, frep->an_numrrsets,
+			frep->ns_numrrsets, frep->ar_numrrsets,
+			frep->rrset_count, frep->security);
+	if (!drep)
+		return 0;
+
+	/* allocate ub_key structures special or not */
+	if (!repinfo_alloc_rrset_keys(drep, dest->region))
+		return 0;
+
+	/* copy rrsets from replyinfo to dest and replace dname */
+	if (!repinfo_copy_rrsets(drep, frep, dest->region, rewrite_env))
+		return 0;
+
+	if (!dest->return_msg) {
+		dest->return_msg = (struct dns_msg*)regional_alloc(
+				dest->region, sizeof(struct dns_msg));
+		if (!dest->return_msg)
+			return 0;
+		dest->return_msg->qinfo = dest->qinfo;
+	}
+	dest->return_msg->rep = drep;
+	return 1;
+}
+
+
+/**
+ * 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
+rewrite_inform_super(struct module_qstate* qstate, int id,
+	struct module_qstate* super)
+{
+	log_query_info(VERB_ALGO, "rewrite: inform_super, sub is",
+			&qstate->qinfo);
+	log_query_info(VERB_ALGO, "super is", &super->qinfo);
+
+	/* sub-query has finished */
+	qstate->minfo[id] = NULL;
+
+	/* if there is no successful answer, we're done */
+	if (qstate->return_rcode != LDNS_RCODE_NOERROR
+			|| !qstate->return_msg
+			|| !qstate->return_msg->rep)
+		return;
+
+	/* copy and rewrite rrsets */
+	if (!rewrite_copy_rrsets(qstate, id, super))
+		log_err("rewrite: cannot copy rrsets from subquery");
+
+	/* store the generated response in cache */
+	if (!dns_cache_store(super->env, &super->qinfo, super->return_msg->rep,
+				0, 0, 0, NULL, super->query_flags))
+		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
+rewrite_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
+rewrite_get_mem(struct module_env* env, int id)
+{
+	size_t mem = 0;
+	struct rewrite_env* cur;
+	for(cur = (struct rewrite_env*)env->modinfo[id]; cur; cur = cur->next) {
+		mem += sizeof(*cur);
+		mem += cur->from.dname_len * sizeof(*cur->from.dname);
+		mem += cur->to.dname_len * sizeof(*cur->to.dname);
+	}
+	return mem;
+}
+
+/**
+ * The rewrite function block.
+ */
+static struct module_func_block rewrite_block = {
+	"rewrite",
+	&rewrite_init, &rewrite_deinit, &rewrite_operate, &rewrite_inform_super,
+	&rewrite_clear, &rewrite_get_mem
+};
+
+struct module_func_block *
+rewrite_get_funcblock()
+{
+	return &rewrite_block;
+}
--- /dev/null	1970-01-01 01:00:00.000000000 +0100
+++ unbound-1.5.10/rewrite/rewrite.h	2017-11-09 23:10:03.903004770 +0100
@@ -0,0 +1,63 @@
+/*
+ * Copyright (c) 2015, Manuel Mausz. 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.
+ */
+
+#ifndef REWRITE_REWRITE_H
+#define REWRITE_REWRITE_H
+#include "util/module.h"
+
+/**
+ * Get the rewrite function block.
+ * @return: function block with function pointers to rewrite methods.
+ */
+struct module_func_block *rewrite_get_funcblock(void);
+
+/** rewrite init */
+int rewrite_init(struct module_env* env, int id);
+
+/** rewrite deinit */
+void rewrite_deinit(struct module_env* env, int id);
+
+/** rewrite operate on a query */
+void rewrite_operate(struct module_qstate* qstate, enum module_ev event, int id,
+		struct outbound_entry* outbound);
+
+void rewrite_inform_super(struct module_qstate* qstate, int id,
+    struct module_qstate* super);
+
+/** rewrite cleanup query state */
+void rewrite_clear(struct module_qstate* qstate, int id);
+
+/** rewrite alloc size routine */
+size_t rewrite_get_mem(struct module_env* env, int id);
+
+#endif