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, strerror(errno)));
360   return ResolverFileDescriptor;
361 }
362
363 /*
364  * restart_resolver - flush the cache, reread resolv.conf, reopen socket
365  */
366 void restart_resolver(void)
367 {
368   /* flush_cache();  flush the dns cache */
369   start_resolver();
370 }
371
372 /*
373  * add_local_domain - Add the domain to hostname, if it is missing
374  * (as suggested by eps@TOASTER.SFSU.EDU)
375  */
376 void add_local_domain(char* hname, unsigned int size)
377 {
378   assert(0 != hname);
379   /* 
380    * try to fix up unqualified names 
381    */
382   if ((_res.options & RES_DEFNAMES) && !strchr(hname, '.')) {
383     if (_res.defdname[0]) {
384       unsigned len = strlen(hname);
385       if ((strlen(_res.defdname) + len + 2) < size) {
386         hname[len++] = '.';
387         strcpy(hname + len, _res.defdname);
388       }
389     }
390   }
391 }
392
393 /*
394  * add_request - place a new request in the request list
395  */
396 static void add_request(struct ResRequest* request)
397 {
398   assert(0 != request);
399   if (!requestListHead)
400     requestListHead = requestListTail = request;
401   else {
402     requestListTail->next = request;
403     requestListTail = request;
404   }
405   request->next = NULL;
406   ++reinfo.re_requests;
407 }
408
409 /*
410  * rem_request - remove a request from the list. 
411  * This must also free any memory that has been allocated for 
412  * temporary storage of DNS results.
413  */
414 static void rem_request(struct ResRequest* request)
415 {
416   struct ResRequest** current;
417   struct ResRequest*  prev = NULL;
418
419   assert(0 != request);
420   for (current = &requestListHead; *current; ) {
421     if (*current == request) {
422       *current = request->next;
423       if (requestListTail == request)
424         requestListTail = prev;
425       break;
426     } 
427     prev    = *current;
428     current = &(*current)->next;
429   }
430   MyFree(request->he.buf);
431   MyFree(request->name);
432   MyFree(request);
433 }
434
435 /*
436  * make_request - Create a DNS request record for the server.
437  */
438 static struct ResRequest* make_request(const struct DNSQuery* query)
439 {
440   struct ResRequest* request;
441   assert(0 != query);
442   request = (struct ResRequest*) MyMalloc(sizeof(struct ResRequest));
443   memset(request, 0, sizeof(struct ResRequest));
444
445   request->sentat           = CurrentTime;
446   request->retries          = 3;
447   request->resend           = 1;
448   request->timeout          = 5;    /* start at 5 per RFC1123 */
449   request->addr.s_addr      = INADDR_NONE;
450   request->he.h.h_addrtype  = AF_INET;
451   request->he.h.h_length    = sizeof(struct in_addr);
452   request->query.vptr       = query->vptr;
453   request->query.callback   = query->callback;
454
455 #if defined(NULL_POINTER_NOT_ZERO)
456   request->next             = NULL;
457   request->he.buf           = NULL;
458   request->he.h.h_name      = NULL;
459   request->he.h.h_aliases   = NULL;
460   request->he.h.h_addr_list = NULL;
461 #endif
462   add_request(request);
463   return request;
464 }
465
466 /*
467  * timeout_query_list - Remove queries from the list which have been 
468  * there too long without being resolved.
469  */
470 static time_t timeout_query_list(time_t now)
471 {
472   struct ResRequest* request;
473   struct ResRequest* next_request = 0;
474   time_t             next_time    = 0;
475   time_t             timeout      = 0;
476
477   Debug((DEBUG_DNS, "Resolver: timeout_query_list at %s", myctime(now)));
478   for (request = requestListHead; request; request = next_request) {
479     next_request = request->next;
480     timeout = request->sentat + request->timeout;
481     if (timeout < now) {
482       if (--request->retries <= 0) {
483         ++reinfo.re_timeouts;
484         (*request->query.callback)(request->query.vptr, 0);
485         rem_request(request);
486         continue;
487       }
488       else {
489         request->sentat = now;
490         request->timeout += request->timeout;
491         resend_query(request);
492       }
493     }
494     if (!next_time || timeout < next_time) {
495       next_time = timeout;
496     }
497   }
498   return (next_time > now) ? next_time : (now + AR_TTL);
499 }
500
501 /*
502  * expire_cache - removes entries from the cache which are older 
503  * than their expiry times. returns the time at which the server 
504  * should next poll the cache.
505  */
506 static time_t expire_cache(time_t now)
507 {
508   struct CacheEntry* cp;
509   struct CacheEntry* cp_next;
510   time_t             expire = 0;
511
512   Debug((DEBUG_DNS, "Resolver: expire_cache at %s", myctime(now)));
513   for (cp = cacheTop; cp; cp = cp_next) {
514     cp_next = cp->list_next;
515     if (cp->expireat < now) {
516       ++cainfo.ca_expires;
517       rem_cache(cp);
518     }
519     else if (!expire || expire > cp->expireat)
520       expire = cp->expireat;
521   }
522   return (expire > now) ? expire : (now + AR_TTL);
523 }
524
525 /*
526  * timeout_resolver - check request list and cache for expired entries
527  */
528 time_t timeout_resolver(time_t now)
529 {
530   if (nextDNSCheck < now)
531     nextDNSCheck = timeout_query_list(now);
532   if (nextCacheExpire < now)
533     nextCacheExpire = expire_cache(now);
534   return IRCD_MIN(nextDNSCheck, nextCacheExpire);
535 }
536
537
538 /*
539  * delete_resolver_queries - cleanup outstanding queries 
540  * for which there no longer exist clients or conf lines.
541  */
542 void delete_resolver_queries(const void* vptr)
543 {
544   struct ResRequest* request;
545   struct ResRequest* next_request;
546
547   for (request = requestListHead; request; request = next_request) {
548     next_request = request->next;
549     if (vptr == request->query.vptr)
550       rem_request(request);
551   }
552 }
553
554 /*
555  * send_res_msg - sends msg to all nameservers found in the "_res" structure.
556  * This should reflect /etc/resolv.conf. We will get responses
557  * which arent needed but is easier than checking to see if nameserver
558  * isnt present. Returns number of messages successfully sent to 
559  * nameservers or -1 if no successful sends.
560  */
561 static int send_res_msg(const u_char* msg, int len, int rcount)
562 {
563   int i;
564   int sent = 0;
565   int max_queries = IRCD_MIN(_res.nscount, rcount);
566
567   assert(0 != msg);
568   /*
569    * RES_PRIMARY option is not implemented
570    * if (_res.options & RES_PRIMARY || 0 == max_queries)
571    */
572   if (0 == max_queries)
573     max_queries = 1;
574
575   Debug((DEBUG_DNS, "Resolver: sendto %d", max_queries));
576
577   for (i = 0; i < max_queries; i++) {
578     if (sendto(ResolverFileDescriptor, msg, len, 0, 
579                (struct sockaddr*) &(_res.nsaddr_list[i]),
580                sizeof(struct sockaddr_in)) == len) {
581       ++reinfo.re_sent;
582       ++sent;
583     }
584     else
585       ircd_log(L_ERROR, "Resolver: send failed %s", strerror(errno));
586   }
587   return sent;
588 }
589
590 /*
591  * find_id - find a dns request id (id is determined by dn_mkquery)
592  */
593 static struct ResRequest* find_id(int id)
594 {
595   struct ResRequest* request;
596
597   for (request = requestListHead; request; request = request->next) {
598     if (request->id == id)
599       return request;
600   }
601   return NULL;
602 }
603
604 /*
605  * gethost_byname - get host address from name
606  */
607 struct DNSReply* gethost_byname(const char* name, 
608                                const struct DNSQuery* query)
609 {
610   struct CacheEntry* cp;
611   assert(0 != name);
612
613   Debug((DEBUG_DNS, "Resolver: gethost_byname %s", name));
614   ++reinfo.re_na_look;
615   if ((cp = find_cache_name(name)))
616     return &(cp->reply);
617
618   do_query_name(query, name, NULL);
619   nextDNSCheck = 1;
620   return NULL;
621 }
622
623 /*
624  * gethost_byaddr - get host name from address
625  */
626 struct DNSReply* gethost_byaddr(const char* addr,
627                                 const struct DNSQuery* query)
628 {
629   struct CacheEntry *cp;
630
631   assert(0 != addr);
632
633   Debug((DEBUG_DNS, "Resolver: gethost_byaddr %s", ircd_ntoa(addr)));
634
635   ++reinfo.re_nu_look;
636   if ((cp = find_cache_number(NULL, addr)))
637     return &(cp->reply);
638
639   do_query_number(query, (const struct in_addr*) addr, NULL);
640   nextDNSCheck = 1;
641   return NULL;
642 }
643
644 /*
645  * do_query_name - nameserver lookup name
646  */
647 static void do_query_name(const struct DNSQuery* query, 
648                           const char* name, struct ResRequest* request)
649 {
650   char  hname[HOSTLEN + 1];
651   assert(0 != name);
652
653   ircd_strncpy(hname, name, HOSTLEN);
654   hname[HOSTLEN] = '\0';
655 #if 0
656   /*
657    * removed, this is incorrect for anything it's used for
658    */
659   add_local_domain(hname, HOSTLEN);
660 #endif
661
662   if (!request) {
663     request       = make_request(query);
664     request->type = T_A;
665     request->name = (char*) MyMalloc(strlen(hname) + 1);
666     strcpy(request->name, hname);
667   }
668   query_name(hname, C_IN, T_A, request);
669 }
670
671 /*
672  * do_query_number - Use this to do reverse IP# lookups.
673  */
674 static void do_query_number(const struct DNSQuery* query, 
675                             const struct in_addr* addr,
676                             struct ResRequest* request)
677 {
678   char  ipbuf[32];
679   const unsigned char* cp;
680
681   assert(0 != addr);
682   cp = (const unsigned char*) &addr->s_addr;
683   sprintf_irc(ipbuf, "%u.%u.%u.%u.in-addr.arpa.",
684               (unsigned int)(cp[3]), (unsigned int)(cp[2]),
685               (unsigned int)(cp[1]), (unsigned int)(cp[0]));
686
687   if (!request) {
688     request              = make_request(query);
689     request->type        = T_PTR;
690     request->addr.s_addr = addr->s_addr;
691   }
692   query_name(ipbuf, C_IN, T_PTR, request);
693 }
694
695 /*
696  * query_name - generate a query based on class, type and name.
697  */
698 static void query_name(const char* name, int query_class,
699                        int type, struct ResRequest* request)
700 {
701   char buf[MAXPACKET];
702   int  request_len = 0;
703
704   assert(0 != name);
705   assert(0 != request);
706
707   Debug((DEBUG_DNS, "Resolver: query_name: %s %d %d", name, query_class, type));
708   memset(buf, 0, sizeof(buf));
709   if ((request_len = res_mkquery(QUERY, name, query_class, type, 
710                                  NULL, 0, NULL, buf, sizeof(buf))) > 0) {
711     HEADER* header = (HEADER*) buf;
712 #ifndef LRAND48
713     int            k = 0;
714     struct timeval tv;
715 #endif
716     /*
717      * generate a unique id
718      * NOTE: we don't have to worry about converting this to and from
719      * network byte order, the nameserver does not interpret this value
720      * and returns it unchanged
721      */
722 #ifdef LRAND48
723     do {
724       header->id = (header->id + lrand48()) & 0xffff;
725     } while (find_id(header->id));
726 #else
727     gettimeofday(&tv, NULL);
728     do {
729       header->id = (header->id + k + tv.tv_usec) & 0xffff;
730       ++k;
731     } while (find_id(header->id));
732 #endif /* LRAND48 */
733     request->id = header->id;
734     ++request->sends;
735     Debug((DEBUG_DNS, "Resolver: query_name %d: %s %d %d", request->id, 
736           name, query_class, type));
737     request->sent += send_res_msg(buf, request_len, request->sends);
738   }
739 }
740
741 static void resend_query(struct ResRequest* request)
742 {
743   assert(0 != request);
744
745   if (request->resend == 0)
746     return;
747   ++reinfo.re_resends;
748   switch(request->type) {
749   case T_PTR:
750     do_query_number(NULL, &request->addr, request);
751     break;
752   case T_A:
753     do_query_name(NULL, request->name, request);
754     break;
755   default:
756     break;
757   }
758 }
759
760 /*
761  * proc_answer - process name server reply
762  * build a hostent struct in the passed request
763  */
764 static int proc_answer(struct ResRequest* request, HEADER* header,
765                        u_char* buf, u_char* eob)
766 {
767   char   hostbuf[HOSTLEN + 1]; /* working buffer */
768   u_char* current;             /* current position in buf */
769   char** alias;                /* alias list */
770   char** addr;                 /* address list */
771   char*  name;                 /* pointer to name string */
772   char*  address;              /* pointer to address */
773   char*  endp;                 /* end of our buffer */
774   int    query_class;          /* answer class */
775   int    type;                 /* answer type */
776   int    rd_length;            /* record data length */
777   int    answer_count = 0;     /* answer counter */
778   int    n;                    /* temp count */
779   int    addr_count  = 0;      /* number of addresses in hostent */
780   int    alias_count = 0;      /* number of aliases in hostent */
781   struct hostent* hp;          /* hostent getting filled */
782
783   assert(0 != request);
784   assert(0 != header);
785   assert(0 != buf);
786   assert(0 != eob);
787   
788   current = buf + sizeof(HEADER);
789   hp = &(request->he.h);
790   /*
791    * lazy allocation of request->he.buf, we don't allocate a buffer
792    * unless there is something to put in it.
793    */
794   if (!request->he.buf) {
795     request->he.buf = (char*) MyMalloc(MAXGETHOSTLEN + 1);
796     request->he.buf[MAXGETHOSTLEN] = '\0';
797     /*
798      * array of alias list pointers starts at beginning of buf
799      */
800     hp->h_aliases = (char**) request->he.buf;
801     hp->h_aliases[0] = NULL;
802     /*
803      * array of address list pointers starts after alias list pointers
804      * the actual addresses follow the the address list pointers
805      */ 
806     hp->h_addr_list = (char**)(request->he.buf + ALIAS_BLEN);
807     /*
808      * don't copy the host address to the beginning of h_addr_list
809      */
810     hp->h_addr_list[0] = NULL;
811   }
812   endp = request->he.buf + MAXGETHOSTLEN;
813   /*
814    * find the end of the address list
815    */
816   addr = hp->h_addr_list;
817   while (*addr) {
818     ++addr;
819     ++addr_count;
820   }
821   /*
822    * make address point to first available address slot
823    */
824   address = request->he.buf + ADDRS_OFFSET +
825                     (sizeof(struct in_addr) * addr_count);
826   /*
827    * find the end of the alias list
828    */
829   alias = hp->h_aliases;
830   while (*alias) {
831     ++alias;
832     ++alias_count;
833   }
834   /*
835    * make name point to first available space in request->buf
836    */
837   if (alias_count > 0) {
838     name = hp->h_aliases[alias_count - 1];
839     name += (strlen(name) + 1);
840   }
841   else if (hp->h_name)
842     name = hp->h_name + strlen(hp->h_name) + 1;
843   else
844     name = request->he.buf + ADDRS_OFFSET + ADDRS_DLEN;
845  
846   /*
847    * skip past queries
848    */ 
849   while (header->qdcount-- > 0) {
850     if ((n = dn_skipname(current, eob)) < 0)
851       break;
852     current += (n + QFIXEDSZ);
853   }
854   /*
855    * process each answer sent to us blech.
856    */
857   while (header->ancount-- > 0 && current < eob && name < endp) {
858     n = dn_expand(buf, eob, current, hostbuf, sizeof(hostbuf));
859     if (n <= 0) {
860       /*
861        * no more answers left
862        */
863       return answer_count;
864     }
865     hostbuf[HOSTLEN] = '\0';
866     /* 
867      * With Address arithmetic you have to be very anal
868      * this code was not working on alpha due to that
869      * (spotted by rodder/jailbird/dianora)
870      */
871     current += (size_t) n;
872
873     if (!((current + ANSWER_FIXED_SIZE) < eob))
874       break;
875
876     type = _getshort(current);
877     current += TYPE_SIZE;
878
879     query_class = _getshort(current);
880     current += CLASS_SIZE;
881
882     request->ttl = _getlong(current);
883     current += TTL_SIZE;
884
885     rd_length = _getshort(current);
886     current += RDLENGTH_SIZE;
887
888     /* 
889      * Wait to set request->type until we verify this structure 
890      */
891 #if 0
892     add_local_domain(hostbuf, HOSTLEN);
893 #endif
894
895     switch(type) {
896     case T_A:
897       /*
898        * check for invalid rd_length or too many addresses
899        */
900       if (rd_length != sizeof(struct in_addr))
901         return answer_count;
902       if (++addr_count < RES_MAXADDRS) {
903         if (answer_count == 1)
904           hp->h_addrtype = (query_class == C_IN) ?  AF_INET : AF_UNSPEC;
905
906         memcpy(address, current, sizeof(struct in_addr));
907         *addr++ = address;
908         *addr = 0;
909         address += sizeof(struct in_addr);
910
911         if (!hp->h_name) {
912           strcpy(name, hostbuf);
913           hp->h_name = name;
914           name += strlen(name) + 1;
915         }
916         Debug((DEBUG_DNS, "Resolver: A %s for %s", 
917                ircd_ntoa((char*) hp->h_addr_list[addr_count - 1]), hostbuf));
918       }
919       current += rd_length;
920       ++answer_count;
921       break;
922     case T_PTR:
923       n = dn_expand(buf, eob, current, hostbuf, sizeof(hostbuf));
924       if (n < 0) {
925         /*
926          * broken message
927          */
928         return 0;
929       }
930       else if (n == 0) {
931         /*
932          * no more answers left
933          */
934         return answer_count;
935       }
936       /*
937        * This comment is based on analysis by Shadowfax, Wohali and johan, 
938        * not me.  (Dianora) I am only commenting it.
939        *
940        * dn_expand is guaranteed to not return more than sizeof(hostbuf)
941        * but do all implementations of dn_expand also guarantee
942        * buffer is terminated with null byte? Lets not take chances.
943        *  -Dianora
944        */
945       hostbuf[HOSTLEN] = '\0';
946       current += (size_t) n;
947
948       Debug((DEBUG_DNS, "Resolver: PTR %s", hostbuf));
949       /*
950        * copy the returned hostname into the host name
951        * ignore duplicate ptr records
952        */
953       if (!hp->h_name) {
954         strcpy(name, hostbuf);
955         hp->h_name = name;
956         name += strlen(name) + 1;
957       }
958       ++answer_count;
959       break;
960     case T_CNAME:
961       Debug((DEBUG_DNS, "Resolver: CNAME %s", hostbuf));
962       if (++alias_count < RES_MAXALIASES) {
963         ircd_strncpy(name, hostbuf, endp - name);
964         *alias++ = name;
965         *alias   = 0;
966         name += strlen(name) + 1;
967       }
968       current += rd_length;
969       ++answer_count;
970       break;
971     default :
972       Debug((DEBUG_DNS,"Resolver: proc_answer type: %d for: %s", type, hostbuf));
973       break;
974     }
975   }
976   return answer_count;
977 }
978
979 /*
980  * resolver_read - read a dns reply from the nameserver and process it.
981  * return 0 if nothing was read from the socket, otherwise return 1
982  */
983 int resolver_read(void)
984 {
985   u_char             buf[sizeof(HEADER) + MAXPACKET];
986   HEADER*            header       = 0;
987   struct ResRequest* request      = 0;
988   struct CacheEntry* cp           = 0;
989   unsigned int       rc           = 0;
990   int                answer_count = 0;
991   struct sockaddr_in sin;
992
993   Debug((DEBUG_DNS, "Resolver: read"));
994   if (IO_SUCCESS != os_recvfrom_nonb(ResolverFileDescriptor,
995                                      buf, sizeof(buf), &rc, &sin)) {
996     return 0;
997   }
998   if (rc < sizeof(HEADER)) {
999     Debug((DEBUG_DNS, "Resolver: short reply %d: %s", rc, strerror(errno)));
1000     return 0;
1001   }
1002   /*
1003    * convert DNS reply reader from Network byte order to CPU byte order.
1004    */
1005   header = (HEADER*) buf;
1006   /* header->id = ntohs(header->id); */
1007   header->ancount = ntohs(header->ancount);
1008   header->qdcount = ntohs(header->qdcount);
1009   header->nscount = ntohs(header->nscount);
1010   header->arcount = ntohs(header->arcount);
1011   ++reinfo.re_replies;
1012   /*
1013    * response for an id which we have already received an answer for
1014    * just ignore this response.
1015    */
1016   if (0 == (request = find_id(header->id))) {
1017     Debug((DEBUG_DNS, "Resolver: can't find request id: %d", header->id));
1018     return 1;
1019   }
1020   /*
1021    * check against possibly fake replies
1022    */
1023   if (!res_ourserver(&_res, &sin)) {
1024     Debug((DEBUG_DNS, "Resolver: fake reply from: %s",
1025            (const char*) &sin.sin_addr));
1026     ++reinfo.re_unkrep;
1027     return 1;
1028   }
1029
1030   if ((header->rcode != NOERROR) || (header->ancount == 0)) {
1031     ++reinfo.re_errors;
1032     if (SERVFAIL == header->rcode)
1033       resend_query(request);
1034     else {
1035       /*
1036        * If a bad error was returned, we stop here and dont send
1037        * send any more (no retries granted).
1038        * Isomer: Perhaps we should return these error messages back to
1039        *         the client?
1040        */
1041 #ifdef DEBUGMODE
1042       switch (header->rcode) {
1043         case NOERROR:
1044           Debug((DEBUG_DNS, "Fatal DNS error: No Error"));
1045           break;
1046         case FORMERR:
1047           Debug((DEBUG_DNS, "Fatal DNS error: Format Error"));
1048           break;
1049         case SERVFAIL:
1050           Debug((DEBUG_DNS, "Fatal DNS error: Server Failure"));
1051           break;
1052         case NXDOMAIN:
1053           Debug((DEBUG_DNS, "DNS error: Non Existant Domain"));
1054           break;
1055         case NOTIMP:
1056           Debug((DEBUG_DNS, "Fatal DNS error: Not Implemented"));
1057           break;
1058         case REFUSED:
1059           Debug((DEBUG_DNS, "Fatal DNS error: Query Refused"));
1060           break;
1061         default:
1062           Debug((DEBUG_DNS, "Unassigned fatal DNS error: %i", header->rcode));
1063           break;
1064       }
1065 #endif /* DEBUGMODE */
1066       (*request->query.callback)(request->query.vptr, 0);
1067       rem_request(request);
1068     } 
1069     return 1;
1070   }
1071   /*
1072    * If this fails there was an error decoding the received packet, 
1073    * try it again and hope it works the next time.
1074    */
1075   answer_count = proc_answer(request, header, buf, buf + rc);
1076   if (answer_count) {
1077     if (T_PTR == request->type) {
1078       struct DNSReply* reply = NULL;
1079       if (0 == request->he.h.h_name) {
1080         /*
1081          * got a PTR response with no name, something bogus is happening
1082          * don't bother trying again, the client address doesn't resolve 
1083          */
1084         (*request->query.callback)(request->query.vptr, reply);
1085         rem_request(request); 
1086         return 1;
1087       }
1088       Debug((DEBUG_DNS, "relookup %s <-> %s",
1089              request->he.h.h_name, ircd_ntoa((char*) &request->addr)));
1090       /*
1091        * Lookup the 'authoritive' name that we were given for the
1092        * ip#.  By using this call rather than regenerating the
1093        * type we automatically gain the use of the cache with no
1094        * extra kludges.
1095        */
1096       reply = gethost_byname(request->he.h.h_name, &request->query);
1097       if (0 == reply) {
1098         /*
1099          * If name wasn't found, a request has been queued and it will
1100          * be the last one queued.  This is rather nasty way to keep
1101          * a host alias with the query. -avalon
1102          */
1103         MyFree(requestListTail->he.buf);
1104         requestListTail->he.buf = request->he.buf;
1105         request->he.buf = 0;
1106         memcpy(&requestListTail->he.h, &request->he.h, sizeof(struct hostent));
1107       }
1108       else
1109         (*request->query.callback)(request->query.vptr, reply);
1110       rem_request(request);
1111     }
1112     else {
1113       /*
1114        * got a name and address response, client resolved
1115        * XXX - Bug found here by Dianora -
1116        * make_cache() occasionally returns a NULL pointer when a
1117        * PTR returned a CNAME, cp was not checked before so the
1118        * callback was being called with a value of 0x2C != NULL.
1119        */
1120       cp = make_cache(request);
1121       (*request->query.callback)(request->query.vptr,
1122                                  (cp) ? &cp->reply : 0);
1123       rem_request(request);
1124     }
1125   }
1126   else if (!request->sent) {
1127     /*
1128      * XXX - we got a response for a query we didn't send with a valid id?
1129      * this should never happen, bail here and leave the client unresolved
1130      */
1131     (*request->query.callback)(request->query.vptr, 0);
1132     rem_request(request);
1133   }
1134   return 1;
1135 }
1136
1137 /*
1138  * resolver_read_multiple - process up to count reads
1139  */
1140 void resolver_read_multiple(int count)
1141 {
1142   int i = 0;
1143   for ( ; i < count; ++i) {
1144     if (0 == resolver_read())
1145       return;
1146   }
1147 }
1148
1149 static size_t calc_hostent_buffer_size(const struct hostent* hp)
1150 {
1151   char** p;
1152   size_t count = 0;
1153   assert(0 != hp);
1154
1155   /*
1156    * space for name
1157    */
1158   count += (strlen(hp->h_name) + 1);
1159   /*
1160    * space for aliases
1161    */
1162   for (p = hp->h_aliases; *p; ++p)
1163     count += (strlen(*p) + 1 + sizeof(char*));
1164   /*
1165    * space for addresses
1166    */
1167   for (p = hp->h_addr_list; *p; ++p)
1168     count += (hp->h_length + sizeof(char*));
1169   /*
1170    * space for 2 nulls to terminate h_aliases and h_addr_list 
1171    */
1172   count += (2 * sizeof(char*));
1173   return count;
1174 }
1175
1176
1177 /*
1178  * dup_hostent - Duplicate a hostent struct, allocate only enough memory for
1179  * the data we're putting in it.
1180  */
1181 static void dup_hostent(aHostent* new_hp, struct hostent* hp)
1182 {
1183   char*  p;
1184   char** ap;
1185   char** pp;
1186   int    alias_count = 0;
1187   int    addr_count = 0;
1188   size_t bytes_needed = 0;
1189
1190   assert(0 != new_hp);
1191   assert(0 != hp);
1192
1193   /* how much buffer do we need? */
1194   bytes_needed += (strlen(hp->h_name) + 1);
1195
1196   pp = hp->h_aliases;
1197   while (*pp) {
1198     bytes_needed += (strlen(*pp++) + 1 + sizeof(char*));
1199     ++alias_count;
1200   }
1201   pp = hp->h_addr_list;
1202   while (*pp++) {
1203     bytes_needed += (hp->h_length + sizeof(char*));
1204     ++addr_count;
1205   }
1206   /* Reserve space for 2 nulls to terminate h_aliases and h_addr_list */
1207   bytes_needed += (2 * sizeof(char*));
1208
1209   /* Allocate memory */
1210   new_hp->buf = (char*) MyMalloc(bytes_needed);
1211
1212   new_hp->h.h_addrtype = hp->h_addrtype;
1213   new_hp->h.h_length = hp->h_length;
1214
1215   /* first write the address list */
1216   pp = hp->h_addr_list;
1217   ap = new_hp->h.h_addr_list =
1218       (char**)(new_hp->buf + ((alias_count + 1) * sizeof(char*)));
1219   p = (char*)ap + ((addr_count + 1) * sizeof(char*));
1220   while (*pp)
1221   {
1222     *ap++ = p;
1223     memcpy(p, *pp++, hp->h_length);
1224     p += hp->h_length;
1225   }
1226   *ap = 0;
1227   /* next write the name */
1228   new_hp->h.h_name = p;
1229   strcpy(p, hp->h_name);
1230   p += (strlen(p) + 1);
1231
1232   /* last write the alias list */
1233   pp = hp->h_aliases;
1234   ap = new_hp->h.h_aliases = (char**) new_hp->buf;
1235   while (*pp) {
1236     *ap++ = p;
1237     strcpy(p, *pp++);
1238     p += (strlen(p) + 1);
1239   }
1240   *ap = 0;
1241 }
1242
1243 /*
1244  * update_hostent - Add records to a Hostent struct in place.
1245  */
1246 static void update_hostent(aHostent* hp, char** addr, char** alias)
1247 {
1248   char*  p;
1249   char** ap;
1250   char** pp;
1251   int    alias_count = 0;
1252   int    addr_count = 0;
1253   char*  buf = NULL;
1254   size_t bytes_needed = 0;
1255
1256   if (!hp || !hp->buf)
1257     return;
1258
1259   /* how much buffer do we need? */
1260   bytes_needed = strlen(hp->h.h_name) + 1;
1261   pp = hp->h.h_aliases;
1262   while (*pp) {
1263     bytes_needed += (strlen(*pp++) + 1 + sizeof(char*));
1264     ++alias_count;
1265   }
1266   if (alias) {
1267     pp = alias;
1268     while (*pp) {
1269       bytes_needed += (strlen(*pp++) + 1 + sizeof(char*));
1270       ++alias_count;
1271     }
1272   }
1273   pp = hp->h.h_addr_list;
1274   while (*pp++) {
1275     bytes_needed += (hp->h.h_length + sizeof(char*));
1276     ++addr_count;
1277   }
1278   if (addr) {
1279     pp = addr;
1280     while (*pp++) {
1281       bytes_needed += (hp->h.h_length + sizeof(char*));
1282       ++addr_count;
1283     }
1284   }
1285   /* Reserve space for 2 nulls to terminate h_aliases and h_addr_list */
1286   bytes_needed += 2 * sizeof(char*);
1287
1288   /* Allocate memory */
1289   buf = (char*) MyMalloc(bytes_needed);
1290   assert(0 != buf);
1291
1292   /* first write the address list */
1293   pp = hp->h.h_addr_list;
1294   ap = hp->h.h_addr_list =
1295       (char**)(buf + ((alias_count + 1) * sizeof(char*)));
1296   p = (char*)ap + ((addr_count + 1) * sizeof(char*));
1297   while (*pp) {
1298     memcpy(p, *pp++, hp->h.h_length);
1299     *ap++ = p;
1300     p += hp->h.h_length;
1301   }
1302   if (addr) {
1303     while (*addr) {
1304       memcpy(p, *addr++, hp->h.h_length);
1305       *ap++ = p;
1306       p += hp->h.h_length;
1307     }
1308   }
1309   *ap = 0;
1310
1311   /* next write the name */
1312   strcpy(p, hp->h.h_name);
1313   hp->h.h_name = p;
1314   p += (strlen(p) + 1);
1315
1316   /* last write the alias list */
1317   pp = hp->h.h_aliases;
1318   ap = hp->h.h_aliases = (char**) buf;
1319   while (*pp) {
1320     strcpy(p, *pp++);
1321     *ap++ = p;
1322     p += (strlen(p) + 1);
1323   }
1324   if (alias) {
1325     while (*alias) {
1326       strcpy(p, *alias++);
1327       *ap++ = p;
1328       p += (strlen(p) + 1);
1329     }
1330   }
1331   *ap = 0;
1332   /* release the old buffer */
1333   p = hp->buf;
1334   hp->buf = buf;
1335   MyFree(p);
1336 }
1337
1338 /*
1339  * hash_number - IP address hash function
1340  */
1341 static int hash_number(const unsigned char* ip)
1342 {
1343   /* could use loop but slower */
1344   unsigned int hashv;
1345   const u_char* p = (const u_char*) ip;
1346
1347   assert(0 != p);
1348
1349   hashv = *p++;
1350   hashv += hashv + *p++;
1351   hashv += hashv + *p++;
1352   hashv += hashv + *p;
1353   hashv %= ARES_CACSIZE;
1354   return hashv;
1355 }
1356
1357 /*
1358  * hash_name - hostname hash function
1359  */
1360 static int hash_name(const char* name)
1361 {
1362   unsigned int hashv = 0;
1363   const u_char* p = (const u_char*) name;
1364
1365   assert(0 != p);
1366
1367   for (; *p && *p != '.'; ++p)
1368     hashv += *p;
1369   hashv %= ARES_CACSIZE;
1370   return hashv;
1371 }
1372
1373 /*
1374  * add_to_cache - Add a new cache item to the queue and hash table.
1375  */
1376 static struct CacheEntry* add_to_cache(struct CacheEntry* ocp)
1377 {
1378   int  hashv;
1379
1380   assert(0 != ocp);
1381
1382   ocp->list_next = cacheTop;
1383   cacheTop = ocp;
1384
1385   hashv = hash_name(ocp->he.h.h_name);
1386
1387   ocp->hname_next = hashtable[hashv].name_list;
1388   hashtable[hashv].name_list = ocp;
1389
1390   hashv = hash_number(ocp->he.h.h_addr);
1391
1392   ocp->hnum_next = hashtable[hashv].num_list;
1393   hashtable[hashv].num_list = ocp;
1394
1395   /*
1396    * LRU deletion of excessive cache entries.
1397    */
1398   if (++cachedCount > MAXCACHED) {
1399     struct CacheEntry* cp;
1400     struct CacheEntry* cp_next;
1401     for (cp = ocp->list_next; cp; cp = cp_next) {
1402       cp_next = cp->list_next;
1403       rem_cache(cp);
1404     }
1405   }
1406   ++cainfo.ca_adds;
1407   return ocp;
1408 }
1409
1410 /*
1411  * update_list - does not alter the cache structure passed. It is assumed that
1412  * it already contains the correct expire time, if it is a new entry. Old
1413  * entries have the expirey time updated.
1414 */
1415 static void update_list(struct ResRequest* request, struct CacheEntry* cachep)
1416 {
1417 #if 0
1418   struct CacheEntry** cpp;
1419 #endif
1420   struct CacheEntry*  cp = cachep;
1421   char*    s;
1422   char*    t;
1423   int      i;
1424   int      j;
1425   char**   ap;
1426   char*    addrs[RES_MAXADDRS + 1];
1427   char*    aliases[RES_MAXALIASES + 1];
1428
1429   /*
1430    * search for the new cache item in the cache list by hostname.
1431    * If found, move the entry to the top of the list and return.
1432    */
1433   ++cainfo.ca_updates;
1434 #if 0
1435   for (cpp = &cacheTop; *cpp; cpp = &((*cpp)->list_next)) {
1436     if (cp == *cpp)
1437       break;
1438   }
1439   if (!*cpp)
1440     return;
1441   *cpp = cp->list_next;
1442   cp->list_next = cacheTop;
1443   cacheTop = cp;
1444 #endif
1445
1446   if (!request)
1447     return;
1448   /*
1449    * Compare the cache entry against the new record.  Add any
1450    * previously missing names for this entry.
1451    */
1452   *aliases = 0;
1453   ap = aliases;
1454   for (i = 0, s = request->he.h.h_name; s; s = request->he.h.h_aliases[i++]) {
1455     for (j = 0, t = cp->he.h.h_name; t; t = cp->he.h.h_aliases[j++]) {
1456       if (0 == ircd_strcmp(t, s))
1457         break;
1458     }
1459     if (!t) {
1460       *ap++ = s;
1461       *ap = 0;
1462     }
1463   }
1464   /*
1465    * Do the same again for IP#'s.
1466    */
1467   *addrs = 0;
1468   ap = addrs;
1469   for (i = 0; (s = request->he.h.h_addr_list[i]); i++) {
1470     for (j = 0; (t = cp->he.h.h_addr_list[j]); j++) {
1471       if (!memcmp(t, s, sizeof(struct in_addr)))
1472         break;
1473     }
1474     if (!t) {
1475       *ap++ = s;
1476       *ap = 0;
1477     }
1478   }
1479   if (*addrs || *aliases)
1480     update_hostent(&cp->he, addrs, aliases);
1481 }
1482
1483 /*
1484  * find_cache_name - find name in nameserver cache
1485  */
1486 static struct CacheEntry* find_cache_name(const char* name)
1487 {
1488   struct CacheEntry* cp;
1489   char*   s;
1490   int     hashv;
1491   int     i;
1492
1493   assert(0 != name);
1494   hashv = hash_name(name);
1495
1496   cp = hashtable[hashv].name_list;
1497
1498   for (; cp; cp = cp->hname_next) {
1499     for (i = 0, s = cp->he.h.h_name; s; s = cp->he.h.h_aliases[i++]) {
1500       if (0 == ircd_strcmp(s, name)) {
1501         ++cainfo.ca_na_hits;
1502         return cp;
1503       }
1504     }
1505   }
1506
1507   for (cp = cacheTop; cp; cp = cp->list_next) {
1508     /*
1509      * if no aliases or the hash value matches, we've already
1510      * done this entry and all possiblilities concerning it.
1511      */
1512     if (!cp->he.h.h_name || hashv == hash_name(cp->he.h.h_name))
1513       continue;
1514     for (i = 0, s = cp->he.h.h_aliases[i]; s; s = cp->he.h.h_aliases[++i]) {
1515       if (0 == ircd_strcmp(name, s)) {
1516         ++cainfo.ca_na_hits;
1517         return cp;
1518       }
1519     }
1520   }
1521   return NULL;
1522 }
1523
1524 /*
1525  * find_cache_number - find a cache entry by ip# and update its expire time
1526  */
1527 static struct CacheEntry* find_cache_number(struct ResRequest* request,
1528                                             const char* addr)
1529 {
1530   struct CacheEntry* cp;
1531   int     hashv;
1532   int     i;
1533
1534   assert(0 != addr);
1535   hashv = hash_number(addr);
1536   cp = hashtable[hashv].num_list;
1537
1538   for (; cp; cp = cp->hnum_next) {
1539     for (i = 0; cp->he.h.h_addr_list[i]; ++i) {
1540       if (!memcmp(cp->he.h.h_addr_list[i], addr, sizeof(struct in_addr))) {
1541         ++cainfo.ca_nu_hits;
1542         return cp;
1543       }
1544     }
1545   }
1546   for (cp = cacheTop; cp; cp = cp->list_next) {
1547     /*
1548      * single address entry...would have been done by hashed
1549      * search above...
1550      * if the first IP# has the same hashnumber as the IP# we
1551      * are looking for, its been done already.
1552      */
1553     if (!cp->he.h.h_addr_list[1] || 
1554         hashv == hash_number(cp->he.h.h_addr_list[0]))
1555       continue;
1556     for (i = 1; cp->he.h.h_addr_list[i]; ++i) {
1557       if (!memcmp(cp->he.h.h_addr_list[i], addr, sizeof(struct in_addr))) {
1558         ++cainfo.ca_nu_hits;
1559         return cp;
1560       }
1561     }
1562   }
1563   return NULL;
1564 }
1565
1566 static struct CacheEntry* make_cache(struct ResRequest* request)
1567 {
1568   struct CacheEntry* cp;
1569   int     i;
1570   struct hostent* hp;
1571   assert(0 != request);
1572
1573   hp = &request->he.h;
1574   /*
1575    * shouldn't happen but it just might...
1576    */
1577   assert(0 != hp->h_name);
1578   assert(0 != hp->h_addr_list[0]);
1579   if (!hp->h_name || !hp->h_addr_list[0])
1580     return NULL;
1581   /*
1582    * Make cache entry.  First check to see if the cache already exists
1583    * and if so, return a pointer to it.
1584    */
1585   for (i = 0; hp->h_addr_list[i]; ++i) {
1586     if ((cp = find_cache_number(request, hp->h_addr_list[i]))) {
1587       update_list(request, cp);
1588       return cp;
1589     }
1590   }
1591   /*
1592    * a matching entry wasnt found in the cache so go and make one up.
1593    */ 
1594   cp = (struct CacheEntry*) MyMalloc(sizeof(struct CacheEntry));
1595   assert(0 != cp);
1596
1597   memset(cp, 0, sizeof(struct CacheEntry));
1598   dup_hostent(&cp->he, hp);
1599   cp->reply.hp = &cp->he.h;
1600   /*
1601    * hmmm... we could time out the cache after 10 minutes regardless
1602    * would that be reasonable since we don't save the reply?
1603    */ 
1604   if (request->ttl < AR_TTL) {
1605     ++reinfo.re_shortttl;
1606     cp->ttl = AR_TTL;
1607   }
1608   else
1609     cp->ttl = request->ttl;
1610   cp->expireat = CurrentTime + cp->ttl;
1611   return add_to_cache(cp);
1612 }
1613
1614 /*
1615  * rem_cache - delete a cache entry from the cache structures 
1616  * and lists and return all memory used for the cache back to the memory pool.
1617  */
1618 static void rem_cache(struct CacheEntry* ocp)
1619 {
1620   struct CacheEntry** cp;
1621   int                 hashv;
1622   struct hostent*     hp;
1623   assert(0 != ocp);
1624
1625
1626   if (0 < ocp->reply.ref_count) {
1627     if (ocp->expireat < CurrentTime) {
1628       ocp->expireat = CurrentTime + AR_TTL;
1629       Debug((DEBUG_DNS, "Resolver: referenced cache entry not removed for: %s",
1630             ocp->he.h.h_name));
1631     }
1632     return;
1633   }
1634   /*
1635    * remove cache entry from linked list
1636    */
1637   for (cp = &cacheTop; *cp; cp = &((*cp)->list_next)) {
1638     if (*cp == ocp) {
1639       *cp = ocp->list_next;
1640       break;
1641     }
1642   }
1643   hp = &ocp->he.h;
1644   /*
1645    * remove cache entry from hashed name list
1646    */
1647   assert(0 != hp->h_name);
1648   hashv = hash_name(hp->h_name);
1649
1650   for (cp = &hashtable[hashv].name_list; *cp; cp = &((*cp)->hname_next)) {
1651     if (*cp == ocp) {
1652       *cp = ocp->hname_next;
1653       break;
1654     }
1655   }
1656   /*
1657    * remove cache entry from hashed number list
1658    */
1659   hashv = hash_number(hp->h_addr);
1660   assert(-1 < hashv);
1661
1662   for (cp = &hashtable[hashv].num_list; *cp; cp = &((*cp)->hnum_next)) {
1663     if (*cp == ocp) {
1664       *cp = ocp->hnum_next;
1665       break;
1666     }
1667   }
1668   /*
1669    * free memory used to hold the various host names and the array
1670    * of alias pointers.
1671    */
1672   MyFree(ocp->he.buf);
1673   MyFree(ocp);
1674   --cachedCount;
1675   ++cainfo.ca_dels;
1676 }
1677
1678 void flush_resolver_cache(void)
1679 {
1680   /*
1681    * stubbed - iterate cache and remove everything that isn't referenced
1682    */
1683 }
1684
1685 /*
1686  * m_dns - dns status query
1687  */
1688 int m_dns(struct Client *cptr, struct Client *sptr, int parc, char *parv[])
1689 {
1690 #if !defined(NDEBUG)
1691   struct CacheEntry* cp;
1692   int     i;
1693   struct hostent* hp;
1694
1695   if (parv[1] && *parv[1] == 'l') {
1696     for(cp = cacheTop; cp; cp = cp->list_next) {
1697       hp = &cp->he.h;
1698       sendto_one(sptr, "NOTICE %s :Ex %d ttl %d host %s(%s)",
1699                  parv[0], cp->expireat - CurrentTime, cp->ttl,
1700                  hp->h_name, ircd_ntoa(hp->h_addr));
1701       for (i = 0; hp->h_aliases[i]; i++)
1702         sendto_one(sptr,"NOTICE %s : %s = %s (CN)",
1703                    parv[0], hp->h_name, hp->h_aliases[i]);
1704       for (i = 1; hp->h_addr_list[i]; i++)
1705         sendto_one(sptr,"NOTICE %s : %s = %s (IP)",
1706                    parv[0], hp->h_name, ircd_ntoa(hp->h_addr_list[i]));
1707     }
1708     return 0;
1709   }
1710   if (parv[1] && *parv[1] == 'd') {
1711     sendto_one(sptr, "NOTICE %s :ResolverFileDescriptor = %d", 
1712                parv[0], ResolverFileDescriptor);
1713     return 0;
1714   }
1715   sendto_one(sptr,"NOTICE %s :Ca %d Cd %d Ce %d Cl %d Ch %d:%d Cu %d",
1716              sptr->name,
1717              cainfo.ca_adds, cainfo.ca_dels, cainfo.ca_expires,
1718              cainfo.ca_lookups, cainfo.ca_na_hits, cainfo.ca_nu_hits, 
1719              cainfo.ca_updates);
1720   
1721   sendto_one(sptr,"NOTICE %s :Re %d Rl %d/%d Rp %d Rq %d",
1722              sptr->name, reinfo.re_errors, reinfo.re_nu_look,
1723              reinfo.re_na_look, reinfo.re_replies, reinfo.re_requests);
1724   sendto_one(sptr,"NOTICE %s :Ru %d Rsh %d Rs %d(%d) Rt %d", sptr->name,
1725              reinfo.re_unkrep, reinfo.re_shortttl, reinfo.re_sent,
1726              reinfo.re_resends, reinfo.re_timeouts);
1727 #endif
1728   return 0;
1729 }
1730
1731 size_t cres_mem(struct Client* sptr)
1732 {
1733   struct CacheEntry* entry;
1734   struct ResRequest* request;
1735   size_t cache_mem     = 0;
1736   size_t request_mem   = 0;
1737   int    cache_count   = 0;
1738   int    request_count = 0;
1739
1740   for (entry = cacheTop; entry; entry = entry->list_next) {
1741     cache_mem += sizeof(struct CacheEntry);
1742     cache_mem += calc_hostent_buffer_size(&entry->he.h); 
1743     ++cache_count;
1744   }
1745   for (request = requestListHead; request; request = request->next) {
1746     request_mem += sizeof(struct ResRequest);
1747     if (request->name)
1748       request_mem += strlen(request->name) + 1; 
1749     if (request->he.buf)
1750       request_mem += MAXGETHOSTLEN + 1;
1751     ++request_count;
1752   }
1753   if (cachedCount != cache_count) {
1754     sendto_one(sptr, 
1755                ":%s %d %s :Resolver: cache count mismatch: %d != %d",
1756                me.name, RPL_STATSDEBUG, sptr->name, cachedCount, cache_count);
1757     assert(cachedCount == cache_count);
1758   }
1759   sendto_one(sptr, ":%s %d %s :Resolver: cache %d(%d) requests %d(%d)",
1760              me.name, RPL_STATSDEBUG, sptr->name, cache_count, cache_mem,
1761              request_count, request_mem);
1762   return cache_mem + request_mem;
1763 }
1764