cf0d68b86e0a00711bccc6e3988663e5dd0ffccb
[ircu2.10.12-pk.git] / ircd / res_adns.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 "adns.h"
16 #include "res.h"
17 #include "client.h"
18 #include "ircd.h"
19 #include "ircd_alloc.h"
20 #include "ircd_events.h"
21 #include "ircd_log.h"
22 #include "ircd_osdep.h"
23 #include "ircd_reply.h"
24 #include "ircd_snprintf.h"
25 #include "ircd_string.h"
26 #include "msg.h"
27 #include "numeric.h"
28 #include "s_bsd.h"
29 #include "s_debug.h"
30 #include "s_misc.h"
31 #include "send.h"
32 #include "struct.h"
33 #include "support.h"
34 #include "sys.h"
35
36 #include <assert.h>
37 #include <errno.h>
38 #include <string.h>
39 #include <stdlib.h>
40 #include <sys/time.h>
41 #include <sys/types.h>
42 #include <sys/socket.h>
43 #include <fcntl.h>
44 #include <unistd.h>
45 #include <regex.h>
46
47 #include <arpa/nameser.h>
48 #include <resolv.h>
49 #include <netdb.h>
50 #include <arpa/inet.h>
51
52 #include <limits.h>
53 #if (CHAR_BIT != 8)
54 #error this code needs to be able to address individual octets 
55 #endif
56
57 /*
58  * Some systems do not define INADDR_NONE (255.255.255.255)
59  * INADDR_NONE is actually a valid address, but it should never
60  * be returned from any nameserver.
61  * NOTE: The bit pattern for INADDR_NONE and INADDR_ANY (0.0.0.0) should be 
62  * the same on all hosts so we shouldn't need to use htonl or ntohl to
63  * compare or set the values.
64  */ 
65 #ifndef INADDR_NONE
66 #define INADDR_NONE ((unsigned int) 0xffffffff)
67 #endif
68
69 #define MAXPACKET       1024  /* rfc sez 512 but we expand names so ... */
70 #define RES_MAXALIASES  35    /* maximum aliases allowed */
71 #define RES_MAXADDRS    35    /* maximum addresses allowed */
72 /*
73  * OSF1 doesn't have RES_NOALIASES
74  */
75 #ifndef RES_NOALIASES
76 #define RES_NOALIASES 0
77 #endif
78
79 /*
80  * macros used to calulate offsets into fixed query buffer
81  */
82 #define ALIAS_BLEN  ((RES_MAXALIASES + 1) * sizeof(char*))
83 #define ADDRS_BLEN  ((RES_MAXADDRS + 1) * sizeof(struct in_addr*))
84
85 #define ADDRS_OFFSET   (ALIAS_BLEN + ADDRS_BLEN)
86 #define ADDRS_DLEN     (RES_MAXADDRS * sizeof(struct in_addr))
87 #define NAMES_OFFSET   (ADDRS_OFFSET + ADDRS_DLEN)
88 #define MAXGETHOSTLEN  (NAMES_OFFSET + MAXPACKET)
89
90 #define AR_TTL          600   /* TTL in seconds for dns cache entries */
91
92 /*
93  * the following values should be prime
94  */
95 #define ARES_CACSIZE    307
96 #define MAXCACHED       17
97 //#define MAXCACHED       281
98
99 /*
100  * Building the Hostent
101  * The Hostent struct is arranged like this:
102  *          +-------------------------------+
103  * Hostent: | struct hostent h              |
104  *          |-------------------------------|
105  *          | char *buf                     |
106  *          +-------------------------------+
107  *
108  * allocated:
109  *
110  *          +-------------------------------+
111  * buf:     | h_aliases pointer array       | Max size: ALIAS_BLEN;
112  *          | NULL                          | contains `char *'s
113  *          |-------------------------------|
114  *          | h_addr_list pointer array     | Max size: ADDRS_BLEN;
115  *          | NULL                          | contains `struct in_addr *'s
116  *          |-------------------------------|
117  *          | h_addr_list addresses         | Max size: ADDRS_DLEN;
118  *          |                               | contains `struct in_addr's
119  *          |-------------------------------|
120  *          | storage for hostname strings  | Max size: ALIAS_DLEN;
121  *          +-------------------------------+ contains `char's
122  *
123  *  For requests the size of the h_aliases, and h_addr_list pointer
124  *  array sizes are set to MAXALISES and MAXADDRS respectively, and
125  *  buf is a fixed size with enough space to hold the largest expected
126  *  reply from a nameserver, see RFC 1034 and RFC 1035.
127  *  For cached entries the sizes are dependent on the actual number
128  *  of aliases and addresses. If new aliases and addresses are found
129  *  for cached entries, the buffer is grown and the new entries are added.
130  *  The hostent struct is filled in with the addresses of the entries in
131  *  the Hostent buf as follows:
132  *  h_name      - contains a pointer to the start of the hostname string area,
133  *                or NULL if none is set.  The h_name is followed by the
134  *                aliases, in the storage for hostname strings area.
135  *  h_aliases   - contains a pointer to the start of h_aliases pointer array.
136  *                This array contains pointers to the storage for hostname
137  *                strings area and is terminated with a NULL.  The first alias
138  *                is stored directly after the h_name.
139  *  h_addr_list - contains a pointer to the start of h_addr_list pointer array.
140  *                This array contains pointers to in_addr structures in the
141  *                h_addr_list addresses area and is terminated with a NULL.
142  *
143  *  Filling the buffer this way allows for proper alignment of the h_addr_list
144  *  addresses.
145  *
146  *  This arrangement allows us to alias a Hostent struct pointer as a
147  *  real struct hostent* without lying. It also allows us to change the
148  *  values contained in the cached entries and requests without changing
149  *  the actual hostent pointer, which is saved in a client struct and can't
150  *  be changed without blowing things up or a lot more fiddling around.
151  *  It also allows for defered allocation of the fixed size buffers until
152  *  they are really needed.
153  *  Nov. 17, 1997 --Bleep
154  */
155
156 struct Hostent {
157   struct hostent h;        /* the hostent struct we are passing around */
158   char           buf[1];   /* buffer for data pointed to from hostent */
159 };
160
161 struct ResRequest {
162   struct ResRequest* next;
163
164   adns_rrtype        type;
165   adns_query         aq;
166
167   struct in_addr     addr;
168   char*              name;
169   struct DNSQuery    query;         /* query callback for this request */
170   struct hostent     he;
171   char*              buf;
172 };
173
174 int ResolverFileDescriptor    = -1;   /* GLOBAL - used in s_bsd.c */
175
176 static struct Socket resSock;           /* Socket describing resolver */
177 static struct Timer  resExpireDNS;      /* Timer for DNS expiration */
178 static adns_state adns = NULL;
179
180 /*
181  * Keep a spare file descriptor open. res_init calls fopen to read the
182  * resolv.conf file. If ircd is hogging all the file descriptors below 256,
183  * on systems with crippled FILE structures this will cause wierd bugs.
184  * This is definitely needed for Solaris which uses an unsigned char to
185  * hold the file descriptor.  --Dianora
186  */ 
187 static int                spare_fd = -1;
188
189 static struct ResRequest* requestListHead;   /* head of resolver request list */
190 static struct ResRequest* requestListTail;   /* tail of resolver request list */
191
192
193 static void     add_request(struct ResRequest* request);
194 static void     rem_request(struct ResRequest* request);
195 static struct ResRequest*   make_request(const struct DNSQuery* query);
196 static time_t   timeout_query_list(time_t now);
197 static void     do_query_name(const struct DNSQuery* query, 
198                               const char* name, 
199                               struct ResRequest* request);
200 static void     do_query_number(const struct DNSQuery* query,
201                                 const struct in_addr*, 
202                                 struct ResRequest* request);
203 static void res_adns_callback(adns_state state, adns_query q, void *context);
204
205 static struct  resinfo {
206   int  re_errors;
207   int  re_nu_look;
208   int  re_na_look;
209   int  re_replies;
210   int  re_requests;
211   int  re_resends;
212   int  re_sent;
213   int  re_timeouts;
214   int  re_shortttl;
215   int  re_unkrep;
216 } reinfo;
217
218
219 /*
220  * int
221  * res_isourserver(ina)
222  *      looks up "ina" in _res.ns_addr_list[]
223  * returns:
224  *      0  : not found
225  *      >0 : found
226  * author:
227  *      paul vixie, 29may94
228  */
229 static int
230 res_ourserver(const struct __res_state* statp, const struct sockaddr_in* inp) 
231 {
232   struct sockaddr_in ina;
233   int ns;
234
235   ina = *inp;
236   for (ns = 0;  ns < statp->nscount;  ns++) {
237     const struct sockaddr_in *srv = &statp->nsaddr_list[ns];
238
239     if (srv->sin_family == ina.sin_family &&
240          srv->sin_port == ina.sin_port &&
241          (srv->sin_addr.s_addr == INADDR_ANY ||
242           srv->sin_addr.s_addr == ina.sin_addr.s_addr))
243              return (1);
244   }
245   return (0);
246 }
247
248 /* Socket callback for resolver */
249 static void res_socket_callback(struct Event* ev)
250 {
251   struct timeval tv;
252
253   assert(ev_type(ev) == ET_READ || ev_type(ev) == ET_ERROR);
254
255   tv.tv_sec = CurrentTime;
256   tv.tv_usec = 0;
257   adns_processreadable(adns, ResolverFileDescriptor, &tv);
258 }
259
260 /*
261  * start_resolver - do everything we need to read the resolv.conf file
262  * and initialize the resolver file descriptor if needed
263  */
264 static void start_resolver(void)
265 {
266   int res;
267
268   Debug((DEBUG_DNS, "Resolver: start_resolver"));
269   /*
270    * close the spare file descriptor so res_init can read resolv.conf
271    * successfully. Needed on Solaris
272    */
273   if (spare_fd > -1)
274     close(spare_fd);
275
276   /*
277    * make sure we have a valid file descriptor below 256 so we can
278    * do this again. Needed on Solaris
279    */
280   spare_fd = open("/dev/null",O_RDONLY,0);
281   if ((spare_fd < 0) || (spare_fd > 255)) {
282     char sparemsg[80];
283     ircd_snprintf(0, sparemsg, sizeof(sparemsg), "invalid spare_fd %d",
284                   spare_fd);
285     server_restart(sparemsg);
286   }
287
288   if (adns)
289     return;
290
291   if ((res = adns_init(&adns, adns_if_debug /*| adns_if_noautosys*/, NULL))) {
292     report_error("Resolver: error initializing adns for %s: %s",
293                  cli_name(&me), res);
294     errno = res;
295     return;
296   }
297   errno = 0;
298   
299   ResolverFileDescriptor = adns_get_fd(adns);
300
301   if (!socket_add(&resSock, res_socket_callback, 0, SS_DATAGRAM,
302                   SOCK_EVENT_READABLE, ResolverFileDescriptor))
303     report_error("Resolver: unable to queue resolver file descriptor for %s",
304                  cli_name(&me), ENFILE);
305 }
306
307 /* Call the query timeout function */
308 static void expire_DNS_callback(struct Event* ev)
309 {
310   time_t next;
311
312   next = timeout_query_list(CurrentTime);
313
314   timer_add(&resExpireDNS, expire_DNS_callback, 0, TT_ABSOLUTE, next);
315 }
316
317 /*
318  * init_resolver - initialize resolver and resolver library
319  */
320 int init_resolver(void)
321 {
322   Debug((DEBUG_DNS, "Resolver: init_resolver"));
323 #ifdef  LRAND48
324   srand48(CurrentTime);
325 #endif
326   memset(&reinfo,   0, sizeof(reinfo));
327
328   requestListHead = requestListTail = 0;
329
330   /* initiate the resolver timers */
331   timer_add(timer_init(&resExpireDNS), expire_DNS_callback, 0,
332             TT_RELATIVE, 1);
333
334   errno = h_errno = 0;
335
336   start_resolver();
337   Debug((DEBUG_DNS, "Resolver: fd %d errno: %d h_errno: %d: %s",
338          ResolverFileDescriptor, errno, h_errno, 
339          (strerror(errno)) ? strerror(errno) : "Unknown"));
340   return ResolverFileDescriptor;
341 }
342
343 /*
344  * restart_resolver - flush the cache, reread resolv.conf, reopen socket
345  */
346 void restart_resolver(void)
347 {
348   start_resolver();
349 }
350
351 /*
352  * add_request - place a new request in the request list
353  */
354 static void add_request(struct ResRequest* request)
355 {
356   assert(0 != request);
357   if (!requestListHead)
358     requestListHead = requestListTail = request;
359   else {
360     requestListTail->next = request;
361     requestListTail = request;
362   }
363   request->next = NULL;
364   ++reinfo.re_requests;
365 }
366
367 /*
368  * rem_request - remove a request from the list. 
369  * This must also free any memory that has been allocated for 
370  * temporary storage of DNS results.
371  */
372 static void rem_request(struct ResRequest* request)
373 {
374   struct ResRequest** current;
375   struct ResRequest*  prev = NULL;
376
377   assert(0 != request);
378   for (current = &requestListHead; *current; ) {
379     if (*current == request) {
380       *current = request->next;
381       if (requestListTail == request)
382         requestListTail = prev;
383       break;
384     } 
385     prev    = *current;
386     current = &(*current)->next;
387   }
388   if (request->aq)
389     adns_cancel(request->aq);
390   MyFree(request->buf);
391   MyFree(request->name);
392   MyFree(request);
393 }
394
395 /*
396  * make_request - Create a DNS request record for the server.
397  */
398 static struct ResRequest* make_request(const struct DNSQuery* query)
399 {
400   struct ResRequest* request;
401   assert(0 != query);
402   request = (struct ResRequest*) MyMalloc(sizeof(struct ResRequest));
403   memset(request, 0, sizeof(struct ResRequest));
404
405   request->addr.s_addr    = INADDR_NONE;
406   request->he.h_addrtype  = AF_INET;
407   request->he.h_length    = sizeof(struct in_addr);
408   request->query.vptr     = query->vptr;
409   request->query.callback = query->callback;
410
411   add_request(request);
412   return request;
413 }
414
415 /*
416  * timeout_query_list - Remove queries from the list which have been 
417  * there too long without being resolved.
418  */
419 static time_t timeout_query_list(time_t now)
420 {
421   struct timeval tv, tv_buf, *tv_mod = NULL;
422   time_t next;
423   
424   Debug((DEBUG_DNS, "Resolver: timeout_query_list at %s", myctime(now)));
425
426   tv.tv_sec = now;
427   tv.tv_usec = 0;
428   adns_processtimeouts(adns, &tv);
429   adns_firsttimeout(adns, &tv_mod, &tv_buf, tv);
430   next = tv_mod ? tv_mod->tv_sec : AR_TTL;
431   if (!next)
432     next = 1;
433
434   Debug((DEBUG_DNS, "Resolver: next timeout_query_list in %d seconds", next));
435   return now + next;
436 }
437
438 /*
439  * delete_resolver_queries - cleanup outstanding queries 
440  * for which there no longer exist clients or conf lines.
441  */
442 void delete_resolver_queries(const void* vptr)
443 {
444   struct ResRequest* request;
445   struct ResRequest* next_request;
446
447   for (request = requestListHead; request; request = next_request) {
448     next_request = request->next;
449     if (vptr == request->query.vptr)
450       rem_request(request);
451   }
452 }
453
454 /*
455  * gethost_byname - get host address from name
456  */
457 void gethost_byname(const char* name, const struct DNSQuery* query)
458 {
459   assert(0 != name);
460
461   Debug((DEBUG_DNS, "Resolver: gethost_byname %s", name));
462   ++reinfo.re_na_look;
463   do_query_name(query, name, NULL);
464 }
465
466 /*
467  * gethost_byaddr - get host name from address
468  */
469 void gethost_byaddr(const char* addr, const struct DNSQuery* query)
470 {
471   assert(0 != addr);
472   Debug((DEBUG_DNS, "Resolver: gethost_byaddr %s", ircd_ntoa(addr)));
473   ++reinfo.re_nu_look;
474   do_query_number(query, (const struct in_addr*) addr, NULL);
475 }
476
477 /*
478  * do_query_name - nameserver lookup name
479  */
480 static void do_query_name(const struct DNSQuery* query, 
481                           const char* name, struct ResRequest* request)
482 {
483   char  hname[HOSTLEN + 1];
484   int   res;
485   assert(0 != name);
486
487   ircd_strncpy(hname, name, HOSTLEN);
488   hname[HOSTLEN] = '\0';
489
490   if (!request) {
491     request       = make_request(query);
492     request->type = adns_r_a;
493     request->name = (char*) MyMalloc(strlen(hname) + 1);
494     strcpy(request->name, hname);
495   }
496   res = adns_submit_callback(adns, hname, adns_r_a, adns_qf_owner,
497                              request, &request->aq, res_adns_callback);
498   assert(!res);
499   timer_chg(&resExpireDNS, TT_RELATIVE, 1);
500 }
501
502 /*
503  * do_query_number - Use this to do reverse IP# lookups.
504  */
505 static void do_query_number(const struct DNSQuery* query, 
506                             const struct in_addr* addr,
507                             struct ResRequest* request)
508 {
509   char  ipbuf[32];
510   const unsigned char* cp;
511   int   res;
512
513   assert(0 != addr);
514   cp = (const unsigned char*) &addr->s_addr;
515   ircd_snprintf(0, ipbuf, sizeof(ipbuf), "%u.%u.%u.%u.in-addr.arpa.",
516                 (unsigned int)(cp[3]), (unsigned int)(cp[2]),
517                 (unsigned int)(cp[1]), (unsigned int)(cp[0]));
518
519   if (!request) {
520     request              = make_request(query);
521     request->type        = adns_r_ptr;
522     request->addr.s_addr = addr->s_addr;
523   }
524   res = adns_submit_callback(adns, ipbuf, adns_r_ptr, adns_qf_owner,
525                              request, &request->aq, res_adns_callback);
526   assert(!res);
527   timer_chg(&resExpireDNS, TT_RELATIVE, 1);
528 }
529
530 /* Returns true if this is a valid name */
531 static int validate_name(char *name)
532 {
533   while (*name) {
534     if ((*name<'A' || *name>'Z') 
535      && (*name<'a' || *name>'z') 
536      && (*name<'0' || *name>'9')
537      && (*name!='-')) {
538       return 0;
539     }
540     name++;
541   }
542   return 1;
543 }
544
545 /*
546  * dup_hostent - Duplicate a hostent struct, allocate only enough memory for
547  * the data we're putting in it.
548  */
549 static struct hostent* dup_hostent(struct hostent* hp)
550 {
551   char*  p;
552   char** ap;
553   char** pp;
554   int    alias_count = 0;
555   int    addr_count = 0;
556   size_t bytes_needed = 0;
557   struct Hostent* new_hp = 0;
558
559   assert(0 != hp);
560
561   /* how much buffer do we need? */
562   bytes_needed += (strlen(hp->h_name) + 1);
563
564   pp = hp->h_aliases;
565   while (*pp) {
566     bytes_needed += (strlen(*pp++) + 1 + sizeof(char*));
567     ++alias_count;
568   }
569   pp = hp->h_addr_list;
570   while (*pp++) {
571     bytes_needed += (hp->h_length + sizeof(char*));
572     ++addr_count;
573   }
574   /* Reserve space for 2 nulls to terminate h_aliases and h_addr_list */
575   bytes_needed += (2 * sizeof(char*));
576
577   /* Allocate memory */
578   new_hp = (struct Hostent*) MyMalloc(sizeof(struct Hostent) + bytes_needed);
579
580   new_hp->h.h_addrtype = hp->h_addrtype;
581   new_hp->h.h_length = hp->h_length;
582
583   /* first write the address list */
584   pp = hp->h_addr_list;
585   ap = new_hp->h.h_addr_list =
586       (char**)(new_hp->buf + ((alias_count + 1) * sizeof(char*)));
587   p = (char*)ap + ((addr_count + 1) * sizeof(char*));
588   while (*pp)
589   {
590     *ap++ = p;
591     memcpy(p, *pp++, hp->h_length);
592     p += hp->h_length;
593   }
594   *ap = 0;
595   /* next write the name */
596   new_hp->h.h_name = p;
597   strcpy(p, hp->h_name);
598   p += (strlen(p) + 1);
599
600   /* last write the alias list */
601   pp = hp->h_aliases;
602   ap = new_hp->h.h_aliases = (char**) new_hp->buf;
603   while (*pp) {
604     *ap++ = p;
605     strcpy(p, *pp++);
606     p += (strlen(p) + 1);
607   }
608   *ap = 0;
609   return (struct hostent*) new_hp;
610 }
611
612 static void res_adns_callback(adns_state state, adns_query q, void *context)
613 {
614   struct ResRequest *request = (struct ResRequest *) context;
615   adns_answer *answer = NULL;
616   int res, k;
617   struct hostent* hp;          /* hostent getting filled */
618   char** alias;                /* alias list */
619   char** addr;                 /* address list */
620   char** base_addr;            /* original pointer to address list */
621   char*  name;                 /* pointer to name string */
622   char*  address;              /* pointer to address */
623   char*  base_address;         /* original pointer to address */
624   char*  endp;                 /* end of our buffer */
625   int    addr_count  = 0;      /* number of addresses in hostent */
626   int    alias_count = 0;      /* number of aliases in hostent */
627   
628   assert(request);
629   assert(q);
630   assert(request->aq == q);
631   res = adns_check(adns, &q, &answer, NULL);
632   request->aq = NULL;
633
634   if (res) {
635     /* adns_check returned an error, bail */
636     Debug((DEBUG_DNS, "Resolver: adns_check result %d", res));
637
638     (*request->query.callback)(request->query.vptr, 0);
639     rem_request(request);
640     return;
641   }
642
643   Debug((DEBUG_DNS, "Resolver: adns_check status %d nrrs %d",
644         answer->status, answer->nrrs));
645   
646   /* No error, we have a valid answer structure */
647   if (answer->status != adns_s_ok || !answer->nrrs) {
648     /* Status is not 'ok', or there were no RRs found */
649     ++reinfo.re_errors;
650     (*request->query.callback)(request->query.vptr, 0);
651   } else {
652     ++reinfo.re_replies;
653     hp = &(request->he);    
654     if (!request->buf) {
655       request->buf = (char*) MyMalloc(MAXGETHOSTLEN + 1);
656       request->buf[MAXGETHOSTLEN] = '\0';
657       /*
658        * array of alias list pointers starts at beginning of buf
659        */
660       hp->h_aliases = (char**) request->buf;
661       hp->h_aliases[0] = NULL;
662       /*
663        * array of address list pointers starts after alias list pointers
664        * the actual addresses follow the the address list pointers
665        */ 
666       hp->h_addr_list = (char**)(request->buf + ALIAS_BLEN);
667       /*
668        * don't copy the host address to the beginning of h_addr_list
669        */
670       hp->h_addr_list[0] = NULL;
671     }
672
673     hp->h_addrtype = AF_INET;
674
675     endp = request->buf + MAXGETHOSTLEN;
676     /*
677      * find the end of the address list
678      */
679     addr = hp->h_addr_list;
680     while (*addr) {
681       ++addr;
682       ++addr_count;
683     }
684     base_addr = addr;
685     /*
686      * make address point to first available address slot
687      */
688     address = request->buf + ADDRS_OFFSET +
689                       (sizeof(struct in_addr) * addr_count);
690     base_address = address;
691
692     /*
693      * find the end of the alias list
694      */
695     alias = hp->h_aliases;
696     while (*alias) {
697       ++alias;
698       ++alias_count;
699     }
700     /*
701      * make name point to first available space in request->buf
702      */
703     if (alias_count > 0) {
704       name = hp->h_aliases[alias_count - 1];
705       name += (strlen(name) + 1);
706     }
707     else if (hp->h_name)
708       name = hp->h_name + strlen(hp->h_name) + 1;
709     else
710       name = request->buf + ADDRS_OFFSET + ADDRS_DLEN;
711
712     switch (request->type) {
713     case adns_r_a:
714       for (k = 0; k < answer->nrrs; k++) {
715         if (++addr_count < RES_MAXADDRS) {
716           memcpy(address, &answer->rrs.inaddr[k], sizeof(struct in_addr));
717           *addr++ = address;
718           *addr = 0;
719           address += sizeof(struct in_addr);
720         }
721         Debug((DEBUG_DNS, "Resolver: A %s for %s",
722               ircd_ntoa((char*) &answer->rrs.inaddr[k]), answer->owner));
723       }
724       if (!hp->h_name) {
725         strcpy(name, answer->owner);
726         hp->h_name = name;
727         name += strlen(name) + 1;
728       }
729       break;
730     case adns_r_ptr:
731       strcpy(name, answer->rrs.str[0]);
732       hp->h_name = name;
733       name += strlen(name) + 1;
734
735       Debug((DEBUG_DNS, "Resolver: PTR %s for %s", hp->h_name, answer->owner));
736       break;
737     default:
738       /* ignore */
739       break;
740     }
741
742     if (answer->cname) {
743       alias_count++;
744       ircd_strncpy(name, answer->cname, endp - name);
745       *alias++ = name;
746       *alias   = 0;
747       name += strlen(name) + 1;
748       Debug((DEBUG_DNS, "Resolver: CNAME %s for %s",
749             answer->cname, answer->owner));
750     }
751
752     if (request->type == adns_r_ptr) {
753
754       Debug((DEBUG_DNS, "relookup %s <-> %s",
755              request->he.h_name, ircd_ntoa((char*) &request->addr)));
756       /*
757        * Lookup the 'authoritive' name that we were given for the
758        * ip#.  By using this call rather than regenerating the
759        * type we automatically gain the use of the cache with no
760        * extra kludges.
761        */
762       gethost_byname(request->he.h_name, &request->query);
763       /*
764        * If name wasn't found, a request has been queued and it will
765        * be the last one queued.  This is rather nasty way to keep
766        * a host alias with the query. -avalon
767        */
768       MyFree(requestListTail->buf);
769       requestListTail->buf = request->buf;
770       request->buf = 0;
771       memcpy(&requestListTail->he, &request->he, sizeof(struct hostent));
772     } else {
773       /*
774        * got a name and address response, client resolved
775        * XXX - Bug found here by Dianora -
776        * make_cache() occasionally returns a NULL pointer when a
777        * PTR returned a CNAME, cp was not checked before so the
778        * callback was being called with a value of 0x2C != NULL.
779        */
780       struct hostent* he = dup_hostent(&request->he);
781       (*request->query.callback)(request->query.vptr, he);
782     }
783   }
784   
785   rem_request(request);
786   
787   /*
788    * adns doesn't use MyMalloc, so we don't use MyFree
789    */
790   free(answer);
791 }
792
793 /*
794  * m_dns - dns status query
795  */
796 int m_dns(struct Client *cptr, struct Client *sptr, int parc, char *parv[])
797 {
798 #if !defined(NDEBUG)
799   if (parv[1] && *parv[1] == 'd') {
800     sendcmdto_one(&me, CMD_NOTICE, sptr, "%C :ResolverFileDescriptor = %d", 
801                   sptr, ResolverFileDescriptor);
802     return 0;
803   }
804   sendcmdto_one(&me, CMD_NOTICE, sptr,"%C :Re %d Rl %d/%d Rp %d Rq %d",
805                 sptr, reinfo.re_errors, reinfo.re_nu_look,
806                 reinfo.re_na_look, reinfo.re_replies, reinfo.re_requests);
807   sendcmdto_one(&me, CMD_NOTICE, sptr,"%C :Ru %d Rsh %d Rs %d(%d) Rt %d", sptr,
808                 reinfo.re_unkrep, reinfo.re_shortttl, reinfo.re_sent,
809                 reinfo.re_resends, reinfo.re_timeouts);
810 #endif
811   return 0;
812 }
813
814 size_t cres_mem(struct Client* sptr)
815 {
816   struct ResRequest* request;
817   size_t request_mem   = 0;
818   int    request_count = 0;
819
820   for (request = requestListHead; request; request = request->next) {
821     request_mem += sizeof(struct ResRequest);
822     if (request->name)
823       request_mem += strlen(request->name) + 1; 
824     if (request->buf)
825       request_mem += MAXGETHOSTLEN + 1;
826     ++request_count;
827   }
828
829   send_reply(sptr, SND_EXPLICIT | RPL_STATSDEBUG,
830              ":Resolver: requests %d(%d)", request_count, request_mem);
831   return request_mem;
832 }
833