xref: /openbsd-src/usr.sbin/unbound/testcode/asynclook.c (revision 83152a15cdc99ac1e0df21c06e440e1660f29e5f)
1712b2f30Ssthen /*
2712b2f30Ssthen  * testcode/asynclook.c - debug program perform async libunbound queries.
3712b2f30Ssthen  *
4712b2f30Ssthen  * Copyright (c) 2008, NLnet Labs. All rights reserved.
5712b2f30Ssthen  *
6712b2f30Ssthen  * This software is open source.
7712b2f30Ssthen  *
8712b2f30Ssthen  * Redistribution and use in source and binary forms, with or without
9712b2f30Ssthen  * modification, are permitted provided that the following conditions
10712b2f30Ssthen  * are met:
11712b2f30Ssthen  *
12712b2f30Ssthen  * Redistributions of source code must retain the above copyright notice,
13712b2f30Ssthen  * this list of conditions and the following disclaimer.
14712b2f30Ssthen  *
15712b2f30Ssthen  * Redistributions in binary form must reproduce the above copyright notice,
16712b2f30Ssthen  * this list of conditions and the following disclaimer in the documentation
17712b2f30Ssthen  * and/or other materials provided with the distribution.
18712b2f30Ssthen  *
19712b2f30Ssthen  * Neither the name of the NLNET LABS nor the names of its contributors may
20712b2f30Ssthen  * be used to endorse or promote products derived from this software without
21712b2f30Ssthen  * specific prior written permission.
22712b2f30Ssthen  *
23712b2f30Ssthen  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
24712b2f30Ssthen  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
25712b2f30Ssthen  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
26712b2f30Ssthen  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
27712b2f30Ssthen  * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
28712b2f30Ssthen  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
29712b2f30Ssthen  * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
30712b2f30Ssthen  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
31712b2f30Ssthen  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
32712b2f30Ssthen  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
33712b2f30Ssthen  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
34712b2f30Ssthen  */
35712b2f30Ssthen 
36712b2f30Ssthen /**
37712b2f30Ssthen  * \file
38712b2f30Ssthen  *
39712b2f30Ssthen  * This program shows the results from several background lookups,
40712b2f30Ssthen  * while printing time in the foreground.
41712b2f30Ssthen  */
42712b2f30Ssthen 
43712b2f30Ssthen #include "config.h"
44712b2f30Ssthen #ifdef HAVE_GETOPT_H
45712b2f30Ssthen #include <getopt.h>
46712b2f30Ssthen #endif
47712b2f30Ssthen #include "libunbound/unbound.h"
48712b2f30Ssthen #include "libunbound/context.h"
49712b2f30Ssthen #include "util/locks.h"
50712b2f30Ssthen #include "util/log.h"
51712b2f30Ssthen #include "sldns/rrdef.h"
52712b2f30Ssthen #ifdef UNBOUND_ALLOC_LITE
53712b2f30Ssthen #undef malloc
54712b2f30Ssthen #undef calloc
55712b2f30Ssthen #undef realloc
56712b2f30Ssthen #undef free
57712b2f30Ssthen #undef strdup
58712b2f30Ssthen #endif
59712b2f30Ssthen #ifdef HAVE_SSL
60712b2f30Ssthen #ifdef HAVE_OPENSSL_SSL_H
61712b2f30Ssthen #include <openssl/ssl.h>
62712b2f30Ssthen #endif
63712b2f30Ssthen #ifdef HAVE_OPENSSL_ERR_H
64712b2f30Ssthen #include <openssl/err.h>
65712b2f30Ssthen #endif
66712b2f30Ssthen #endif /* HAVE_SSL */
67712b2f30Ssthen 
68712b2f30Ssthen 
69712b2f30Ssthen /** keeping track of the async ids */
70712b2f30Ssthen struct track_id {
71712b2f30Ssthen 	/** the id to pass to libunbound to cancel */
72712b2f30Ssthen 	int id;
73712b2f30Ssthen 	/** true if cancelled */
74712b2f30Ssthen 	int cancel;
75712b2f30Ssthen 	/** a lock on this structure for thread safety */
76712b2f30Ssthen 	lock_basic_type lock;
77712b2f30Ssthen };
78712b2f30Ssthen 
79712b2f30Ssthen /**
80712b2f30Ssthen  * result list for the lookups
81712b2f30Ssthen  */
82712b2f30Ssthen struct lookinfo {
83712b2f30Ssthen 	/** name to look up */
84712b2f30Ssthen 	char* name;
85712b2f30Ssthen 	/** tracking number that can be used to cancel the query */
86712b2f30Ssthen 	int async_id;
87712b2f30Ssthen 	/** error code from libunbound */
88712b2f30Ssthen 	int err;
89712b2f30Ssthen 	/** result from lookup */
90712b2f30Ssthen 	struct ub_result* result;
91712b2f30Ssthen };
92712b2f30Ssthen 
93712b2f30Ssthen /** global variable to see how many queries we have left */
94712b2f30Ssthen static int num_wait = 0;
95712b2f30Ssthen 
96712b2f30Ssthen /** usage information for asynclook */
usage(char * argv[])97712b2f30Ssthen static void usage(char* argv[])
98712b2f30Ssthen {
99712b2f30Ssthen 	printf("usage: %s [options] name ...\n", argv[0]);
100712b2f30Ssthen 	printf("names are looked up at the same time, asynchronously.\n");
101712b2f30Ssthen 	printf("	-b : use blocking requests\n");
102712b2f30Ssthen 	printf("	-c : cancel the requests\n");
103712b2f30Ssthen 	printf("	-d : enable debug output\n");
104712b2f30Ssthen 	printf("	-f addr : use addr, forward to that server\n");
105712b2f30Ssthen 	printf("	-h : this help message\n");
106712b2f30Ssthen 	printf("	-H fname : read hosts from fname\n");
107712b2f30Ssthen 	printf("	-r fname : read resolv.conf from fname\n");
108712b2f30Ssthen 	printf("	-t : use a resolver thread instead of forking a process\n");
109712b2f30Ssthen 	printf("	-x : perform extended threaded test\n");
110712b2f30Ssthen 	exit(1);
111712b2f30Ssthen }
112712b2f30Ssthen 
113712b2f30Ssthen /** print result from lookup nicely */
114712b2f30Ssthen static void
print_result(struct lookinfo * info)115712b2f30Ssthen print_result(struct lookinfo* info)
116712b2f30Ssthen {
117712b2f30Ssthen 	char buf[100];
118712b2f30Ssthen 	if(info->err) /* error (from libunbound) */
119712b2f30Ssthen 		printf("%s: error %s\n", info->name,
120712b2f30Ssthen 			ub_strerror(info->err));
121712b2f30Ssthen 	else if(!info->result)
122712b2f30Ssthen 		printf("%s: cancelled\n", info->name);
123712b2f30Ssthen 	else if(info->result->havedata)
124712b2f30Ssthen 		printf("%s: %s\n", info->name,
125712b2f30Ssthen 			inet_ntop(AF_INET, info->result->data[0],
126712b2f30Ssthen 			buf, (socklen_t)sizeof(buf)));
127712b2f30Ssthen 	else {
128712b2f30Ssthen 		/* there is no data, why that? */
129712b2f30Ssthen 		if(info->result->rcode == 0 /*noerror*/ ||
130712b2f30Ssthen 			info->result->nxdomain)
131712b2f30Ssthen 			printf("%s: no data %s\n", info->name,
132712b2f30Ssthen 			info->result->nxdomain?"(no such host)":
133712b2f30Ssthen 			"(no IP4 address)");
134712b2f30Ssthen 		else	/* some error (from the server) */
135712b2f30Ssthen 			printf("%s: DNS error %d\n", info->name,
136712b2f30Ssthen 				info->result->rcode);
137712b2f30Ssthen 	}
138712b2f30Ssthen }
139712b2f30Ssthen 
140712b2f30Ssthen /** this is a function of type ub_callback_t */
141712b2f30Ssthen static void
lookup_is_done(void * mydata,int err,struct ub_result * result)142712b2f30Ssthen lookup_is_done(void* mydata, int err, struct ub_result* result)
143712b2f30Ssthen {
144712b2f30Ssthen 	/* cast mydata back to the correct type */
145712b2f30Ssthen 	struct lookinfo* info = (struct lookinfo*)mydata;
146712b2f30Ssthen 	fprintf(stderr, "name %s resolved\n", info->name);
147712b2f30Ssthen 	info->err = err;
148712b2f30Ssthen 	info->result = result;
149712b2f30Ssthen 	/* one less to wait for */
150712b2f30Ssthen 	num_wait--;
151712b2f30Ssthen }
152712b2f30Ssthen 
153712b2f30Ssthen /** check error, if bad, exit with error message */
154712b2f30Ssthen static void
checkerr(const char * desc,int err)155712b2f30Ssthen checkerr(const char* desc, int err)
156712b2f30Ssthen {
157712b2f30Ssthen 	if(err != 0) {
158712b2f30Ssthen 		printf("%s error: %s\n", desc, ub_strerror(err));
159712b2f30Ssthen 		exit(1);
160712b2f30Ssthen 	}
161712b2f30Ssthen }
162712b2f30Ssthen 
163712b2f30Ssthen #ifdef THREADS_DISABLED
164712b2f30Ssthen /** only one process can communicate with async worker */
165712b2f30Ssthen #define NUMTHR 1
166712b2f30Ssthen #else /* have threads */
167712b2f30Ssthen /** number of threads to make in extended test */
168712b2f30Ssthen #define NUMTHR 10
169712b2f30Ssthen #endif
170712b2f30Ssthen 
171712b2f30Ssthen /** struct for extended thread info */
172712b2f30Ssthen struct ext_thr_info {
173712b2f30Ssthen 	/** thread num for debug */
174712b2f30Ssthen 	int thread_num;
175712b2f30Ssthen 	/** thread id */
176712b2f30Ssthen 	ub_thread_type tid;
177712b2f30Ssthen 	/** context */
178712b2f30Ssthen 	struct ub_ctx* ctx;
179712b2f30Ssthen 	/** size of array to query */
180712b2f30Ssthen 	int argc;
181712b2f30Ssthen 	/** array of names to query */
182712b2f30Ssthen 	char** argv;
183712b2f30Ssthen 	/** number of queries to do */
184712b2f30Ssthen 	int numq;
1858771e50fSsthen 	/** list of ids to free once threads are done */
1868771e50fSsthen 	struct track_id* id_list;
187712b2f30Ssthen };
188712b2f30Ssthen 
189712b2f30Ssthen /** if true, we are testing against 'localhost' and extra checking is done */
190712b2f30Ssthen static int q_is_localhost = 0;
191712b2f30Ssthen 
192712b2f30Ssthen /** check result structure for the 'correct' answer */
193712b2f30Ssthen static void
ext_check_result(const char * desc,int err,struct ub_result * result)194712b2f30Ssthen ext_check_result(const char* desc, int err, struct ub_result* result)
195712b2f30Ssthen {
196712b2f30Ssthen 	checkerr(desc, err);
197712b2f30Ssthen 	if(result == NULL) {
198712b2f30Ssthen 		printf("%s: error result is NULL.\n", desc);
199712b2f30Ssthen 		exit(1);
200712b2f30Ssthen 	}
201712b2f30Ssthen 	if(q_is_localhost) {
202712b2f30Ssthen 		if(strcmp(result->qname, "localhost") != 0) {
203712b2f30Ssthen 			printf("%s: error result has wrong qname.\n", desc);
204712b2f30Ssthen 			exit(1);
205712b2f30Ssthen 		}
206712b2f30Ssthen 		if(result->qtype != LDNS_RR_TYPE_A) {
207712b2f30Ssthen 			printf("%s: error result has wrong qtype.\n", desc);
208712b2f30Ssthen 			exit(1);
209712b2f30Ssthen 		}
210712b2f30Ssthen 		if(result->qclass != LDNS_RR_CLASS_IN) {
211712b2f30Ssthen 			printf("%s: error result has wrong qclass.\n", desc);
212712b2f30Ssthen 			exit(1);
213712b2f30Ssthen 		}
214712b2f30Ssthen 		if(result->data == NULL) {
215712b2f30Ssthen 			printf("%s: error result->data is NULL.\n", desc);
216712b2f30Ssthen 			exit(1);
217712b2f30Ssthen 		}
218712b2f30Ssthen 		if(result->len == NULL) {
219712b2f30Ssthen 			printf("%s: error result->len is NULL.\n", desc);
220712b2f30Ssthen 			exit(1);
221712b2f30Ssthen 		}
222712b2f30Ssthen 		if(result->rcode != 0) {
223712b2f30Ssthen 			printf("%s: error result->rcode is set.\n", desc);
224712b2f30Ssthen 			exit(1);
225712b2f30Ssthen 		}
226712b2f30Ssthen 		if(result->havedata == 0) {
227712b2f30Ssthen 			printf("%s: error result->havedata is unset.\n", desc);
228712b2f30Ssthen 			exit(1);
229712b2f30Ssthen 		}
230712b2f30Ssthen 		if(result->nxdomain != 0) {
231712b2f30Ssthen 			printf("%s: error result->nxdomain is set.\n", desc);
232712b2f30Ssthen 			exit(1);
233712b2f30Ssthen 		}
234712b2f30Ssthen 		if(result->secure || result->bogus) {
235712b2f30Ssthen 			printf("%s: error result->secure or bogus is set.\n",
236712b2f30Ssthen 				desc);
237712b2f30Ssthen 			exit(1);
238712b2f30Ssthen 		}
239712b2f30Ssthen 		if(result->data[0] == NULL) {
240712b2f30Ssthen 			printf("%s: error result->data[0] is NULL.\n", desc);
241712b2f30Ssthen 			exit(1);
242712b2f30Ssthen 		}
243712b2f30Ssthen 		if(result->len[0] != 4) {
244712b2f30Ssthen 			printf("%s: error result->len[0] is wrong.\n", desc);
245712b2f30Ssthen 			exit(1);
246712b2f30Ssthen 		}
247712b2f30Ssthen 		if(result->len[1] != 0 || result->data[1] != NULL) {
248712b2f30Ssthen 			printf("%s: error result->data[1] or len[1] is "
249712b2f30Ssthen 				"wrong.\n", desc);
250712b2f30Ssthen 			exit(1);
251712b2f30Ssthen 		}
252712b2f30Ssthen 		if(result->answer_packet == NULL) {
253712b2f30Ssthen 			printf("%s: error result->answer_packet is NULL.\n",
254712b2f30Ssthen 				desc);
255712b2f30Ssthen 			exit(1);
256712b2f30Ssthen 		}
257712b2f30Ssthen 		if(result->answer_len != 54) {
258712b2f30Ssthen 			printf("%s: error result->answer_len is wrong.\n",
259712b2f30Ssthen 				desc);
260712b2f30Ssthen 			exit(1);
261712b2f30Ssthen 		}
262712b2f30Ssthen 	}
263712b2f30Ssthen }
264712b2f30Ssthen 
265712b2f30Ssthen /** extended bg result callback, this function is ub_callback_t */
266712b2f30Ssthen static void
ext_callback(void * mydata,int err,struct ub_result * result)267712b2f30Ssthen ext_callback(void* mydata, int err, struct ub_result* result)
268712b2f30Ssthen {
269712b2f30Ssthen 	struct track_id* my_id = (struct track_id*)mydata;
270712b2f30Ssthen 	int doprint = 0;
271712b2f30Ssthen 	if(my_id) {
272712b2f30Ssthen 		/* I have an id, make sure we are not cancelled */
273712b2f30Ssthen 		lock_basic_lock(&my_id->lock);
274712b2f30Ssthen 		if(doprint)
275712b2f30Ssthen 			printf("cb %d: ", my_id->id);
276712b2f30Ssthen 		if(my_id->cancel) {
277712b2f30Ssthen 			printf("error: query id=%d returned, but was cancelled\n",
278712b2f30Ssthen 				my_id->id);
279712b2f30Ssthen 			abort();
280712b2f30Ssthen 			exit(1);
281712b2f30Ssthen 		}
282712b2f30Ssthen 		lock_basic_unlock(&my_id->lock);
283712b2f30Ssthen 	}
284712b2f30Ssthen 	ext_check_result("ext_callback", err, result);
285712b2f30Ssthen 	log_assert(result);
286712b2f30Ssthen 	if(doprint) {
287712b2f30Ssthen 		struct lookinfo pi;
288712b2f30Ssthen 		pi.name = result?result->qname:"noname";
289712b2f30Ssthen 		pi.result = result;
290712b2f30Ssthen 		pi.err = 0;
291712b2f30Ssthen 		print_result(&pi);
292712b2f30Ssthen 	}
293712b2f30Ssthen 	ub_resolve_free(result);
294712b2f30Ssthen }
295712b2f30Ssthen 
296712b2f30Ssthen /** extended thread worker */
297712b2f30Ssthen static void*
ext_thread(void * arg)298712b2f30Ssthen ext_thread(void* arg)
299712b2f30Ssthen {
300712b2f30Ssthen 	struct ext_thr_info* inf = (struct ext_thr_info*)arg;
301712b2f30Ssthen 	int i, r;
302712b2f30Ssthen 	struct ub_result* result;
303712b2f30Ssthen 	struct track_id* async_ids = NULL;
304712b2f30Ssthen 	log_thread_set(&inf->thread_num);
305712b2f30Ssthen 	if(inf->thread_num > NUMTHR*2/3) {
306712b2f30Ssthen 		async_ids = (struct track_id*)calloc((size_t)inf->numq, sizeof(struct track_id));
307712b2f30Ssthen 		if(!async_ids) {
308712b2f30Ssthen 			printf("out of memory\n");
309712b2f30Ssthen 			exit(1);
310712b2f30Ssthen 		}
311712b2f30Ssthen 		for(i=0; i<inf->numq; i++) {
312712b2f30Ssthen 			lock_basic_init(&async_ids[i].lock);
313712b2f30Ssthen 		}
3148771e50fSsthen 		inf->id_list = async_ids;
315712b2f30Ssthen 	}
316712b2f30Ssthen 	for(i=0; i<inf->numq; i++) {
317712b2f30Ssthen 		if(async_ids) {
318712b2f30Ssthen 			r = ub_resolve_async(inf->ctx,
319712b2f30Ssthen 				inf->argv[i%inf->argc], LDNS_RR_TYPE_A,
320712b2f30Ssthen 				LDNS_RR_CLASS_IN, &async_ids[i], ext_callback,
321712b2f30Ssthen 				&async_ids[i].id);
322712b2f30Ssthen 			checkerr("ub_resolve_async", r);
323712b2f30Ssthen 			if(i > 100) {
324712b2f30Ssthen 				lock_basic_lock(&async_ids[i-100].lock);
325712b2f30Ssthen 				r = ub_cancel(inf->ctx, async_ids[i-100].id);
326712b2f30Ssthen 				if(r != UB_NOID)
327712b2f30Ssthen 					async_ids[i-100].cancel=1;
328712b2f30Ssthen 				lock_basic_unlock(&async_ids[i-100].lock);
329712b2f30Ssthen 				if(r != UB_NOID)
330712b2f30Ssthen 					checkerr("ub_cancel", r);
331712b2f30Ssthen 			}
332712b2f30Ssthen 		} else if(inf->thread_num > NUMTHR/2) {
333712b2f30Ssthen 			/* async */
334712b2f30Ssthen 			r = ub_resolve_async(inf->ctx,
335712b2f30Ssthen 				inf->argv[i%inf->argc], LDNS_RR_TYPE_A,
336712b2f30Ssthen 				LDNS_RR_CLASS_IN, NULL, ext_callback, NULL);
337712b2f30Ssthen 			checkerr("ub_resolve_async", r);
338712b2f30Ssthen 		} else  {
339712b2f30Ssthen 			/* blocking */
340712b2f30Ssthen 			r = ub_resolve(inf->ctx, inf->argv[i%inf->argc],
341712b2f30Ssthen 				LDNS_RR_TYPE_A, LDNS_RR_CLASS_IN, &result);
342712b2f30Ssthen 			ext_check_result("ub_resolve", r, result);
343712b2f30Ssthen 			ub_resolve_free(result);
344712b2f30Ssthen 		}
345712b2f30Ssthen 	}
346712b2f30Ssthen 	if(inf->thread_num > NUMTHR/2) {
347712b2f30Ssthen 		r = ub_wait(inf->ctx);
348712b2f30Ssthen 		checkerr("ub_ctx_wait", r);
349712b2f30Ssthen 	}
350712b2f30Ssthen 	/* if these locks are destroyed, or if the async_ids is freed, then
351712b2f30Ssthen 	   a use-after-free happens in another thread.
352712b2f30Ssthen 	   The allocation is only part of this test, though. */
353712b2f30Ssthen 
354712b2f30Ssthen 	return NULL;
355712b2f30Ssthen }
356712b2f30Ssthen 
357712b2f30Ssthen /** perform extended threaded test */
358712b2f30Ssthen static int
ext_test(struct ub_ctx * ctx,int argc,char ** argv)359712b2f30Ssthen ext_test(struct ub_ctx* ctx, int argc, char** argv)
360712b2f30Ssthen {
361712b2f30Ssthen 	struct ext_thr_info inf[NUMTHR];
362712b2f30Ssthen 	int i;
363712b2f30Ssthen 	if(argc == 1 && strcmp(argv[0], "localhost") == 0)
364712b2f30Ssthen 		q_is_localhost = 1;
365712b2f30Ssthen 	printf("extended test start (%d threads)\n", NUMTHR);
366712b2f30Ssthen 	for(i=0; i<NUMTHR; i++) {
367712b2f30Ssthen 		/* 0 = this, 1 = library bg worker */
368712b2f30Ssthen 		inf[i].thread_num = i+2;
369712b2f30Ssthen 		inf[i].ctx = ctx;
370712b2f30Ssthen 		inf[i].argc = argc;
371712b2f30Ssthen 		inf[i].argv = argv;
372712b2f30Ssthen 		inf[i].numq = 100;
3738771e50fSsthen 		inf[i].id_list = NULL;
374712b2f30Ssthen 		ub_thread_create(&inf[i].tid, ext_thread, &inf[i]);
375712b2f30Ssthen 	}
376712b2f30Ssthen 	/* the work happens here */
377712b2f30Ssthen 	for(i=0; i<NUMTHR; i++) {
378712b2f30Ssthen 		ub_thread_join(inf[i].tid);
379712b2f30Ssthen 	}
380712b2f30Ssthen 	printf("extended test end\n");
3818771e50fSsthen 	/* free the id lists */
3828771e50fSsthen 	for(i=0; i<NUMTHR; i++) {
3838771e50fSsthen 		if(inf[i].id_list) {
3848771e50fSsthen 			int j;
3858771e50fSsthen 			for(j=0; j<inf[i].numq; j++) {
3868771e50fSsthen 				lock_basic_destroy(&inf[i].id_list[j].lock);
3878771e50fSsthen 			}
3888771e50fSsthen 			free(inf[i].id_list);
3898771e50fSsthen 		}
3908771e50fSsthen 	}
391712b2f30Ssthen 	ub_ctx_delete(ctx);
392712b2f30Ssthen 	checklock_stop();
393712b2f30Ssthen 	return 0;
394712b2f30Ssthen }
395712b2f30Ssthen 
396712b2f30Ssthen /** getopt global, in case header files fail to declare it. */
397712b2f30Ssthen extern int optind;
398712b2f30Ssthen /** getopt global, in case header files fail to declare it. */
399712b2f30Ssthen extern char* optarg;
400712b2f30Ssthen 
401712b2f30Ssthen /** main program for asynclook */
main(int argc,char ** argv)402712b2f30Ssthen int main(int argc, char** argv)
403712b2f30Ssthen {
404712b2f30Ssthen 	int c;
405712b2f30Ssthen 	struct ub_ctx* ctx;
406712b2f30Ssthen 	struct lookinfo* lookups;
407712b2f30Ssthen 	int i, r, cancel=0, blocking=0, ext=0;
408712b2f30Ssthen 
409*83152a15Ssthen 	checklock_start();
410712b2f30Ssthen 	/* init log now because solaris thr_key_create() is not threadsafe */
411712b2f30Ssthen 	log_init(0,0,0);
412712b2f30Ssthen 	/* lock debug start (if any) */
413712b2f30Ssthen 
414712b2f30Ssthen 	/* create context */
415712b2f30Ssthen 	ctx = ub_ctx_create();
416712b2f30Ssthen 	if(!ctx) {
417712b2f30Ssthen 		printf("could not create context, %s\n", strerror(errno));
418712b2f30Ssthen 		return 1;
419712b2f30Ssthen 	}
420712b2f30Ssthen 
421712b2f30Ssthen 	/* command line options */
422712b2f30Ssthen 	if(argc == 1) {
423712b2f30Ssthen 		usage(argv);
424712b2f30Ssthen 	}
425712b2f30Ssthen 	while( (c=getopt(argc, argv, "bcdf:hH:r:tx")) != -1) {
426712b2f30Ssthen 		switch(c) {
427712b2f30Ssthen 			case 'd':
428712b2f30Ssthen 				r = ub_ctx_debuglevel(ctx, 3);
429712b2f30Ssthen 				checkerr("ub_ctx_debuglevel", r);
430712b2f30Ssthen 				break;
431712b2f30Ssthen 			case 't':
432712b2f30Ssthen 				r = ub_ctx_async(ctx, 1);
433712b2f30Ssthen 				checkerr("ub_ctx_async", r);
434712b2f30Ssthen 				break;
435712b2f30Ssthen 			case 'c':
436712b2f30Ssthen 				cancel = 1;
437712b2f30Ssthen 				break;
438712b2f30Ssthen 			case 'b':
439712b2f30Ssthen 				blocking = 1;
440712b2f30Ssthen 				break;
441712b2f30Ssthen 			case 'r':
442712b2f30Ssthen 				r = ub_ctx_resolvconf(ctx, optarg);
443712b2f30Ssthen 				if(r != 0) {
444712b2f30Ssthen 					printf("ub_ctx_resolvconf "
445712b2f30Ssthen 						"error: %s : %s\n",
446712b2f30Ssthen 						ub_strerror(r),
447712b2f30Ssthen 						strerror(errno));
448712b2f30Ssthen 					return 1;
449712b2f30Ssthen 				}
450712b2f30Ssthen 				break;
451712b2f30Ssthen 			case 'H':
452712b2f30Ssthen 				r = ub_ctx_hosts(ctx, optarg);
453712b2f30Ssthen 				if(r != 0) {
454712b2f30Ssthen 					printf("ub_ctx_hosts "
455712b2f30Ssthen 						"error: %s : %s\n",
456712b2f30Ssthen 						ub_strerror(r),
457712b2f30Ssthen 						strerror(errno));
458712b2f30Ssthen 					return 1;
459712b2f30Ssthen 				}
460712b2f30Ssthen 				break;
461712b2f30Ssthen 			case 'f':
462712b2f30Ssthen 				r = ub_ctx_set_fwd(ctx, optarg);
463712b2f30Ssthen 				checkerr("ub_ctx_set_fwd", r);
464712b2f30Ssthen 				break;
465712b2f30Ssthen 			case 'x':
466712b2f30Ssthen 				ext = 1;
467712b2f30Ssthen 				break;
468712b2f30Ssthen 			case 'h':
469712b2f30Ssthen 			case '?':
470712b2f30Ssthen 			default:
471712b2f30Ssthen 				usage(argv);
472712b2f30Ssthen 		}
473712b2f30Ssthen 	}
474712b2f30Ssthen 	argc -= optind;
475712b2f30Ssthen 	argv += optind;
476712b2f30Ssthen 
477712b2f30Ssthen #ifdef HAVE_SSL
478712b2f30Ssthen #ifdef HAVE_ERR_LOAD_CRYPTO_STRINGS
479712b2f30Ssthen 	ERR_load_crypto_strings();
480712b2f30Ssthen #endif
481712b2f30Ssthen #if OPENSSL_VERSION_NUMBER < 0x10100000 || !defined(HAVE_OPENSSL_INIT_SSL)
482712b2f30Ssthen 	ERR_load_SSL_strings();
483712b2f30Ssthen #endif
484712b2f30Ssthen #if OPENSSL_VERSION_NUMBER < 0x10100000 || !defined(HAVE_OPENSSL_INIT_CRYPTO)
485b0dfc31bSsthen #  ifndef S_SPLINT_S
486712b2f30Ssthen 	OpenSSL_add_all_algorithms();
487b0dfc31bSsthen #  endif
488712b2f30Ssthen #else
489712b2f30Ssthen 	OPENSSL_init_crypto(OPENSSL_INIT_ADD_ALL_CIPHERS
490712b2f30Ssthen 		| OPENSSL_INIT_ADD_ALL_DIGESTS
491712b2f30Ssthen 		| OPENSSL_INIT_LOAD_CRYPTO_STRINGS, NULL);
492712b2f30Ssthen #endif
493712b2f30Ssthen #if OPENSSL_VERSION_NUMBER < 0x10100000 || !defined(HAVE_OPENSSL_INIT_SSL)
494712b2f30Ssthen 	(void)SSL_library_init();
495712b2f30Ssthen #else
496712b2f30Ssthen 	(void)OPENSSL_init_ssl(OPENSSL_INIT_LOAD_SSL_STRINGS, NULL);
497712b2f30Ssthen #endif
498712b2f30Ssthen #endif /* HAVE_SSL */
499712b2f30Ssthen 
500712b2f30Ssthen 	if(ext)
501712b2f30Ssthen 		return ext_test(ctx, argc, argv);
502712b2f30Ssthen 
503712b2f30Ssthen 	/* allocate array for results. */
504712b2f30Ssthen 	lookups = (struct lookinfo*)calloc((size_t)argc,
505712b2f30Ssthen 		sizeof(struct lookinfo));
506712b2f30Ssthen 	if(!lookups) {
507712b2f30Ssthen 		printf("out of memory\n");
508712b2f30Ssthen 		return 1;
509712b2f30Ssthen 	}
510712b2f30Ssthen 
511712b2f30Ssthen 	/* perform asynchronous calls */
512712b2f30Ssthen 	num_wait = argc;
513712b2f30Ssthen 	for(i=0; i<argc; i++) {
514712b2f30Ssthen 		lookups[i].name = argv[i];
515712b2f30Ssthen 		if(blocking) {
516712b2f30Ssthen 			fprintf(stderr, "lookup %s\n", argv[i]);
517712b2f30Ssthen 			r = ub_resolve(ctx, argv[i], LDNS_RR_TYPE_A,
518712b2f30Ssthen 				LDNS_RR_CLASS_IN, &lookups[i].result);
519712b2f30Ssthen 			checkerr("ub_resolve", r);
520712b2f30Ssthen 		} else {
521712b2f30Ssthen 			fprintf(stderr, "start async lookup %s\n", argv[i]);
522712b2f30Ssthen 			r = ub_resolve_async(ctx, argv[i], LDNS_RR_TYPE_A,
523712b2f30Ssthen 				LDNS_RR_CLASS_IN, &lookups[i], &lookup_is_done,
524712b2f30Ssthen 				&lookups[i].async_id);
525712b2f30Ssthen 			checkerr("ub_resolve_async", r);
526712b2f30Ssthen 		}
527712b2f30Ssthen 	}
528712b2f30Ssthen 	if(blocking)
529712b2f30Ssthen 		num_wait = 0;
530712b2f30Ssthen 	else if(cancel) {
531712b2f30Ssthen 		for(i=0; i<argc; i++) {
532712b2f30Ssthen 			fprintf(stderr, "cancel %s\n", argv[i]);
533712b2f30Ssthen 			r = ub_cancel(ctx, lookups[i].async_id);
534712b2f30Ssthen 			if(r != UB_NOID)
535712b2f30Ssthen 				checkerr("ub_cancel", r);
536712b2f30Ssthen 		}
537712b2f30Ssthen 		num_wait = 0;
538712b2f30Ssthen 	}
539712b2f30Ssthen 
540712b2f30Ssthen 	/* wait while the hostnames are looked up. Do something useful here */
541712b2f30Ssthen 	if(num_wait > 0)
542712b2f30Ssthen 	    for(i=0; i<1000; i++) {
543712b2f30Ssthen 		usleep(100000);
544712b2f30Ssthen 		fprintf(stderr, "%g seconds passed\n", 0.1*(double)i);
545712b2f30Ssthen 		r = ub_process(ctx);
546712b2f30Ssthen 		checkerr("ub_process", r);
547712b2f30Ssthen 		if(num_wait == 0)
548712b2f30Ssthen 			break;
549712b2f30Ssthen 	}
550712b2f30Ssthen 	if(i>=999) {
551712b2f30Ssthen 		printf("timed out\n");
552712b2f30Ssthen 		return 0;
553712b2f30Ssthen 	}
554712b2f30Ssthen 	printf("lookup complete\n");
555712b2f30Ssthen 
556712b2f30Ssthen 	/* print lookup results */
557712b2f30Ssthen 	for(i=0; i<argc; i++) {
558712b2f30Ssthen 		print_result(&lookups[i]);
559712b2f30Ssthen 		ub_resolve_free(lookups[i].result);
560712b2f30Ssthen 	}
561712b2f30Ssthen 
562712b2f30Ssthen 	ub_ctx_delete(ctx);
563712b2f30Ssthen 	free(lookups);
564712b2f30Ssthen 	checklock_stop();
565712b2f30Ssthen 	return 0;
566712b2f30Ssthen }
567