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