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