unrealircd- supernets unrealircd source & configuration |
git clone git://git.acid.vegas/unrealircd.git |
Log | Files | Refs | Archive | README | LICENSE |
dns.c (18687B)
1 /************************************************************************ 2 * IRC - Internet Relay Chat, src/dns.c 3 * (C) 2005 Bram Matthys (Syzop) and the UnrealIRCd Team 4 * 5 * This program is free software; you can redistribute it and/or modify 6 * it under the terms of the GNU General Public License as published by 7 * the Free Software Foundation; either version 1, or (at your option) 8 * any later version. 9 * 10 * This program is distributed in the hope that it will be useful, 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 * GNU General Public License for more details. 14 * 15 * You should have received a copy of the GNU General Public License 16 * along with this program; if not, write to the Free Software 17 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 18 * 19 */ 20 21 #include "unrealircd.h" 22 #include "dns.h" 23 24 #if !defined(UNREAL_VERSION_TIME) 25 #error "YOU MUST RUN ./Config WHENEVER YOU ARE UPGRADING UNREAL!!!!" 26 #endif 27 28 29 /* Prevent crashes due to invalid prototype/ABI. 30 * And force the use of at least the version shipped with Unreal 31 * (or at least one without known security issues). 32 */ 33 #if ARES_VERSION < 0x010600 34 #error "You have an old c-ares version on your system and/or Unreals c-ares failed to compile!" 35 #endif 36 37 /* Forward declerations */ 38 void unrealdns_cb_iptoname(void *arg, int status, int timeouts, struct hostent *he); 39 void unrealdns_cb_nametoip_verify(void *arg, int status, int timeouts, struct hostent *he); 40 void unrealdns_cb_nametoip_link(void *arg, int status, int timeouts, struct hostent *he); 41 void unrealdns_delasyncconnects(void); 42 static uint64_t unrealdns_hash_ip(const char *ip); 43 static void unrealdns_addtocache(const char *name, const char *ip); 44 static const char *unrealdns_findcache_ip(const char *ip); 45 struct hostent *unreal_create_hostent(const char *name, const char *ip); 46 static void unrealdns_freeandremovereq(DNSReq *r); 47 void unrealdns_removecacherecord(DNSCache *c); 48 49 /* Externs */ 50 extern void proceed_normal_client_handshake(Client *client, struct hostent *he); 51 52 /* Global variables */ 53 54 ares_channel resolver_channel; /**< The resolver channel. */ 55 56 DNSStats dnsstats; 57 58 static DNSReq *requests = NULL; /**< Linked list of requests (pending responses). */ 59 60 static DNSCache *cache_list = NULL; /**< Linked list of cache */ 61 static DNSCache *cache_hashtbl[DNS_HASH_SIZE]; /**< Hash table of cache */ 62 63 static unsigned int unrealdns_num_cache = 0; /**< # of cache entries in memory */ 64 65 static char siphashkey_dns_ip[SIPHASH_KEY_LENGTH]; 66 67 static void unrealdns_io_cb(int fd, int revents, void *data) 68 { 69 ares_socket_t read_fd, write_fd; 70 FDEntry *fde; 71 72 read_fd = write_fd = ARES_SOCKET_BAD; 73 fde = &fd_table[fd]; 74 75 if (revents & FD_SELECT_READ) 76 read_fd = fde->fd; 77 78 if (revents & FD_SELECT_WRITE) 79 write_fd = fde->fd; 80 81 ares_process_fd(resolver_channel, read_fd, write_fd); 82 } 83 84 static void unrealdns_sock_state_cb(void *data, ares_socket_t fd, int read, int write) 85 { 86 int selflags = 0; 87 88 if (!read && !write) 89 { 90 fd_close(fd); 91 return; 92 } 93 94 if (read) 95 selflags |= FD_SELECT_READ; 96 97 if (write) 98 selflags |= FD_SELECT_WRITE; 99 100 fd_setselect(fd, selflags, unrealdns_io_cb, data); 101 } 102 103 /* Who thought providing a socket OPEN callback without a socket CLOSE callback was 104 * a good idea...? --nenolod 105 */ 106 static int unrealdns_sock_create_cb(ares_socket_t fd, int type, void *data) 107 { 108 /* NOTE: We use FDCLOSE_NONE here because c-ares 109 * will take care of the closing. So *WE* must 110 * never close the socket. 111 */ 112 fd_open(fd, "DNS Resolver Socket", FDCLOSE_NONE); 113 return ARES_SUCCESS; 114 } 115 116 EVENT(unrealdns_timeout) 117 { 118 ares_process_fd(resolver_channel, ARES_SOCKET_BAD, ARES_SOCKET_BAD); 119 } 120 121 static Event *unrealdns_timeout_hdl = NULL; 122 123 void init_resolver(int firsttime) 124 { 125 struct ares_options options; 126 int n; 127 int optmask; 128 129 if (requests) 130 abort(); /* should never happen */ 131 132 if (firsttime) 133 { 134 memset(&cache_hashtbl, 0, sizeof(cache_hashtbl)); 135 memset(&dnsstats, 0, sizeof(dnsstats)); 136 siphash_generate_key(siphashkey_dns_ip); 137 ares_library_init(ARES_LIB_INIT_ALL); 138 } 139 140 memset(&options, 0, sizeof(options)); 141 options.timeout = 1500; /* 1.5 seconds */ 142 options.tries = 2; 143 /* Note that the effective DNS timeout is NOT simply 1500*2=3000. 144 * This is because c-ares does some incremental timeout stuff itself 145 * that may add up to twice the timeout in the second round, 146 * so effective max is 1500ms first and then up to 3000s, so 4500ms in total 147 * (until they change the algorithm again, that is...). 148 */ 149 options.flags |= ARES_FLAG_NOALIASES|ARES_FLAG_IGNTC; 150 options.sock_state_cb = unrealdns_sock_state_cb; 151 optmask = ARES_OPT_TIMEOUTMS|ARES_OPT_TRIES|ARES_OPT_FLAGS|ARES_OPT_SOCK_STATE_CB; 152 #ifndef _WIN32 153 /* on *NIX don't use the hosts file, since it causes countless useless reads. 154 * on Windows we use it for now, this could be changed in the future. 155 */ 156 options.lookups = "b"; 157 optmask |= ARES_OPT_LOOKUPS; 158 #endif 159 n = ares_init_options(&resolver_channel, &options, optmask); 160 if (n != ARES_SUCCESS) 161 { 162 /* FATAL */ 163 config_error("resolver: ares_init_options() failed with error code %d [%s]", n, ares_strerror(n)); 164 #ifdef _WIN32 165 win_error(); 166 #endif 167 exit(-7); 168 } 169 170 ares_set_socket_callback(resolver_channel, unrealdns_sock_create_cb, NULL); 171 unrealdns_timeout_hdl = EventAdd(NULL, "unrealdns_timeout", unrealdns_timeout, NULL, 500, 0); 172 } 173 174 void reinit_resolver(Client *client) 175 { 176 EventDel(unrealdns_timeout_hdl); 177 178 unreal_log(ULOG_INFO, "dns", "REINIT_RESOLVER", client, 179 "$client requested reinitalization of the DNS resolver"); 180 ares_destroy(resolver_channel); 181 init_resolver(0); 182 } 183 184 void unrealdns_addreqtolist(DNSReq *r) 185 { 186 if (requests) 187 { 188 r->next = requests; 189 requests->prev = r; 190 } 191 requests = r; 192 } 193 194 /** Get (and verify) the host for an incoming client. 195 * - it checks the cache first, returns the host if found (and valid). 196 * - if not found in cache it does ip->name and then name->ip, if both resolve 197 * to the same name it is accepted, otherwise not. 198 * We return NULL in this case and an asynchronic request is done. 199 * When done, proceed_normal_client_handshake() is called. 200 */ 201 struct hostent *unrealdns_doclient(Client *client) 202 { 203 DNSReq *r; 204 const char *cache_name; 205 206 cache_name = unrealdns_findcache_ip(client->ip); 207 if (cache_name) 208 return unreal_create_hostent(cache_name, client->ip); 209 210 /* Create a request */ 211 r = safe_alloc(sizeof(DNSReq)); 212 r->client = client; 213 r->ipv6 = IsIPV6(client); 214 unrealdns_addreqtolist(r); 215 216 /* Execute it */ 217 if (r->ipv6) 218 { 219 struct in6_addr addr; 220 memset(&addr, 0, sizeof(addr)); 221 inet_pton(AF_INET6, client->ip, &addr); 222 ares_gethostbyaddr(resolver_channel, &addr, 16, AF_INET6, unrealdns_cb_iptoname, r); 223 } else { 224 struct in_addr addr; 225 memset(&addr, 0, sizeof(addr)); 226 inet_pton(AF_INET, client->ip, &addr); 227 ares_gethostbyaddr(resolver_channel, &addr, 4, AF_INET, unrealdns_cb_iptoname, r); 228 } 229 230 return NULL; 231 } 232 233 /** Resolve a name to an IP, for a link block. 234 */ 235 void unrealdns_gethostbyname_link(const char *name, ConfigItem_link *conf, int ipv4_only) 236 { 237 DNSReq *r; 238 239 /* Create a request */ 240 r = safe_alloc(sizeof(DNSReq)); 241 r->linkblock = conf; 242 safe_strdup(r->name, name); 243 if (!DISABLE_IPV6 && !ipv4_only) 244 { 245 /* We try an IPv6 lookup first, and if that fails we try IPv4. */ 246 r->ipv6 = 1; 247 } 248 249 unrealdns_addreqtolist(r); 250 251 /* Execute it */ 252 ares_gethostbyname(resolver_channel, r->name, r->ipv6 ? AF_INET6 : AF_INET, unrealdns_cb_nametoip_link, r); 253 } 254 255 void unrealdns_cb_iptoname(void *arg, int status, int timeouts, struct hostent *he) 256 { 257 DNSReq *r = (DNSReq *)arg; 258 DNSReq *newr; 259 Client *client = r->client; 260 char ipv6 = r->ipv6; 261 262 unrealdns_freeandremovereq(r); 263 264 if (!client) 265 return; 266 267 /* Check for status and null name (yes, we must) */ 268 if ((status != 0) || !he->h_name || !*he->h_name) 269 { 270 /* Failed */ 271 proceed_normal_client_handshake(client, NULL); 272 return; 273 } 274 275 /* Good, we got a valid response, now prepare for name -> ip */ 276 newr = safe_alloc(sizeof(DNSReq)); 277 newr->client = client; 278 newr->ipv6 = ipv6; 279 safe_strdup(newr->name, he->h_name); 280 unrealdns_addreqtolist(newr); 281 282 ares_gethostbyname(resolver_channel, he->h_name, ipv6 ? AF_INET6 : AF_INET, unrealdns_cb_nametoip_verify, newr); 283 } 284 285 void unrealdns_cb_nametoip_verify(void *arg, int status, int timeouts, struct hostent *he) 286 { 287 DNSReq *r = (DNSReq *)arg; 288 Client *client = r->client; 289 char ipv6 = r->ipv6; 290 int i; 291 struct hostent *he2; 292 293 if (!client) 294 goto bad; 295 296 if ((status != 0) || (ipv6 && (he->h_length != 16)) || (!ipv6 && (he->h_length != 4))) 297 { 298 /* Failed: error code, or data length is incorrect */ 299 proceed_normal_client_handshake(client, NULL); 300 goto bad; 301 } 302 303 /* Verify ip->name and name->ip mapping... */ 304 for (i = 0; he->h_addr_list[i]; i++) 305 { 306 if (r->ipv6) 307 { 308 struct in6_addr addr; 309 if (inet_pton(AF_INET6, client->ip, &addr) != 1) 310 continue; /* something fucked */ 311 if (!memcmp(he->h_addr_list[i], &addr, 16)) 312 break; /* MATCH */ 313 } else { 314 struct in_addr addr; 315 if (inet_pton(AF_INET, client->ip, &addr) != 1) 316 continue; /* something fucked */ 317 if (!memcmp(he->h_addr_list[i], &addr, 4)) 318 break; /* MATCH */ 319 } 320 } 321 322 if (!he->h_addr_list[i]) 323 { 324 /* Failed name <-> IP mapping */ 325 proceed_normal_client_handshake(client, NULL); 326 goto bad; 327 } 328 329 if (!valid_host(r->name, 1)) 330 { 331 /* Hostname is bad, don't cache and consider unresolved */ 332 proceed_normal_client_handshake(client, NULL); 333 goto bad; 334 } 335 336 /* Get rid of stupid uppercase DNS names... */ 337 strtolower(r->name); 338 339 /* Entry was found, verified, and can be added to cache */ 340 341 unrealdns_addtocache(r->name, client->ip); 342 343 he2 = unreal_create_hostent(r->name, client->ip); 344 proceed_normal_client_handshake(client, he2); 345 346 bad: 347 unrealdns_freeandremovereq(r); 348 } 349 350 void unrealdns_cb_nametoip_link(void *arg, int status, int timeouts, struct hostent *he) 351 { 352 DNSReq *r = (DNSReq *)arg; 353 int n; 354 struct hostent *he2; 355 char ipbuf[HOSTLEN+1]; 356 const char *ip = NULL; 357 358 if (!r->linkblock) 359 { 360 /* Possible if deleted due to rehash async removal */ 361 unrealdns_freeandremovereq(r); 362 return; 363 } 364 365 if ((status != 0) || !he->h_addr_list || !he->h_addr_list[0]) 366 { 367 if (r->ipv6) 368 { 369 /* Retry for IPv4... */ 370 r->ipv6 = 0; 371 ares_gethostbyname(resolver_channel, r->name, AF_INET, unrealdns_cb_nametoip_link, r); 372 373 return; 374 } 375 376 /* fatal error while resolving */ 377 unreal_log(ULOG_ERROR, "link", "LINK_ERROR_RESOLVING", NULL, 378 "Unable to resolve hostname $link_block.hostname, when trying to connect to server $link_block.", 379 log_data_link_block(r->linkblock)); 380 r->linkblock->refcount--; 381 unrealdns_freeandremovereq(r); 382 return; 383 } 384 r->linkblock->refcount--; 385 386 if (!he->h_addr_list[0] || (he->h_length != (r->ipv6 ? 16 : 4)) || 387 !(ip = inetntop(r->ipv6 ? AF_INET6 : AF_INET, he->h_addr_list[0], ipbuf, sizeof(ipbuf)))) 388 { 389 /* Illegal response -- fatal */ 390 unreal_log(ULOG_ERROR, "link", "LINK_ERROR_RESOLVING", NULL, 391 "Unable to resolve hostname $link_block.hostname, when trying to connect to server $link_block.", 392 log_data_link_block(r->linkblock)); 393 unrealdns_freeandremovereq(r); 394 return; 395 } 396 397 /* Ok, since we got here, it seems things were actually succesfull */ 398 399 /* Fill in [linkblockstruct]->ipnum */ 400 safe_strdup(r->linkblock->connect_ip, ip); 401 he2 = unreal_create_hostent(he->h_name, ip); 402 403 /* Try to connect to the server */ 404 connect_server(r->linkblock, r->client, he2); 405 406 unrealdns_freeandremovereq(r); 407 /* DONE */ 408 } 409 410 static uint64_t unrealdns_hash_ip(const char *ip) 411 { 412 return siphash(ip, siphashkey_dns_ip) % DNS_HASH_SIZE; 413 } 414 415 static void unrealdns_addtocache(const char *name, const char *ip) 416 { 417 unsigned int hashv; 418 DNSCache *c; 419 420 dnsstats.cache_adds++; 421 422 hashv = unrealdns_hash_ip(ip); 423 424 /* Check first if it is already present in the cache. 425 * This is possible, when 2 clients connect at the same time. 426 */ 427 for (c = cache_hashtbl[hashv]; c; c = c->hnext) 428 if (!strcmp(ip, c->ip)) 429 return; /* already present in cache */ 430 431 /* Remove last item, if we got too many entries.. */ 432 if (unrealdns_num_cache >= DNS_MAX_ENTRIES) 433 { 434 for (c = cache_list; c->next; c = c->next); 435 unrealdns_removecacherecord(c); 436 } 437 438 /* Create record */ 439 c = safe_alloc(sizeof(DNSCache)); 440 safe_strdup(c->name, name); 441 safe_strdup(c->ip, ip); 442 c->expires = TStime() + DNSCACHE_TTL; 443 444 /* Add to hash table */ 445 if (cache_hashtbl[hashv]) 446 { 447 cache_hashtbl[hashv]->hprev = c; 448 c->hnext = cache_hashtbl[hashv]; 449 } 450 cache_hashtbl[hashv] = c; 451 452 /* Add to linked list */ 453 if (cache_list) 454 { 455 cache_list->prev = c; 456 c->next = cache_list; 457 } 458 cache_list = c; 459 460 unrealdns_num_cache++; 461 /* DONE */ 462 } 463 464 /** Search the cache for a confirmed ip->name and name->ip match, by address. 465 * @returns The resolved hostname, or NULL if not found in cache. 466 */ 467 static const char *unrealdns_findcache_ip(const char *ip) 468 { 469 unsigned int hashv; 470 DNSCache *c; 471 472 hashv = unrealdns_hash_ip(ip); 473 474 for (c = cache_hashtbl[hashv]; c; c = c->hnext) 475 if (!strcmp(ip, c->ip)) 476 { 477 dnsstats.cache_hits++; 478 return c->name; 479 } 480 481 dnsstats.cache_misses++; 482 return NULL; 483 } 484 485 /** Removes dns cache record from list (and frees it). 486 */ 487 void unrealdns_removecacherecord(DNSCache *c) 488 { 489 unsigned int hashv; 490 491 /* We basically got 4 pointers to update: 492 * <previous listitem>->next 493 * <next listitem>->previous 494 * <previous hashitem>->next 495 * <next hashitem>->prev. 496 * And we need to update 'cache_list' and 'cache_hash[]' if needed. 497 */ 498 if (c->prev) 499 c->prev->next = c->next; 500 else 501 cache_list = c->next; /* new list HEAD */ 502 503 if (c->next) 504 c->next->prev = c->prev; 505 506 if (c->hprev) 507 c->hprev->hnext = c->hnext; 508 else { 509 /* new hash HEAD */ 510 hashv = unrealdns_hash_ip(c->ip); 511 if (cache_hashtbl[hashv] != c) 512 abort(); /* impossible */ 513 cache_hashtbl[hashv] = c->hnext; 514 } 515 516 if (c->hnext) 517 c->hnext->hprev = c->hprev; 518 519 safe_free(c->name); 520 safe_free(c->ip); 521 safe_free(c); 522 523 unrealdns_num_cache--; 524 } 525 526 /** This regulary removes old dns records from the cache */ 527 EVENT(unrealdns_removeoldrecords) 528 { 529 DNSCache *c, *next; 530 531 for (c = cache_list; c; c = next) 532 { 533 next = c->next; 534 if (c->expires < TStime()) 535 unrealdns_removecacherecord(c); 536 } 537 } 538 539 struct hostent *unreal_create_hostent(const char *name, const char *ip) 540 { 541 struct hostent *he; 542 543 /* Create a hostent structure (I HATE HOSTENTS) and return it.. */ 544 he = safe_alloc(sizeof(struct hostent)); 545 safe_strdup(he->h_name, name); 546 if (strchr(ip, ':')) 547 { 548 /* IPv6 */ 549 he->h_addrtype = AF_INET6; 550 he->h_length = sizeof(struct in6_addr); 551 he->h_addr_list = safe_alloc(sizeof(char *) * 2); /* alocate an array of 2 pointers */ 552 he->h_addr_list[0] = safe_alloc(sizeof(struct in6_addr)); 553 inet_pton(AF_INET6, ip, he->h_addr_list[0]); 554 } else { 555 he->h_addrtype = AF_INET; 556 he->h_length = sizeof(struct in_addr); 557 he->h_addr_list = safe_alloc(sizeof(char *) * 2); /* alocate an array of 2 pointers */ 558 he->h_addr_list[0] = safe_alloc(sizeof(struct in_addr)); 559 inet_pton(AF_INET, ip, he->h_addr_list[0]); 560 } 561 562 return he; 563 } 564 565 void unreal_free_hostent(struct hostent *he) 566 { 567 safe_free(he->h_name); 568 safe_free(he->h_addr_list[0]); 569 safe_free(he->h_addr_list); 570 safe_free(he); 571 } 572 573 static void unrealdns_freeandremovereq(DNSReq *r) 574 { 575 if (r->prev) 576 r->prev->next = r->next; 577 else 578 requests = r->next; /* new HEAD */ 579 580 if (r->next) 581 r->next->prev = r->prev; 582 583 safe_free(r->name); 584 safe_free(r); 585 } 586 587 /** Delete requests for client 'client'. 588 * Actually we DO NOT (and should not) delete them, but simply mark them as 'dead'. 589 */ 590 void unrealdns_delreq_bycptr(Client *client) 591 { 592 DNSReq *r; 593 594 for (r = requests; r; r = r->next) 595 if (r->client == client) 596 r->client = NULL; 597 } 598 599 void unrealdns_delasyncconnects(void) 600 { 601 DNSReq *r; 602 603 for (r = requests; r; r = r->next) 604 if (r->type == DNSREQ_CONNECT) 605 r->linkblock = NULL; 606 607 } 608 609 CMD_FUNC(cmd_dns) 610 { 611 DNSCache *c; 612 DNSReq *r; 613 const char *param; 614 615 if (!ValidatePermissionsForPath("server:dns",client,NULL,NULL,NULL)) 616 { 617 sendnumeric(client, ERR_NOPRIVILEGES); 618 return; 619 } 620 621 if ((parc > 1) && !BadPtr(parv[1])) 622 param = parv[1]; 623 else 624 param = ""; 625 626 if (*param == 'l') /* LIST CACHE */ 627 { 628 sendtxtnumeric(client, "DNS CACHE List (%u items):", unrealdns_num_cache); 629 for (c = cache_list; c; c = c->next) 630 sendtxtnumeric(client, " %s [%s]", c->name, c->ip); 631 } else 632 if (*param == 'r') /* LIST REQUESTS */ 633 { 634 sendtxtnumeric(client, "DNS Request List:"); 635 for (r = requests; r; r = r->next) 636 sendtxtnumeric(client, " %s", r->client ? r->client->ip : "<client lost>"); 637 } else 638 if (*param == 'c') /* CLEAR CACHE */ 639 { 640 unreal_log(ULOG_INFO, "dns", "DNS_CACHE_CLEARED", client, 641 "DNS cache cleared by $client"); 642 643 while (cache_list) 644 { 645 c = cache_list->next; 646 safe_free(cache_list->name); 647 safe_free(cache_list->ip); 648 safe_free(cache_list); 649 cache_list = c; 650 } 651 memset(&cache_hashtbl, 0, sizeof(cache_hashtbl)); 652 unrealdns_num_cache = 0; 653 sendnotice(client, "DNS Cache has been cleared"); 654 } else 655 if (*param == 'i') /* INFORMATION */ 656 { 657 struct ares_options inf; 658 struct ares_addr_node *serverlist = NULL, *ns; 659 int i; 660 int optmask; 661 662 sendtxtnumeric(client, "****** DNS Configuration Information ******"); 663 sendtxtnumeric(client, " c-ares version: %s",ares_version(NULL)); 664 665 i = 0; 666 ares_get_servers(resolver_channel, &serverlist); 667 for (ns = serverlist; ns; ns = ns->next) 668 { 669 char ipbuf[128]; 670 const char *ip; 671 i++; 672 673 ip = inetntop(ns->family, &ns->addr, ipbuf, sizeof(ipbuf)); 674 sendtxtnumeric(client, " server #%d: %s", i, ip ? ip : "<error>"); 675 } 676 ares_free_data(serverlist); 677 678 ares_save_options(resolver_channel, &inf, &optmask); 679 if (optmask & ARES_OPT_TIMEOUTMS) 680 sendtxtnumeric(client, " timeout: %d", inf.timeout); 681 if (optmask & ARES_OPT_TRIES) 682 sendtxtnumeric(client, " tries: %d", inf.tries); 683 if (optmask & ARES_OPT_DOMAINS) 684 { 685 sendtxtnumeric(client, " # of search domains: %d", inf.ndomains); 686 for (i = 0; i < inf.ndomains; i++) 687 sendtxtnumeric(client, " domain #%d: %s", i+1, inf.domains[i]); 688 } 689 sendtxtnumeric(client, "****** End of DNS Configuration Info ******"); 690 691 ares_destroy_options(&inf); 692 } else /* STATISTICS */ 693 { 694 sendtxtnumeric(client, "DNS CACHE Stats:"); 695 sendtxtnumeric(client, " hits: %d", dnsstats.cache_hits); 696 sendtxtnumeric(client, " misses: %d", dnsstats.cache_misses); 697 } 698 return; 699 } 700 701 /* Little helper function for dnsbl module. 702 * No we will NOT copy the entire c-ares api, just this one. 703 */ 704 void unreal_gethostbyname(const char *name, int family, ares_host_callback callback, void *arg) 705 { 706 ares_gethostbyname(resolver_channel, name, family, callback, arg); 707 }