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