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