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