libworker.c revision 249141
1238106Sdes/*
2238106Sdes * libunbound/worker.c - worker thread or process that resolves
3238106Sdes *
4238106Sdes * Copyright (c) 2007, NLnet Labs. All rights reserved.
5238106Sdes *
6238106Sdes * This software is open source.
7238106Sdes *
8238106Sdes * Redistribution and use in source and binary forms, with or without
9238106Sdes * modification, are permitted provided that the following conditions
10238106Sdes * are met:
11238106Sdes *
12238106Sdes * Redistributions of source code must retain the above copyright notice,
13238106Sdes * this list of conditions and the following disclaimer.
14238106Sdes *
15238106Sdes * Redistributions in binary form must reproduce the above copyright notice,
16238106Sdes * this list of conditions and the following disclaimer in the documentation
17238106Sdes * and/or other materials provided with the distribution.
18238106Sdes *
19238106Sdes * Neither the name of the NLNET LABS nor the names of its contributors may
20238106Sdes * be used to endorse or promote products derived from this software without
21238106Sdes * specific prior written permission.
22238106Sdes *
23238106Sdes * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
24238106Sdes * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
25238106Sdes * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
26238106Sdes * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE
27238106Sdes * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
28238106Sdes * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
29238106Sdes * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
30238106Sdes * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
31238106Sdes * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
32238106Sdes * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
33238106Sdes * POSSIBILITY OF SUCH DAMAGE.
34238106Sdes */
35238106Sdes
36238106Sdes/**
37238106Sdes * \file
38238106Sdes *
39238106Sdes * This file contains the worker process or thread that performs
40238106Sdes * the DNS resolving and validation. The worker is called by a procedure
41238106Sdes * and if in the background continues until exit, if in the foreground
42238106Sdes * returns from the procedure when done.
43238106Sdes */
44238106Sdes#include "config.h"
45238106Sdes#include <ldns/dname.h>
46238106Sdes#include <ldns/wire2host.h>
47249141Sdes#ifdef HAVE_SSL
48238106Sdes#include <openssl/ssl.h>
49249141Sdes#endif
50238106Sdes#include "libunbound/libworker.h"
51238106Sdes#include "libunbound/context.h"
52238106Sdes#include "libunbound/unbound.h"
53238106Sdes#include "services/outside_network.h"
54238106Sdes#include "services/mesh.h"
55238106Sdes#include "services/localzone.h"
56238106Sdes#include "services/cache/rrset.h"
57238106Sdes#include "services/outbound_list.h"
58238106Sdes#include "util/module.h"
59238106Sdes#include "util/regional.h"
60238106Sdes#include "util/random.h"
61238106Sdes#include "util/config_file.h"
62238106Sdes#include "util/netevent.h"
63238106Sdes#include "util/storage/lookup3.h"
64238106Sdes#include "util/storage/slabhash.h"
65238106Sdes#include "util/net_help.h"
66238106Sdes#include "util/data/dname.h"
67238106Sdes#include "util/data/msgreply.h"
68238106Sdes#include "util/data/msgencode.h"
69238106Sdes#include "util/tube.h"
70238106Sdes#include "iterator/iter_fwd.h"
71238106Sdes#include "iterator/iter_hints.h"
72238106Sdes
73238106Sdes/** handle new query command for bg worker */
74238106Sdesstatic void handle_newq(struct libworker* w, uint8_t* buf, uint32_t len);
75238106Sdes
76238106Sdes/** delete libworker struct */
77238106Sdesstatic void
78238106Sdeslibworker_delete(struct libworker* w)
79238106Sdes{
80238106Sdes	if(!w) return;
81238106Sdes	if(w->env) {
82238106Sdes		outside_network_quit_prepare(w->back);
83238106Sdes		mesh_delete(w->env->mesh);
84238106Sdes		context_release_alloc(w->ctx, w->env->alloc,
85238106Sdes			!w->is_bg || w->is_bg_thread);
86238106Sdes		ldns_buffer_free(w->env->scratch_buffer);
87238106Sdes		regional_destroy(w->env->scratch);
88238106Sdes		forwards_delete(w->env->fwds);
89238106Sdes		hints_delete(w->env->hints);
90238106Sdes		ub_randfree(w->env->rnd);
91238106Sdes		free(w->env);
92238106Sdes	}
93249141Sdes#ifdef HAVE_SSL
94238106Sdes	SSL_CTX_free(w->sslctx);
95249141Sdes#endif
96238106Sdes	outside_network_delete(w->back);
97238106Sdes	comm_base_delete(w->base);
98238106Sdes	free(w);
99238106Sdes}
100238106Sdes
101238106Sdes/** setup fresh libworker struct */
102238106Sdesstatic struct libworker*
103238106Sdeslibworker_setup(struct ub_ctx* ctx, int is_bg)
104238106Sdes{
105238106Sdes	unsigned int seed;
106238106Sdes	struct libworker* w = (struct libworker*)calloc(1, sizeof(*w));
107238106Sdes	struct config_file* cfg = ctx->env->cfg;
108238106Sdes	int* ports;
109238106Sdes	int numports;
110238106Sdes	if(!w) return NULL;
111238106Sdes	w->is_bg = is_bg;
112238106Sdes	w->ctx = ctx;
113238106Sdes	w->env = (struct module_env*)malloc(sizeof(*w->env));
114238106Sdes	if(!w->env) {
115238106Sdes		free(w);
116238106Sdes		return NULL;
117238106Sdes	}
118238106Sdes	*w->env = *ctx->env;
119238106Sdes	w->env->alloc = context_obtain_alloc(ctx, !w->is_bg || w->is_bg_thread);
120238106Sdes	if(!w->env->alloc) {
121238106Sdes		libworker_delete(w);
122238106Sdes		return NULL;
123238106Sdes	}
124238106Sdes	w->thread_num = w->env->alloc->thread_num;
125238106Sdes	alloc_set_id_cleanup(w->env->alloc, &libworker_alloc_cleanup, w);
126238106Sdes	if(!w->is_bg || w->is_bg_thread) {
127238106Sdes		lock_basic_lock(&ctx->cfglock);
128238106Sdes	}
129238106Sdes	w->env->scratch = regional_create_custom(cfg->msg_buffer_size);
130238106Sdes	w->env->scratch_buffer = ldns_buffer_new(cfg->msg_buffer_size);
131238106Sdes	w->env->fwds = forwards_create();
132238106Sdes	if(w->env->fwds && !forwards_apply_cfg(w->env->fwds, cfg)) {
133238106Sdes		forwards_delete(w->env->fwds);
134238106Sdes		w->env->fwds = NULL;
135238106Sdes	}
136238106Sdes	w->env->hints = hints_create();
137238106Sdes	if(w->env->hints && !hints_apply_cfg(w->env->hints, cfg)) {
138238106Sdes		hints_delete(w->env->hints);
139238106Sdes		w->env->hints = NULL;
140238106Sdes	}
141238106Sdes	if(cfg->ssl_upstream) {
142238106Sdes		w->sslctx = connect_sslctx_create(NULL, NULL, NULL);
143238106Sdes		if(!w->sslctx) {
144238106Sdes			/* to make the setup fail after unlock */
145238106Sdes			hints_delete(w->env->hints);
146238106Sdes			w->env->hints = NULL;
147238106Sdes		}
148238106Sdes	}
149238106Sdes	if(!w->is_bg || w->is_bg_thread) {
150238106Sdes		lock_basic_unlock(&ctx->cfglock);
151238106Sdes	}
152238106Sdes	if(!w->env->scratch || !w->env->scratch_buffer || !w->env->fwds ||
153238106Sdes		!w->env->hints) {
154238106Sdes		libworker_delete(w);
155238106Sdes		return NULL;
156238106Sdes	}
157238106Sdes	w->env->worker = (struct worker*)w;
158238106Sdes	w->env->probe_timer = NULL;
159238106Sdes	seed = (unsigned int)time(NULL) ^ (unsigned int)getpid() ^
160238106Sdes		(((unsigned int)w->thread_num)<<17);
161238106Sdes	seed ^= (unsigned int)w->env->alloc->next_id;
162238106Sdes	if(!w->is_bg || w->is_bg_thread) {
163238106Sdes		lock_basic_lock(&ctx->cfglock);
164238106Sdes	}
165238106Sdes	if(!(w->env->rnd = ub_initstate(seed, ctx->seed_rnd))) {
166238106Sdes		if(!w->is_bg || w->is_bg_thread) {
167238106Sdes			lock_basic_unlock(&ctx->cfglock);
168238106Sdes		}
169238106Sdes		seed = 0;
170238106Sdes		libworker_delete(w);
171238106Sdes		return NULL;
172238106Sdes	}
173238106Sdes	if(!w->is_bg || w->is_bg_thread) {
174238106Sdes		lock_basic_unlock(&ctx->cfglock);
175238106Sdes	}
176238106Sdes	if(1) {
177238106Sdes		/* primitive lockout for threading: if it overwrites another
178238106Sdes		 * thread it is like wiping the cache (which is likely empty
179238106Sdes		 * at the start) */
180238106Sdes		/* note we are holding the ctx lock in normal threaded
181238106Sdes		 * cases so that is solved properly, it is only for many ctx
182238106Sdes		 * in different threads that this may clash */
183238106Sdes		static int done_raninit = 0;
184238106Sdes		if(!done_raninit) {
185238106Sdes			done_raninit = 1;
186238106Sdes			hash_set_raninit((uint32_t)ub_random(w->env->rnd));
187238106Sdes		}
188238106Sdes	}
189238106Sdes	seed = 0;
190238106Sdes
191238106Sdes	w->base = comm_base_create(0);
192238106Sdes	if(!w->base) {
193238106Sdes		libworker_delete(w);
194238106Sdes		return NULL;
195238106Sdes	}
196238106Sdes	if(!w->is_bg || w->is_bg_thread) {
197238106Sdes		lock_basic_lock(&ctx->cfglock);
198238106Sdes	}
199238106Sdes	numports = cfg_condense_ports(cfg, &ports);
200238106Sdes	if(numports == 0) {
201238106Sdes		libworker_delete(w);
202238106Sdes		return NULL;
203238106Sdes	}
204238106Sdes	w->back = outside_network_create(w->base, cfg->msg_buffer_size,
205238106Sdes		(size_t)cfg->outgoing_num_ports, cfg->out_ifs,
206238106Sdes		cfg->num_out_ifs, cfg->do_ip4, cfg->do_ip6,
207238106Sdes		cfg->do_tcp?cfg->outgoing_num_tcp:0,
208238106Sdes		w->env->infra_cache, w->env->rnd, cfg->use_caps_bits_for_id,
209238106Sdes		ports, numports, cfg->unwanted_threshold,
210238106Sdes		&libworker_alloc_cleanup, w, cfg->do_udp, w->sslctx);
211238106Sdes	if(!w->is_bg || w->is_bg_thread) {
212238106Sdes		lock_basic_unlock(&ctx->cfglock);
213238106Sdes	}
214238106Sdes	free(ports);
215238106Sdes	if(!w->back) {
216238106Sdes		libworker_delete(w);
217238106Sdes		return NULL;
218238106Sdes	}
219238106Sdes	w->env->mesh = mesh_create(&ctx->mods, w->env);
220238106Sdes	if(!w->env->mesh) {
221238106Sdes		libworker_delete(w);
222238106Sdes		return NULL;
223238106Sdes	}
224238106Sdes	w->env->send_query = &libworker_send_query;
225238106Sdes	w->env->detach_subs = &mesh_detach_subs;
226238106Sdes	w->env->attach_sub = &mesh_attach_sub;
227238106Sdes	w->env->kill_sub = &mesh_state_delete;
228238106Sdes	w->env->detect_cycle = &mesh_detect_cycle;
229238106Sdes	comm_base_timept(w->base, &w->env->now, &w->env->now_tv);
230238106Sdes	return w;
231238106Sdes}
232238106Sdes
233238106Sdes/** handle cancel command for bg worker */
234238106Sdesstatic void
235238106Sdeshandle_cancel(struct libworker* w, uint8_t* buf, uint32_t len)
236238106Sdes{
237238106Sdes	struct ctx_query* q;
238238106Sdes	if(w->is_bg_thread) {
239238106Sdes		lock_basic_lock(&w->ctx->cfglock);
240238106Sdes		q = context_deserialize_cancel(w->ctx, buf, len);
241238106Sdes		lock_basic_unlock(&w->ctx->cfglock);
242238106Sdes	} else {
243238106Sdes		q = context_deserialize_cancel(w->ctx, buf, len);
244238106Sdes	}
245238106Sdes	if(!q) {
246238106Sdes		/* probably simply lookup failed, i.e. the message had been
247238106Sdes		 * processed and answered before the cancel arrived */
248238106Sdes		return;
249238106Sdes	}
250238106Sdes	q->cancelled = 1;
251238106Sdes	free(buf);
252238106Sdes}
253238106Sdes
254238106Sdes/** do control command coming into bg server */
255238106Sdesstatic void
256238106Sdeslibworker_do_cmd(struct libworker* w, uint8_t* msg, uint32_t len)
257238106Sdes{
258238106Sdes	switch(context_serial_getcmd(msg, len)) {
259238106Sdes		default:
260238106Sdes		case UB_LIBCMD_ANSWER:
261238106Sdes			log_err("unknown command for bg worker %d",
262238106Sdes				(int)context_serial_getcmd(msg, len));
263238106Sdes			/* and fall through to quit */
264238106Sdes		case UB_LIBCMD_QUIT:
265238106Sdes			free(msg);
266238106Sdes			comm_base_exit(w->base);
267238106Sdes			break;
268238106Sdes		case UB_LIBCMD_NEWQUERY:
269238106Sdes			handle_newq(w, msg, len);
270238106Sdes			break;
271238106Sdes		case UB_LIBCMD_CANCEL:
272238106Sdes			handle_cancel(w, msg, len);
273238106Sdes			break;
274238106Sdes	}
275238106Sdes}
276238106Sdes
277238106Sdes/** handle control command coming into server */
278238106Sdesvoid
279238106Sdeslibworker_handle_control_cmd(struct tube* ATTR_UNUSED(tube),
280238106Sdes	uint8_t* msg, size_t len, int err, void* arg)
281238106Sdes{
282238106Sdes	struct libworker* w = (struct libworker*)arg;
283238106Sdes
284238106Sdes	if(err != 0) {
285238106Sdes		free(msg);
286238106Sdes		/* it is of no use to go on, exit */
287238106Sdes		comm_base_exit(w->base);
288238106Sdes		return;
289238106Sdes	}
290238106Sdes	libworker_do_cmd(w, msg, len); /* also frees the buf */
291238106Sdes}
292238106Sdes
293238106Sdes/** the background thread func */
294238106Sdesstatic void*
295238106Sdeslibworker_dobg(void* arg)
296238106Sdes{
297238106Sdes	/* setup */
298238106Sdes	uint32_t m;
299238106Sdes	struct libworker* w = (struct libworker*)arg;
300238106Sdes	struct ub_ctx* ctx;
301238106Sdes	if(!w) {
302238106Sdes		log_err("libunbound bg worker init failed, nomem");
303238106Sdes		return NULL;
304238106Sdes	}
305238106Sdes	ctx = w->ctx;
306238106Sdes	log_thread_set(&w->thread_num);
307238106Sdes#ifdef THREADS_DISABLED
308238106Sdes	/* we are forked */
309238106Sdes	w->is_bg_thread = 0;
310238106Sdes	/* close non-used parts of the pipes */
311238106Sdes	tube_close_write(ctx->qq_pipe);
312238106Sdes	tube_close_read(ctx->rr_pipe);
313238106Sdes#endif
314238106Sdes	if(!tube_setup_bg_listen(ctx->qq_pipe, w->base,
315238106Sdes		libworker_handle_control_cmd, w)) {
316238106Sdes		log_err("libunbound bg worker init failed, no bglisten");
317238106Sdes		return NULL;
318238106Sdes	}
319238106Sdes	if(!tube_setup_bg_write(ctx->rr_pipe, w->base)) {
320238106Sdes		log_err("libunbound bg worker init failed, no bgwrite");
321238106Sdes		return NULL;
322238106Sdes	}
323238106Sdes
324238106Sdes	/* do the work */
325238106Sdes	comm_base_dispatch(w->base);
326238106Sdes
327238106Sdes	/* cleanup */
328238106Sdes	m = UB_LIBCMD_QUIT;
329238106Sdes	tube_remove_bg_listen(w->ctx->qq_pipe);
330238106Sdes	tube_remove_bg_write(w->ctx->rr_pipe);
331238106Sdes	libworker_delete(w);
332238106Sdes	(void)tube_write_msg(ctx->rr_pipe, (uint8_t*)&m,
333238106Sdes		(uint32_t)sizeof(m), 0);
334238106Sdes#ifdef THREADS_DISABLED
335238106Sdes	/* close pipes from forked process before exit */
336238106Sdes	tube_close_read(ctx->qq_pipe);
337238106Sdes	tube_close_write(ctx->rr_pipe);
338238106Sdes#endif
339238106Sdes	return NULL;
340238106Sdes}
341238106Sdes
342238106Sdesint libworker_bg(struct ub_ctx* ctx)
343238106Sdes{
344238106Sdes	struct libworker* w;
345238106Sdes	/* fork or threadcreate */
346238106Sdes	lock_basic_lock(&ctx->cfglock);
347238106Sdes	if(ctx->dothread) {
348238106Sdes		lock_basic_unlock(&ctx->cfglock);
349238106Sdes		w = libworker_setup(ctx, 1);
350238106Sdes		if(!w) return UB_NOMEM;
351238106Sdes		w->is_bg_thread = 1;
352238106Sdes#ifdef ENABLE_LOCK_CHECKS
353238106Sdes		w->thread_num = 1; /* for nicer DEBUG checklocks */
354238106Sdes#endif
355238106Sdes		ub_thread_create(&ctx->bg_tid, libworker_dobg, w);
356238106Sdes	} else {
357238106Sdes		lock_basic_unlock(&ctx->cfglock);
358238106Sdes#ifndef HAVE_FORK
359238106Sdes		/* no fork on windows */
360238106Sdes		return UB_FORKFAIL;
361238106Sdes#else /* HAVE_FORK */
362238106Sdes		switch((ctx->bg_pid=fork())) {
363238106Sdes			case 0:
364238106Sdes				w = libworker_setup(ctx, 1);
365238106Sdes				if(!w) fatal_exit("out of memory");
366238106Sdes				/* close non-used parts of the pipes */
367238106Sdes				tube_close_write(ctx->qq_pipe);
368238106Sdes				tube_close_read(ctx->rr_pipe);
369238106Sdes				(void)libworker_dobg(w);
370238106Sdes				exit(0);
371238106Sdes				break;
372238106Sdes			case -1:
373238106Sdes				return UB_FORKFAIL;
374238106Sdes			default:
375238106Sdes				break;
376238106Sdes		}
377238106Sdes#endif /* HAVE_FORK */
378238106Sdes	}
379238106Sdes	return UB_NOERROR;
380238106Sdes}
381238106Sdes
382238106Sdes/** get msg reply struct (in temp region) */
383238106Sdesstatic struct reply_info*
384238106Sdesparse_reply(ldns_buffer* pkt, struct regional* region, struct query_info* qi)
385238106Sdes{
386238106Sdes	struct reply_info* rep;
387238106Sdes	struct msg_parse* msg;
388238106Sdes	if(!(msg = regional_alloc(region, sizeof(*msg)))) {
389238106Sdes		return NULL;
390238106Sdes	}
391238106Sdes	memset(msg, 0, sizeof(*msg));
392238106Sdes	ldns_buffer_set_position(pkt, 0);
393238106Sdes	if(parse_packet(pkt, msg, region) != 0)
394238106Sdes		return 0;
395238106Sdes	if(!parse_create_msg(pkt, msg, NULL, qi, &rep, region)) {
396238106Sdes		return 0;
397238106Sdes	}
398238106Sdes	return rep;
399238106Sdes}
400238106Sdes
401238106Sdes/** insert canonname */
402238106Sdesstatic int
403238106Sdesfill_canon(struct ub_result* res, uint8_t* s)
404238106Sdes{
405238106Sdes	char buf[255+2];
406238106Sdes	dname_str(s, buf);
407238106Sdes	res->canonname = strdup(buf);
408238106Sdes	return res->canonname != 0;
409238106Sdes}
410238106Sdes
411238106Sdes/** fill data into result */
412238106Sdesstatic int
413238106Sdesfill_res(struct ub_result* res, struct ub_packed_rrset_key* answer,
414249141Sdes	uint8_t* finalcname, struct query_info* rq, struct reply_info* rep)
415238106Sdes{
416238106Sdes	size_t i;
417238106Sdes	struct packed_rrset_data* data;
418249141Sdes	res->ttl = 0;
419238106Sdes	if(!answer) {
420238106Sdes		if(finalcname) {
421238106Sdes			if(!fill_canon(res, finalcname))
422238106Sdes				return 0; /* out of memory */
423238106Sdes		}
424249141Sdes		if(rep->rrset_count != 0)
425249141Sdes			res->ttl = (int)rep->ttl;
426238106Sdes		res->data = (char**)calloc(1, sizeof(char*));
427238106Sdes		res->len = (int*)calloc(1, sizeof(int));
428238106Sdes		return (res->data && res->len);
429238106Sdes	}
430238106Sdes	data = (struct packed_rrset_data*)answer->entry.data;
431238106Sdes	if(query_dname_compare(rq->qname, answer->rk.dname) != 0) {
432238106Sdes		if(!fill_canon(res, answer->rk.dname))
433238106Sdes			return 0; /* out of memory */
434238106Sdes	} else	res->canonname = NULL;
435238106Sdes	res->data = (char**)calloc(data->count+1, sizeof(char*));
436238106Sdes	res->len = (int*)calloc(data->count+1, sizeof(int));
437238106Sdes	if(!res->data || !res->len)
438238106Sdes		return 0; /* out of memory */
439238106Sdes	for(i=0; i<data->count; i++) {
440238106Sdes		/* remove rdlength from rdata */
441238106Sdes		res->len[i] = (int)(data->rr_len[i] - 2);
442238106Sdes		res->data[i] = memdup(data->rr_data[i]+2, (size_t)res->len[i]);
443238106Sdes		if(!res->data[i])
444238106Sdes			return 0; /* out of memory */
445238106Sdes	}
446249141Sdes	/* ttl for positive answers, from CNAME and answer RRs */
447249141Sdes	if(data->count != 0) {
448249141Sdes		size_t j;
449249141Sdes		res->ttl = (int)data->ttl;
450249141Sdes		for(j=0; j<rep->an_numrrsets; j++) {
451249141Sdes			struct packed_rrset_data* d =
452249141Sdes				(struct packed_rrset_data*)rep->rrsets[j]->
453249141Sdes				entry.data;
454249141Sdes			if((int)d->ttl < res->ttl)
455249141Sdes				res->ttl = (int)d->ttl;
456249141Sdes		}
457249141Sdes	}
458249141Sdes	/* ttl for negative answers */
459249141Sdes	if(data->count == 0 && rep->rrset_count != 0)
460249141Sdes		res->ttl = (int)rep->ttl;
461238106Sdes	res->data[data->count] = NULL;
462238106Sdes	res->len[data->count] = 0;
463238106Sdes	return 1;
464238106Sdes}
465238106Sdes
466238106Sdes/** fill result from parsed message, on error fills servfail */
467238106Sdesvoid
468238106Sdeslibworker_enter_result(struct ub_result* res, ldns_buffer* buf,
469238106Sdes	struct regional* temp, enum sec_status msg_security)
470238106Sdes{
471238106Sdes	struct query_info rq;
472238106Sdes	struct reply_info* rep;
473238106Sdes	res->rcode = LDNS_RCODE_SERVFAIL;
474238106Sdes	rep = parse_reply(buf, temp, &rq);
475238106Sdes	if(!rep) {
476238106Sdes		log_err("cannot parse buf");
477238106Sdes		return; /* error parsing buf, or out of memory */
478238106Sdes	}
479238106Sdes	if(!fill_res(res, reply_find_answer_rrset(&rq, rep),
480249141Sdes		reply_find_final_cname_target(&rq, rep), &rq, rep))
481238106Sdes		return; /* out of memory */
482238106Sdes	/* rcode, havedata, nxdomain, secure, bogus */
483238106Sdes	res->rcode = (int)FLAGS_GET_RCODE(rep->flags);
484238106Sdes	if(res->data && res->data[0])
485238106Sdes		res->havedata = 1;
486238106Sdes	if(res->rcode == LDNS_RCODE_NXDOMAIN)
487238106Sdes		res->nxdomain = 1;
488238106Sdes	if(msg_security == sec_status_secure)
489238106Sdes		res->secure = 1;
490238106Sdes	if(msg_security == sec_status_bogus)
491238106Sdes		res->bogus = 1;
492238106Sdes}
493238106Sdes
494238106Sdes/** fillup fg results */
495238106Sdesstatic void
496238106Sdeslibworker_fillup_fg(struct ctx_query* q, int rcode, ldns_buffer* buf,
497238106Sdes	enum sec_status s, char* why_bogus)
498238106Sdes{
499238106Sdes	if(why_bogus)
500238106Sdes		q->res->why_bogus = strdup(why_bogus);
501238106Sdes	if(rcode != 0) {
502238106Sdes		q->res->rcode = rcode;
503238106Sdes		q->msg_security = s;
504238106Sdes		return;
505238106Sdes	}
506238106Sdes
507238106Sdes	q->res->rcode = LDNS_RCODE_SERVFAIL;
508238106Sdes	q->msg_security = 0;
509238106Sdes	q->msg = memdup(ldns_buffer_begin(buf), ldns_buffer_limit(buf));
510238106Sdes	q->msg_len = ldns_buffer_limit(buf);
511238106Sdes	if(!q->msg) {
512238106Sdes		return; /* the error is in the rcode */
513238106Sdes	}
514238106Sdes
515238106Sdes	/* canonname and results */
516238106Sdes	q->msg_security = s;
517238106Sdes	libworker_enter_result(q->res, buf, q->w->env->scratch, s);
518238106Sdes}
519238106Sdes
520238106Sdesvoid
521238106Sdeslibworker_fg_done_cb(void* arg, int rcode, ldns_buffer* buf, enum sec_status s,
522238106Sdes	char* why_bogus)
523238106Sdes{
524238106Sdes	struct ctx_query* q = (struct ctx_query*)arg;
525238106Sdes	/* fg query is done; exit comm base */
526238106Sdes	comm_base_exit(q->w->base);
527238106Sdes
528238106Sdes	libworker_fillup_fg(q, rcode, buf, s, why_bogus);
529238106Sdes}
530238106Sdes
531238106Sdes/** setup qinfo and edns */
532238106Sdesstatic int
533238106Sdessetup_qinfo_edns(struct libworker* w, struct ctx_query* q,
534238106Sdes	struct query_info* qinfo, struct edns_data* edns)
535238106Sdes{
536238106Sdes	ldns_rdf* rdf;
537238106Sdes	qinfo->qtype = (uint16_t)q->res->qtype;
538238106Sdes	qinfo->qclass = (uint16_t)q->res->qclass;
539238106Sdes	rdf = ldns_dname_new_frm_str(q->res->qname);
540238106Sdes	if(!rdf) {
541238106Sdes		return 0;
542238106Sdes	}
543238106Sdes#ifdef UNBOUND_ALLOC_LITE
544238106Sdes	qinfo->qname = memdup(ldns_rdf_data(rdf), ldns_rdf_size(rdf));
545238106Sdes	qinfo->qname_len = ldns_rdf_size(rdf);
546238106Sdes	ldns_rdf_deep_free(rdf);
547238106Sdes	rdf = 0;
548238106Sdes#else
549238106Sdes	qinfo->qname = ldns_rdf_data(rdf);
550238106Sdes	qinfo->qname_len = ldns_rdf_size(rdf);
551238106Sdes#endif
552238106Sdes	edns->edns_present = 1;
553238106Sdes	edns->ext_rcode = 0;
554238106Sdes	edns->edns_version = 0;
555238106Sdes	edns->bits = EDNS_DO;
556238106Sdes	if(ldns_buffer_capacity(w->back->udp_buff) < 65535)
557238106Sdes		edns->udp_size = (uint16_t)ldns_buffer_capacity(
558238106Sdes			w->back->udp_buff);
559238106Sdes	else	edns->udp_size = 65535;
560238106Sdes	ldns_rdf_free(rdf);
561238106Sdes	return 1;
562238106Sdes}
563238106Sdes
564238106Sdesint libworker_fg(struct ub_ctx* ctx, struct ctx_query* q)
565238106Sdes{
566238106Sdes	struct libworker* w = libworker_setup(ctx, 0);
567238106Sdes	uint16_t qflags, qid;
568238106Sdes	struct query_info qinfo;
569238106Sdes	struct edns_data edns;
570238106Sdes	if(!w)
571238106Sdes		return UB_INITFAIL;
572238106Sdes	if(!setup_qinfo_edns(w, q, &qinfo, &edns)) {
573238106Sdes		libworker_delete(w);
574238106Sdes		return UB_SYNTAX;
575238106Sdes	}
576238106Sdes	qid = 0;
577238106Sdes	qflags = BIT_RD;
578238106Sdes	q->w = w;
579238106Sdes	/* see if there is a fixed answer */
580238106Sdes	ldns_buffer_write_u16_at(w->back->udp_buff, 0, qid);
581238106Sdes	ldns_buffer_write_u16_at(w->back->udp_buff, 2, qflags);
582238106Sdes	if(local_zones_answer(ctx->local_zones, &qinfo, &edns,
583238106Sdes		w->back->udp_buff, w->env->scratch)) {
584238106Sdes		regional_free_all(w->env->scratch);
585238106Sdes		libworker_fillup_fg(q, LDNS_RCODE_NOERROR,
586238106Sdes			w->back->udp_buff, sec_status_insecure, NULL);
587238106Sdes		libworker_delete(w);
588238106Sdes		free(qinfo.qname);
589238106Sdes		return UB_NOERROR;
590238106Sdes	}
591238106Sdes	/* process new query */
592238106Sdes	if(!mesh_new_callback(w->env->mesh, &qinfo, qflags, &edns,
593238106Sdes		w->back->udp_buff, qid, libworker_fg_done_cb, q)) {
594238106Sdes		free(qinfo.qname);
595238106Sdes		return UB_NOMEM;
596238106Sdes	}
597238106Sdes	free(qinfo.qname);
598238106Sdes
599238106Sdes	/* wait for reply */
600238106Sdes	comm_base_dispatch(w->base);
601238106Sdes
602238106Sdes	libworker_delete(w);
603238106Sdes	return UB_NOERROR;
604238106Sdes}
605238106Sdes
606238106Sdes/** add result to the bg worker result queue */
607238106Sdesstatic void
608238106Sdesadd_bg_result(struct libworker* w, struct ctx_query* q, ldns_buffer* pkt,
609238106Sdes	int err, char* reason)
610238106Sdes{
611238106Sdes	uint8_t* msg = NULL;
612238106Sdes	uint32_t len = 0;
613238106Sdes
614238106Sdes	/* serialize and delete unneeded q */
615238106Sdes	if(w->is_bg_thread) {
616238106Sdes		lock_basic_lock(&w->ctx->cfglock);
617238106Sdes		if(reason)
618238106Sdes			q->res->why_bogus = strdup(reason);
619238106Sdes		if(pkt) {
620238106Sdes			q->msg_len = ldns_buffer_remaining(pkt);
621238106Sdes			q->msg = memdup(ldns_buffer_begin(pkt), q->msg_len);
622238106Sdes			if(!q->msg)
623238106Sdes				msg = context_serialize_answer(q, UB_NOMEM,
624238106Sdes				NULL, &len);
625238106Sdes			else	msg = context_serialize_answer(q, err,
626238106Sdes				NULL, &len);
627238106Sdes		} else msg = context_serialize_answer(q, err, NULL, &len);
628238106Sdes		lock_basic_unlock(&w->ctx->cfglock);
629238106Sdes	} else {
630238106Sdes		if(reason)
631238106Sdes			q->res->why_bogus = strdup(reason);
632238106Sdes		msg = context_serialize_answer(q, err, pkt, &len);
633238106Sdes		(void)rbtree_delete(&w->ctx->queries, q->node.key);
634238106Sdes		w->ctx->num_async--;
635238106Sdes		context_query_delete(q);
636238106Sdes	}
637238106Sdes
638238106Sdes	if(!msg) {
639238106Sdes		log_err("out of memory for async answer");
640238106Sdes		return;
641238106Sdes	}
642238106Sdes	if(!tube_queue_item(w->ctx->rr_pipe, msg, len)) {
643238106Sdes		log_err("out of memory for async answer");
644238106Sdes		return;
645238106Sdes	}
646238106Sdes}
647238106Sdes
648238106Sdesvoid
649238106Sdeslibworker_bg_done_cb(void* arg, int rcode, ldns_buffer* buf, enum sec_status s,
650238106Sdes	char* why_bogus)
651238106Sdes{
652238106Sdes	struct ctx_query* q = (struct ctx_query*)arg;
653238106Sdes
654238106Sdes	if(q->cancelled) {
655238106Sdes		if(q->w->is_bg_thread) {
656238106Sdes			/* delete it now */
657238106Sdes			struct ub_ctx* ctx = q->w->ctx;
658238106Sdes			lock_basic_lock(&ctx->cfglock);
659238106Sdes			(void)rbtree_delete(&ctx->queries, q->node.key);
660238106Sdes			ctx->num_async--;
661238106Sdes			context_query_delete(q);
662238106Sdes			lock_basic_unlock(&ctx->cfglock);
663238106Sdes		}
664238106Sdes		/* cancelled, do not give answer */
665238106Sdes		return;
666238106Sdes	}
667238106Sdes	q->msg_security = s;
668249141Sdes	if(!buf)
669249141Sdes		buf = q->w->env->scratch_buffer;
670238106Sdes	if(rcode != 0) {
671238106Sdes		error_encode(buf, rcode, NULL, 0, BIT_RD, NULL);
672238106Sdes	}
673238106Sdes	add_bg_result(q->w, q, buf, UB_NOERROR, why_bogus);
674238106Sdes}
675238106Sdes
676238106Sdes
677238106Sdes/** handle new query command for bg worker */
678238106Sdesstatic void
679238106Sdeshandle_newq(struct libworker* w, uint8_t* buf, uint32_t len)
680238106Sdes{
681238106Sdes	uint16_t qflags, qid;
682238106Sdes	struct query_info qinfo;
683238106Sdes	struct edns_data edns;
684238106Sdes	struct ctx_query* q;
685238106Sdes	if(w->is_bg_thread) {
686238106Sdes		lock_basic_lock(&w->ctx->cfglock);
687238106Sdes		q = context_lookup_new_query(w->ctx, buf, len);
688238106Sdes		lock_basic_unlock(&w->ctx->cfglock);
689238106Sdes	} else {
690238106Sdes		q = context_deserialize_new_query(w->ctx, buf, len);
691238106Sdes	}
692238106Sdes	free(buf);
693238106Sdes	if(!q) {
694238106Sdes		log_err("failed to deserialize newq");
695238106Sdes		return;
696238106Sdes	}
697238106Sdes	if(!setup_qinfo_edns(w, q, &qinfo, &edns)) {
698238106Sdes		add_bg_result(w, q, NULL, UB_SYNTAX, NULL);
699238106Sdes		return;
700238106Sdes	}
701238106Sdes	qid = 0;
702238106Sdes	qflags = BIT_RD;
703238106Sdes	/* see if there is a fixed answer */
704238106Sdes	ldns_buffer_write_u16_at(w->back->udp_buff, 0, qid);
705238106Sdes	ldns_buffer_write_u16_at(w->back->udp_buff, 2, qflags);
706238106Sdes	if(local_zones_answer(w->ctx->local_zones, &qinfo, &edns,
707238106Sdes		w->back->udp_buff, w->env->scratch)) {
708238106Sdes		regional_free_all(w->env->scratch);
709238106Sdes		q->msg_security = sec_status_insecure;
710238106Sdes		add_bg_result(w, q, w->back->udp_buff, UB_NOERROR, NULL);
711238106Sdes		free(qinfo.qname);
712238106Sdes		return;
713238106Sdes	}
714238106Sdes	q->w = w;
715238106Sdes	/* process new query */
716238106Sdes	if(!mesh_new_callback(w->env->mesh, &qinfo, qflags, &edns,
717238106Sdes		w->back->udp_buff, qid, libworker_bg_done_cb, q)) {
718238106Sdes		add_bg_result(w, q, NULL, UB_NOMEM, NULL);
719238106Sdes	}
720238106Sdes	free(qinfo.qname);
721238106Sdes}
722238106Sdes
723238106Sdesvoid libworker_alloc_cleanup(void* arg)
724238106Sdes{
725238106Sdes	struct libworker* w = (struct libworker*)arg;
726238106Sdes	slabhash_clear(&w->env->rrset_cache->table);
727238106Sdes        slabhash_clear(w->env->msg_cache);
728238106Sdes}
729238106Sdes
730238106Sdesstruct outbound_entry* libworker_send_query(uint8_t* qname, size_t qnamelen,
731238106Sdes        uint16_t qtype, uint16_t qclass, uint16_t flags, int dnssec,
732238106Sdes	int want_dnssec, struct sockaddr_storage* addr, socklen_t addrlen,
733238106Sdes	uint8_t* zone, size_t zonelen, struct module_qstate* q)
734238106Sdes{
735238106Sdes	struct libworker* w = (struct libworker*)q->env->worker;
736238106Sdes	struct outbound_entry* e = (struct outbound_entry*)regional_alloc(
737238106Sdes		q->region, sizeof(*e));
738238106Sdes	if(!e)
739238106Sdes		return NULL;
740238106Sdes	e->qstate = q;
741238106Sdes	e->qsent = outnet_serviced_query(w->back, qname,
742238106Sdes		qnamelen, qtype, qclass, flags, dnssec, want_dnssec,
743238106Sdes		q->env->cfg->tcp_upstream, q->env->cfg->ssl_upstream, addr,
744238106Sdes		addrlen, zone, zonelen, libworker_handle_service_reply, e,
745249141Sdes		w->back->udp_buff);
746238106Sdes	if(!e->qsent) {
747238106Sdes		return NULL;
748238106Sdes	}
749238106Sdes	return e;
750238106Sdes}
751238106Sdes
752238106Sdesint
753238106Sdeslibworker_handle_reply(struct comm_point* c, void* arg, int error,
754238106Sdes        struct comm_reply* reply_info)
755238106Sdes{
756238106Sdes	struct module_qstate* q = (struct module_qstate*)arg;
757238106Sdes	struct libworker* lw = (struct libworker*)q->env->worker;
758238106Sdes	struct outbound_entry e;
759238106Sdes	e.qstate = q;
760238106Sdes	e.qsent = NULL;
761238106Sdes
762238106Sdes	if(error != 0) {
763238106Sdes		mesh_report_reply(lw->env->mesh, &e, reply_info, error);
764238106Sdes		return 0;
765238106Sdes	}
766238106Sdes	/* sanity check. */
767238106Sdes	if(!LDNS_QR_WIRE(ldns_buffer_begin(c->buffer))
768238106Sdes		|| LDNS_OPCODE_WIRE(ldns_buffer_begin(c->buffer)) !=
769238106Sdes			LDNS_PACKET_QUERY
770238106Sdes		|| LDNS_QDCOUNT(ldns_buffer_begin(c->buffer)) > 1) {
771238106Sdes		/* error becomes timeout for the module as if this reply
772238106Sdes		 * never arrived. */
773238106Sdes		mesh_report_reply(lw->env->mesh, &e, reply_info,
774238106Sdes			NETEVENT_TIMEOUT);
775238106Sdes		return 0;
776238106Sdes	}
777238106Sdes	mesh_report_reply(lw->env->mesh, &e, reply_info, NETEVENT_NOERROR);
778238106Sdes	return 0;
779238106Sdes}
780238106Sdes
781238106Sdesint
782238106Sdeslibworker_handle_service_reply(struct comm_point* c, void* arg, int error,
783238106Sdes        struct comm_reply* reply_info)
784238106Sdes{
785238106Sdes	struct outbound_entry* e = (struct outbound_entry*)arg;
786238106Sdes	struct libworker* lw = (struct libworker*)e->qstate->env->worker;
787238106Sdes
788238106Sdes	if(error != 0) {
789238106Sdes		mesh_report_reply(lw->env->mesh, e, reply_info, error);
790238106Sdes		return 0;
791238106Sdes	}
792238106Sdes	/* sanity check. */
793238106Sdes	if(!LDNS_QR_WIRE(ldns_buffer_begin(c->buffer))
794238106Sdes		|| LDNS_OPCODE_WIRE(ldns_buffer_begin(c->buffer)) !=
795238106Sdes			LDNS_PACKET_QUERY
796238106Sdes		|| LDNS_QDCOUNT(ldns_buffer_begin(c->buffer)) > 1) {
797238106Sdes		/* error becomes timeout for the module as if this reply
798238106Sdes		 * never arrived. */
799238106Sdes		mesh_report_reply(lw->env->mesh, e, reply_info,
800238106Sdes			NETEVENT_TIMEOUT);
801238106Sdes		return 0;
802238106Sdes	}
803238106Sdes	mesh_report_reply(lw->env->mesh,  e, reply_info, NETEVENT_NOERROR);
804238106Sdes	return 0;
805238106Sdes}
806238106Sdes
807238106Sdes/* --- fake callbacks for fptr_wlist to work --- */
808238106Sdesvoid worker_handle_control_cmd(struct tube* ATTR_UNUSED(tube),
809238106Sdes	uint8_t* ATTR_UNUSED(buffer), size_t ATTR_UNUSED(len),
810238106Sdes	int ATTR_UNUSED(error), void* ATTR_UNUSED(arg))
811238106Sdes{
812238106Sdes	log_assert(0);
813238106Sdes}
814238106Sdes
815238106Sdesint worker_handle_request(struct comm_point* ATTR_UNUSED(c),
816238106Sdes	void* ATTR_UNUSED(arg), int ATTR_UNUSED(error),
817238106Sdes        struct comm_reply* ATTR_UNUSED(repinfo))
818238106Sdes{
819238106Sdes	log_assert(0);
820238106Sdes	return 0;
821238106Sdes}
822238106Sdes
823238106Sdesint worker_handle_reply(struct comm_point* ATTR_UNUSED(c),
824238106Sdes	void* ATTR_UNUSED(arg), int ATTR_UNUSED(error),
825238106Sdes        struct comm_reply* ATTR_UNUSED(reply_info))
826238106Sdes{
827238106Sdes	log_assert(0);
828238106Sdes	return 0;
829238106Sdes}
830238106Sdes
831238106Sdesint worker_handle_service_reply(struct comm_point* ATTR_UNUSED(c),
832238106Sdes	void* ATTR_UNUSED(arg), int ATTR_UNUSED(error),
833238106Sdes        struct comm_reply* ATTR_UNUSED(reply_info))
834238106Sdes{
835238106Sdes	log_assert(0);
836238106Sdes	return 0;
837238106Sdes}
838238106Sdes
839238106Sdesint remote_accept_callback(struct comm_point* ATTR_UNUSED(c),
840238106Sdes	void* ATTR_UNUSED(arg), int ATTR_UNUSED(error),
841238106Sdes        struct comm_reply* ATTR_UNUSED(repinfo))
842238106Sdes{
843238106Sdes	log_assert(0);
844238106Sdes	return 0;
845238106Sdes}
846238106Sdes
847238106Sdesint remote_control_callback(struct comm_point* ATTR_UNUSED(c),
848238106Sdes	void* ATTR_UNUSED(arg), int ATTR_UNUSED(error),
849238106Sdes        struct comm_reply* ATTR_UNUSED(repinfo))
850238106Sdes{
851238106Sdes	log_assert(0);
852238106Sdes	return 0;
853238106Sdes}
854238106Sdes
855238106Sdesvoid worker_sighandler(int ATTR_UNUSED(sig), void* ATTR_UNUSED(arg))
856238106Sdes{
857238106Sdes	log_assert(0);
858238106Sdes}
859238106Sdes
860238106Sdesstruct outbound_entry* worker_send_query(uint8_t* ATTR_UNUSED(qname),
861238106Sdes	size_t ATTR_UNUSED(qnamelen), uint16_t ATTR_UNUSED(qtype),
862238106Sdes	uint16_t ATTR_UNUSED(qclass), uint16_t ATTR_UNUSED(flags),
863238106Sdes	int ATTR_UNUSED(dnssec), int ATTR_UNUSED(want_dnssec),
864238106Sdes	struct sockaddr_storage* ATTR_UNUSED(addr),
865238106Sdes	socklen_t ATTR_UNUSED(addrlen), struct module_qstate* ATTR_UNUSED(q))
866238106Sdes{
867238106Sdes	log_assert(0);
868238106Sdes	return 0;
869238106Sdes}
870238106Sdes
871238106Sdesvoid
872238106Sdesworker_alloc_cleanup(void* ATTR_UNUSED(arg))
873238106Sdes{
874238106Sdes	log_assert(0);
875238106Sdes}
876238106Sdes
877238106Sdesvoid worker_stat_timer_cb(void* ATTR_UNUSED(arg))
878238106Sdes{
879238106Sdes	log_assert(0);
880238106Sdes}
881238106Sdes
882238106Sdesvoid worker_probe_timer_cb(void* ATTR_UNUSED(arg))
883238106Sdes{
884238106Sdes	log_assert(0);
885238106Sdes}
886238106Sdes
887238106Sdesvoid worker_start_accept(void* ATTR_UNUSED(arg))
888238106Sdes{
889238106Sdes	log_assert(0);
890238106Sdes}
891238106Sdes
892238106Sdesvoid worker_stop_accept(void* ATTR_UNUSED(arg))
893238106Sdes{
894238106Sdes	log_assert(0);
895238106Sdes}
896238106Sdes
897238106Sdesint order_lock_cmp(const void* ATTR_UNUSED(e1), const void* ATTR_UNUSED(e2))
898238106Sdes{
899238106Sdes	log_assert(0);
900238106Sdes	return 0;
901238106Sdes}
902238106Sdes
903238106Sdesint
904238106Sdescodeline_cmp(const void* ATTR_UNUSED(a), const void* ATTR_UNUSED(b))
905238106Sdes{
906238106Sdes	log_assert(0);
907238106Sdes	return 0;
908238106Sdes}
909238106Sdes
910238106Sdesint replay_var_compare(const void* ATTR_UNUSED(a), const void* ATTR_UNUSED(b))
911238106Sdes{
912238106Sdes        log_assert(0);
913238106Sdes        return 0;
914238106Sdes}
915238106Sdes
916238106Sdesvoid remote_get_opt_ssl(char* ATTR_UNUSED(str), void* ATTR_UNUSED(arg))
917238106Sdes{
918238106Sdes        log_assert(0);
919238106Sdes}
920238106Sdes
921238106Sdes#ifdef UB_ON_WINDOWS
922238106Sdesvoid
923238106Sdesworker_win_stop_cb(int ATTR_UNUSED(fd), short ATTR_UNUSED(ev), void*
924238106Sdes        ATTR_UNUSED(arg)) {
925238106Sdes        log_assert(0);
926238106Sdes}
927238106Sdes
928238106Sdesvoid
929238106Sdeswsvc_cron_cb(void* ATTR_UNUSED(arg))
930238106Sdes{
931238106Sdes        log_assert(0);
932238106Sdes}
933238106Sdes#endif /* UB_ON_WINDOWS */
934