]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - testcode/asynclook.c
import unbound 1.5.8
[FreeBSD/FreeBSD.git] / 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 "sldns/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 these locks are destroyed, or if the async_ids is freed, then
339            a use-after-free happens in another thread.
340            The allocation is only part of this test, though. */
341         /*
342         if(async_ids) {
343                 for(i=0; i<inf->numq; i++) {
344                         lock_basic_destroy(&async_ids[i].lock);
345                 }
346         }
347         free(async_ids);
348         */
349         
350         return NULL;
351 }
352
353 /** perform extended threaded test */
354 static int
355 ext_test(struct ub_ctx* ctx, int argc, char** argv)
356 {
357         struct ext_thr_info inf[NUMTHR];
358         int i;
359         if(argc == 1 && strcmp(argv[0], "localhost") == 0)
360                 q_is_localhost = 1;
361         printf("extended test start (%d threads)\n", NUMTHR);
362         for(i=0; i<NUMTHR; i++) {
363                 /* 0 = this, 1 = library bg worker */
364                 inf[i].thread_num = i+2;
365                 inf[i].ctx = ctx;
366                 inf[i].argc = argc;
367                 inf[i].argv = argv;
368                 inf[i].numq = 100;
369                 ub_thread_create(&inf[i].tid, ext_thread, &inf[i]);
370         }
371         /* the work happens here */
372         for(i=0; i<NUMTHR; i++) {
373                 ub_thread_join(inf[i].tid);
374         }
375         printf("extended test end\n");
376         ub_ctx_delete(ctx);
377         checklock_stop();
378         return 0;
379 }
380
381 /** getopt global, in case header files fail to declare it. */
382 extern int optind;
383 /** getopt global, in case header files fail to declare it. */
384 extern char* optarg;
385
386 /** main program for asynclook */
387 int main(int argc, char** argv) 
388 {
389         int c;
390         struct ub_ctx* ctx;
391         struct lookinfo* lookups;
392         int i, r, cancel=0, blocking=0, ext=0;
393
394         /* init log now because solaris thr_key_create() is not threadsafe */
395         log_init(0,0,0);
396         /* lock debug start (if any) */
397         checklock_start();
398
399         /* create context */
400         ctx = ub_ctx_create();
401         if(!ctx) {
402                 printf("could not create context, %s\n", strerror(errno));
403                 return 1;
404         }
405
406         /* command line options */
407         if(argc == 1) {
408                 usage(argv);
409         }
410         while( (c=getopt(argc, argv, "bcdf:hH:r:tx")) != -1) {
411                 switch(c) {
412                         case 'd':
413                                 r = ub_ctx_debuglevel(ctx, 3);
414                                 checkerr("ub_ctx_debuglevel", r);
415                                 break;
416                         case 't':
417                                 r = ub_ctx_async(ctx, 1);
418                                 checkerr("ub_ctx_async", r);
419                                 break;
420                         case 'c':
421                                 cancel = 1;
422                                 break;
423                         case 'b':
424                                 blocking = 1;
425                                 break;
426                         case 'r':
427                                 r = ub_ctx_resolvconf(ctx, optarg);
428                                 if(r != 0) {
429                                         printf("ub_ctx_resolvconf "
430                                                 "error: %s : %s\n",
431                                                 ub_strerror(r), 
432                                                 strerror(errno));
433                                         return 1;
434                                 }
435                                 break;
436                         case 'H':
437                                 r = ub_ctx_hosts(ctx, optarg);
438                                 if(r != 0) {
439                                         printf("ub_ctx_hosts "
440                                                 "error: %s : %s\n",
441                                                 ub_strerror(r), 
442                                                 strerror(errno));
443                                         return 1;
444                                 }
445                                 break;
446                         case 'f':
447                                 r = ub_ctx_set_fwd(ctx, optarg);
448                                 checkerr("ub_ctx_set_fwd", r);
449                                 break;
450                         case 'x':
451                                 ext = 1;
452                                 break;
453                         case 'h':
454                         case '?':
455                         default:
456                                 usage(argv);
457                 }
458         }
459         argc -= optind;
460         argv += optind;
461
462         if(ext)
463                 return ext_test(ctx, argc, argv);
464
465         /* allocate array for results. */
466         lookups = (struct lookinfo*)calloc((size_t)argc, 
467                 sizeof(struct lookinfo));
468         if(!lookups) {
469                 printf("out of memory\n");
470                 return 1;
471         }
472
473         /* perform asyncronous calls */
474         num_wait = argc;
475         for(i=0; i<argc; i++) {
476                 lookups[i].name = argv[i];
477                 if(blocking) {
478                         fprintf(stderr, "lookup %s\n", argv[i]);
479                         r = ub_resolve(ctx, argv[i], LDNS_RR_TYPE_A,
480                                 LDNS_RR_CLASS_IN, &lookups[i].result);
481                         checkerr("ub_resolve", r);
482                 } else {
483                         fprintf(stderr, "start async lookup %s\n", argv[i]);
484                         r = ub_resolve_async(ctx, argv[i], LDNS_RR_TYPE_A,
485                                 LDNS_RR_CLASS_IN, &lookups[i], &lookup_is_done, 
486                                 &lookups[i].async_id);
487                         checkerr("ub_resolve_async", r);
488                 }
489         }
490         if(blocking)
491                 num_wait = 0;
492         else if(cancel) {
493                 for(i=0; i<argc; i++) {
494                         fprintf(stderr, "cancel %s\n", argv[i]);
495                         r = ub_cancel(ctx, lookups[i].async_id);
496                         if(r != UB_NOID) 
497                                 checkerr("ub_cancel", r);
498                 }
499                 num_wait = 0;
500         }
501
502         /* wait while the hostnames are looked up. Do something useful here */
503         if(num_wait > 0)
504             for(i=0; i<1000; i++) {
505                 usleep(100000);
506                 fprintf(stderr, "%g seconds passed\n", 0.1*(double)i);
507                 r = ub_process(ctx);
508                 checkerr("ub_process", r);
509                 if(num_wait == 0)
510                         break;
511         }
512         if(i>=999) {
513                 printf("timed out\n");
514                 return 0;
515         }
516         printf("lookup complete\n");
517
518         /* print lookup results */
519         for(i=0; i<argc; i++) {
520                 print_result(&lookups[i]);
521                 ub_resolve_free(lookups[i].result);
522         }
523
524         ub_ctx_delete(ctx);
525         free(lookups);
526         checklock_stop();
527         return 0;
528 }