]> git.saurik.com Git - apple/network_cmds.git/blob - unbound/testcode/asynclook.c
network_cmds-480.tar.gz
[apple/network_cmds.git] / unbound / testcode / asynclook.c
1 /*
2 * testcode/asynclook.c - debug program perform async libunbound queries.
3 *
4 * Copyright (c) 2008, NLnet Labs. All rights reserved.
5 *
6 * This software is open source.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 *
12 * Redistributions of source code must retain the above copyright notice,
13 * this list of conditions and the following disclaimer.
14 *
15 * Redistributions in binary form must reproduce the above copyright notice,
16 * this list of conditions and the following disclaimer in the documentation
17 * and/or other materials provided with the distribution.
18 *
19 * Neither the name of the NLNET LABS nor the names of its contributors may
20 * be used to endorse or promote products derived from this software without
21 * specific prior written permission.
22 *
23 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
24 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
25 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
26 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
27 * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
28 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
29 * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
30 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
31 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
32 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
33 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
34 */
35
36 /**
37 * \file
38 *
39 * This program shows the results from several background lookups,
40 * while printing time in the foreground.
41 */
42
43 #include "config.h"
44 #ifdef HAVE_GETOPT_H
45 #include <getopt.h>
46 #endif
47 #include "libunbound/unbound.h"
48 #include "libunbound/context.h"
49 #include "util/locks.h"
50 #include "util/log.h"
51 #include "ldns/rrdef.h"
52 #ifdef UNBOUND_ALLOC_LITE
53 #undef malloc
54 #undef calloc
55 #undef realloc
56 #undef free
57 #undef strdup
58 #endif
59
60 /** keeping track of the async ids */
61 struct track_id {
62 /** the id to pass to libunbound to cancel */
63 int id;
64 /** true if cancelled */
65 int cancel;
66 /** a lock on this structure for thread safety */
67 lock_basic_t lock;
68 };
69
70 /**
71 * result list for the lookups
72 */
73 struct lookinfo {
74 /** name to look up */
75 char* name;
76 /** tracking number that can be used to cancel the query */
77 int async_id;
78 /** error code from libunbound */
79 int err;
80 /** result from lookup */
81 struct ub_result* result;
82 };
83
84 /** global variable to see how many queries we have left */
85 static int num_wait = 0;
86
87 /** usage information for asynclook */
88 static void usage(char* argv[])
89 {
90 printf("usage: %s [options] name ...\n", argv[0]);
91 printf("names are looked up at the same time, asynchronously.\n");
92 printf(" -b : use blocking requests\n");
93 printf(" -c : cancel the requests\n");
94 printf(" -d : enable debug output\n");
95 printf(" -f addr : use addr, forward to that server\n");
96 printf(" -h : this help message\n");
97 printf(" -H fname : read hosts from fname\n");
98 printf(" -r fname : read resolv.conf from fname\n");
99 printf(" -t : use a resolver thread instead of forking a process\n");
100 printf(" -x : perform extended threaded test\n");
101 exit(1);
102 }
103
104 /** print result from lookup nicely */
105 static void
106 print_result(struct lookinfo* info)
107 {
108 char buf[100];
109 if(info->err) /* error (from libunbound) */
110 printf("%s: error %s\n", info->name,
111 ub_strerror(info->err));
112 else if(!info->result)
113 printf("%s: cancelled\n", info->name);
114 else if(info->result->havedata)
115 printf("%s: %s\n", info->name,
116 inet_ntop(AF_INET, info->result->data[0],
117 buf, (socklen_t)sizeof(buf)));
118 else {
119 /* there is no data, why that? */
120 if(info->result->rcode == 0 /*noerror*/ ||
121 info->result->nxdomain)
122 printf("%s: no data %s\n", info->name,
123 info->result->nxdomain?"(no such host)":
124 "(no IP4 address)");
125 else /* some error (from the server) */
126 printf("%s: DNS error %d\n", info->name,
127 info->result->rcode);
128 }
129 }
130
131 /** this is a function of type ub_callback_t */
132 static void
133 lookup_is_done(void* mydata, int err, struct ub_result* result)
134 {
135 /* cast mydata back to the correct type */
136 struct lookinfo* info = (struct lookinfo*)mydata;
137 fprintf(stderr, "name %s resolved\n", info->name);
138 info->err = err;
139 info->result = result;
140 /* one less to wait for */
141 num_wait--;
142 }
143
144 /** check error, if bad, exit with error message */
145 static void
146 checkerr(const char* desc, int err)
147 {
148 if(err != 0) {
149 printf("%s error: %s\n", desc, ub_strerror(err));
150 exit(1);
151 }
152 }
153
154 #ifdef THREADS_DISABLED
155 /** only one process can communicate with async worker */
156 #define NUMTHR 1
157 #else /* have threads */
158 /** number of threads to make in extended test */
159 #define NUMTHR 10
160 #endif
161
162 /** struct for extended thread info */
163 struct ext_thr_info {
164 /** thread num for debug */
165 int thread_num;
166 /** thread id */
167 ub_thread_t tid;
168 /** context */
169 struct ub_ctx* ctx;
170 /** size of array to query */
171 int argc;
172 /** array of names to query */
173 char** argv;
174 /** number of queries to do */
175 int numq;
176 };
177
178 /** if true, we are testing against 'localhost' and extra checking is done */
179 static int q_is_localhost = 0;
180
181 /** check result structure for the 'correct' answer */
182 static void
183 ext_check_result(const char* desc, int err, struct ub_result* result)
184 {
185 checkerr(desc, err);
186 if(result == NULL) {
187 printf("%s: error result is NULL.\n", desc);
188 exit(1);
189 }
190 if(q_is_localhost) {
191 if(strcmp(result->qname, "localhost") != 0) {
192 printf("%s: error result has wrong qname.\n", desc);
193 exit(1);
194 }
195 if(result->qtype != LDNS_RR_TYPE_A) {
196 printf("%s: error result has wrong qtype.\n", desc);
197 exit(1);
198 }
199 if(result->qclass != LDNS_RR_CLASS_IN) {
200 printf("%s: error result has wrong qclass.\n", desc);
201 exit(1);
202 }
203 if(result->data == NULL) {
204 printf("%s: error result->data is NULL.\n", desc);
205 exit(1);
206 }
207 if(result->len == NULL) {
208 printf("%s: error result->len is NULL.\n", desc);
209 exit(1);
210 }
211 if(result->rcode != 0) {
212 printf("%s: error result->rcode is set.\n", desc);
213 exit(1);
214 }
215 if(result->havedata == 0) {
216 printf("%s: error result->havedata is unset.\n", desc);
217 exit(1);
218 }
219 if(result->nxdomain != 0) {
220 printf("%s: error result->nxdomain is set.\n", desc);
221 exit(1);
222 }
223 if(result->secure || result->bogus) {
224 printf("%s: error result->secure or bogus is set.\n",
225 desc);
226 exit(1);
227 }
228 if(result->data[0] == NULL) {
229 printf("%s: error result->data[0] is NULL.\n", desc);
230 exit(1);
231 }
232 if(result->len[0] != 4) {
233 printf("%s: error result->len[0] is wrong.\n", desc);
234 exit(1);
235 }
236 if(result->len[1] != 0 || result->data[1] != NULL) {
237 printf("%s: error result->data[1] or len[1] is "
238 "wrong.\n", desc);
239 exit(1);
240 }
241 if(result->answer_packet == NULL) {
242 printf("%s: error result->answer_packet is NULL.\n",
243 desc);
244 exit(1);
245 }
246 if(result->answer_len != 54) {
247 printf("%s: error result->answer_len is wrong.\n",
248 desc);
249 exit(1);
250 }
251 }
252 }
253
254 /** extended bg result callback, this function is ub_callback_t */
255 static void
256 ext_callback(void* mydata, int err, struct ub_result* result)
257 {
258 struct track_id* my_id = (struct track_id*)mydata;
259 int doprint = 0;
260 if(my_id) {
261 /* I have an id, make sure we are not cancelled */
262 lock_basic_lock(&my_id->lock);
263 if(doprint)
264 printf("cb %d: ", my_id->id);
265 if(my_id->cancel) {
266 printf("error: query id=%d returned, but was cancelled\n",
267 my_id->id);
268 abort();
269 exit(1);
270 }
271 lock_basic_unlock(&my_id->lock);
272 }
273 ext_check_result("ext_callback", err, result);
274 log_assert(result);
275 if(doprint) {
276 struct lookinfo pi;
277 pi.name = result?result->qname:"noname";
278 pi.result = result;
279 pi.err = 0;
280 print_result(&pi);
281 }
282 ub_resolve_free(result);
283 }
284
285 /** extended thread worker */
286 static void*
287 ext_thread(void* arg)
288 {
289 struct ext_thr_info* inf = (struct ext_thr_info*)arg;
290 int i, r;
291 struct ub_result* result;
292 struct track_id* async_ids = NULL;
293 log_thread_set(&inf->thread_num);
294 if(inf->thread_num > NUMTHR*2/3) {
295 async_ids = (struct track_id*)calloc((size_t)inf->numq, sizeof(struct track_id));
296 if(!async_ids) {
297 printf("out of memory\n");
298 exit(1);
299 }
300 for(i=0; i<inf->numq; i++) {
301 lock_basic_init(&async_ids[i].lock);
302 }
303 }
304 for(i=0; i<inf->numq; i++) {
305 if(async_ids) {
306 r = ub_resolve_async(inf->ctx,
307 inf->argv[i%inf->argc], LDNS_RR_TYPE_A,
308 LDNS_RR_CLASS_IN, &async_ids[i], ext_callback,
309 &async_ids[i].id);
310 checkerr("ub_resolve_async", r);
311 if(i > 100) {
312 lock_basic_lock(&async_ids[i-100].lock);
313 r = ub_cancel(inf->ctx, async_ids[i-100].id);
314 if(r != UB_NOID)
315 async_ids[i-100].cancel=1;
316 lock_basic_unlock(&async_ids[i-100].lock);
317 if(r != UB_NOID)
318 checkerr("ub_cancel", r);
319 }
320 } else if(inf->thread_num > NUMTHR/2) {
321 /* async */
322 r = ub_resolve_async(inf->ctx,
323 inf->argv[i%inf->argc], LDNS_RR_TYPE_A,
324 LDNS_RR_CLASS_IN, NULL, ext_callback, NULL);
325 checkerr("ub_resolve_async", r);
326 } else {
327 /* blocking */
328 r = ub_resolve(inf->ctx, inf->argv[i%inf->argc],
329 LDNS_RR_TYPE_A, LDNS_RR_CLASS_IN, &result);
330 ext_check_result("ub_resolve", r, result);
331 ub_resolve_free(result);
332 }
333 }
334 if(inf->thread_num > NUMTHR/2) {
335 r = ub_wait(inf->ctx);
336 checkerr("ub_ctx_wait", r);
337 }
338 if(async_ids) {
339 for(i=0; i<inf->numq; i++) {
340 lock_basic_destroy(&async_ids[i].lock);
341 }
342 }
343 free(async_ids);
344
345 return NULL;
346 }
347
348 /** perform extended threaded test */
349 static int
350 ext_test(struct ub_ctx* ctx, int argc, char** argv)
351 {
352 struct ext_thr_info inf[NUMTHR];
353 int i;
354 if(argc == 1 && strcmp(argv[0], "localhost") == 0)
355 q_is_localhost = 1;
356 printf("extended test start (%d threads)\n", NUMTHR);
357 for(i=0; i<NUMTHR; i++) {
358 /* 0 = this, 1 = library bg worker */
359 inf[i].thread_num = i+2;
360 inf[i].ctx = ctx;
361 inf[i].argc = argc;
362 inf[i].argv = argv;
363 inf[i].numq = 100;
364 ub_thread_create(&inf[i].tid, ext_thread, &inf[i]);
365 }
366 /* the work happens here */
367 for(i=0; i<NUMTHR; i++) {
368 ub_thread_join(inf[i].tid);
369 }
370 printf("extended test end\n");
371 ub_ctx_delete(ctx);
372 checklock_stop();
373 return 0;
374 }
375
376 /** getopt global, in case header files fail to declare it. */
377 extern int optind;
378 /** getopt global, in case header files fail to declare it. */
379 extern char* optarg;
380
381 /** main program for asynclook */
382 int main(int argc, char** argv)
383 {
384 int c;
385 struct ub_ctx* ctx;
386 struct lookinfo* lookups;
387 int i, r, cancel=0, blocking=0, ext=0;
388
389 /* init log now because solaris thr_key_create() is not threadsafe */
390 log_init(0,0,0);
391 /* lock debug start (if any) */
392 checklock_start();
393
394 /* create context */
395 ctx = ub_ctx_create();
396 if(!ctx) {
397 printf("could not create context, %s\n", strerror(errno));
398 return 1;
399 }
400
401 /* command line options */
402 if(argc == 1) {
403 usage(argv);
404 }
405 while( (c=getopt(argc, argv, "bcdf:hH:r:tx")) != -1) {
406 switch(c) {
407 case 'd':
408 r = ub_ctx_debuglevel(ctx, 3);
409 checkerr("ub_ctx_debuglevel", r);
410 break;
411 case 't':
412 r = ub_ctx_async(ctx, 1);
413 checkerr("ub_ctx_async", r);
414 break;
415 case 'c':
416 cancel = 1;
417 break;
418 case 'b':
419 blocking = 1;
420 break;
421 case 'r':
422 r = ub_ctx_resolvconf(ctx, optarg);
423 if(r != 0) {
424 printf("ub_ctx_resolvconf "
425 "error: %s : %s\n",
426 ub_strerror(r),
427 strerror(errno));
428 return 1;
429 }
430 break;
431 case 'H':
432 r = ub_ctx_hosts(ctx, optarg);
433 if(r != 0) {
434 printf("ub_ctx_hosts "
435 "error: %s : %s\n",
436 ub_strerror(r),
437 strerror(errno));
438 return 1;
439 }
440 break;
441 case 'f':
442 r = ub_ctx_set_fwd(ctx, optarg);
443 checkerr("ub_ctx_set_fwd", r);
444 break;
445 case 'x':
446 ext = 1;
447 break;
448 case 'h':
449 case '?':
450 default:
451 usage(argv);
452 }
453 }
454 argc -= optind;
455 argv += optind;
456
457 if(ext)
458 return ext_test(ctx, argc, argv);
459
460 /* allocate array for results. */
461 lookups = (struct lookinfo*)calloc((size_t)argc,
462 sizeof(struct lookinfo));
463 if(!lookups) {
464 printf("out of memory\n");
465 return 1;
466 }
467
468 /* perform asyncronous calls */
469 num_wait = argc;
470 for(i=0; i<argc; i++) {
471 lookups[i].name = argv[i];
472 if(blocking) {
473 fprintf(stderr, "lookup %s\n", argv[i]);
474 r = ub_resolve(ctx, argv[i], LDNS_RR_TYPE_A,
475 LDNS_RR_CLASS_IN, &lookups[i].result);
476 checkerr("ub_resolve", r);
477 } else {
478 fprintf(stderr, "start async lookup %s\n", argv[i]);
479 r = ub_resolve_async(ctx, argv[i], LDNS_RR_TYPE_A,
480 LDNS_RR_CLASS_IN, &lookups[i], &lookup_is_done,
481 &lookups[i].async_id);
482 checkerr("ub_resolve_async", r);
483 }
484 }
485 if(blocking)
486 num_wait = 0;
487 else if(cancel) {
488 for(i=0; i<argc; i++) {
489 fprintf(stderr, "cancel %s\n", argv[i]);
490 r = ub_cancel(ctx, lookups[i].async_id);
491 if(r != UB_NOID)
492 checkerr("ub_cancel", r);
493 }
494 num_wait = 0;
495 }
496
497 /* wait while the hostnames are looked up. Do something useful here */
498 if(num_wait > 0)
499 for(i=0; i<1000; i++) {
500 usleep(100000);
501 fprintf(stderr, "%g seconds passed\n", 0.1*(double)i);
502 r = ub_process(ctx);
503 checkerr("ub_process", r);
504 if(num_wait == 0)
505 break;
506 }
507 if(i>=999) {
508 printf("timed out\n");
509 return 0;
510 }
511 printf("lookup complete\n");
512
513 /* print lookup results */
514 for(i=0; i<argc; i++) {
515 print_result(&lookups[i]);
516 ub_resolve_free(lookups[i].result);
517 }
518
519 ub_ctx_delete(ctx);
520 free(lookups);
521 checklock_stop();
522 return 0;
523 }