1/* 2 * Copyright (C) 2009 Internet Systems Consortium, Inc. ("ISC") 3 * 4 * Permission to use, copy, modify, and/or distribute this software for any 5 * purpose with or without fee is hereby granted, provided that the above 6 * copyright notice and this permission notice appear in all copies. 7 * 8 * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH 9 * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 10 * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, 11 * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 12 * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE 13 * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 14 * PERFORMANCE OF THIS SOFTWARE. 15 */ 16 17/* $Id: sample-async.c,v 1.5 2009/09/29 15:06:07 fdupont Exp $ */ 18 19#include <config.h> 20 21#include <sys/types.h> 22#include <sys/socket.h> 23 24#include <netinet/in.h> 25 26#include <arpa/inet.h> 27 28#include <unistd.h> 29#include <stdio.h> 30#include <stdlib.h> 31#include <string.h> 32 33#include <isc/app.h> 34#include <isc/buffer.h> 35#include <isc/lib.h> 36#include <isc/mem.h> 37#include <isc/socket.h> 38#include <isc/sockaddr.h> 39#include <isc/task.h> 40#include <isc/timer.h> 41#include <isc/util.h> 42 43#include <dns/client.h> 44#include <dns/fixedname.h> 45#include <dns/lib.h> 46#include <dns/name.h> 47#include <dns/rdataset.h> 48#include <dns/rdatatype.h> 49#include <dns/result.h> 50 51#define MAX_SERVERS 10 52#define MAX_QUERIES 100 53 54static dns_client_t *client = NULL; 55static isc_task_t *query_task = NULL; 56static isc_appctx_t *query_actx = NULL; 57static unsigned int outstanding_queries = 0; 58static const char *def_server = "127.0.0.1"; 59static FILE *fp; 60 61struct query_trans { 62 int id; 63 isc_boolean_t inuse; 64 dns_rdatatype_t type; 65 dns_fixedname_t fixedname; 66 dns_name_t *qname; 67 dns_namelist_t answerlist; 68 dns_clientrestrans_t *xid; 69}; 70 71static struct query_trans query_array[MAX_QUERIES]; 72 73static isc_result_t dispatch_query(struct query_trans *trans); 74 75static void 76ctxs_destroy(isc_mem_t **mctxp, isc_appctx_t **actxp, 77 isc_taskmgr_t **taskmgrp, isc_socketmgr_t **socketmgrp, 78 isc_timermgr_t **timermgrp) 79{ 80 if (*taskmgrp != NULL) 81 isc_taskmgr_destroy(taskmgrp); 82 83 if (*timermgrp != NULL) 84 isc_timermgr_destroy(timermgrp); 85 86 if (*socketmgrp != NULL) 87 isc_socketmgr_destroy(socketmgrp); 88 89 if (*actxp != NULL) 90 isc_appctx_destroy(actxp); 91 92 if (*mctxp != NULL) 93 isc_mem_destroy(mctxp); 94} 95 96static isc_result_t 97ctxs_init(isc_mem_t **mctxp, isc_appctx_t **actxp, 98 isc_taskmgr_t **taskmgrp, isc_socketmgr_t **socketmgrp, 99 isc_timermgr_t **timermgrp) 100{ 101 isc_result_t result; 102 103 result = isc_mem_create(0, 0, mctxp); 104 if (result != ISC_R_SUCCESS) 105 goto fail; 106 107 result = isc_appctx_create(*mctxp, actxp); 108 if (result != ISC_R_SUCCESS) 109 goto fail; 110 111 result = isc_taskmgr_createinctx(*mctxp, *actxp, 1, 0, taskmgrp); 112 if (result != ISC_R_SUCCESS) 113 goto fail; 114 115 result = isc_socketmgr_createinctx(*mctxp, *actxp, socketmgrp); 116 if (result != ISC_R_SUCCESS) 117 goto fail; 118 119 result = isc_timermgr_createinctx(*mctxp, *actxp, timermgrp); 120 if (result != ISC_R_SUCCESS) 121 goto fail; 122 123 return (ISC_R_SUCCESS); 124 125 fail: 126 ctxs_destroy(mctxp, actxp, taskmgrp, socketmgrp, timermgrp); 127 128 return (result); 129} 130 131static isc_result_t 132printdata(dns_rdataset_t *rdataset, dns_name_t *owner) { 133 isc_buffer_t target; 134 isc_result_t result; 135 isc_region_t r; 136 char t[4096]; 137 138 isc_buffer_init(&target, t, sizeof(t)); 139 140 if (!dns_rdataset_isassociated(rdataset)) 141 return (ISC_R_SUCCESS); 142 result = dns_rdataset_totext(rdataset, owner, ISC_FALSE, ISC_FALSE, 143 &target); 144 if (result != ISC_R_SUCCESS) 145 return (result); 146 isc_buffer_usedregion(&target, &r); 147 printf(" %.*s", (int)r.length, (char *)r.base); 148 149 return (ISC_R_SUCCESS); 150} 151 152static void 153process_answer(isc_task_t *task, isc_event_t *event) { 154 struct query_trans *trans = event->ev_arg; 155 dns_clientresevent_t *rev = (dns_clientresevent_t *)event; 156 dns_name_t *name; 157 dns_rdataset_t *rdataset; 158 isc_result_t result; 159 160 REQUIRE(task == query_task); 161 REQUIRE(trans->inuse == ISC_TRUE); 162 REQUIRE(outstanding_queries > 0); 163 164 printf("answer[%2d]\n", trans->id); 165 166 if (rev->result != ISC_R_SUCCESS) 167 printf(" failed: %d(%s)\n", rev->result, 168 dns_result_totext(rev->result)); 169 170 for (name = ISC_LIST_HEAD(rev->answerlist); name != NULL; 171 name = ISC_LIST_NEXT(name, link)) { 172 for (rdataset = ISC_LIST_HEAD(name->list); 173 rdataset != NULL; 174 rdataset = ISC_LIST_NEXT(rdataset, link)) { 175 (void)printdata(rdataset, name); 176 } 177 } 178 179 dns_client_freeresanswer(client, &rev->answerlist); 180 dns_client_destroyrestrans(&trans->xid); 181 182 isc_event_free(&event); 183 184 trans->inuse = ISC_FALSE; 185 dns_fixedname_invalidate(&trans->fixedname); 186 trans->qname = NULL; 187 outstanding_queries--; 188 189 result = dispatch_query(trans); 190#if 0 /* for cancel test */ 191 if (result == ISC_R_SUCCESS) { 192 static int count = 0; 193 194 if ((++count) % 10 == 0) 195 dns_client_cancelresolve(trans->xid); 196 } 197#endif 198 if (result == ISC_R_NOMORE && outstanding_queries == 0) 199 isc_app_ctxshutdown(query_actx); 200} 201 202static isc_result_t 203dispatch_query(struct query_trans *trans) { 204 isc_result_t result; 205 size_t namelen; 206 isc_buffer_t b; 207 char buf[4096]; /* XXX ad hoc constant, but should be enough */ 208 char *cp; 209 210 REQUIRE(trans != NULL); 211 REQUIRE(trans->inuse == ISC_FALSE); 212 REQUIRE(ISC_LIST_EMPTY(trans->answerlist)); 213 REQUIRE(outstanding_queries < MAX_QUERIES); 214 215 /* Construct qname */ 216 cp = fgets(buf, sizeof(buf), fp); 217 if (cp == NULL) 218 return (ISC_R_NOMORE); 219 /* zap NL if any */ 220 if ((cp = strchr(buf, '\n')) != NULL) 221 *cp = '\0'; 222 namelen = strlen(buf); 223 isc_buffer_init(&b, buf, namelen); 224 isc_buffer_add(&b, namelen); 225 dns_fixedname_init(&trans->fixedname); 226 trans->qname = dns_fixedname_name(&trans->fixedname); 227 result = dns_name_fromtext(trans->qname, &b, dns_rootname, 0, NULL); 228 if (result != ISC_R_SUCCESS) 229 goto cleanup; 230 231 /* Start resolution */ 232 result = dns_client_startresolve(client, trans->qname, 233 dns_rdataclass_in, trans->type, 0, 234 query_task, process_answer, trans, 235 &trans->xid); 236 if (result != ISC_R_SUCCESS) 237 goto cleanup; 238 239 trans->inuse = ISC_TRUE; 240 outstanding_queries++; 241 242 return (ISC_R_SUCCESS); 243 244 cleanup: 245 dns_fixedname_invalidate(&trans->fixedname); 246 247 return (result); 248} 249 250ISC_PLATFORM_NORETURN_PRE static void 251usage(void) ISC_PLATFORM_NORETURN_POST; 252 253static void 254usage(void) { 255 fprintf(stderr, "usage: sample-async [-s server_address] [-t RR type] " 256 "input_file\n"); 257 258 exit(1); 259} 260 261int 262main(int argc, char *argv[]) { 263 int ch; 264 isc_textregion_t tr; 265 isc_mem_t *mctx = NULL; 266 isc_taskmgr_t *taskmgr = NULL; 267 isc_socketmgr_t *socketmgr = NULL; 268 isc_timermgr_t *timermgr = NULL; 269 int nservers = 0; 270 const char *serveraddr[MAX_SERVERS]; 271 isc_sockaddr_t sa[MAX_SERVERS]; 272 isc_sockaddrlist_t servers; 273 dns_rdatatype_t type = dns_rdatatype_a; 274 struct in_addr inaddr; 275 isc_result_t result; 276 int i; 277 278 while ((ch = getopt(argc, argv, "s:t:")) != -1) { 279 switch (ch) { 280 case 't': 281 tr.base = optarg; 282 tr.length = strlen(optarg); 283 result = dns_rdatatype_fromtext(&type, &tr); 284 if (result != ISC_R_SUCCESS) { 285 fprintf(stderr, 286 "invalid RRtype: %s\n", optarg); 287 exit(1); 288 } 289 break; 290 case 's': 291 if (nservers == MAX_SERVERS) { 292 fprintf(stderr, 293 "too many servers (up to %d)\n", 294 MAX_SERVERS); 295 exit(1); 296 } 297 serveraddr[nservers++] = (const char *)optarg; 298 break; 299 default: 300 usage(); 301 } 302 } 303 304 argc -= optind; 305 argv += optind; 306 if (argc < 1) 307 usage(); 308 309 if (nservers == 0) { 310 nservers = 1; 311 serveraddr[0] = def_server; 312 } 313 314 for (i = 0; i < MAX_QUERIES; i++) { 315 query_array[i].id = i; 316 query_array[i].inuse = ISC_FALSE; 317 query_array[i].type = type; 318 dns_fixedname_init(&query_array[i].fixedname); 319 query_array[i].qname = NULL; 320 ISC_LIST_INIT(query_array[i].answerlist); 321 query_array[i].xid = NULL; 322 } 323 324 isc_lib_register(); 325 result = dns_lib_init(); 326 if (result != ISC_R_SUCCESS) { 327 fprintf(stderr, "dns_lib_init failed: %d\n", result); 328 exit(1); 329 } 330 331 result = ctxs_init(&mctx, &query_actx, &taskmgr, &socketmgr, 332 &timermgr); 333 if (result != ISC_R_SUCCESS) { 334 fprintf(stderr, "ctx create failed: %d\n", result); 335 exit(1); 336 } 337 338 isc_app_ctxstart(query_actx); 339 340 result = dns_client_createx(mctx, query_actx, taskmgr, socketmgr, 341 timermgr, 0, &client); 342 if (result != ISC_R_SUCCESS) { 343 fprintf(stderr, "dns_client_createx failed: %d\n", result); 344 exit(1); 345 } 346 347 /* Set nameservers */ 348 ISC_LIST_INIT(servers); 349 for (i = 0; i < nservers; i++) { 350 if (inet_pton(AF_INET, serveraddr[i], &inaddr) != 1) { 351 fprintf(stderr, "failed to parse IPv4 address %s\n", 352 serveraddr[i]); 353 exit(1); 354 } 355 isc_sockaddr_fromin(&sa[i], &inaddr, 53); 356 ISC_LIST_APPEND(servers, &sa[i], link); 357 } 358 result = dns_client_setservers(client, dns_rdataclass_in, NULL, 359 &servers); 360 if (result != ISC_R_SUCCESS) { 361 fprintf(stderr, "set server failed: %d\n", result); 362 exit(1); 363 } 364 365 /* Create the main task */ 366 query_task = NULL; 367 result = isc_task_create(taskmgr, 0, &query_task); 368 if (result != ISC_R_SUCCESS) { 369 fprintf(stderr, "failed to create task: %d\n", result); 370 exit(1); 371 } 372 373 /* Open input file */ 374 fp = fopen(argv[0], "r"); 375 if (fp == NULL) { 376 fprintf(stderr, "failed to open input file: %s\n", argv[1]); 377 exit(1); 378 } 379 380 /* Dispatch initial queries */ 381 for (i = 0; i < MAX_QUERIES; i++) { 382 result = dispatch_query(&query_array[i]); 383 if (result == ISC_R_NOMORE) 384 break; 385 } 386 387 /* Start event loop */ 388 isc_app_ctxrun(query_actx); 389 390 /* Sanity check */ 391 for (i = 0; i < MAX_QUERIES; i++) 392 INSIST(query_array[i].inuse == ISC_FALSE); 393 394 /* Cleanup */ 395 isc_task_detach(&query_task); 396 dns_client_destroy(&client); 397 dns_lib_shutdown(); 398 isc_app_ctxfinish(query_actx); 399 ctxs_destroy(&mctx, &query_actx, &taskmgr, &socketmgr, &timermgr); 400 401 exit(0); 402} 403