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