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 }