1238106Sdes/*
2238106Sdes * unbound.c - unbound validating resolver public API implementation
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
24269257Sdes * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
25269257Sdes * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
26269257Sdes * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
27269257Sdes * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
28269257Sdes * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
29269257Sdes * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
30269257Sdes * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
31269257Sdes * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
32269257Sdes * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
33269257Sdes * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
34238106Sdes */
35238106Sdes
36238106Sdes/**
37238106Sdes * \file
38238106Sdes *
39238106Sdes * This file contains functions to resolve DNS queries and
40238106Sdes * validate the answers. Synchonously and asynchronously.
41238106Sdes *
42238106Sdes */
43238106Sdes
44238106Sdes/* include the public api first, it should be able to stand alone */
45238106Sdes#include "libunbound/unbound.h"
46269257Sdes#include "libunbound/unbound-event.h"
47238106Sdes#include "config.h"
48238106Sdes#include <ctype.h>
49238106Sdes#include "libunbound/context.h"
50238106Sdes#include "libunbound/libworker.h"
51238106Sdes#include "util/locks.h"
52238106Sdes#include "util/config_file.h"
53238106Sdes#include "util/alloc.h"
54238106Sdes#include "util/module.h"
55238106Sdes#include "util/regional.h"
56238106Sdes#include "util/log.h"
57238106Sdes#include "util/random.h"
58238106Sdes#include "util/net_help.h"
59238106Sdes#include "util/tube.h"
60238106Sdes#include "services/modstack.h"
61238106Sdes#include "services/localzone.h"
62238106Sdes#include "services/cache/infra.h"
63238106Sdes#include "services/cache/rrset.h"
64269257Sdes#include "ldns/sbuffer.h"
65269257Sdes#ifdef HAVE_PTHREAD
66269257Sdes#include <signal.h>
67269257Sdes#endif
68238106Sdes
69238106Sdes#if defined(UB_ON_WINDOWS) && defined (HAVE_WINDOWS_H)
70238106Sdes#include <windows.h>
71238106Sdes#include <iphlpapi.h>
72238106Sdes#endif /* UB_ON_WINDOWS */
73238106Sdes
74269257Sdes/** create context functionality, but no pipes */
75269257Sdesstatic struct ub_ctx* ub_ctx_create_nopipe(void)
76238106Sdes{
77238106Sdes	struct ub_ctx* ctx;
78238106Sdes	unsigned int seed;
79238106Sdes#ifdef USE_WINSOCK
80238106Sdes	int r;
81238106Sdes	WSADATA wsa_data;
82238106Sdes#endif
83238106Sdes
84238106Sdes	log_init(NULL, 0, NULL); /* logs to stderr */
85238106Sdes	log_ident_set("libunbound");
86238106Sdes#ifdef USE_WINSOCK
87238106Sdes	if((r = WSAStartup(MAKEWORD(2,2), &wsa_data)) != 0) {
88238106Sdes		log_err("could not init winsock. WSAStartup: %s",
89238106Sdes			wsa_strerror(r));
90238106Sdes		return NULL;
91238106Sdes	}
92238106Sdes#endif
93238106Sdes	verbosity = 0; /* errors only */
94238106Sdes	checklock_start();
95238106Sdes	ctx = (struct ub_ctx*)calloc(1, sizeof(*ctx));
96238106Sdes	if(!ctx) {
97238106Sdes		errno = ENOMEM;
98238106Sdes		return NULL;
99238106Sdes	}
100238106Sdes	alloc_init(&ctx->superalloc, NULL, 0);
101238106Sdes	seed = (unsigned int)time(NULL) ^ (unsigned int)getpid();
102238106Sdes	if(!(ctx->seed_rnd = ub_initstate(seed, NULL))) {
103238106Sdes		seed = 0;
104238106Sdes		ub_randfree(ctx->seed_rnd);
105238106Sdes		free(ctx);
106238106Sdes		errno = ENOMEM;
107238106Sdes		return NULL;
108238106Sdes	}
109238106Sdes	seed = 0;
110238106Sdes	lock_basic_init(&ctx->qqpipe_lock);
111238106Sdes	lock_basic_init(&ctx->rrpipe_lock);
112238106Sdes	lock_basic_init(&ctx->cfglock);
113238106Sdes	ctx->env = (struct module_env*)calloc(1, sizeof(*ctx->env));
114238106Sdes	if(!ctx->env) {
115238106Sdes		ub_randfree(ctx->seed_rnd);
116238106Sdes		free(ctx);
117238106Sdes		errno = ENOMEM;
118238106Sdes		return NULL;
119238106Sdes	}
120238106Sdes	ctx->env->cfg = config_create_forlib();
121238106Sdes	if(!ctx->env->cfg) {
122238106Sdes		free(ctx->env);
123238106Sdes		ub_randfree(ctx->seed_rnd);
124238106Sdes		free(ctx);
125238106Sdes		errno = ENOMEM;
126238106Sdes		return NULL;
127238106Sdes	}
128238106Sdes	ctx->env->alloc = &ctx->superalloc;
129238106Sdes	ctx->env->worker = NULL;
130238106Sdes	ctx->env->need_to_validate = 0;
131238106Sdes	modstack_init(&ctx->mods);
132238106Sdes	rbtree_init(&ctx->queries, &context_query_cmp);
133238106Sdes	return ctx;
134238106Sdes}
135238106Sdes
136269257Sdesstruct ub_ctx*
137269257Sdesub_ctx_create(void)
138269257Sdes{
139269257Sdes	struct ub_ctx* ctx = ub_ctx_create_nopipe();
140269257Sdes	if(!ctx)
141269257Sdes		return NULL;
142269257Sdes	if((ctx->qq_pipe = tube_create()) == NULL) {
143269257Sdes		int e = errno;
144269257Sdes		ub_randfree(ctx->seed_rnd);
145269257Sdes		config_delete(ctx->env->cfg);
146269257Sdes		modstack_desetup(&ctx->mods, ctx->env);
147269257Sdes		free(ctx->env);
148269257Sdes		free(ctx);
149269257Sdes		errno = e;
150269257Sdes		return NULL;
151269257Sdes	}
152269257Sdes	if((ctx->rr_pipe = tube_create()) == NULL) {
153269257Sdes		int e = errno;
154269257Sdes		tube_delete(ctx->qq_pipe);
155269257Sdes		ub_randfree(ctx->seed_rnd);
156269257Sdes		config_delete(ctx->env->cfg);
157269257Sdes		modstack_desetup(&ctx->mods, ctx->env);
158269257Sdes		free(ctx->env);
159269257Sdes		free(ctx);
160269257Sdes		errno = e;
161269257Sdes		return NULL;
162269257Sdes	}
163269257Sdes	return ctx;
164269257Sdes}
165269257Sdes
166269257Sdesstruct ub_ctx*
167269257Sdesub_ctx_create_event(struct event_base* eb)
168269257Sdes{
169269257Sdes	struct ub_ctx* ctx = ub_ctx_create_nopipe();
170269257Sdes	if(!ctx)
171269257Sdes		return NULL;
172269257Sdes	/* no pipes, but we have the locks to make sure everything works */
173269257Sdes	ctx->created_bg = 0;
174269257Sdes	ctx->dothread = 1; /* the processing is in the same process,
175269257Sdes		makes ub_cancel and ub_ctx_delete do the right thing */
176269257Sdes	ctx->event_base = eb;
177269257Sdes	return ctx;
178269257Sdes}
179269257Sdes
180238106Sdes/** delete q */
181238106Sdesstatic void
182238106Sdesdelq(rbnode_t* n, void* ATTR_UNUSED(arg))
183238106Sdes{
184238106Sdes	struct ctx_query* q = (struct ctx_query*)n;
185238106Sdes	context_query_delete(q);
186238106Sdes}
187238106Sdes
188269257Sdes/** stop the bg thread */
189269257Sdesstatic void ub_stop_bg(struct ub_ctx* ctx)
190238106Sdes{
191238106Sdes	/* stop the bg thread */
192238106Sdes	lock_basic_lock(&ctx->cfglock);
193238106Sdes	if(ctx->created_bg) {
194238106Sdes		uint8_t* msg;
195238106Sdes		uint32_t len;
196238106Sdes		uint32_t cmd = UB_LIBCMD_QUIT;
197238106Sdes		lock_basic_unlock(&ctx->cfglock);
198238106Sdes		lock_basic_lock(&ctx->qqpipe_lock);
199238106Sdes		(void)tube_write_msg(ctx->qq_pipe, (uint8_t*)&cmd,
200238106Sdes			(uint32_t)sizeof(cmd), 0);
201238106Sdes		lock_basic_unlock(&ctx->qqpipe_lock);
202238106Sdes		lock_basic_lock(&ctx->rrpipe_lock);
203238106Sdes		while(tube_read_msg(ctx->rr_pipe, &msg, &len, 0)) {
204238106Sdes			/* discard all results except a quit confirm */
205238106Sdes			if(context_serial_getcmd(msg, len) == UB_LIBCMD_QUIT) {
206238106Sdes				free(msg);
207238106Sdes				break;
208238106Sdes			}
209238106Sdes			free(msg);
210238106Sdes		}
211238106Sdes		lock_basic_unlock(&ctx->rrpipe_lock);
212238106Sdes
213238106Sdes		/* if bg worker is a thread, wait for it to exit, so that all
214238106Sdes	 	 * resources are really gone. */
215238106Sdes		lock_basic_lock(&ctx->cfglock);
216238106Sdes		if(ctx->dothread) {
217238106Sdes			lock_basic_unlock(&ctx->cfglock);
218238106Sdes			ub_thread_join(ctx->bg_tid);
219238106Sdes		} else {
220238106Sdes			lock_basic_unlock(&ctx->cfglock);
221238106Sdes		}
222238106Sdes	}
223238106Sdes	else {
224238106Sdes		lock_basic_unlock(&ctx->cfglock);
225238106Sdes	}
226269257Sdes}
227238106Sdes
228269257Sdesvoid
229269257Sdesub_ctx_delete(struct ub_ctx* ctx)
230269257Sdes{
231269257Sdes	struct alloc_cache* a, *na;
232269257Sdes	int do_stop = 1;
233269257Sdes	if(!ctx) return;
234238106Sdes
235269257Sdes	/* see if bg thread is created and if threads have been killed */
236269257Sdes	/* no locks, because those may be held by terminated threads */
237269257Sdes	/* for processes the read pipe is closed and we see that on read */
238269257Sdes#ifdef HAVE_PTHREAD
239269257Sdes	if(ctx->created_bg && ctx->dothread) {
240269257Sdes		if(pthread_kill(ctx->bg_tid, 0) == ESRCH) {
241269257Sdes			/* thread has been killed */
242269257Sdes			do_stop = 0;
243269257Sdes		}
244269257Sdes	}
245269257Sdes#endif /* HAVE_PTHREAD */
246269257Sdes	if(do_stop)
247269257Sdes		ub_stop_bg(ctx);
248269257Sdes	libworker_delete_event(ctx->event_worker);
249269257Sdes
250238106Sdes	modstack_desetup(&ctx->mods, ctx->env);
251238106Sdes	a = ctx->alloc_list;
252238106Sdes	while(a) {
253238106Sdes		na = a->super;
254238106Sdes		a->super = &ctx->superalloc;
255238106Sdes		alloc_clear(a);
256238106Sdes		free(a);
257238106Sdes		a = na;
258238106Sdes	}
259238106Sdes	local_zones_delete(ctx->local_zones);
260238106Sdes	lock_basic_destroy(&ctx->qqpipe_lock);
261238106Sdes	lock_basic_destroy(&ctx->rrpipe_lock);
262238106Sdes	lock_basic_destroy(&ctx->cfglock);
263238106Sdes	tube_delete(ctx->qq_pipe);
264238106Sdes	tube_delete(ctx->rr_pipe);
265238106Sdes	if(ctx->env) {
266238106Sdes		slabhash_delete(ctx->env->msg_cache);
267238106Sdes		rrset_cache_delete(ctx->env->rrset_cache);
268238106Sdes		infra_delete(ctx->env->infra_cache);
269238106Sdes		config_delete(ctx->env->cfg);
270238106Sdes		free(ctx->env);
271238106Sdes	}
272238106Sdes	ub_randfree(ctx->seed_rnd);
273238106Sdes	alloc_clear(&ctx->superalloc);
274238106Sdes	traverse_postorder(&ctx->queries, delq, NULL);
275238106Sdes	free(ctx);
276238106Sdes#ifdef USE_WINSOCK
277238106Sdes	WSACleanup();
278238106Sdes#endif
279238106Sdes}
280238106Sdes
281238106Sdesint
282255584Sdesub_ctx_set_option(struct ub_ctx* ctx, const char* opt, const char* val)
283238106Sdes{
284238106Sdes	lock_basic_lock(&ctx->cfglock);
285238106Sdes	if(ctx->finalized) {
286238106Sdes		lock_basic_unlock(&ctx->cfglock);
287238106Sdes		return UB_AFTERFINAL;
288238106Sdes	}
289238106Sdes	if(!config_set_option(ctx->env->cfg, opt, val)) {
290238106Sdes		lock_basic_unlock(&ctx->cfglock);
291238106Sdes		return UB_SYNTAX;
292238106Sdes	}
293238106Sdes	lock_basic_unlock(&ctx->cfglock);
294238106Sdes	return UB_NOERROR;
295238106Sdes}
296238106Sdes
297238106Sdesint
298255584Sdesub_ctx_get_option(struct ub_ctx* ctx, const char* opt, char** str)
299238106Sdes{
300238106Sdes	int r;
301238106Sdes	lock_basic_lock(&ctx->cfglock);
302238106Sdes	r = config_get_option_collate(ctx->env->cfg, opt, str);
303238106Sdes	lock_basic_unlock(&ctx->cfglock);
304238106Sdes	if(r == 0) r = UB_NOERROR;
305238106Sdes	else if(r == 1) r = UB_SYNTAX;
306238106Sdes	else if(r == 2) r = UB_NOMEM;
307238106Sdes	return r;
308238106Sdes}
309238106Sdes
310238106Sdesint
311255584Sdesub_ctx_config(struct ub_ctx* ctx, const char* fname)
312238106Sdes{
313238106Sdes	lock_basic_lock(&ctx->cfglock);
314238106Sdes	if(ctx->finalized) {
315238106Sdes		lock_basic_unlock(&ctx->cfglock);
316238106Sdes		return UB_AFTERFINAL;
317238106Sdes	}
318238106Sdes	if(!config_read(ctx->env->cfg, fname, NULL)) {
319238106Sdes		lock_basic_unlock(&ctx->cfglock);
320238106Sdes		return UB_SYNTAX;
321238106Sdes	}
322238106Sdes	lock_basic_unlock(&ctx->cfglock);
323238106Sdes	return UB_NOERROR;
324238106Sdes}
325238106Sdes
326238106Sdesint
327255584Sdesub_ctx_add_ta(struct ub_ctx* ctx, const char* ta)
328238106Sdes{
329238106Sdes	char* dup = strdup(ta);
330238106Sdes	if(!dup) return UB_NOMEM;
331238106Sdes	lock_basic_lock(&ctx->cfglock);
332238106Sdes	if(ctx->finalized) {
333238106Sdes		lock_basic_unlock(&ctx->cfglock);
334238106Sdes		free(dup);
335238106Sdes		return UB_AFTERFINAL;
336238106Sdes	}
337238106Sdes	if(!cfg_strlist_insert(&ctx->env->cfg->trust_anchor_list, dup)) {
338238106Sdes		lock_basic_unlock(&ctx->cfglock);
339238106Sdes		free(dup);
340238106Sdes		return UB_NOMEM;
341238106Sdes	}
342238106Sdes	lock_basic_unlock(&ctx->cfglock);
343238106Sdes	return UB_NOERROR;
344238106Sdes}
345238106Sdes
346238106Sdesint
347255584Sdesub_ctx_add_ta_file(struct ub_ctx* ctx, const char* fname)
348238106Sdes{
349238106Sdes	char* dup = strdup(fname);
350238106Sdes	if(!dup) return UB_NOMEM;
351238106Sdes	lock_basic_lock(&ctx->cfglock);
352238106Sdes	if(ctx->finalized) {
353238106Sdes		lock_basic_unlock(&ctx->cfglock);
354238106Sdes		free(dup);
355238106Sdes		return UB_AFTERFINAL;
356238106Sdes	}
357238106Sdes	if(!cfg_strlist_insert(&ctx->env->cfg->trust_anchor_file_list, dup)) {
358238106Sdes		lock_basic_unlock(&ctx->cfglock);
359238106Sdes		free(dup);
360238106Sdes		return UB_NOMEM;
361238106Sdes	}
362238106Sdes	lock_basic_unlock(&ctx->cfglock);
363238106Sdes	return UB_NOERROR;
364238106Sdes}
365238106Sdes
366238106Sdesint
367255584Sdesub_ctx_trustedkeys(struct ub_ctx* ctx, const char* fname)
368238106Sdes{
369238106Sdes	char* dup = strdup(fname);
370238106Sdes	if(!dup) return UB_NOMEM;
371238106Sdes	lock_basic_lock(&ctx->cfglock);
372238106Sdes	if(ctx->finalized) {
373238106Sdes		lock_basic_unlock(&ctx->cfglock);
374238106Sdes		free(dup);
375238106Sdes		return UB_AFTERFINAL;
376238106Sdes	}
377238106Sdes	if(!cfg_strlist_insert(&ctx->env->cfg->trusted_keys_file_list, dup)) {
378238106Sdes		lock_basic_unlock(&ctx->cfglock);
379238106Sdes		free(dup);
380238106Sdes		return UB_NOMEM;
381238106Sdes	}
382238106Sdes	lock_basic_unlock(&ctx->cfglock);
383238106Sdes	return UB_NOERROR;
384238106Sdes}
385238106Sdes
386238106Sdesint
387238106Sdesub_ctx_debuglevel(struct ub_ctx* ctx, int d)
388238106Sdes{
389238106Sdes	lock_basic_lock(&ctx->cfglock);
390238106Sdes	verbosity = d;
391238106Sdes	ctx->env->cfg->verbosity = d;
392238106Sdes	lock_basic_unlock(&ctx->cfglock);
393238106Sdes	return UB_NOERROR;
394238106Sdes}
395238106Sdes
396238106Sdesint ub_ctx_debugout(struct ub_ctx* ctx, void* out)
397238106Sdes{
398238106Sdes	lock_basic_lock(&ctx->cfglock);
399238106Sdes	log_file((FILE*)out);
400238106Sdes	ctx->logfile_override = 1;
401238106Sdes	ctx->log_out = out;
402238106Sdes	lock_basic_unlock(&ctx->cfglock);
403238106Sdes	return UB_NOERROR;
404238106Sdes}
405238106Sdes
406238106Sdesint
407238106Sdesub_ctx_async(struct ub_ctx* ctx, int dothread)
408238106Sdes{
409238106Sdes#ifdef THREADS_DISABLED
410238106Sdes	if(dothread) /* cannot do threading */
411238106Sdes		return UB_NOERROR;
412238106Sdes#endif
413238106Sdes	lock_basic_lock(&ctx->cfglock);
414238106Sdes	if(ctx->finalized) {
415238106Sdes		lock_basic_unlock(&ctx->cfglock);
416238106Sdes		return UB_AFTERFINAL;
417238106Sdes	}
418238106Sdes	ctx->dothread = dothread;
419238106Sdes	lock_basic_unlock(&ctx->cfglock);
420238106Sdes	return UB_NOERROR;
421238106Sdes}
422238106Sdes
423238106Sdesint
424238106Sdesub_poll(struct ub_ctx* ctx)
425238106Sdes{
426238106Sdes	/* no need to hold lock while testing for readability. */
427238106Sdes	return tube_poll(ctx->rr_pipe);
428238106Sdes}
429238106Sdes
430238106Sdesint
431238106Sdesub_fd(struct ub_ctx* ctx)
432238106Sdes{
433238106Sdes	return tube_read_fd(ctx->rr_pipe);
434238106Sdes}
435238106Sdes
436238106Sdes/** process answer from bg worker */
437238106Sdesstatic int
438238106Sdesprocess_answer_detail(struct ub_ctx* ctx, uint8_t* msg, uint32_t len,
439238106Sdes	ub_callback_t* cb, void** cbarg, int* err,
440238106Sdes	struct ub_result** res)
441238106Sdes{
442238106Sdes	struct ctx_query* q;
443238106Sdes	if(context_serial_getcmd(msg, len) != UB_LIBCMD_ANSWER) {
444238106Sdes		log_err("error: bad data from bg worker %d",
445238106Sdes			(int)context_serial_getcmd(msg, len));
446238106Sdes		return 0;
447238106Sdes	}
448238106Sdes
449238106Sdes	lock_basic_lock(&ctx->cfglock);
450238106Sdes	q = context_deserialize_answer(ctx, msg, len, err);
451238106Sdes	if(!q) {
452238106Sdes		lock_basic_unlock(&ctx->cfglock);
453238106Sdes		/* probably simply the lookup that failed, i.e.
454238106Sdes		 * response returned before cancel was sent out, so noerror */
455238106Sdes		return 1;
456238106Sdes	}
457238106Sdes	log_assert(q->async);
458238106Sdes
459238106Sdes	/* grab cb while locked */
460238106Sdes	if(q->cancelled) {
461238106Sdes		*cb = NULL;
462238106Sdes		*cbarg = NULL;
463238106Sdes	} else {
464238106Sdes		*cb = q->cb;
465238106Sdes		*cbarg = q->cb_arg;
466238106Sdes	}
467238106Sdes	if(*err) {
468238106Sdes		*res = NULL;
469238106Sdes		ub_resolve_free(q->res);
470238106Sdes	} else {
471238106Sdes		/* parse the message, extract rcode, fill result */
472269257Sdes		sldns_buffer* buf = sldns_buffer_new(q->msg_len);
473238106Sdes		struct regional* region = regional_create();
474238106Sdes		*res = q->res;
475238106Sdes		(*res)->rcode = LDNS_RCODE_SERVFAIL;
476238106Sdes		if(region && buf) {
477269257Sdes			sldns_buffer_clear(buf);
478269257Sdes			sldns_buffer_write(buf, q->msg, q->msg_len);
479269257Sdes			sldns_buffer_flip(buf);
480238106Sdes			libworker_enter_result(*res, buf, region,
481238106Sdes				q->msg_security);
482238106Sdes		}
483238106Sdes		(*res)->answer_packet = q->msg;
484238106Sdes		(*res)->answer_len = (int)q->msg_len;
485238106Sdes		q->msg = NULL;
486269257Sdes		sldns_buffer_free(buf);
487238106Sdes		regional_destroy(region);
488238106Sdes	}
489238106Sdes	q->res = NULL;
490238106Sdes	/* delete the q from list */
491238106Sdes	(void)rbtree_delete(&ctx->queries, q->node.key);
492238106Sdes	ctx->num_async--;
493238106Sdes	context_query_delete(q);
494238106Sdes	lock_basic_unlock(&ctx->cfglock);
495238106Sdes
496238106Sdes	if(*cb) return 2;
497238106Sdes	ub_resolve_free(*res);
498238106Sdes	return 1;
499238106Sdes}
500238106Sdes
501238106Sdes/** process answer from bg worker */
502238106Sdesstatic int
503238106Sdesprocess_answer(struct ub_ctx* ctx, uint8_t* msg, uint32_t len)
504238106Sdes{
505238106Sdes	int err;
506238106Sdes	ub_callback_t cb;
507238106Sdes	void* cbarg;
508238106Sdes	struct ub_result* res;
509238106Sdes	int r;
510238106Sdes
511238106Sdes	r = process_answer_detail(ctx, msg, len, &cb, &cbarg, &err, &res);
512238106Sdes
513238106Sdes	/* no locks held while calling callback, so that library is
514238106Sdes	 * re-entrant. */
515238106Sdes	if(r == 2)
516238106Sdes		(*cb)(cbarg, err, res);
517238106Sdes
518238106Sdes	return r;
519238106Sdes}
520238106Sdes
521238106Sdesint
522238106Sdesub_process(struct ub_ctx* ctx)
523238106Sdes{
524238106Sdes	int r;
525238106Sdes	uint8_t* msg;
526238106Sdes	uint32_t len;
527238106Sdes	while(1) {
528238106Sdes		msg = NULL;
529238106Sdes		lock_basic_lock(&ctx->rrpipe_lock);
530238106Sdes		r = tube_read_msg(ctx->rr_pipe, &msg, &len, 1);
531238106Sdes		lock_basic_unlock(&ctx->rrpipe_lock);
532238106Sdes		if(r == 0)
533238106Sdes			return UB_PIPE;
534238106Sdes		else if(r == -1)
535238106Sdes			break;
536238106Sdes		if(!process_answer(ctx, msg, len)) {
537238106Sdes			free(msg);
538238106Sdes			return UB_PIPE;
539238106Sdes		}
540238106Sdes		free(msg);
541238106Sdes	}
542238106Sdes	return UB_NOERROR;
543238106Sdes}
544238106Sdes
545238106Sdesint
546238106Sdesub_wait(struct ub_ctx* ctx)
547238106Sdes{
548238106Sdes	int err;
549238106Sdes	ub_callback_t cb;
550238106Sdes	void* cbarg;
551238106Sdes	struct ub_result* res;
552238106Sdes	int r;
553238106Sdes	uint8_t* msg;
554238106Sdes	uint32_t len;
555238106Sdes	/* this is basically the same loop as _process(), but with changes.
556238106Sdes	 * holds the rrpipe lock and waits with tube_wait */
557238106Sdes	while(1) {
558238106Sdes		lock_basic_lock(&ctx->rrpipe_lock);
559238106Sdes		lock_basic_lock(&ctx->cfglock);
560238106Sdes		if(ctx->num_async == 0) {
561238106Sdes			lock_basic_unlock(&ctx->cfglock);
562238106Sdes			lock_basic_unlock(&ctx->rrpipe_lock);
563238106Sdes			break;
564238106Sdes		}
565238106Sdes		lock_basic_unlock(&ctx->cfglock);
566238106Sdes
567238106Sdes		/* keep rrpipe locked, while
568238106Sdes		 * 	o waiting for pipe readable
569238106Sdes		 * 	o parsing message
570238106Sdes		 * 	o possibly decrementing num_async
571238106Sdes		 * do callback without lock
572238106Sdes		 */
573238106Sdes		r = tube_wait(ctx->rr_pipe);
574238106Sdes		if(r) {
575238106Sdes			r = tube_read_msg(ctx->rr_pipe, &msg, &len, 1);
576238106Sdes			if(r == 0) {
577238106Sdes				lock_basic_unlock(&ctx->rrpipe_lock);
578238106Sdes				return UB_PIPE;
579238106Sdes			}
580238106Sdes			if(r == -1) {
581238106Sdes				lock_basic_unlock(&ctx->rrpipe_lock);
582238106Sdes				continue;
583238106Sdes			}
584238106Sdes			r = process_answer_detail(ctx, msg, len,
585238106Sdes				&cb, &cbarg, &err, &res);
586238106Sdes			lock_basic_unlock(&ctx->rrpipe_lock);
587238106Sdes			free(msg);
588238106Sdes			if(r == 0)
589238106Sdes				return UB_PIPE;
590238106Sdes			if(r == 2)
591238106Sdes				(*cb)(cbarg, err, res);
592238106Sdes		} else {
593238106Sdes			lock_basic_unlock(&ctx->rrpipe_lock);
594238106Sdes		}
595238106Sdes	}
596238106Sdes	return UB_NOERROR;
597238106Sdes}
598238106Sdes
599238106Sdesint
600255584Sdesub_resolve(struct ub_ctx* ctx, const char* name, int rrtype,
601238106Sdes	int rrclass, struct ub_result** result)
602238106Sdes{
603238106Sdes	struct ctx_query* q;
604238106Sdes	int r;
605238106Sdes	*result = NULL;
606238106Sdes
607238106Sdes	lock_basic_lock(&ctx->cfglock);
608238106Sdes	if(!ctx->finalized) {
609238106Sdes		r = context_finalize(ctx);
610238106Sdes		if(r) {
611238106Sdes			lock_basic_unlock(&ctx->cfglock);
612238106Sdes			return r;
613238106Sdes		}
614238106Sdes	}
615238106Sdes	/* create new ctx_query and attempt to add to the list */
616238106Sdes	lock_basic_unlock(&ctx->cfglock);
617238106Sdes	q = context_new(ctx, name, rrtype, rrclass, NULL, NULL);
618238106Sdes	if(!q)
619238106Sdes		return UB_NOMEM;
620238106Sdes	/* become a resolver thread for a bit */
621238106Sdes
622238106Sdes	r = libworker_fg(ctx, q);
623238106Sdes	if(r) {
624238106Sdes		lock_basic_lock(&ctx->cfglock);
625238106Sdes		(void)rbtree_delete(&ctx->queries, q->node.key);
626238106Sdes		context_query_delete(q);
627238106Sdes		lock_basic_unlock(&ctx->cfglock);
628238106Sdes		return r;
629238106Sdes	}
630238106Sdes	q->res->answer_packet = q->msg;
631238106Sdes	q->res->answer_len = (int)q->msg_len;
632238106Sdes	q->msg = NULL;
633238106Sdes	*result = q->res;
634238106Sdes	q->res = NULL;
635238106Sdes
636238106Sdes	lock_basic_lock(&ctx->cfglock);
637238106Sdes	(void)rbtree_delete(&ctx->queries, q->node.key);
638238106Sdes	context_query_delete(q);
639238106Sdes	lock_basic_unlock(&ctx->cfglock);
640238106Sdes	return UB_NOERROR;
641238106Sdes}
642238106Sdes
643238106Sdesint
644269257Sdesub_resolve_event(struct ub_ctx* ctx, const char* name, int rrtype,
645269257Sdes	int rrclass, void* mydata, ub_event_callback_t callback, int* async_id)
646269257Sdes{
647269257Sdes	struct ctx_query* q;
648269257Sdes	int r;
649269257Sdes
650269257Sdes	if(async_id)
651269257Sdes		*async_id = 0;
652269257Sdes	lock_basic_lock(&ctx->cfglock);
653269257Sdes	if(!ctx->finalized) {
654269257Sdes		int r = context_finalize(ctx);
655269257Sdes		if(r) {
656269257Sdes			lock_basic_unlock(&ctx->cfglock);
657269257Sdes			return r;
658269257Sdes		}
659269257Sdes	}
660269257Sdes	lock_basic_unlock(&ctx->cfglock);
661269257Sdes	if(!ctx->event_worker) {
662269257Sdes		ctx->event_worker = libworker_create_event(ctx,
663269257Sdes			ctx->event_base);
664269257Sdes		if(!ctx->event_worker) {
665269257Sdes			return UB_INITFAIL;
666269257Sdes		}
667269257Sdes	}
668269257Sdes
669269257Sdes	/* create new ctx_query and attempt to add to the list */
670269257Sdes	q = context_new(ctx, name, rrtype, rrclass, (ub_callback_t)callback,
671269257Sdes		mydata);
672269257Sdes	if(!q)
673269257Sdes		return UB_NOMEM;
674269257Sdes
675269257Sdes	/* attach to mesh */
676269257Sdes	if((r=libworker_attach_mesh(ctx, q, async_id)) != 0)
677269257Sdes		return r;
678269257Sdes	return UB_NOERROR;
679269257Sdes}
680269257Sdes
681269257Sdes
682269257Sdesint
683255584Sdesub_resolve_async(struct ub_ctx* ctx, const char* name, int rrtype,
684238106Sdes	int rrclass, void* mydata, ub_callback_t callback, int* async_id)
685238106Sdes{
686238106Sdes	struct ctx_query* q;
687238106Sdes	uint8_t* msg = NULL;
688238106Sdes	uint32_t len = 0;
689238106Sdes
690238106Sdes	if(async_id)
691238106Sdes		*async_id = 0;
692238106Sdes	lock_basic_lock(&ctx->cfglock);
693238106Sdes	if(!ctx->finalized) {
694238106Sdes		int r = context_finalize(ctx);
695238106Sdes		if(r) {
696238106Sdes			lock_basic_unlock(&ctx->cfglock);
697238106Sdes			return r;
698238106Sdes		}
699238106Sdes	}
700238106Sdes	if(!ctx->created_bg) {
701238106Sdes		int r;
702238106Sdes		ctx->created_bg = 1;
703238106Sdes		lock_basic_unlock(&ctx->cfglock);
704238106Sdes		r = libworker_bg(ctx);
705238106Sdes		if(r) {
706238106Sdes			lock_basic_lock(&ctx->cfglock);
707238106Sdes			ctx->created_bg = 0;
708238106Sdes			lock_basic_unlock(&ctx->cfglock);
709238106Sdes			return r;
710238106Sdes		}
711238106Sdes	} else {
712238106Sdes		lock_basic_unlock(&ctx->cfglock);
713238106Sdes	}
714238106Sdes
715238106Sdes	/* create new ctx_query and attempt to add to the list */
716238106Sdes	q = context_new(ctx, name, rrtype, rrclass, callback, mydata);
717238106Sdes	if(!q)
718238106Sdes		return UB_NOMEM;
719238106Sdes
720238106Sdes	/* write over pipe to background worker */
721238106Sdes	lock_basic_lock(&ctx->cfglock);
722238106Sdes	msg = context_serialize_new_query(q, &len);
723238106Sdes	if(!msg) {
724238106Sdes		(void)rbtree_delete(&ctx->queries, q->node.key);
725238106Sdes		ctx->num_async--;
726238106Sdes		context_query_delete(q);
727238106Sdes		lock_basic_unlock(&ctx->cfglock);
728238106Sdes		return UB_NOMEM;
729238106Sdes	}
730238106Sdes	if(async_id)
731238106Sdes		*async_id = q->querynum;
732238106Sdes	lock_basic_unlock(&ctx->cfglock);
733238106Sdes
734238106Sdes	lock_basic_lock(&ctx->qqpipe_lock);
735238106Sdes	if(!tube_write_msg(ctx->qq_pipe, msg, len, 0)) {
736238106Sdes		lock_basic_unlock(&ctx->qqpipe_lock);
737238106Sdes		free(msg);
738238106Sdes		return UB_PIPE;
739238106Sdes	}
740238106Sdes	lock_basic_unlock(&ctx->qqpipe_lock);
741238106Sdes	free(msg);
742238106Sdes	return UB_NOERROR;
743238106Sdes}
744238106Sdes
745238106Sdesint
746238106Sdesub_cancel(struct ub_ctx* ctx, int async_id)
747238106Sdes{
748238106Sdes	struct ctx_query* q;
749238106Sdes	uint8_t* msg = NULL;
750238106Sdes	uint32_t len = 0;
751238106Sdes	lock_basic_lock(&ctx->cfglock);
752238106Sdes	q = (struct ctx_query*)rbtree_search(&ctx->queries, &async_id);
753238106Sdes	if(!q || !q->async) {
754238106Sdes		/* it is not there, so nothing to do */
755238106Sdes		lock_basic_unlock(&ctx->cfglock);
756238106Sdes		return UB_NOID;
757238106Sdes	}
758238106Sdes	log_assert(q->async);
759238106Sdes	q->cancelled = 1;
760238106Sdes
761238106Sdes	/* delete it */
762238106Sdes	if(!ctx->dothread) { /* if forked */
763238106Sdes		(void)rbtree_delete(&ctx->queries, q->node.key);
764238106Sdes		ctx->num_async--;
765238106Sdes		msg = context_serialize_cancel(q, &len);
766238106Sdes		context_query_delete(q);
767238106Sdes		lock_basic_unlock(&ctx->cfglock);
768238106Sdes		if(!msg) {
769238106Sdes			return UB_NOMEM;
770238106Sdes		}
771238106Sdes		/* send cancel to background worker */
772238106Sdes		lock_basic_lock(&ctx->qqpipe_lock);
773238106Sdes		if(!tube_write_msg(ctx->qq_pipe, msg, len, 0)) {
774238106Sdes			lock_basic_unlock(&ctx->qqpipe_lock);
775238106Sdes			free(msg);
776238106Sdes			return UB_PIPE;
777238106Sdes		}
778238106Sdes		lock_basic_unlock(&ctx->qqpipe_lock);
779238106Sdes		free(msg);
780238106Sdes	} else {
781238106Sdes		lock_basic_unlock(&ctx->cfglock);
782238106Sdes	}
783238106Sdes	return UB_NOERROR;
784238106Sdes}
785238106Sdes
786238106Sdesvoid
787238106Sdesub_resolve_free(struct ub_result* result)
788238106Sdes{
789238106Sdes	char** p;
790238106Sdes	if(!result) return;
791238106Sdes	free(result->qname);
792238106Sdes	if(result->canonname != result->qname)
793238106Sdes		free(result->canonname);
794238106Sdes	if(result->data)
795238106Sdes		for(p = result->data; *p; p++)
796238106Sdes			free(*p);
797238106Sdes	free(result->data);
798238106Sdes	free(result->len);
799238106Sdes	free(result->answer_packet);
800238106Sdes	free(result->why_bogus);
801238106Sdes	free(result);
802238106Sdes}
803238106Sdes
804238106Sdesconst char*
805238106Sdesub_strerror(int err)
806238106Sdes{
807238106Sdes	switch(err) {
808238106Sdes		case UB_NOERROR: return "no error";
809238106Sdes		case UB_SOCKET: return "socket io error";
810238106Sdes		case UB_NOMEM: return "out of memory";
811238106Sdes		case UB_SYNTAX: return "syntax error";
812238106Sdes		case UB_SERVFAIL: return "server failure";
813238106Sdes		case UB_FORKFAIL: return "could not fork";
814238106Sdes		case UB_INITFAIL: return "initialization failure";
815238106Sdes		case UB_AFTERFINAL: return "setting change after finalize";
816238106Sdes		case UB_PIPE: return "error in pipe communication with async";
817238106Sdes		case UB_READFILE: return "error reading file";
818238106Sdes		case UB_NOID: return "error async_id does not exist";
819238106Sdes		default: return "unknown error";
820238106Sdes	}
821238106Sdes}
822238106Sdes
823238106Sdesint
824255584Sdesub_ctx_set_fwd(struct ub_ctx* ctx, const char* addr)
825238106Sdes{
826238106Sdes	struct sockaddr_storage storage;
827238106Sdes	socklen_t stlen;
828238106Sdes	struct config_stub* s;
829238106Sdes	char* dupl;
830238106Sdes	lock_basic_lock(&ctx->cfglock);
831238106Sdes	if(ctx->finalized) {
832238106Sdes		lock_basic_unlock(&ctx->cfglock);
833238106Sdes		errno=EINVAL;
834238106Sdes		return UB_AFTERFINAL;
835238106Sdes	}
836238106Sdes	if(!addr) {
837238106Sdes		/* disable fwd mode - the root stub should be first. */
838238106Sdes		if(ctx->env->cfg->forwards &&
839238106Sdes			strcmp(ctx->env->cfg->forwards->name, ".") == 0) {
840238106Sdes			s = ctx->env->cfg->forwards;
841238106Sdes			ctx->env->cfg->forwards = s->next;
842238106Sdes			s->next = NULL;
843238106Sdes			config_delstubs(s);
844238106Sdes		}
845238106Sdes		lock_basic_unlock(&ctx->cfglock);
846238106Sdes		return UB_NOERROR;
847238106Sdes	}
848238106Sdes	lock_basic_unlock(&ctx->cfglock);
849238106Sdes
850238106Sdes	/* check syntax for addr */
851238106Sdes	if(!extstrtoaddr(addr, &storage, &stlen)) {
852238106Sdes		errno=EINVAL;
853238106Sdes		return UB_SYNTAX;
854238106Sdes	}
855238106Sdes
856238106Sdes	/* it parses, add root stub in front of list */
857238106Sdes	lock_basic_lock(&ctx->cfglock);
858238106Sdes	if(!ctx->env->cfg->forwards ||
859238106Sdes		strcmp(ctx->env->cfg->forwards->name, ".") != 0) {
860238106Sdes		s = calloc(1, sizeof(*s));
861238106Sdes		if(!s) {
862238106Sdes			lock_basic_unlock(&ctx->cfglock);
863238106Sdes			errno=ENOMEM;
864238106Sdes			return UB_NOMEM;
865238106Sdes		}
866238106Sdes		s->name = strdup(".");
867238106Sdes		if(!s->name) {
868238106Sdes			free(s);
869238106Sdes			lock_basic_unlock(&ctx->cfglock);
870238106Sdes			errno=ENOMEM;
871238106Sdes			return UB_NOMEM;
872238106Sdes		}
873238106Sdes		s->next = ctx->env->cfg->forwards;
874238106Sdes		ctx->env->cfg->forwards = s;
875238106Sdes	} else {
876238106Sdes		log_assert(ctx->env->cfg->forwards);
877238106Sdes		s = ctx->env->cfg->forwards;
878238106Sdes	}
879238106Sdes	dupl = strdup(addr);
880238106Sdes	if(!dupl) {
881238106Sdes		lock_basic_unlock(&ctx->cfglock);
882238106Sdes		errno=ENOMEM;
883238106Sdes		return UB_NOMEM;
884238106Sdes	}
885238106Sdes	if(!cfg_strlist_insert(&s->addrs, dupl)) {
886238106Sdes		free(dupl);
887238106Sdes		lock_basic_unlock(&ctx->cfglock);
888238106Sdes		errno=ENOMEM;
889238106Sdes		return UB_NOMEM;
890238106Sdes	}
891238106Sdes	lock_basic_unlock(&ctx->cfglock);
892238106Sdes	return UB_NOERROR;
893238106Sdes}
894238106Sdes
895238106Sdesint
896255584Sdesub_ctx_resolvconf(struct ub_ctx* ctx, const char* fname)
897238106Sdes{
898238106Sdes	FILE* in;
899238106Sdes	int numserv = 0;
900238106Sdes	char buf[1024];
901238106Sdes	char* parse, *addr;
902238106Sdes	int r;
903238106Sdes
904238106Sdes	if(fname == NULL) {
905238106Sdes#if !defined(UB_ON_WINDOWS) || !defined(HAVE_WINDOWS_H)
906238106Sdes		fname = "/etc/resolv.conf";
907238106Sdes#else
908238106Sdes		FIXED_INFO *info;
909238106Sdes		ULONG buflen = sizeof(*info);
910238106Sdes		IP_ADDR_STRING *ptr;
911238106Sdes
912238106Sdes		info = (FIXED_INFO *) malloc(sizeof (FIXED_INFO));
913238106Sdes		if (info == NULL)
914238106Sdes			return UB_READFILE;
915238106Sdes
916238106Sdes		if (GetNetworkParams(info, &buflen) == ERROR_BUFFER_OVERFLOW) {
917238106Sdes			free(info);
918238106Sdes			info = (FIXED_INFO *) malloc(buflen);
919238106Sdes			if (info == NULL)
920238106Sdes				return UB_READFILE;
921238106Sdes		}
922238106Sdes
923238106Sdes		if (GetNetworkParams(info, &buflen) == NO_ERROR) {
924238106Sdes			int retval=0;
925238106Sdes			ptr = &(info->DnsServerList);
926238106Sdes			while (ptr) {
927238106Sdes				numserv++;
928238106Sdes				if((retval=ub_ctx_set_fwd(ctx,
929238106Sdes					ptr->IpAddress.String)!=0)) {
930238106Sdes					free(info);
931238106Sdes					return retval;
932238106Sdes				}
933238106Sdes				ptr = ptr->Next;
934238106Sdes			}
935238106Sdes			free(info);
936238106Sdes			if (numserv==0)
937238106Sdes				return UB_READFILE;
938238106Sdes			return UB_NOERROR;
939238106Sdes		}
940238106Sdes		free(info);
941238106Sdes		return UB_READFILE;
942238106Sdes#endif /* WINDOWS */
943238106Sdes	}
944238106Sdes	in = fopen(fname, "r");
945238106Sdes	if(!in) {
946238106Sdes		/* error in errno! perror(fname) */
947238106Sdes		return UB_READFILE;
948238106Sdes	}
949238106Sdes	while(fgets(buf, (int)sizeof(buf), in)) {
950238106Sdes		buf[sizeof(buf)-1] = 0;
951238106Sdes		parse=buf;
952238106Sdes		while(*parse == ' ' || *parse == '\t')
953238106Sdes			parse++;
954238106Sdes		if(strncmp(parse, "nameserver", 10) == 0) {
955238106Sdes			numserv++;
956238106Sdes			parse += 10; /* skip 'nameserver' */
957238106Sdes			/* skip whitespace */
958238106Sdes			while(*parse == ' ' || *parse == '\t')
959238106Sdes				parse++;
960238106Sdes			addr = parse;
961238106Sdes			/* skip [0-9a-fA-F.:]*, i.e. IP4 and IP6 address */
962238106Sdes			while(isxdigit(*parse) || *parse=='.' || *parse==':')
963238106Sdes				parse++;
964238106Sdes			/* terminate after the address, remove newline */
965238106Sdes			*parse = 0;
966238106Sdes
967238106Sdes			if((r = ub_ctx_set_fwd(ctx, addr)) != UB_NOERROR) {
968238106Sdes				fclose(in);
969238106Sdes				return r;
970238106Sdes			}
971238106Sdes		}
972238106Sdes	}
973238106Sdes	fclose(in);
974238106Sdes	if(numserv == 0) {
975238106Sdes		/* from resolv.conf(5) if none given, use localhost */
976238106Sdes		return ub_ctx_set_fwd(ctx, "127.0.0.1");
977238106Sdes	}
978238106Sdes	return UB_NOERROR;
979238106Sdes}
980238106Sdes
981238106Sdesint
982255584Sdesub_ctx_hosts(struct ub_ctx* ctx, const char* fname)
983238106Sdes{
984238106Sdes	FILE* in;
985238106Sdes	char buf[1024], ldata[1024];
986238106Sdes	char* parse, *addr, *name, *ins;
987238106Sdes	lock_basic_lock(&ctx->cfglock);
988238106Sdes	if(ctx->finalized) {
989238106Sdes		lock_basic_unlock(&ctx->cfglock);
990238106Sdes		errno=EINVAL;
991238106Sdes		return UB_AFTERFINAL;
992238106Sdes	}
993238106Sdes	lock_basic_unlock(&ctx->cfglock);
994238106Sdes	if(fname == NULL) {
995238106Sdes#if defined(UB_ON_WINDOWS) && defined(HAVE_WINDOWS_H)
996238106Sdes		/*
997238106Sdes		 * If this is Windows NT/XP/2K it's in
998238106Sdes		 * %WINDIR%\system32\drivers\etc\hosts.
999238106Sdes		 * If this is Windows 95/98/Me it's in %WINDIR%\hosts.
1000238106Sdes		 */
1001238106Sdes		name = getenv("WINDIR");
1002238106Sdes		if (name != NULL) {
1003238106Sdes			int retval=0;
1004238106Sdes			snprintf(buf, sizeof(buf), "%s%s", name,
1005238106Sdes				"\\system32\\drivers\\etc\\hosts");
1006238106Sdes			if((retval=ub_ctx_hosts(ctx, buf)) !=0 ) {
1007238106Sdes				snprintf(buf, sizeof(buf), "%s%s", name,
1008238106Sdes					"\\hosts");
1009238106Sdes				retval=ub_ctx_hosts(ctx, buf);
1010238106Sdes			}
1011238106Sdes			free(name);
1012238106Sdes			return retval;
1013238106Sdes		}
1014238106Sdes		return UB_READFILE;
1015238106Sdes#else
1016238106Sdes		fname = "/etc/hosts";
1017238106Sdes#endif /* WIN32 */
1018238106Sdes	}
1019238106Sdes	in = fopen(fname, "r");
1020238106Sdes	if(!in) {
1021238106Sdes		/* error in errno! perror(fname) */
1022238106Sdes		return UB_READFILE;
1023238106Sdes	}
1024238106Sdes	while(fgets(buf, (int)sizeof(buf), in)) {
1025238106Sdes		buf[sizeof(buf)-1] = 0;
1026238106Sdes		parse=buf;
1027238106Sdes		while(*parse == ' ' || *parse == '\t')
1028238106Sdes			parse++;
1029238106Sdes		if(*parse == '#')
1030238106Sdes			continue; /* skip comment */
1031238106Sdes		/* format: <addr> spaces <name> spaces <name> ... */
1032238106Sdes		addr = parse;
1033238106Sdes		/* skip addr */
1034238106Sdes		while(isxdigit(*parse) || *parse == '.' || *parse == ':')
1035238106Sdes			parse++;
1036238106Sdes		if(*parse == '\n' || *parse == 0)
1037238106Sdes			continue;
1038238106Sdes		if(*parse == '%')
1039238106Sdes			continue; /* ignore macOSX fe80::1%lo0 localhost */
1040238106Sdes		if(*parse != ' ' && *parse != '\t') {
1041238106Sdes			/* must have whitespace after address */
1042238106Sdes			fclose(in);
1043238106Sdes			errno=EINVAL;
1044238106Sdes			return UB_SYNTAX;
1045238106Sdes		}
1046238106Sdes		*parse++ = 0; /* end delimiter for addr ... */
1047238106Sdes		/* go to names and add them */
1048238106Sdes		while(*parse) {
1049238106Sdes			while(*parse == ' ' || *parse == '\t' || *parse=='\n')
1050238106Sdes				parse++;
1051238106Sdes			if(*parse == 0 || *parse == '#')
1052238106Sdes				break;
1053238106Sdes			/* skip name, allows (too) many printable characters */
1054238106Sdes			name = parse;
1055238106Sdes			while('!' <= *parse && *parse <= '~')
1056238106Sdes				parse++;
1057238106Sdes			if(*parse)
1058238106Sdes				*parse++ = 0; /* end delimiter for name */
1059238106Sdes			snprintf(ldata, sizeof(ldata), "%s %s %s",
1060238106Sdes				name, str_is_ip6(addr)?"AAAA":"A", addr);
1061238106Sdes			ins = strdup(ldata);
1062238106Sdes			if(!ins) {
1063238106Sdes				/* out of memory */
1064238106Sdes				fclose(in);
1065238106Sdes				errno=ENOMEM;
1066238106Sdes				return UB_NOMEM;
1067238106Sdes			}
1068238106Sdes			lock_basic_lock(&ctx->cfglock);
1069238106Sdes			if(!cfg_strlist_insert(&ctx->env->cfg->local_data,
1070238106Sdes				ins)) {
1071238106Sdes				lock_basic_unlock(&ctx->cfglock);
1072238106Sdes				fclose(in);
1073238106Sdes				free(ins);
1074238106Sdes				errno=ENOMEM;
1075238106Sdes				return UB_NOMEM;
1076238106Sdes			}
1077238106Sdes			lock_basic_unlock(&ctx->cfglock);
1078238106Sdes		}
1079238106Sdes	}
1080238106Sdes	fclose(in);
1081238106Sdes	return UB_NOERROR;
1082238106Sdes}
1083238106Sdes
1084238106Sdes/** finalize the context, if not already finalized */
1085238106Sdesstatic int ub_ctx_finalize(struct ub_ctx* ctx)
1086238106Sdes{
1087238106Sdes	int res = 0;
1088238106Sdes	lock_basic_lock(&ctx->cfglock);
1089238106Sdes	if (!ctx->finalized) {
1090238106Sdes		res = context_finalize(ctx);
1091238106Sdes	}
1092238106Sdes	lock_basic_unlock(&ctx->cfglock);
1093238106Sdes	return res;
1094238106Sdes}
1095238106Sdes
1096238106Sdes/* Print local zones and RR data */
1097238106Sdesint ub_ctx_print_local_zones(struct ub_ctx* ctx)
1098238106Sdes{
1099238106Sdes	int res = ub_ctx_finalize(ctx);
1100238106Sdes	if (res) return res;
1101238106Sdes
1102238106Sdes	local_zones_print(ctx->local_zones);
1103238106Sdes
1104238106Sdes	return UB_NOERROR;
1105238106Sdes}
1106238106Sdes
1107238106Sdes/* Add a new zone */
1108269257Sdesint ub_ctx_zone_add(struct ub_ctx* ctx, const char *zone_name,
1109269257Sdes	const char *zone_type)
1110238106Sdes{
1111238106Sdes	enum localzone_type t;
1112238106Sdes	struct local_zone* z;
1113238106Sdes	uint8_t* nm;
1114238106Sdes	int nmlabs;
1115238106Sdes	size_t nmlen;
1116238106Sdes
1117238106Sdes	int res = ub_ctx_finalize(ctx);
1118238106Sdes	if (res) return res;
1119238106Sdes
1120238106Sdes	if(!local_zone_str2type(zone_type, &t)) {
1121238106Sdes		return UB_SYNTAX;
1122238106Sdes	}
1123238106Sdes
1124238106Sdes	if(!parse_dname(zone_name, &nm, &nmlen, &nmlabs)) {
1125238106Sdes		return UB_SYNTAX;
1126238106Sdes	}
1127238106Sdes
1128269257Sdes	lock_rw_wrlock(&ctx->local_zones->lock);
1129238106Sdes	if((z=local_zones_find(ctx->local_zones, nm, nmlen, nmlabs,
1130238106Sdes		LDNS_RR_CLASS_IN))) {
1131238106Sdes		/* already present in tree */
1132238106Sdes		lock_rw_wrlock(&z->lock);
1133238106Sdes		z->type = t; /* update type anyway */
1134238106Sdes		lock_rw_unlock(&z->lock);
1135269257Sdes		lock_rw_unlock(&ctx->local_zones->lock);
1136238106Sdes		free(nm);
1137238106Sdes		return UB_NOERROR;
1138238106Sdes	}
1139238106Sdes	if(!local_zones_add_zone(ctx->local_zones, nm, nmlen, nmlabs,
1140238106Sdes		LDNS_RR_CLASS_IN, t)) {
1141269257Sdes		lock_rw_unlock(&ctx->local_zones->lock);
1142238106Sdes		return UB_NOMEM;
1143238106Sdes	}
1144269257Sdes	lock_rw_unlock(&ctx->local_zones->lock);
1145238106Sdes	return UB_NOERROR;
1146238106Sdes}
1147238106Sdes
1148238106Sdes/* Remove zone */
1149269257Sdesint ub_ctx_zone_remove(struct ub_ctx* ctx, const char *zone_name)
1150238106Sdes{
1151238106Sdes	struct local_zone* z;
1152238106Sdes	uint8_t* nm;
1153238106Sdes	int nmlabs;
1154238106Sdes	size_t nmlen;
1155238106Sdes
1156238106Sdes	int res = ub_ctx_finalize(ctx);
1157238106Sdes	if (res) return res;
1158238106Sdes
1159238106Sdes	if(!parse_dname(zone_name, &nm, &nmlen, &nmlabs)) {
1160238106Sdes		return UB_SYNTAX;
1161238106Sdes	}
1162238106Sdes
1163269257Sdes	lock_rw_wrlock(&ctx->local_zones->lock);
1164238106Sdes	if((z=local_zones_find(ctx->local_zones, nm, nmlen, nmlabs,
1165238106Sdes		LDNS_RR_CLASS_IN))) {
1166238106Sdes		/* present in tree */
1167238106Sdes		local_zones_del_zone(ctx->local_zones, z);
1168238106Sdes	}
1169269257Sdes	lock_rw_unlock(&ctx->local_zones->lock);
1170238106Sdes	free(nm);
1171238106Sdes	return UB_NOERROR;
1172238106Sdes}
1173238106Sdes
1174238106Sdes/* Add new RR data */
1175269257Sdesint ub_ctx_data_add(struct ub_ctx* ctx, const char *data)
1176238106Sdes{
1177238106Sdes	int res = ub_ctx_finalize(ctx);
1178238106Sdes	if (res) return res;
1179238106Sdes
1180269257Sdes	res = local_zones_add_RR(ctx->local_zones, data);
1181238106Sdes	return (!res) ? UB_NOMEM : UB_NOERROR;
1182238106Sdes}
1183238106Sdes
1184238106Sdes/* Remove RR data */
1185269257Sdesint ub_ctx_data_remove(struct ub_ctx* ctx, const char *data)
1186238106Sdes{
1187238106Sdes	uint8_t* nm;
1188238106Sdes	int nmlabs;
1189238106Sdes	size_t nmlen;
1190238106Sdes	int res = ub_ctx_finalize(ctx);
1191238106Sdes	if (res) return res;
1192238106Sdes
1193238106Sdes	if(!parse_dname(data, &nm, &nmlen, &nmlabs))
1194238106Sdes		return UB_SYNTAX;
1195238106Sdes
1196238106Sdes	local_zones_del_data(ctx->local_zones, nm, nmlen, nmlabs,
1197238106Sdes		LDNS_RR_CLASS_IN);
1198238106Sdes
1199238106Sdes	free(nm);
1200238106Sdes	return UB_NOERROR;
1201238106Sdes}
1202238106Sdes
1203238106Sdesconst char* ub_version(void)
1204238106Sdes{
1205238106Sdes	return PACKAGE_VERSION;
1206238106Sdes}
1207269257Sdes
1208269257Sdesint
1209269257Sdesub_ctx_set_event(struct ub_ctx* ctx, struct event_base* base) {
1210269257Sdes	if (!ctx || !ctx->event_base || !base) {
1211269257Sdes		return UB_INITFAIL;
1212269257Sdes	}
1213269257Sdes	if (ctx->event_base == base) {
1214269257Sdes		/* already set */
1215269257Sdes		return UB_NOERROR;
1216269257Sdes	}
1217269257Sdes
1218269257Sdes	lock_basic_lock(&ctx->cfglock);
1219269257Sdes	/* destroy the current worker - safe to pass in NULL */
1220269257Sdes	libworker_delete_event(ctx->event_worker);
1221269257Sdes	ctx->event_worker = NULL;
1222269257Sdes	ctx->event_base = base;
1223269257Sdes	ctx->created_bg = 0;
1224269257Sdes	ctx->dothread = 1;
1225269257Sdes	lock_basic_unlock(&ctx->cfglock);
1226269257Sdes	return UB_NOERROR;
1227269257Sdes}
1228