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