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