Author: Bleep <helveytw@home.com>
[ircu2.10.12-pk.git] / ircd / res.c
1 /*
2  * src/res.c (C)opyright 1992 Darren Reed. All rights reserved.
3  * This file may not be distributed without the author's permission in any
4  * shape or form. The author takes no responsibility for any damage or loss
5  * of property which results from the use of this software.
6  *
7  * $Id$
8  *
9  * July 1999 - Rewrote a bunch of stuff here. Change hostent builder code,
10  *     added callbacks and reference counting of returned hostents.
11  *     --Bleep (Thomas Helvey <tomh@inxpress.net>)
12  */
13 #include "res.h"
14 #include "client.h"
15 #include "ircd.h"
16 #include "ircd_alloc.h"
17 #include "ircd_log.h"
18 #include "ircd_osdep.h"
19 #include "ircd_reply.h"
20 #include "ircd_string.h"
21 #include "msg.h"
22 #include "numeric.h"
23 #include "s_bsd.h"
24 #include "s_debug.h"
25 #include "s_misc.h"
26 #include "send.h"
27 #include "sprintf_irc.h"
28 #include "struct.h"
29 #include "support.h"
30 #include "sys.h"
31
32 #include <assert.h>
33 #include <errno.h>
34 #include <string.h>
35 #include <stdlib.h>
36 #include <sys/time.h>
37 #include <sys/socket.h>
38 #include <fcntl.h>
39 #include <unistd.h>
40 #include <regex.h>
41
42 #include <arpa/nameser.h>
43 #include <resolv.h>
44 #include <netdb.h>
45 #include <arpa/inet.h>
46
47 #include <limits.h>
48 #if (CHAR_BIT != 8)
49 #error this code needs to be able to address individual octets 
50 #endif
51
52 /*
53  * Some systems do not define INADDR_NONE (255.255.255.255)
54  * INADDR_NONE is actually a valid address, but it should never
55  * be returned from any nameserver.
56  * NOTE: The bit pattern for INADDR_NONE and INADDR_ANY (0.0.0.0) should be 
57  * the same on all hosts so we shouldn't need to use htonl or ntohl to
58  * compare or set the values.
59  */ 
60 #ifndef INADDR_NONE
61 #define INADDR_NONE ((unsigned int) 0xffffffff)
62 #endif
63
64 #define MAXPACKET       1024  /* rfc sez 512 but we expand names so ... */
65 #define RES_MAXALIASES  35    /* maximum aliases allowed */
66 #define RES_MAXADDRS    35    /* maximum addresses allowed */
67 /*
68  * OSF1 doesn't have RES_NOALIASES
69  */
70 #ifndef RES_NOALIASES
71 #define RES_NOALIASES 0
72 #endif
73
74 /*
75  * macros used to calulate offsets into fixed query buffer
76  */
77 #define ALIAS_BLEN  ((RES_MAXALIASES + 1) * sizeof(char*))
78 #define ADDRS_BLEN  ((RES_MAXADDRS + 1) * sizeof(struct in_addr*))
79
80 #define ADDRS_OFFSET   (ALIAS_BLEN + ADDRS_BLEN)
81 #define ADDRS_DLEN     (RES_MAXADDRS * sizeof(struct in_addr))
82 #define NAMES_OFFSET   (ADDRS_OFFSET + ADDRS_DLEN)
83 #define MAXGETHOSTLEN  (NAMES_OFFSET + MAXPACKET)
84
85 #define AR_TTL          600   /* TTL in seconds for dns cache entries */
86
87 /*
88  * the following values should be prime
89  */
90 #define ARES_CACSIZE    307
91 #define MAXCACHED       281
92
93 /*
94  * RFC 1104/1105 wasn't very helpful about what these fields
95  * should be named, so for now, we'll just name them this way.
96  * we probably should look at what named calls them or something.
97  */
98 #define TYPE_SIZE       2
99 #define CLASS_SIZE      2
100 #define TTL_SIZE        4
101 #define RDLENGTH_SIZE   2
102 #define ANSWER_FIXED_SIZE (TYPE_SIZE + CLASS_SIZE + TTL_SIZE + RDLENGTH_SIZE)
103
104 /*
105  * Building the Hostent
106  * The Hostent struct is arranged like this:
107  *          +-------------------------------+
108  * Hostent: | struct hostent h              |
109  *          |-------------------------------|
110  *          | char *buf                     |
111  *          +-------------------------------+
112  *
113  * allocated:
114  *
115  *          +-------------------------------+
116  * buf:     | h_aliases pointer array       | Max size: ALIAS_BLEN;
117  *          | NULL                          | contains `char *'s
118  *          |-------------------------------|
119  *          | h_addr_list pointer array     | Max size: ADDRS_BLEN;
120  *          | NULL                          | contains `struct in_addr *'s
121  *          |-------------------------------|
122  *          | h_addr_list addresses         | Max size: ADDRS_DLEN;
123  *          |                               | contains `struct in_addr's
124  *          |-------------------------------|
125  *          | storage for hostname strings  | Max size: ALIAS_DLEN;
126  *          +-------------------------------+ contains `char's
127  *
128  *  For requests the size of the h_aliases, and h_addr_list pointer
129  *  array sizes are set to MAXALISES and MAXADDRS respectively, and
130  *  buf is a fixed size with enough space to hold the largest expected
131  *  reply from a nameserver, see RFC 1034 and RFC 1035.
132  *  For cached entries the sizes are dependent on the actual number
133  *  of aliases and addresses. If new aliases and addresses are found
134  *  for cached entries, the buffer is grown and the new entries are added.
135  *  The hostent struct is filled in with the addresses of the entries in
136  *  the Hostent buf as follows:
137  *  h_name      - contains a pointer to the start of the hostname string area,
138  *                or NULL if none is set.  The h_name is followed by the
139  *                aliases, in the storage for hostname strings area.
140  *  h_aliases   - contains a pointer to the start of h_aliases pointer array.
141  *                This array contains pointers to the storage for hostname
142  *                strings area and is terminated with a NULL.  The first alias
143  *                is stored directly after the h_name.
144  *  h_addr_list - contains a pointer to the start of h_addr_list pointer array.
145  *                This array contains pointers to in_addr structures in the
146  *                h_addr_list addresses area and is terminated with a NULL.
147  *
148  *  Filling the buffer this way allows for proper alignment of the h_addr_list
149  *  addresses.
150  *
151  *  This arrangement allows us to alias a Hostent struct pointer as a
152  *  real struct hostent* without lying. It also allows us to change the
153  *  values contained in the cached entries and requests without changing
154  *  the actual hostent pointer, which is saved in a client struct and can't
155  *  be changed without blowing things up or a lot more fiddling around.
156  *  It also allows for defered allocation of the fixed size buffers until
157  *  they are really needed.
158  *  Nov. 17, 1997 --Bleep
159  */
160
161 struct Hostent {
162   struct hostent h;      /* the hostent struct we are passing around */
163   char*          buf;    /* buffer for data pointed to from hostent */
164 };
165
166 struct ResRequest {
167   struct ResRequest* next;
168   int                id;
169   int                sent;          /* number of requests sent */
170   time_t             ttl;
171   char               type;
172   char               retries;       /* retry counter */
173   char               sends;         /* number of sends (>1 means resent) */
174   char               resend;        /* send flag. 0 == dont resend */
175   time_t             sentat;
176   time_t             timeout;
177   struct in_addr     addr;
178   char*              name;
179   struct DNSQuery    query;         /* query callback for this request */
180   struct Hostent     he;
181 };
182
183 struct CacheEntry {
184   struct CacheEntry* hname_next;
185   struct CacheEntry* hnum_next;
186   struct CacheEntry* list_next;
187   time_t             expireat;
188   time_t             ttl;
189   struct Hostent     he;
190   struct DNSReply    reply;
191 };
192
193 struct CacheTable {
194   struct CacheEntry* num_list;
195   struct CacheEntry* name_list;
196 };
197
198
199 int ResolverFileDescriptor    = -1;   /* GLOBAL - used in s_bsd.c */
200
201 static time_t nextDNSCheck    = 0;
202 static time_t nextCacheExpire = 1;
203
204 /*
205  * Keep a spare file descriptor open. res_init calls fopen to read the
206  * resolv.conf file. If ircd is hogging all the file descriptors below 256,
207  * on systems with crippled FILE structures this will cause wierd bugs.
208  * This is definitely needed for Solaris which uses an unsigned char to
209  * hold the file descriptor.  --Dianora
210  */ 
211 static int                spare_fd = -1;
212
213 static int                cachedCount = 0;
214 static struct CacheTable  hashtable[ARES_CACSIZE];
215 static struct CacheEntry* cacheTop;
216 static struct ResRequest* requestListHead;   /* head of resolver request list */
217 static struct ResRequest* requestListTail;   /* tail of resolver request list */
218
219
220 static void     add_request(struct ResRequest* request);
221 static void     rem_request(struct ResRequest* request);
222 static struct ResRequest*   make_request(const struct DNSQuery* query);
223 static void     rem_cache(struct CacheEntry*);
224 static void     do_query_name(const struct DNSQuery* query, 
225                               const char* name, 
226                               struct ResRequest* request);
227 static void     do_query_number(const struct DNSQuery* query,
228                                 const struct in_addr*, 
229                                 struct ResRequest* request);
230 static void     query_name(const char* name, 
231                            int query_class, 
232                            int query_type, 
233                            struct ResRequest* request);
234 static void     resend_query(struct ResRequest* request);
235 static struct CacheEntry*  make_cache(struct ResRequest* request);
236 static struct CacheEntry*  find_cache_name(const char* name);
237 static struct CacheEntry*  find_cache_number(struct ResRequest* request, 
238                                              const char* addr);
239 static struct ResRequest*   find_id(int);
240
241 static struct cacheinfo {
242   int  ca_adds;
243   int  ca_dels;
244   int  ca_expires;
245   int  ca_lookups;
246   int  ca_na_hits;
247   int  ca_nu_hits;
248   int  ca_updates;
249 } cainfo;
250
251 static  struct  resinfo {
252   int  re_errors;
253   int  re_nu_look;
254   int  re_na_look;
255   int  re_replies;
256   int  re_requests;
257   int  re_resends;
258   int  re_sent;
259   int  re_timeouts;
260   int  re_shortttl;
261   int  re_unkrep;
262 } reinfo;
263
264
265 /*
266  * From bind 8.3, these aren't declared in earlier versions of bind
267  */
268 extern u_short  _getshort(const u_char *);
269 extern u_int    _getlong(const u_char *);
270 /*
271  * int
272  * res_isourserver(ina)
273  *      looks up "ina" in _res.ns_addr_list[]
274  * returns:
275  *      0  : not found
276  *      >0 : found
277  * author:
278  *      paul vixie, 29may94
279  */
280 static int
281 res_ourserver(const struct __res_state* statp, const struct sockaddr_in* inp) 
282 {
283   struct sockaddr_in ina;
284   int ns;
285
286   ina = *inp;
287   for (ns = 0;  ns < statp->nscount;  ns++) {
288     const struct sockaddr_in *srv = &statp->nsaddr_list[ns];
289
290     if (srv->sin_family == ina.sin_family &&
291          srv->sin_port == ina.sin_port &&
292          (srv->sin_addr.s_addr == INADDR_ANY ||
293           srv->sin_addr.s_addr == ina.sin_addr.s_addr))
294              return (1);
295   }
296   return (0);
297 }
298
299 /*
300  * start_resolver - do everything we need to read the resolv.conf file
301  * and initialize the resolver file descriptor if needed
302  */
303 static void start_resolver(void)
304 {
305   Debug((DEBUG_DNS, "Resolver: start_resolver"));
306   /*
307    * close the spare file descriptor so res_init can read resolv.conf
308    * successfully. Needed on Solaris
309    */
310   if (spare_fd > -1)
311     close(spare_fd);
312
313   res_init();      /* res_init always returns 0 */
314   /*
315    * make sure we have a valid file descriptor below 256 so we can
316    * do this again. Needed on Solaris
317    */
318   spare_fd = open("/dev/null",O_RDONLY,0);
319   if ((spare_fd < 0) || (spare_fd > 255)) {
320     char sparemsg[80];
321     sprintf_irc(sparemsg, "invalid spare_fd %d", spare_fd);
322     server_restart(sparemsg);
323   }
324
325   if (!_res.nscount) {
326     _res.nscount = 1;
327     _res.nsaddr_list[0].sin_addr.s_addr = inet_addr("127.0.0.1");
328   }
329   _res.options |= RES_NOALIASES;
330
331   if (ResolverFileDescriptor < 0) {
332     ResolverFileDescriptor = socket(AF_INET, SOCK_DGRAM, 0);
333     if (-1 == ResolverFileDescriptor) {
334       report_error("Resolver: error creating socket for %s: %s", 
335                    me.name, errno);
336       return;
337     }
338     if (!os_set_nonblocking(ResolverFileDescriptor))
339       report_error("Resolver: error setting non-blocking for %s: %s", 
340                    me.name, errno);
341   }
342 }
343
344 /*
345  * init_resolver - initialize resolver and resolver library
346  */
347 int init_resolver(void)
348 {
349   Debug((DEBUG_DNS, "Resolver: init_resolver"));
350 #ifdef  LRAND48
351   srand48(CurrentTime);
352 #endif
353   memset(&cainfo,   0, sizeof(cainfo));
354   memset(hashtable, 0, sizeof(hashtable));
355   memset(&reinfo,   0, sizeof(reinfo));
356
357   requestListHead = requestListTail = 0;
358
359   errno = h_errno = 0;
360
361   start_resolver();
362   Debug((DEBUG_DNS, "Resolver: fd %d errno: %d h_errno: %d: %s",
363          ResolverFileDescriptor, errno, h_errno, 
364          (strerror(errno)) ? strerror(errno) : "Unknown"));
365   return ResolverFileDescriptor;
366 }
367
368 /*
369  * restart_resolver - flush the cache, reread resolv.conf, reopen socket
370  */
371 void restart_resolver(void)
372 {
373   /* flush_cache();  flush the dns cache */
374   start_resolver();
375 }
376
377 static int validate_hostent(const struct hostent* hp)
378 {
379   const char* name;
380   int  i = 0;
381   assert(0 != hp);
382   for (name = hp->h_name; name; name = hp->h_aliases[i++]) {
383     if (!string_is_hostname(name))
384       return 0;
385   }
386   return 1;
387 }
388
389 /*
390  * add_request - place a new request in the request list
391  */
392 static void add_request(struct ResRequest* request)
393 {
394   assert(0 != request);
395   if (!requestListHead)
396     requestListHead = requestListTail = request;
397   else {
398     requestListTail->next = request;
399     requestListTail = request;
400   }
401   request->next = NULL;
402   ++reinfo.re_requests;
403 }
404
405 /*
406  * rem_request - remove a request from the list. 
407  * This must also free any memory that has been allocated for 
408  * temporary storage of DNS results.
409  */
410 static void rem_request(struct ResRequest* request)
411 {
412   struct ResRequest** current;
413   struct ResRequest*  prev = NULL;
414
415   assert(0 != request);
416   for (current = &requestListHead; *current; ) {
417     if (*current == request) {
418       *current = request->next;
419       if (requestListTail == request)
420         requestListTail = prev;
421       break;
422     } 
423     prev    = *current;
424     current = &(*current)->next;
425   }
426   MyFree(request->he.buf);
427   MyFree(request->name);
428   MyFree(request);
429 }
430
431 /*
432  * make_request - Create a DNS request record for the server.
433  */
434 static struct ResRequest* make_request(const struct DNSQuery* query)
435 {
436   struct ResRequest* request;
437   assert(0 != query);
438   request = (struct ResRequest*) MyMalloc(sizeof(struct ResRequest));
439   memset(request, 0, sizeof(struct ResRequest));
440
441   request->sentat           = CurrentTime;
442   request->retries          = 3;
443   request->resend           = 1;
444   request->timeout          = 5;    /* start at 5 per RFC1123 */
445   request->addr.s_addr      = INADDR_NONE;
446   request->he.h.h_addrtype  = AF_INET;
447   request->he.h.h_length    = sizeof(struct in_addr);
448   request->query.vptr       = query->vptr;
449   request->query.callback   = query->callback;
450
451 #if defined(NULL_POINTER_NOT_ZERO)
452   request->next             = NULL;
453   request->he.buf           = NULL;
454   request->he.h.h_name      = NULL;
455   request->he.h.h_aliases   = NULL;
456   request->he.h.h_addr_list = NULL;
457 #endif
458   add_request(request);
459   return request;
460 }
461
462 /*
463  * timeout_query_list - Remove queries from the list which have been 
464  * there too long without being resolved.
465  */
466 static time_t timeout_query_list(time_t now)
467 {
468   struct ResRequest* request;
469   struct ResRequest* next_request = 0;
470   time_t             next_time    = 0;
471   time_t             timeout      = 0;
472
473   Debug((DEBUG_DNS, "Resolver: timeout_query_list at %s", myctime(now)));
474   for (request = requestListHead; request; request = next_request) {
475     next_request = request->next;
476     timeout = request->sentat + request->timeout;
477     if (timeout < now) {
478       if (--request->retries <= 0) {
479         ++reinfo.re_timeouts;
480         (*request->query.callback)(request->query.vptr, 0);
481         rem_request(request);
482         continue;
483       }
484       else {
485         request->sentat = now;
486         request->timeout += request->timeout;
487         resend_query(request);
488       }
489     }
490     if (!next_time || timeout < next_time) {
491       next_time = timeout;
492     }
493   }
494   return (next_time > now) ? next_time : (now + AR_TTL);
495 }
496
497 /*
498  * expire_cache - removes entries from the cache which are older 
499  * than their expiry times. returns the time at which the server 
500  * should next poll the cache.
501  */
502 static time_t expire_cache(time_t now)
503 {
504   struct CacheEntry* cp;
505   struct CacheEntry* cp_next;
506   time_t             expire = 0;
507
508   Debug((DEBUG_DNS, "Resolver: expire_cache at %s", myctime(now)));
509   for (cp = cacheTop; cp; cp = cp_next) {
510     cp_next = cp->list_next;
511     if (cp->expireat < now) {
512       ++cainfo.ca_expires;
513       rem_cache(cp);
514     }
515     else if (!expire || expire > cp->expireat)
516       expire = cp->expireat;
517   }
518   return (expire > now) ? expire : (now + AR_TTL);
519 }
520
521 /*
522  * timeout_resolver - check request list and cache for expired entries
523  */
524 time_t timeout_resolver(time_t now)
525 {
526   if (nextDNSCheck < now)
527     nextDNSCheck = timeout_query_list(now);
528   if (nextCacheExpire < now)
529     nextCacheExpire = expire_cache(now);
530   return IRCD_MIN(nextDNSCheck, nextCacheExpire);
531 }
532
533
534 /*
535  * delete_resolver_queries - cleanup outstanding queries 
536  * for which there no longer exist clients or conf lines.
537  */
538 void delete_resolver_queries(const void* vptr)
539 {
540   struct ResRequest* request;
541   struct ResRequest* next_request;
542
543   for (request = requestListHead; request; request = next_request) {
544     next_request = request->next;
545     if (vptr == request->query.vptr)
546       rem_request(request);
547   }
548 }
549
550 /*
551  * send_res_msg - sends msg to all nameservers found in the "_res" structure.
552  * This should reflect /etc/resolv.conf. We will get responses
553  * which arent needed but is easier than checking to see if nameserver
554  * isnt present. Returns number of messages successfully sent to 
555  * nameservers or -1 if no successful sends.
556  */
557 static int send_res_msg(const u_char* msg, int len, int rcount)
558 {
559   int i;
560   int sent = 0;
561   int max_queries = IRCD_MIN(_res.nscount, rcount);
562
563   assert(0 != msg);
564   /*
565    * RES_PRIMARY option is not implemented
566    * if (_res.options & RES_PRIMARY || 0 == max_queries)
567    */
568   if (0 == max_queries)
569     max_queries = 1;
570
571   Debug((DEBUG_DNS, "Resolver: sendto %d", max_queries));
572
573   for (i = 0; i < max_queries; i++) {
574     if (sendto(ResolverFileDescriptor, msg, len, 0, 
575                (struct sockaddr*) &(_res.nsaddr_list[i]),
576                sizeof(struct sockaddr_in)) == len) {
577       ++reinfo.re_sent;
578       ++sent;
579     }
580     else
581       ircd_log(L_ERROR, "Resolver: send failed %s", 
582                (strerror(errno)) ? strerror(errno) : "Unknown");
583   }
584   return sent;
585 }
586
587 /*
588  * find_id - find a dns request id (id is determined by dn_mkquery)
589  */
590 static struct ResRequest* find_id(int id)
591 {
592   struct ResRequest* request;
593
594   for (request = requestListHead; request; request = request->next) {
595     if (request->id == id)
596       return request;
597   }
598   return NULL;
599 }
600
601 /*
602  * gethost_byname - get host address from name
603  */
604 struct DNSReply* gethost_byname(const char* name, 
605                                const struct DNSQuery* query)
606 {
607   struct CacheEntry* cp;
608   assert(0 != name);
609
610   Debug((DEBUG_DNS, "Resolver: gethost_byname %s", name));
611   ++reinfo.re_na_look;
612   if ((cp = find_cache_name(name)))
613     return &(cp->reply);
614
615   do_query_name(query, name, NULL);
616   nextDNSCheck = 1;
617   return NULL;
618 }
619
620 /*
621  * gethost_byaddr - get host name from address
622  */
623 struct DNSReply* gethost_byaddr(const char* addr,
624                                 const struct DNSQuery* query)
625 {
626   struct CacheEntry *cp;
627
628   assert(0 != addr);
629
630   Debug((DEBUG_DNS, "Resolver: gethost_byaddr %s", ircd_ntoa(addr)));
631
632   ++reinfo.re_nu_look;
633   if ((cp = find_cache_number(NULL, addr)))
634     return &(cp->reply);
635
636   do_query_number(query, (const struct in_addr*) addr, NULL);
637   nextDNSCheck = 1;
638   return NULL;
639 }
640
641 /*
642  * do_query_name - nameserver lookup name
643  */
644 static void do_query_name(const struct DNSQuery* query, 
645                           const char* name, struct ResRequest* request)
646 {
647   char  hname[HOSTLEN + 1];
648   assert(0 != name);
649
650   ircd_strncpy(hname, name, HOSTLEN);
651   hname[HOSTLEN] = '\0';
652
653   if (!request) {
654     request       = make_request(query);
655     request->type = T_A;
656     request->name = (char*) MyMalloc(strlen(hname) + 1);
657     strcpy(request->name, hname);
658   }
659   query_name(hname, C_IN, T_A, request);
660 }
661
662 /*
663  * do_query_number - Use this to do reverse IP# lookups.
664  */
665 static void do_query_number(const struct DNSQuery* query, 
666                             const struct in_addr* addr,
667                             struct ResRequest* request)
668 {
669   char  ipbuf[32];
670   const unsigned char* cp;
671
672   assert(0 != addr);
673   cp = (const unsigned char*) &addr->s_addr;
674   sprintf_irc(ipbuf, "%u.%u.%u.%u.in-addr.arpa.",
675               (unsigned int)(cp[3]), (unsigned int)(cp[2]),
676               (unsigned int)(cp[1]), (unsigned int)(cp[0]));
677
678   if (!request) {
679     request              = make_request(query);
680     request->type        = T_PTR;
681     request->addr.s_addr = addr->s_addr;
682   }
683   query_name(ipbuf, C_IN, T_PTR, request);
684 }
685
686 /*
687  * query_name - generate a query based on class, type and name.
688  */
689 static void query_name(const char* name, int query_class,
690                        int type, struct ResRequest* request)
691 {
692   char buf[MAXPACKET];
693   int  request_len = 0;
694
695   assert(0 != name);
696   assert(0 != request);
697
698   Debug((DEBUG_DNS, "Resolver: query_name: %s %d %d", name, query_class, type));
699   memset(buf, 0, sizeof(buf));
700   if ((request_len = res_mkquery(QUERY, name, query_class, type, 
701                                  0, 0, 0, (unsigned char*) buf, sizeof(buf))) > 0) {
702     HEADER* header = (HEADER*) buf;
703 #ifndef LRAND48
704     int            k = 0;
705     struct timeval tv;
706 #endif
707     /*
708      * generate a unique id
709      * NOTE: we don't have to worry about converting this to and from
710      * network byte order, the nameserver does not interpret this value
711      * and returns it unchanged
712      */
713 #ifdef LRAND48
714     do {
715       header->id = (header->id + lrand48()) & 0xffff;
716     } while (find_id(header->id));
717 #else
718     gettimeofday(&tv, NULL);
719     do {
720       header->id = (header->id + k + tv.tv_usec) & 0xffff;
721       ++k;
722     } while (find_id(header->id));
723 #endif /* LRAND48 */
724     request->id = header->id;
725     ++request->sends;
726     Debug((DEBUG_DNS, "Resolver: query_name %d: %s %d %d", request->id, 
727           name, query_class, type));
728     request->sent += send_res_msg((const unsigned char*) buf, request_len, request->sends);
729   }
730 }
731
732 static void resend_query(struct ResRequest* request)
733 {
734   assert(0 != request);
735
736   if (request->resend == 0)
737     return;
738   ++reinfo.re_resends;
739   switch(request->type) {
740   case T_PTR:
741     do_query_number(NULL, &request->addr, request);
742     break;
743   case T_A:
744     do_query_name(NULL, request->name, request);
745     break;
746   default:
747     break;
748   }
749 }
750
751 /*
752  * proc_answer - process name server reply
753  * build a hostent struct in the passed request
754  */
755 static int proc_answer(struct ResRequest* request, HEADER* header,
756                        u_char* buf, u_char* eob)
757 {
758   char   hostbuf[HOSTLEN + 1]; /* working buffer */
759   u_char* current;             /* current position in buf */
760   char** alias;                /* alias list */
761   char** addr;                 /* address list */
762   char*  name;                 /* pointer to name string */
763   char*  address;              /* pointer to address */
764   char*  endp;                 /* end of our buffer */
765   int    query_class;          /* answer class */
766   int    type;                 /* answer type */
767   int    rd_length;            /* record data length */
768   int    answer_count = 0;     /* answer counter */
769   int    n;                    /* temp count */
770   int    addr_count  = 0;      /* number of addresses in hostent */
771   int    alias_count = 0;      /* number of aliases in hostent */
772   struct hostent* hp;          /* hostent getting filled */
773
774   assert(0 != request);
775   assert(0 != header);
776   assert(0 != buf);
777   assert(0 != eob);
778   
779   current = buf + sizeof(HEADER);
780   hp = &(request->he.h);
781   /*
782    * lazy allocation of request->he.buf, we don't allocate a buffer
783    * unless there is something to put in it.
784    */
785   if (!request->he.buf) {
786     request->he.buf = (char*) MyMalloc(MAXGETHOSTLEN + 1);
787     request->he.buf[MAXGETHOSTLEN] = '\0';
788     /*
789      * array of alias list pointers starts at beginning of buf
790      */
791     hp->h_aliases = (char**) request->he.buf;
792     hp->h_aliases[0] = NULL;
793     /*
794      * array of address list pointers starts after alias list pointers
795      * the actual addresses follow the the address list pointers
796      */ 
797     hp->h_addr_list = (char**)(request->he.buf + ALIAS_BLEN);
798     /*
799      * don't copy the host address to the beginning of h_addr_list
800      */
801     hp->h_addr_list[0] = NULL;
802   }
803   endp = request->he.buf + MAXGETHOSTLEN;
804   /*
805    * find the end of the address list
806    */
807   addr = hp->h_addr_list;
808   while (*addr) {
809     ++addr;
810     ++addr_count;
811   }
812   /*
813    * make address point to first available address slot
814    */
815   address = request->he.buf + ADDRS_OFFSET +
816                     (sizeof(struct in_addr) * addr_count);
817   /*
818    * find the end of the alias list
819    */
820   alias = hp->h_aliases;
821   while (*alias) {
822     ++alias;
823     ++alias_count;
824   }
825   /*
826    * make name point to first available space in request->buf
827    */
828   if (alias_count > 0) {
829     name = hp->h_aliases[alias_count - 1];
830     name += (strlen(name) + 1);
831   }
832   else if (hp->h_name)
833     name = hp->h_name + strlen(hp->h_name) + 1;
834   else
835     name = request->he.buf + ADDRS_OFFSET + ADDRS_DLEN;
836  
837   /*
838    * skip past queries
839    */ 
840   while (header->qdcount-- > 0) {
841     if ((n = dn_skipname(current, eob)) < 0)
842       break;
843     current += (n + QFIXEDSZ);
844   }
845   /*
846    * process each answer sent to us blech.
847    */
848   while (header->ancount-- > 0 && current < eob && name < endp) {
849     n = dn_expand(buf, eob, current, hostbuf, sizeof(hostbuf));
850     if (n <= 0) {
851       /*
852        * no more answers left
853        */
854       return answer_count;
855     }
856     hostbuf[HOSTLEN] = '\0';
857     /* 
858      * With Address arithmetic you have to be very anal
859      * this code was not working on alpha due to that
860      * (spotted by rodder/jailbird/dianora)
861      */
862     current += (size_t) n;
863
864     if (!((current + ANSWER_FIXED_SIZE) < eob))
865       break;
866
867     type = _getshort(current);
868     current += TYPE_SIZE;
869
870     query_class = _getshort(current);
871     current += CLASS_SIZE;
872
873     request->ttl = _getlong(current);
874     current += TTL_SIZE;
875
876     rd_length = _getshort(current);
877     current += RDLENGTH_SIZE;
878
879     /* 
880      * Wait to set request->type until we verify this structure 
881      */
882     switch(type) {
883     case T_A:
884       /*
885        * check for invalid rd_length or too many addresses
886        */
887       if (rd_length != sizeof(struct in_addr))
888         return answer_count;
889       if (++addr_count < RES_MAXADDRS) {
890         if (answer_count == 1)
891           hp->h_addrtype = (query_class == C_IN) ?  AF_INET : AF_UNSPEC;
892
893         memcpy(address, current, sizeof(struct in_addr));
894         *addr++ = address;
895         *addr = 0;
896         address += sizeof(struct in_addr);
897
898         if (!hp->h_name) {
899           strcpy(name, hostbuf);
900           hp->h_name = name;
901           name += strlen(name) + 1;
902         }
903         Debug((DEBUG_DNS, "Resolver: A %s for %s", 
904                ircd_ntoa((char*) hp->h_addr_list[addr_count - 1]), hostbuf));
905       }
906       current += rd_length;
907       ++answer_count;
908       break;
909     case T_PTR:
910       n = dn_expand(buf, eob, current, hostbuf, sizeof(hostbuf));
911       if (n < 0) {
912         /*
913          * broken message
914          */
915         return 0;
916       }
917       else if (n == 0) {
918         /*
919          * no more answers left
920          */
921         return answer_count;
922       }
923       /*
924        * This comment is based on analysis by Shadowfax, Wohali and johan, 
925        * not me.  (Dianora) I am only commenting it.
926        *
927        * dn_expand is guaranteed to not return more than sizeof(hostbuf)
928        * but do all implementations of dn_expand also guarantee
929        * buffer is terminated with null byte? Lets not take chances.
930        *  -Dianora
931        */
932       hostbuf[HOSTLEN] = '\0';
933       current += (size_t) n;
934
935       Debug((DEBUG_DNS, "Resolver: PTR %s", hostbuf));
936       /*
937        * copy the returned hostname into the host name
938        * ignore duplicate ptr records
939        */
940       if (!hp->h_name) {
941         strcpy(name, hostbuf);
942         hp->h_name = name;
943         name += strlen(name) + 1;
944       }
945       ++answer_count;
946       break;
947     case T_CNAME:
948       Debug((DEBUG_DNS, "Resolver: CNAME %s", hostbuf));
949       if (++alias_count < RES_MAXALIASES) {
950         ircd_strncpy(name, hostbuf, endp - name);
951         *alias++ = name;
952         *alias   = 0;
953         name += strlen(name) + 1;
954       }
955       current += rd_length;
956       ++answer_count;
957       break;
958     default :
959       Debug((DEBUG_DNS,"Resolver: proc_answer type: %d for: %s", type, hostbuf));
960       break;
961     }
962   }
963   return answer_count;
964 }
965
966 /*
967  * resolver_read - read a dns reply from the nameserver and process it.
968  * return 0 if nothing was read from the socket, otherwise return 1
969  */
970 int resolver_read(void)
971 {
972   u_char             buf[sizeof(HEADER) + MAXPACKET];
973   HEADER*            header       = 0;
974   struct ResRequest* request      = 0;
975   struct CacheEntry* cp           = 0;
976   unsigned int       rc           = 0;
977   int                answer_count = 0;
978   struct sockaddr_in sin;
979
980   Debug((DEBUG_DNS, "Resolver: read"));
981   if (IO_SUCCESS != os_recvfrom_nonb(ResolverFileDescriptor,
982                                      (char*) buf, sizeof(buf), &rc, &sin)) {
983     return 0;
984   }
985   if (rc < sizeof(HEADER)) {
986     Debug((DEBUG_DNS, "Resolver: short reply %d: %s", rc, 
987            (strerror(errno)) ? strerror(errno) : "Unknown"));
988     return 0;
989   }
990   /*
991    * convert DNS reply reader from Network byte order to CPU byte order.
992    */
993   header = (HEADER*) buf;
994   /* header->id = ntohs(header->id); */
995   header->ancount = ntohs(header->ancount);
996   header->qdcount = ntohs(header->qdcount);
997   header->nscount = ntohs(header->nscount);
998   header->arcount = ntohs(header->arcount);
999   ++reinfo.re_replies;
1000   /*
1001    * response for an id which we have already received an answer for
1002    * just ignore this response.
1003    */
1004   if (0 == (request = find_id(header->id))) {
1005     Debug((DEBUG_DNS, "Resolver: can't find request id: %d", header->id));
1006     return 1;
1007   }
1008   /*
1009    * check against possibly fake replies
1010    */
1011   if (!res_ourserver(&_res, &sin)) {
1012     Debug((DEBUG_DNS, "Resolver: fake reply from: %s", (const char*) &sin.sin_addr));
1013     ++reinfo.re_unkrep;
1014     return 1;
1015   }
1016
1017   if ((header->rcode != NOERROR) || (header->ancount == 0)) {
1018     ++reinfo.re_errors;
1019     if (SERVFAIL == header->rcode)
1020       resend_query(request);
1021     else {
1022       /*
1023        * If a bad error was returned, we stop here and dont send
1024        * send any more (no retries granted).
1025        * Isomer: Perhaps we should return these error messages back to
1026        *         the client?
1027        */
1028 #ifdef DEBUGMODE
1029       switch (header->rcode) {
1030         case NOERROR:
1031           Debug((DEBUG_DNS, "Fatal DNS error: No Error"));
1032           break;
1033         case FORMERR:
1034           Debug((DEBUG_DNS, "Fatal DNS error: Format Error"));
1035           break;
1036         case SERVFAIL:
1037           Debug((DEBUG_DNS, "Fatal DNS error: Server Failure"));
1038           break;
1039         case NXDOMAIN:
1040           Debug((DEBUG_DNS, "DNS error: Non Existant Domain"));
1041           break;
1042         case NOTIMP:
1043           Debug((DEBUG_DNS, "Fatal DNS error: Not Implemented"));
1044           break;
1045         case REFUSED:
1046           Debug((DEBUG_DNS, "Fatal DNS error: Query Refused"));
1047           break;
1048         default:
1049           Debug((DEBUG_DNS, "Unassigned fatal DNS error: %i", header->rcode));
1050           break;
1051       }
1052 #endif /* DEBUGMODE */
1053       (*request->query.callback)(request->query.vptr, 0);
1054       rem_request(request);
1055     } 
1056     return 1;
1057   }
1058   /*
1059    * If this fails there was an error decoding the received packet, 
1060    * try it again and hope it works the next time.
1061    */
1062   answer_count = proc_answer(request, header, buf, buf + rc);
1063   if (answer_count) {
1064     if (T_PTR == request->type) {
1065       struct DNSReply* reply = 0;
1066       if (0 == request->he.h.h_name) {
1067         /*
1068          * got a PTR response with no name, something bogus is happening
1069          * don't bother trying again, the client address doesn't resolve 
1070          */
1071         (*request->query.callback)(request->query.vptr, reply);
1072         rem_request(request); 
1073         return 1;
1074       }
1075       Debug((DEBUG_DNS, "relookup %s <-> %s",
1076              request->he.h.h_name, ircd_ntoa((char*) &request->addr)));
1077       /*
1078        * Lookup the 'authoritive' name that we were given for the
1079        * ip#.  By using this call rather than regenerating the
1080        * type we automatically gain the use of the cache with no
1081        * extra kludges.
1082        */
1083       reply = gethost_byname(request->he.h.h_name, &request->query);
1084       if (reply) {
1085         (*request->query.callback)(request->query.vptr, reply);
1086       }
1087       else {
1088         /*
1089          * If name wasn't found, a request has been queued and it will
1090          * be the last one queued.  This is rather nasty way to keep
1091          * a host alias with the query. -avalon
1092          */
1093         MyFree(requestListTail->he.buf);
1094         requestListTail->he.buf = request->he.buf;
1095         request->he.buf = 0;
1096         memcpy(&requestListTail->he.h, &request->he.h, sizeof(struct hostent));
1097       }
1098       rem_request(request);
1099     }
1100     else {
1101       /*
1102        * got a name and address response, client resolved
1103        * XXX - Bug found here by Dianora -
1104        * make_cache() occasionally returns a NULL pointer when a
1105        * PTR returned a CNAME, cp was not checked before so the
1106        * callback was being called with a value of 0x2C != NULL.
1107        */
1108       struct DNSReply* reply = 0;
1109       if (validate_hostent(&request->he.h)) {
1110         if ((cp = make_cache(request)))
1111           reply = &cp->reply;
1112       }
1113       (*request->query.callback)(request->query.vptr, reply);
1114       rem_request(request);
1115     }
1116   }
1117   else if (!request->sent) {
1118     /*
1119      * XXX - we got a response for a query we didn't send with a valid id?
1120      * this should never happen, bail here and leave the client unresolved
1121      */
1122     (*request->query.callback)(request->query.vptr, 0);
1123     rem_request(request);
1124   }
1125   return 1;
1126 }
1127
1128 /*
1129  * resolver_read_multiple - process up to count reads
1130  */
1131 void resolver_read_multiple(int count)
1132 {
1133   int i = 0;
1134   for ( ; i < count; ++i) {
1135     if (0 == resolver_read())
1136       return;
1137   }
1138 }
1139
1140 static size_t calc_hostent_buffer_size(const struct hostent* hp)
1141 {
1142   char** p;
1143   size_t count = 0;
1144   assert(0 != hp);
1145
1146   /*
1147    * space for name
1148    */
1149   count += (strlen(hp->h_name) + 1);
1150   /*
1151    * space for aliases
1152    */
1153   for (p = hp->h_aliases; *p; ++p)
1154     count += (strlen(*p) + 1 + sizeof(char*));
1155   /*
1156    * space for addresses
1157    */
1158   for (p = hp->h_addr_list; *p; ++p)
1159     count += (hp->h_length + sizeof(char*));
1160   /*
1161    * space for 2 nulls to terminate h_aliases and h_addr_list 
1162    */
1163   count += (2 * sizeof(char*));
1164   return count;
1165 }
1166
1167
1168 /*
1169  * dup_hostent - Duplicate a hostent struct, allocate only enough memory for
1170  * the data we're putting in it.
1171  */
1172 static void dup_hostent(struct Hostent* new_hp, struct hostent* hp)
1173 {
1174   char*  p;
1175   char** ap;
1176   char** pp;
1177   int    alias_count = 0;
1178   int    addr_count = 0;
1179   size_t bytes_needed = 0;
1180
1181   assert(0 != new_hp);
1182   assert(0 != hp);
1183
1184   /* how much buffer do we need? */
1185   bytes_needed += (strlen(hp->h_name) + 1);
1186
1187   pp = hp->h_aliases;
1188   while (*pp) {
1189     bytes_needed += (strlen(*pp++) + 1 + sizeof(char*));
1190     ++alias_count;
1191   }
1192   pp = hp->h_addr_list;
1193   while (*pp++) {
1194     bytes_needed += (hp->h_length + sizeof(char*));
1195     ++addr_count;
1196   }
1197   /* Reserve space for 2 nulls to terminate h_aliases and h_addr_list */
1198   bytes_needed += (2 * sizeof(char*));
1199
1200   /* Allocate memory */
1201   new_hp->buf = (char*) MyMalloc(bytes_needed);
1202
1203   new_hp->h.h_addrtype = hp->h_addrtype;
1204   new_hp->h.h_length = hp->h_length;
1205
1206   /* first write the address list */
1207   pp = hp->h_addr_list;
1208   ap = new_hp->h.h_addr_list =
1209       (char**)(new_hp->buf + ((alias_count + 1) * sizeof(char*)));
1210   p = (char*)ap + ((addr_count + 1) * sizeof(char*));
1211   while (*pp)
1212   {
1213     *ap++ = p;
1214     memcpy(p, *pp++, hp->h_length);
1215     p += hp->h_length;
1216   }
1217   *ap = 0;
1218   /* next write the name */
1219   new_hp->h.h_name = p;
1220   strcpy(p, hp->h_name);
1221   p += (strlen(p) + 1);
1222
1223   /* last write the alias list */
1224   pp = hp->h_aliases;
1225   ap = new_hp->h.h_aliases = (char**) new_hp->buf;
1226   while (*pp) {
1227     *ap++ = p;
1228     strcpy(p, *pp++);
1229     p += (strlen(p) + 1);
1230   }
1231   *ap = 0;
1232 }
1233
1234 /*
1235  * update_hostent - Add records to a Hostent struct in place.
1236  */
1237 static void update_hostent(struct Hostent* hp, char** addr, char** alias)
1238 {
1239   char*  p;
1240   char** ap;
1241   char** pp;
1242   int    alias_count = 0;
1243   int    addr_count = 0;
1244   char*  buf = NULL;
1245   size_t bytes_needed = 0;
1246
1247   if (!hp || !hp->buf)
1248     return;
1249
1250   /* how much buffer do we need? */
1251   bytes_needed = strlen(hp->h.h_name) + 1;
1252   pp = hp->h.h_aliases;
1253   while (*pp) {
1254     bytes_needed += (strlen(*pp++) + 1 + sizeof(char*));
1255     ++alias_count;
1256   }
1257   if (alias) {
1258     pp = alias;
1259     while (*pp) {
1260       bytes_needed += (strlen(*pp++) + 1 + sizeof(char*));
1261       ++alias_count;
1262     }
1263   }
1264   pp = hp->h.h_addr_list;
1265   while (*pp++) {
1266     bytes_needed += (hp->h.h_length + sizeof(char*));
1267     ++addr_count;
1268   }
1269   if (addr) {
1270     pp = addr;
1271     while (*pp++) {
1272       bytes_needed += (hp->h.h_length + sizeof(char*));
1273       ++addr_count;
1274     }
1275   }
1276   /* Reserve space for 2 nulls to terminate h_aliases and h_addr_list */
1277   bytes_needed += 2 * sizeof(char*);
1278
1279   /* Allocate memory */
1280   buf = (char*) MyMalloc(bytes_needed);
1281   assert(0 != buf);
1282
1283   /* first write the address list */
1284   pp = hp->h.h_addr_list;
1285   ap = hp->h.h_addr_list =
1286       (char**)(buf + ((alias_count + 1) * sizeof(char*)));
1287   p = (char*)ap + ((addr_count + 1) * sizeof(char*));
1288   while (*pp) {
1289     memcpy(p, *pp++, hp->h.h_length);
1290     *ap++ = p;
1291     p += hp->h.h_length;
1292   }
1293   if (addr) {
1294     while (*addr) {
1295       memcpy(p, *addr++, hp->h.h_length);
1296       *ap++ = p;
1297       p += hp->h.h_length;
1298     }
1299   }
1300   *ap = 0;
1301
1302   /* next write the name */
1303   strcpy(p, hp->h.h_name);
1304   hp->h.h_name = p;
1305   p += (strlen(p) + 1);
1306
1307   /* last write the alias list */
1308   pp = hp->h.h_aliases;
1309   ap = hp->h.h_aliases = (char**) buf;
1310   while (*pp) {
1311     strcpy(p, *pp++);
1312     *ap++ = p;
1313     p += (strlen(p) + 1);
1314   }
1315   if (alias) {
1316     while (*alias) {
1317       strcpy(p, *alias++);
1318       *ap++ = p;
1319       p += (strlen(p) + 1);
1320     }
1321   }
1322   *ap = 0;
1323   /* release the old buffer */
1324   p = hp->buf;
1325   hp->buf = buf;
1326   MyFree(p);
1327 }
1328
1329 /*
1330  * hash_number - IP address hash function
1331  */
1332 static int hash_number(const unsigned char* ip)
1333 {
1334   /* could use loop but slower */
1335   unsigned int hashv;
1336   const u_char* p = (const u_char*) ip;
1337
1338   assert(0 != p);
1339
1340   hashv = *p++;
1341   hashv += hashv + *p++;
1342   hashv += hashv + *p++;
1343   hashv += hashv + *p;
1344   hashv %= ARES_CACSIZE;
1345   return hashv;
1346 }
1347
1348 /*
1349  * hash_name - hostname hash function
1350  */
1351 static int hash_name(const char* name)
1352 {
1353   unsigned int hashv = 0;
1354   const u_char* p = (const u_char*) name;
1355
1356   assert(0 != p);
1357
1358   for (; *p && *p != '.'; ++p)
1359     hashv += *p;
1360   hashv %= ARES_CACSIZE;
1361   return hashv;
1362 }
1363
1364 /*
1365  * add_to_cache - Add a new cache item to the queue and hash table.
1366  */
1367 static struct CacheEntry* add_to_cache(struct CacheEntry* ocp)
1368 {
1369   int  hashv;
1370
1371   assert(0 != ocp);
1372
1373   ocp->list_next = cacheTop;
1374   cacheTop = ocp;
1375
1376   hashv = hash_name(ocp->he.h.h_name);
1377
1378   ocp->hname_next = hashtable[hashv].name_list;
1379   hashtable[hashv].name_list = ocp;
1380
1381   hashv = hash_number((const unsigned char*) ocp->he.h.h_addr);
1382
1383   ocp->hnum_next = hashtable[hashv].num_list;
1384   hashtable[hashv].num_list = ocp;
1385
1386   /*
1387    * LRU deletion of excessive cache entries.
1388    */
1389   if (++cachedCount > MAXCACHED) {
1390     struct CacheEntry* cp;
1391     struct CacheEntry* cp_next;
1392     for (cp = ocp->list_next; cp; cp = cp_next) {
1393       cp_next = cp->list_next;
1394       rem_cache(cp);
1395     }
1396   }
1397   ++cainfo.ca_adds;
1398   return ocp;
1399 }
1400
1401 /*
1402  * update_list - does not alter the cache structure passed. It is assumed that
1403  * it already contains the correct expire time, if it is a new entry. Old
1404  * entries have the expirey time updated.
1405 */
1406 static void update_list(struct ResRequest* request, struct CacheEntry* cachep)
1407 {
1408 #if 0
1409   struct CacheEntry** cpp;
1410 #endif
1411   struct CacheEntry*  cp = cachep;
1412   char*    s;
1413   char*    t;
1414   int      i;
1415   int      j;
1416   char**   ap;
1417   char*    addrs[RES_MAXADDRS + 1];
1418   char*    aliases[RES_MAXALIASES + 1];
1419
1420   /*
1421    * search for the new cache item in the cache list by hostname.
1422    * If found, move the entry to the top of the list and return.
1423    */
1424   ++cainfo.ca_updates;
1425 #if 0
1426   for (cpp = &cacheTop; *cpp; cpp = &((*cpp)->list_next)) {
1427     if (cp == *cpp)
1428       break;
1429   }
1430   if (!*cpp)
1431     return;
1432   *cpp = cp->list_next;
1433   cp->list_next = cacheTop;
1434   cacheTop = cp;
1435 #endif
1436
1437   if (!request)
1438     return;
1439   /*
1440    * Compare the cache entry against the new record.  Add any
1441    * previously missing names for this entry.
1442    */
1443   *aliases = 0;
1444   ap = aliases;
1445   for (i = 0, s = request->he.h.h_name; s; s = request->he.h.h_aliases[i++]) {
1446     for (j = 0, t = cp->he.h.h_name; t; t = cp->he.h.h_aliases[j++]) {
1447       if (0 == ircd_strcmp(t, s))
1448         break;
1449     }
1450     if (!t) {
1451       *ap++ = s;
1452       *ap = 0;
1453     }
1454   }
1455   /*
1456    * Do the same again for IP#'s.
1457    */
1458   *addrs = 0;
1459   ap = addrs;
1460   for (i = 0; (s = request->he.h.h_addr_list[i]); i++) {
1461     for (j = 0; (t = cp->he.h.h_addr_list[j]); j++) {
1462       if (!memcmp(t, s, sizeof(struct in_addr)))
1463         break;
1464     }
1465     if (!t) {
1466       *ap++ = s;
1467       *ap = 0;
1468     }
1469   }
1470   if (*addrs || *aliases)
1471     update_hostent(&cp->he, addrs, aliases);
1472 }
1473
1474 /*
1475  * find_cache_name - find name in nameserver cache
1476  */
1477 static struct CacheEntry* find_cache_name(const char* name)
1478 {
1479   struct CacheEntry* cp;
1480   char*   s;
1481   int     hashv;
1482   int     i;
1483
1484   assert(0 != name);
1485   hashv = hash_name(name);
1486
1487   cp = hashtable[hashv].name_list;
1488
1489   for (; cp; cp = cp->hname_next) {
1490     for (i = 0, s = cp->he.h.h_name; s; s = cp->he.h.h_aliases[i++]) {
1491       if (0 == ircd_strcmp(s, name)) {
1492         ++cainfo.ca_na_hits;
1493         return cp;
1494       }
1495     }
1496   }
1497
1498   for (cp = cacheTop; cp; cp = cp->list_next) {
1499     /*
1500      * if no aliases or the hash value matches, we've already
1501      * done this entry and all possiblilities concerning it.
1502      */
1503     if (!cp->he.h.h_name || hashv == hash_name(cp->he.h.h_name))
1504       continue;
1505     for (i = 0, s = cp->he.h.h_aliases[i]; s; s = cp->he.h.h_aliases[++i]) {
1506       if (0 == ircd_strcmp(name, s)) {
1507         ++cainfo.ca_na_hits;
1508         return cp;
1509       }
1510     }
1511   }
1512   return NULL;
1513 }
1514
1515 /*
1516  * find_cache_number - find a cache entry by ip# and update its expire time
1517  */
1518 static struct CacheEntry* find_cache_number(struct ResRequest* request,
1519                                             const char* addr)
1520 {
1521   struct CacheEntry* cp;
1522   int     hashv;
1523   int     i;
1524
1525   assert(0 != addr);
1526   hashv = hash_number((const unsigned char*) addr);
1527   cp = hashtable[hashv].num_list;
1528
1529   for (; cp; cp = cp->hnum_next) {
1530     for (i = 0; cp->he.h.h_addr_list[i]; ++i) {
1531       if (!memcmp(cp->he.h.h_addr_list[i], addr, sizeof(struct in_addr))) {
1532         ++cainfo.ca_nu_hits;
1533         return cp;
1534       }
1535     }
1536   }
1537   for (cp = cacheTop; cp; cp = cp->list_next) {
1538     /*
1539      * single address entry...would have been done by hashed
1540      * search above...
1541      * if the first IP# has the same hashnumber as the IP# we
1542      * are looking for, its been done already.
1543      */
1544     if (!cp->he.h.h_addr_list[1] || 
1545         hashv == hash_number((const unsigned char*) cp->he.h.h_addr_list[0]))
1546       continue;
1547     for (i = 1; cp->he.h.h_addr_list[i]; ++i) {
1548       if (!memcmp(cp->he.h.h_addr_list[i], addr, sizeof(struct in_addr))) {
1549         ++cainfo.ca_nu_hits;
1550         return cp;
1551       }
1552     }
1553   }
1554   return NULL;
1555 }
1556
1557 static struct CacheEntry* make_cache(struct ResRequest* request)
1558 {
1559   struct CacheEntry* cp;
1560   int     i;
1561   struct hostent* hp;
1562   assert(0 != request);
1563
1564   hp = &request->he.h;
1565   /*
1566    * shouldn't happen but it just might...
1567    */
1568   assert(0 != hp->h_name);
1569   assert(0 != hp->h_addr_list[0]);
1570   if (!hp->h_name || !hp->h_addr_list[0])
1571     return NULL;
1572   /*
1573    * Make cache entry.  First check to see if the cache already exists
1574    * and if so, return a pointer to it.
1575    */
1576   for (i = 0; hp->h_addr_list[i]; ++i) {
1577     if ((cp = find_cache_number(request, hp->h_addr_list[i]))) {
1578       update_list(request, cp);
1579       return cp;
1580     }
1581   }
1582   /*
1583    * a matching entry wasnt found in the cache so go and make one up.
1584    */ 
1585   cp = (struct CacheEntry*) MyMalloc(sizeof(struct CacheEntry));
1586   assert(0 != cp);
1587
1588   memset(cp, 0, sizeof(struct CacheEntry));
1589   dup_hostent(&cp->he, hp);
1590   cp->reply.hp = &cp->he.h;
1591   /*
1592    * hmmm... we could time out the cache after 10 minutes regardless
1593    * would that be reasonable since we don't save the reply?
1594    */ 
1595   if (request->ttl < AR_TTL) {
1596     ++reinfo.re_shortttl;
1597     cp->ttl = AR_TTL;
1598   }
1599   else
1600     cp->ttl = request->ttl;
1601   cp->expireat = CurrentTime + cp->ttl;
1602   return add_to_cache(cp);
1603 }
1604
1605 /*
1606  * rem_cache - delete a cache entry from the cache structures 
1607  * and lists and return all memory used for the cache back to the memory pool.
1608  */
1609 static void rem_cache(struct CacheEntry* ocp)
1610 {
1611   struct CacheEntry** cp;
1612   int                 hashv;
1613   struct hostent*     hp;
1614   assert(0 != ocp);
1615
1616
1617   if (0 < ocp->reply.ref_count) {
1618     if (ocp->expireat < CurrentTime) {
1619       ocp->expireat = CurrentTime + AR_TTL;
1620       Debug((DEBUG_DNS, "Resolver: referenced cache entry not removed for: %s",
1621             ocp->he.h.h_name));
1622     }
1623     return;
1624   }
1625   /*
1626    * remove cache entry from linked list
1627    */
1628   for (cp = &cacheTop; *cp; cp = &((*cp)->list_next)) {
1629     if (*cp == ocp) {
1630       *cp = ocp->list_next;
1631       break;
1632     }
1633   }
1634   hp = &ocp->he.h;
1635   /*
1636    * remove cache entry from hashed name list
1637    */
1638   assert(0 != hp->h_name);
1639   hashv = hash_name(hp->h_name);
1640
1641   for (cp = &hashtable[hashv].name_list; *cp; cp = &((*cp)->hname_next)) {
1642     if (*cp == ocp) {
1643       *cp = ocp->hname_next;
1644       break;
1645     }
1646   }
1647   /*
1648    * remove cache entry from hashed number list
1649    */
1650   hashv = hash_number((const unsigned char*) hp->h_addr);
1651   assert(-1 < hashv);
1652
1653   for (cp = &hashtable[hashv].num_list; *cp; cp = &((*cp)->hnum_next)) {
1654     if (*cp == ocp) {
1655       *cp = ocp->hnum_next;
1656       break;
1657     }
1658   }
1659   /*
1660    * free memory used to hold the various host names and the array
1661    * of alias pointers.
1662    */
1663   MyFree(ocp->he.buf);
1664   MyFree(ocp);
1665   --cachedCount;
1666   ++cainfo.ca_dels;
1667 }
1668
1669 void flush_resolver_cache(void)
1670 {
1671   /*
1672    * stubbed - iterate cache and remove everything that isn't referenced
1673    */
1674 }
1675
1676 /*
1677  * m_dns - dns status query
1678  */
1679 int m_dns(struct Client *cptr, struct Client *sptr, int parc, char *parv[])
1680 {
1681 #if !defined(NDEBUG)
1682   struct CacheEntry* cp;
1683   int     i;
1684   struct hostent* hp;
1685
1686   if (parv[1] && *parv[1] == 'l') {
1687     for(cp = cacheTop; cp; cp = cp->list_next) {
1688       hp = &cp->he.h;
1689       sendcmdto_one(&me, CMD_NOTICE, sptr, "%C :Ex %d ttl %d host %s(%s)",
1690                     sptr, cp->expireat - CurrentTime, cp->ttl,
1691                     hp->h_name, ircd_ntoa(hp->h_addr));
1692       for (i = 0; hp->h_aliases[i]; i++)
1693         sendcmdto_one(&me, CMD_NOTICE, sptr, "%C : %s = %s (CN)", sptr,
1694                       hp->h_name, hp->h_aliases[i]);
1695       for (i = 1; hp->h_addr_list[i]; i++)
1696         sendcmdto_one(&me, CMD_NOTICE, sptr, "%C : %s = %s (IP)", sptr,
1697                       hp->h_name, ircd_ntoa(hp->h_addr_list[i]));
1698     }
1699     return 0;
1700   }
1701   if (parv[1] && *parv[1] == 'd') {
1702     sendcmdto_one(&me, CMD_NOTICE, sptr, "%C :ResolverFileDescriptor = %d", 
1703                   sptr, ResolverFileDescriptor);
1704     return 0;
1705   }
1706   sendcmdto_one(&me, CMD_NOTICE, sptr,"%C :Ca %d Cd %d Ce %d Cl %d Ch %d:%d "
1707                 "Cu %d", sptr,
1708                 cainfo.ca_adds, cainfo.ca_dels, cainfo.ca_expires,
1709                 cainfo.ca_lookups, cainfo.ca_na_hits, cainfo.ca_nu_hits, 
1710                 cainfo.ca_updates);
1711   
1712   sendcmdto_one(&me, CMD_NOTICE, sptr,"%C :Re %d Rl %d/%d Rp %d Rq %d",
1713                 sptr, reinfo.re_errors, reinfo.re_nu_look,
1714                 reinfo.re_na_look, reinfo.re_replies, reinfo.re_requests);
1715   sendcmdto_one(&me, CMD_NOTICE, sptr,"%C :Ru %d Rsh %d Rs %d(%d) Rt %d", sptr,
1716                 reinfo.re_unkrep, reinfo.re_shortttl, reinfo.re_sent,
1717                 reinfo.re_resends, reinfo.re_timeouts);
1718 #endif
1719   return 0;
1720 }
1721
1722 size_t cres_mem(struct Client* sptr)
1723 {
1724   struct CacheEntry* entry;
1725   struct ResRequest* request;
1726   size_t cache_mem     = 0;
1727   size_t request_mem   = 0;
1728   int    cache_count   = 0;
1729   int    request_count = 0;
1730
1731   for (entry = cacheTop; entry; entry = entry->list_next) {
1732     cache_mem += sizeof(struct CacheEntry);
1733     cache_mem += calc_hostent_buffer_size(&entry->he.h); 
1734     ++cache_count;
1735   }
1736   for (request = requestListHead; request; request = request->next) {
1737     request_mem += sizeof(struct ResRequest);
1738     if (request->name)
1739       request_mem += strlen(request->name) + 1; 
1740     if (request->he.buf)
1741       request_mem += MAXGETHOSTLEN + 1;
1742     ++request_count;
1743   }
1744
1745   if (cachedCount != cache_count) {
1746     send_reply(sptr, SND_EXPLICIT | RPL_STATSDEBUG,
1747                ":Resolver: cache count mismatch: %d != %d", cachedCount,
1748                cache_count);
1749     assert(cachedCount == cache_count);
1750   }
1751   send_reply(sptr, SND_EXPLICIT | RPL_STATSDEBUG,
1752              ":Resolver: cache %d(%d) requests %d(%d)", cache_count,
1753              cache_mem, request_count, request_mem);
1754   return cache_mem + request_mem;
1755 }
1756