Home | History | Annotate | Line # | Download | only in ipsecmod
      1 /*
      2  * ipsecmod/ipsecmod.c - facilitate opportunistic IPsec module
      3  *
      4  * Copyright (c) 2017, NLnet Labs. All rights reserved.
      5  *
      6  * This software is open source.
      7  *
      8  * Redistribution and use in source and binary forms, with or without
      9  * modification, are permitted provided that the following conditions
     10  * are met:
     11  *
     12  * Redistributions of source code must retain the above copyright notice,
     13  * this list of conditions and the following disclaimer.
     14  *
     15  * Redistributions in binary form must reproduce the above copyright notice,
     16  * this list of conditions and the following disclaimer in the documentation
     17  * and/or other materials provided with the distribution.
     18  *
     19  * Neither the name of the NLNET LABS nor the names of its contributors may
     20  * be used to endorse or promote products derived from this software without
     21  * specific prior written permission.
     22  *
     23  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
     24  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
     25  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
     26  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
     27  * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
     28  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
     29  * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
     30  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
     31  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
     32  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
     33  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     34  */
     35 
     36 /**
     37  * \file
     38  *
     39  * This file contains a module that facilitates opportunistic IPsec. It does so
     40  * by also querying for the IPSECKEY for A/AAAA queries and calling a
     41  * configurable hook (eg. signaling an IKE daemon) before replying.
     42  */
     43 
     44 #include "config.h"
     45 #ifdef USE_IPSECMOD
     46 #include "ipsecmod/ipsecmod.h"
     47 #include "ipsecmod/ipsecmod-whitelist.h"
     48 #include "util/fptr_wlist.h"
     49 #include "util/regional.h"
     50 #include "util/net_help.h"
     51 #include "util/config_file.h"
     52 #include "services/cache/dns.h"
     53 #include "sldns/wire2str.h"
     54 #ifdef HAVE_SYS_WAIT_H
     55 #include <sys/wait.h>
     56 #endif
     57 
     58 /** Apply configuration to ipsecmod module 'global' state. */
     59 static int
     60 ipsecmod_apply_cfg(struct ipsecmod_env* ipsecmod_env, struct config_file* cfg)
     61 {
     62 	if(!cfg->ipsecmod_hook || (cfg->ipsecmod_hook && !cfg->ipsecmod_hook[0])) {
     63 		log_err("ipsecmod: missing ipsecmod-hook.");
     64 		return 0;
     65 	}
     66 	if(access(cfg->ipsecmod_hook, X_OK) != 0) {
     67 		log_err("ipsecmod: ipsecmod-hook '%s' is not an executable file: %s",
     68 			cfg->ipsecmod_hook, strerror(errno));
     69 		return 0;
     70 	}
     71 	if(cfg->ipsecmod_whitelist &&
     72 		!ipsecmod_whitelist_apply_cfg(ipsecmod_env, cfg))
     73 		return 0;
     74 	return 1;
     75 }
     76 
     77 int
     78 ipsecmod_init(struct module_env* env, int id)
     79 {
     80 	struct ipsecmod_env* ipsecmod_env = (struct ipsecmod_env*)calloc(1,
     81 		sizeof(struct ipsecmod_env));
     82 	if(!ipsecmod_env) {
     83 		log_err("malloc failure");
     84 		return 0;
     85 	}
     86 	env->modinfo[id] = (void*)ipsecmod_env;
     87 	ipsecmod_env->whitelist = NULL;
     88 	if(!ipsecmod_apply_cfg(ipsecmod_env, env->cfg)) {
     89 		log_err("ipsecmod: could not apply configuration settings.");
     90 		return 0;
     91 	}
     92 	return 1;
     93 }
     94 
     95 void
     96 ipsecmod_deinit(struct module_env* env, int id)
     97 {
     98 	struct ipsecmod_env* ipsecmod_env;
     99 	if(!env || !env->modinfo[id])
    100 		return;
    101 	ipsecmod_env = (struct ipsecmod_env*)env->modinfo[id];
    102 	/* Free contents. */
    103 	ipsecmod_whitelist_delete(ipsecmod_env->whitelist);
    104 	free(ipsecmod_env);
    105 	env->modinfo[id] = NULL;
    106 }
    107 
    108 /** New query for ipsecmod. */
    109 static int
    110 ipsecmod_new(struct module_qstate* qstate, int id)
    111 {
    112 	struct ipsecmod_qstate* iq = (struct ipsecmod_qstate*)regional_alloc(
    113 		qstate->region, sizeof(struct ipsecmod_qstate));
    114 	qstate->minfo[id] = iq;
    115 	if(!iq)
    116 		return 0;
    117 	/* Initialise it. */
    118 	memset(iq, 0, sizeof(*iq));
    119 	iq->enabled = qstate->env->cfg->ipsecmod_enabled;
    120 	iq->is_whitelisted = ipsecmod_domain_is_whitelisted(
    121 		(struct ipsecmod_env*)qstate->env->modinfo[id], qstate->qinfo.qname,
    122 		qstate->qinfo.qname_len, qstate->qinfo.qclass);
    123 	return 1;
    124 }
    125 
    126 /**
    127  * Exit module with an error status.
    128  * @param qstate: query state
    129  * @param id: module id.
    130  */
    131 static void
    132 ipsecmod_error(struct module_qstate* qstate, int id)
    133 {
    134 	qstate->ext_state[id] = module_error;
    135 	qstate->return_rcode = LDNS_RCODE_SERVFAIL;
    136 }
    137 
    138 /**
    139  * Generate a request for the IPSECKEY.
    140  *
    141  * @param qstate: query state that is the parent.
    142  * @param id: module id.
    143  * @param name: what name to query for.
    144  * @param namelen: length of name.
    145  * @param qtype: query type.
    146  * @param qclass: query class.
    147  * @param flags: additional flags, such as the CD bit (BIT_CD), or 0.
    148  * @return false on alloc failure.
    149  */
    150 static int
    151 generate_request(struct module_qstate* qstate, int id, uint8_t* name,
    152 	size_t namelen, uint16_t qtype, uint16_t qclass, uint16_t flags)
    153 {
    154 	struct module_qstate* newq;
    155 	struct query_info ask;
    156 	ask.qname = name;
    157 	ask.qname_len = namelen;
    158 	ask.qtype = qtype;
    159 	ask.qclass = qclass;
    160 	ask.local_alias = NULL;
    161 	log_query_info(VERB_ALGO, "ipsecmod: generate request", &ask);
    162 
    163 	/* Explicitly check for cycle before trying to attach. Will result in
    164 	 * cleaner error message. The attach_sub code also checks for cycle but the
    165 	 * message will be out of memory in both cases then. */
    166 	fptr_ok(fptr_whitelist_modenv_detect_cycle(qstate->env->detect_cycle));
    167 	if((*qstate->env->detect_cycle)(qstate, &ask,
    168 		(uint16_t)(BIT_RD|flags), 0, 0)) {
    169 		verbose(VERB_ALGO, "Could not generate request: cycle detected");
    170 		return 0;
    171 	}
    172 
    173 	fptr_ok(fptr_whitelist_modenv_attach_sub(qstate->env->attach_sub));
    174 	if(!(*qstate->env->attach_sub)(qstate, &ask, NULL,
    175 		(uint16_t)(BIT_RD|flags), 0, 0, &newq)){
    176 		log_err("Could not generate request: out of memory");
    177 		return 0;
    178 	}
    179 	qstate->ext_state[id] = module_wait_subquery;
    180 	return 1;
    181 }
    182 
    183 /**
    184  * Check if the string passed is a valid domain name with safe characters to
    185  * pass to a shell.
    186  * This will only allow:
    187  *  - digits
    188  *  - alphas
    189  *  - hyphen (not at the start)
    190  *  - dot (not at the start, or the only character)
    191  *  - underscore
    192  * @param s: pointer to the string.
    193  * @param slen: string's length.
    194  * @return true if s only contains safe characters; false otherwise.
    195  */
    196 static int
    197 domainname_has_safe_characters(char* s, size_t slen) {
    198 	size_t i;
    199 	for(i = 0; i < slen; i++) {
    200 		if(s[i] == '\0') return 1;
    201 		if((s[i] == '-' && i != 0)
    202 			|| (s[i] == '.' && (i != 0 || s[1] == '\0'))
    203 			|| (s[i] == '_') || (s[i] >= '0' && s[i] <= '9')
    204 			|| (s[i] >= 'A' && s[i] <= 'Z')
    205 			|| (s[i] >= 'a' && s[i] <= 'z')) {
    206 			continue;
    207 		}
    208 		return 0;
    209 	}
    210 	return 1;
    211 }
    212 
    213 /**
    214  * Check if the stringified IPSECKEY RDATA contains safe characters to pass to
    215  * a shell.
    216  * This is only relevant for checking the gateway when the gateway type is 3
    217  * (domainname).
    218  * @param s: pointer to the string.
    219  * @param slen: string's length.
    220  * @return true if s contains only safe characters; false otherwise.
    221  */
    222 static int
    223 ipseckey_has_safe_characters(char* s, size_t slen) {
    224 	int precedence, gateway_type, algorithm;
    225 	char* gateway;
    226 	gateway = (char*)calloc(slen, sizeof(char));
    227 	if(!gateway) {
    228 		log_err("ipsecmod: out of memory when calling the hook");
    229 		return 0;
    230 	}
    231 	if(sscanf(s, "%d %d %d %s ",
    232 			&precedence, &gateway_type, &algorithm, gateway) != 4) {
    233 		free(gateway);
    234 		return 0;
    235 	}
    236 	if(gateway_type != 3) {
    237 		free(gateway);
    238 		return 1;
    239 	}
    240 	if(domainname_has_safe_characters(gateway, slen)) {
    241 		free(gateway);
    242 		return 1;
    243 	}
    244 	free(gateway);
    245 	return 0;
    246 }
    247 
    248 /**
    249  *  Prepare the data and call the hook.
    250  *
    251  *  @param qstate: query state.
    252  *  @param iq: ipsecmod qstate.
    253  *  @param ie: ipsecmod environment.
    254  *  @return true on success, false otherwise.
    255  */
    256 static int
    257 call_hook(struct module_qstate* qstate, struct ipsecmod_qstate* iq,
    258 	struct ipsecmod_env* ATTR_UNUSED(ie))
    259 {
    260 	size_t slen, tempdata_len, tempstring_len, i;
    261 	char qname_s[LDNS_MAX_DOMAINLEN*5+16], ttl_s[32], a_s[32768], k_s[32768];
    262 	char *s, *tempstring;
    263 	int w = 0, w_temp, qtype;
    264 	struct ub_packed_rrset_key* rrset_key;
    265 	struct packed_rrset_data* rrset_data;
    266 	uint8_t *tempdata;
    267 	pid_t pid;
    268 	int st;
    269 	char* argv[6];
    270 
    271 	/* Copy the qname into the buffer. */
    272 	tempstring = sldns_wire2str_dname(qstate->qinfo.qname,
    273 		qstate->qinfo.qname_len);
    274 	if(!tempstring) {
    275 		log_err("ipsecmod: out of memory when calling the hook");
    276 		return 0;
    277 	}
    278 	if(!domainname_has_safe_characters(tempstring, strlen(tempstring))) {
    279 		log_err("ipsecmod: qname has unsafe characters");
    280 		free(tempstring);
    281 		return 0;
    282 	}
    283 	if(strlen(tempstring)+1 > sizeof(qname_s)) {
    284 		log_err("ipsecmod: string too long");
    285 		free(tempstring);
    286 		return 0;
    287 	}
    288 	snprintf(qname_s, sizeof(qname_s), "%s", tempstring);
    289 	free(tempstring);
    290 
    291 	/* Copy the IPSECKEY TTL into the buffer. */
    292 	rrset_data = (struct packed_rrset_data*)iq->ipseckey_rrset->entry.data;
    293 	snprintf(ttl_s, sizeof(ttl_s), "%ld", (long)rrset_data->ttl);
    294 
    295 	rrset_key = reply_find_answer_rrset(&qstate->return_msg->qinfo,
    296 		qstate->return_msg->rep);
    297 	if(!rrset_key) {
    298 		log_err("ipsecmod: could not find answer rrset for A/AAAA");
    299 		return 0;
    300 	}
    301 	/* Double check that the records are indeed A/AAAA.
    302 	 * This should never happen as this function is only executed for A/AAAA
    303 	 * queries but make sure we don't pass anything other than A/AAAA to the
    304 	 * shell. */
    305 	qtype = ntohs(rrset_key->rk.type);
    306 	if(qtype != LDNS_RR_TYPE_AAAA && qtype != LDNS_RR_TYPE_A) {
    307 		log_err("ipsecmod: Answer is not of A or AAAA type");
    308 		return 0;
    309 	}
    310 	rrset_data = (struct packed_rrset_data*)rrset_key->entry.data;
    311 	if(!rrset_data) {
    312 		log_err("ipsecmod: Answer has no data");
    313 		return 0;
    314 	}
    315 	/* Copy the A/AAAA record(s) into the buffer. */
    316 	w = 0;
    317 	s = a_s;
    318 	slen = sizeof(a_s);
    319 	memset(s, 0, slen);
    320 	for(i=0; i<rrset_data->count; i++) {
    321 		if(i > 0) {
    322 			/* Put space into the buffer. */
    323 			w += sldns_str_print(&s, &slen, " ");
    324 		}
    325 		/* Ignore the first two bytes, they are the rr_data len. */
    326 		w_temp = sldns_wire2str_rdata_buf(rrset_data->rr_data[i] + 2,
    327 			rrset_data->rr_len[i] - 2, s, slen, qstate->qinfo.qtype);
    328 		if(w_temp < 0) {
    329 			/* Error in printout. */
    330 			log_err("ipsecmod: Error in printing IP address");
    331 			return 0;
    332 		} else if((size_t)w_temp >= slen) {
    333 			s = NULL; /* We do not want str to point outside of buffer. */
    334 			slen = 0;
    335 			log_err("ipsecmod: command addr argument too long");
    336 			return 0;
    337 		} else {
    338 			s += w_temp;
    339 			slen -= w_temp;
    340 			w += w_temp;
    341 		}
    342 	}
    343 	if(w >= (int)sizeof(a_s)) {
    344 		log_err("ipsecmod: command addr argument too long");
    345 		return 0;
    346 	}
    347 
    348 	/* Copy the IPSECKEY record(s) into the buffer. Start and end this section
    349 	 * with a double quote. */
    350 	w = 0;
    351 	s = k_s;
    352 	slen = sizeof(k_s);
    353 	memset(s, 0, slen);
    354 	rrset_data = (struct packed_rrset_data*)iq->ipseckey_rrset->entry.data;
    355 	for(i=0; i<rrset_data->count; i++) {
    356 		if(i > 0) {
    357 			/* Put space into the buffer. */
    358 			w += sldns_str_print(&s, &slen, " ");
    359 		}
    360 		/* Ignore the first two bytes, they are the rr_data len. */
    361 		tempdata = rrset_data->rr_data[i] + 2;
    362 		tempdata_len = rrset_data->rr_len[i] - 2;
    363 		/* Save the buffer pointers. */
    364 		tempstring = s; tempstring_len = slen;
    365 		w_temp = sldns_wire2str_ipseckey_scan(&tempdata, &tempdata_len, &s,
    366 			&slen, NULL, 0, NULL);
    367 		/* There was an error when parsing the IPSECKEY; reset the buffer
    368 		 * pointers to their previous values. */
    369 		if(w_temp == -1) {
    370 			s = tempstring; slen = tempstring_len;
    371 		} else if(w_temp > 0) {
    372 			if(!ipseckey_has_safe_characters(
    373 					tempstring, tempstring_len - slen)) {
    374 				log_err("ipsecmod: ipseckey has unsafe characters");
    375 				return 0;
    376 			}
    377 			w += w_temp;
    378 		}
    379 	}
    380 	if(w >= (int)sizeof(k_s)) {
    381 		log_err("ipsecmod: command ipseckey argument too long");
    382 		return 0;
    383 	}
    384 
    385 	/* ipsecmod-hook should return 0 on success. */
    386 	/* exec the ipsecmod-hook */
    387 	argv[0] = qstate->env->cfg->ipsecmod_hook;
    388 	argv[1] = qname_s;
    389 	argv[2] = ttl_s;
    390 	argv[3] = a_s;
    391 	argv[4] = k_s;
    392 	argv[5] = NULL;
    393 	verbose(VERB_ALGO, "ipsecmod: exec %s \"%s\" %s \"%s\" \"%s\"",
    394 		argv[0], argv[1], argv[2], argv[3], argv[4]);
    395 	if((pid = fork()) < 0) {
    396 		log_err("ipsecmod: for exec, can not fork: %s",
    397 			strerror(errno));
    398 		return 0;
    399 	}
    400 	if(pid == 0) {
    401 		if(execv(argv[0], argv) < 0)
    402 			fprintf(stderr, "ipsecmod: execv: %s\n",
    403 				strerror(errno));
    404 		_exit(127);
    405 	}
    406 	while(1) {
    407 		if(waitpid(pid, &st, 0) < 0) {
    408 			if(errno == EINTR)
    409 				continue;
    410 			log_err("ipsecmod: wait_pid: %s", strerror(errno));
    411 		}
    412 		break;
    413 	}
    414 	if(!(WIFEXITED(st) && WEXITSTATUS(st) == 0)) {
    415 		/* the command failed */
    416 		return 0;
    417 	}
    418 	return 1;
    419 }
    420 
    421 /**
    422  * Handle an ipsecmod module event with a query
    423  * @param qstate: query state (from the mesh), passed between modules.
    424  * 	contains qstate->env module environment with global caches and so on.
    425  * @param iq: query state specific for this module.  per-query.
    426  * @param ie: environment specific for this module.  global.
    427  * @param id: module id.
    428  */
    429 static void
    430 ipsecmod_handle_query(struct module_qstate* qstate,
    431 	struct ipsecmod_qstate* iq, struct ipsecmod_env* ie, int id)
    432 {
    433 	struct ub_packed_rrset_key* rrset_key;
    434 	struct packed_rrset_data* rrset_data;
    435 	size_t i;
    436 	/* Pass to next module if we are not enabled and whitelisted. */
    437 	if(!(iq->enabled && iq->is_whitelisted)) {
    438 		qstate->ext_state[id] = module_wait_module;
    439 		return;
    440 	}
    441 	/* New query, check if the query is for an A/AAAA record and disable
    442 	 * caching for other modules. */
    443 	if(!iq->ipseckey_done) {
    444 		if(qstate->qinfo.qtype == LDNS_RR_TYPE_A ||
    445 			qstate->qinfo.qtype == LDNS_RR_TYPE_AAAA) {
    446 			char type[16];
    447 			sldns_wire2str_type_buf(qstate->qinfo.qtype, type,
    448 				sizeof(type));
    449 			verbose(VERB_ALGO, "ipsecmod: query for %s; engaging",
    450 				type);
    451 			qstate->no_cache_store = 1;
    452 		}
    453 		/* Pass request to next module. */
    454 		qstate->ext_state[id] = module_wait_module;
    455 		return;
    456 	}
    457 	/* IPSECKEY subquery is finished. */
    458 	/* We have an IPSECKEY answer. */
    459 	if(iq->ipseckey_rrset) {
    460 		rrset_data = (struct packed_rrset_data*)iq->ipseckey_rrset->entry.data;
    461 		if(rrset_data) {
    462 			/* If bogus return SERVFAIL. */
    463 			if(!qstate->env->cfg->ipsecmod_ignore_bogus &&
    464 				rrset_data->security == sec_status_bogus) {
    465 				log_err("ipsecmod: bogus IPSECKEY");
    466 				errinf(qstate, "ipsecmod: bogus IPSECKEY");
    467 				ipsecmod_error(qstate, id);
    468 				return;
    469 			}
    470 			/* We have a valid IPSECKEY reply, call hook. */
    471 			if(!call_hook(qstate, iq, ie) &&
    472 				qstate->env->cfg->ipsecmod_strict) {
    473 				log_err("ipsecmod: ipsecmod-hook failed");
    474 				errinf(qstate, "ipsecmod: ipsecmod-hook failed");
    475 				ipsecmod_error(qstate, id);
    476 				return;
    477 			}
    478 			/* Make sure the A/AAAA's TTL is equal/less than the
    479 			 * ipsecmod_max_ttl. */
    480 			rrset_key = reply_find_answer_rrset(&qstate->return_msg->qinfo,
    481 				qstate->return_msg->rep);
    482 			if(!rrset_key) {
    483 				log_err("ipsecmod: reply-find-answer failed");
    484 				errinf(qstate, "ipsecmod: reply-find-answer failed");
    485 				ipsecmod_error(qstate, id);
    486 				return;
    487 			}
    488 			rrset_data = (struct packed_rrset_data*)rrset_key->entry.data;
    489 			if(rrset_data->ttl > (time_t)qstate->env->cfg->ipsecmod_max_ttl) {
    490 				/* Update TTL for rrset to fixed value. */
    491 				rrset_data->ttl = qstate->env->cfg->ipsecmod_max_ttl;
    492 				for(i=0; i<rrset_data->count+rrset_data->rrsig_count; i++)
    493 					rrset_data->rr_ttl[i] = qstate->env->cfg->ipsecmod_max_ttl;
    494 				/* Also update reply_info's TTL */
    495 				if(qstate->return_msg->rep->ttl > (time_t)qstate->env->cfg->ipsecmod_max_ttl) {
    496 					qstate->return_msg->rep->ttl =
    497 						qstate->env->cfg->ipsecmod_max_ttl;
    498 					qstate->return_msg->rep->prefetch_ttl = PREFETCH_TTL_CALC(
    499 						qstate->return_msg->rep->ttl);
    500 					qstate->return_msg->rep->serve_expired_ttl = qstate->return_msg->rep->ttl +
    501 						qstate->env->cfg->serve_expired_ttl;
    502 				}
    503 			}
    504 		}
    505 	}
    506 	/* Store A/AAAA in cache. */
    507 	if(!dns_cache_store(qstate->env, &qstate->qinfo,
    508 		qstate->return_msg->rep, 0, qstate->prefetch_leeway,
    509 		0, qstate->region, qstate->query_flags, qstate->qstarttime,
    510 		qstate->is_valrec)) {
    511 		log_err("ipsecmod: out of memory caching record");
    512 	}
    513 	qstate->ext_state[id] = module_finished;
    514 }
    515 
    516 /**
    517  * Handle an ipsecmod module event with a response from the iterator.
    518  * @param qstate: query state (from the mesh), passed between modules.
    519  * 	contains qstate->env module environment with global caches and so on.
    520  * @param iq: query state specific for this module.  per-query.
    521  * @param ie: environment specific for this module.  global.
    522  * @param id: module id.
    523  */
    524 static void
    525 ipsecmod_handle_response(struct module_qstate* qstate,
    526 	struct ipsecmod_qstate* ATTR_UNUSED(iq),
    527 	struct ipsecmod_env* ATTR_UNUSED(ie), int id)
    528 {
    529 	/* Pass to previous module if we are not enabled and whitelisted. */
    530 	if(!(iq->enabled && iq->is_whitelisted)) {
    531 		qstate->ext_state[id] = module_finished;
    532 		return;
    533 	}
    534 	/* check if the response is for an A/AAAA query. */
    535 	if((qstate->qinfo.qtype == LDNS_RR_TYPE_A ||
    536 		qstate->qinfo.qtype == LDNS_RR_TYPE_AAAA) &&
    537 		/* check that we had an answer for the A/AAAA query. */
    538 		qstate->return_msg &&
    539 		reply_find_answer_rrset(&qstate->return_msg->qinfo,
    540 		qstate->return_msg->rep) &&
    541 		/* check that another module didn't SERVFAIL. */
    542 		qstate->return_rcode == LDNS_RCODE_NOERROR) {
    543 		char type[16];
    544 		sldns_wire2str_type_buf(qstate->qinfo.qtype, type,
    545 			sizeof(type));
    546 		verbose(VERB_ALGO, "ipsecmod: response for %s; generating IPSECKEY "
    547 			"subquery", type);
    548 		/* generate an IPSECKEY query. */
    549 		if(!generate_request(qstate, id, qstate->qinfo.qname,
    550 			qstate->qinfo.qname_len, LDNS_RR_TYPE_IPSECKEY,
    551 			qstate->qinfo.qclass, 0)) {
    552 			log_err("ipsecmod: could not generate subquery.");
    553 			errinf(qstate, "ipsecmod: could not generate subquery.");
    554 			ipsecmod_error(qstate, id);
    555 		}
    556 		return;
    557 	}
    558 	/* we are done with the query. */
    559 	qstate->ext_state[id] = module_finished;
    560 }
    561 
    562 void
    563 ipsecmod_operate(struct module_qstate* qstate, enum module_ev event, int id,
    564 	struct outbound_entry* outbound)
    565 {
    566 	struct ipsecmod_env* ie = (struct ipsecmod_env*)qstate->env->modinfo[id];
    567 	struct ipsecmod_qstate* iq = (struct ipsecmod_qstate*)qstate->minfo[id];
    568 	verbose(VERB_QUERY, "ipsecmod[module %d] operate: extstate:%s event:%s",
    569 		id, strextstate(qstate->ext_state[id]), strmodulevent(event));
    570 	if(iq) log_query_info(VERB_QUERY, "ipsecmod operate: query",
    571 		&qstate->qinfo);
    572 
    573 	/* create ipsecmod_qstate. */
    574 	if((event == module_event_new || event == module_event_pass) &&
    575 		iq == NULL) {
    576 		if(!ipsecmod_new(qstate, id)) {
    577 			errinf(qstate, "ipsecmod: could not ipsecmod_new");
    578 			ipsecmod_error(qstate, id);
    579 			return;
    580 		}
    581 		iq = (struct ipsecmod_qstate*)qstate->minfo[id];
    582 	}
    583 	if(iq && (event == module_event_pass || event == module_event_new)) {
    584 		ipsecmod_handle_query(qstate, iq, ie, id);
    585 		return;
    586 	}
    587 	if(iq && (event == module_event_moddone)) {
    588 		ipsecmod_handle_response(qstate, iq, ie, id);
    589 		return;
    590 	}
    591 	if(iq && outbound) {
    592 		/* cachedb does not need to process responses at this time
    593 		 * ignore it.
    594 		cachedb_process_response(qstate, iq, ie, id, outbound, event);
    595 		*/
    596 		return;
    597 	}
    598 	if(event == module_event_error) {
    599 		verbose(VERB_ALGO, "got called with event error, giving up");
    600 		errinf(qstate, "ipsecmod: got called with event error");
    601 		ipsecmod_error(qstate, id);
    602 		return;
    603 	}
    604 	if(!iq && (event == module_event_moddone)) {
    605 		/* during priming, module done but we never started. */
    606 		qstate->ext_state[id] = module_finished;
    607 		return;
    608 	}
    609 
    610 	log_err("ipsecmod: bad event %s", strmodulevent(event));
    611 	errinf(qstate, "ipsecmod: operate got bad event");
    612 	ipsecmod_error(qstate, id);
    613 	return;
    614 }
    615 
    616 void
    617 ipsecmod_inform_super(struct module_qstate* qstate, int id,
    618 	struct module_qstate* super)
    619 {
    620 	struct ipsecmod_qstate* siq;
    621 	log_query_info(VERB_ALGO, "ipsecmod: inform_super, sub is",
    622 		&qstate->qinfo);
    623 	log_query_info(VERB_ALGO, "super is", &super->qinfo);
    624 	siq = (struct ipsecmod_qstate*)super->minfo[id];
    625 	if(!siq) {
    626 		verbose(VERB_ALGO, "super has no ipsecmod state");
    627 		return;
    628 	}
    629 
    630 	if(qstate->return_msg) {
    631 		struct ub_packed_rrset_key* rrset_key = reply_find_answer_rrset(
    632 			&qstate->return_msg->qinfo, qstate->return_msg->rep);
    633 		if(rrset_key) {
    634 			/* We have an answer. */
    635 			/* Copy to super's region. */
    636 			rrset_key = packed_rrset_copy_region(rrset_key, super->region, 0);
    637 			siq->ipseckey_rrset = rrset_key;
    638 			if(!rrset_key) {
    639 				log_err("ipsecmod: out of memory.");
    640 			}
    641 		}
    642 	}
    643 	/* Notify super to proceed. */
    644 	siq->ipseckey_done = 1;
    645 }
    646 
    647 void
    648 ipsecmod_clear(struct module_qstate* qstate, int id)
    649 {
    650 	if(!qstate)
    651 		return;
    652 	qstate->minfo[id] = NULL;
    653 }
    654 
    655 size_t
    656 ipsecmod_get_mem(struct module_env* env, int id)
    657 {
    658 	struct ipsecmod_env* ie = (struct ipsecmod_env*)env->modinfo[id];
    659 	if(!ie)
    660 		return 0;
    661 	return sizeof(*ie) + ipsecmod_whitelist_get_mem(ie->whitelist);
    662 }
    663 
    664 /**
    665  * The ipsecmod function block
    666  */
    667 static struct module_func_block ipsecmod_block = {
    668 	"ipsecmod",
    669 	NULL, NULL, &ipsecmod_init, &ipsecmod_deinit, &ipsecmod_operate,
    670 	&ipsecmod_inform_super, &ipsecmod_clear, &ipsecmod_get_mem
    671 };
    672 
    673 struct module_func_block*
    674 ipsecmod_get_funcblock(void)
    675 {
    676 	return &ipsecmod_block;
    677 }
    678 #endif /* USE_IPSECMOD */
    679