Doxyfy res.h and ircd_res.c.
[ircu2.10.12-pk.git] / ircd / ircd_res.c
1 /*
2  * A rewrite of Darren Reeds original res.c As there is nothing
3  * left of Darrens original code, this is now licensed by the hybrid group.
4  * (Well, some of the function names are the same, and bits of the structs..)
5  * You can use it where it is useful, free even. Buy us a beer and stuff.
6  *
7  * The authors takes no responsibility for any damage or loss
8  * of property which results from the use of this software.
9  *
10  * July 1999 - Rewrote a bunch of stuff here. Change hostent builder code,
11  *     added callbacks and reference counting of returned hostents.
12  *     --Bleep (Thomas Helvey <tomh@inxpress.net>)
13  *
14  * This was all needlessly complicated for irc. Simplified. No more hostent
15  * All we really care about is the IP -> hostname mappings. Thats all.
16  *
17  * Apr 28, 2003 --cryogen and Dianora
18  */
19 /** @file
20  * @brief IRC resolver functions.
21  * @version $Id$
22  */
23
24 #include "client.h"
25 #include "ircd_alloc.h"
26 #include "ircd_log.h"
27 #include "ircd_osdep.h"
28 #include "ircd_reply.h"
29 #include "ircd_string.h"
30 #include "ircd_snprintf.h"
31 #include "ircd.h"
32 #include "numeric.h"
33 #include "fileio.h" /* for fbopen / fbclose / fbputs */
34 #include "s_bsd.h"
35 #include "s_debug.h"
36 #include "s_stats.h"
37 #include "send.h"
38 #include "sys.h"
39 #include "res.h"
40 #include "ircd_reslib.h"
41
42 #include <assert.h>
43 #include <string.h>
44 #include <sys/time.h>
45 #include <sys/socket.h>
46 #include <time.h>
47
48 #if (CHAR_BIT != 8)
49 #error this code needs to be able to address individual octets 
50 #endif
51
52 /** Resolver UDP socket. */
53 static struct Socket res_socket;
54 /** Next DNS lookup timeout. */
55 static struct Timer res_timeout;
56
57 /** Maximum DNS packet length.
58  * RFC says 512, but we add extra for expanded names.
59  */
60 #define MAXPACKET      1024
61 #define AR_TTL         600   /**< TTL in seconds for dns cache entries */
62
63 /* RFC 1104/1105 wasn't very helpful about what these fields
64  * should be named, so for now, we'll just name them this way.
65  * we probably should look at what named calls them or something.
66  */
67 /** Size of TYPE field of a DNS RR header. */
68 #define TYPE_SIZE         (size_t)2
69 /** Size of CLASS field of a DNS RR header. */
70 #define CLASS_SIZE        (size_t)2
71 /** Size of TTL field of a DNS RR header. */
72 #define TTL_SIZE          (size_t)4
73 /** Size of RDLENGTH field of a DNS RR header. */
74 #define RDLENGTH_SIZE     (size_t)2
75 /** Size of fixed-format part of a DNS RR header. */
76 #define ANSWER_FIXED_SIZE (TYPE_SIZE + CLASS_SIZE + TTL_SIZE + RDLENGTH_SIZE)
77
78 /** Current request state. */
79 typedef enum
80 {
81   REQ_IDLE,  /**< We're doing not much at all. */
82   REQ_PTR,   /**< Looking up a PTR. */
83   REQ_A,     /**< Looking up an A, possibly because AAAA failed. */
84   REQ_AAAA,  /**< Looking up an AAAA. */
85   REQ_CNAME, /**< We got a CNAME in response, we better get a real answer next. */
86   REQ_INT    /**< ip6.arpa failed, falling back to ip6.int. */
87 } request_state;
88
89 /** Doubly linked list node. */
90 struct dlink
91 {
92     struct dlink *prev; /**< Previous element in list. */
93     struct dlink *next; /**< Next element in list. */
94 };
95
96 /** A single resolver request.
97  * (Do not be fooled by the "list" in the name.)
98  */
99 struct reslist
100 {
101   struct dlink node;       /**< Doubly linked list node. */
102   int id;                  /**< Request ID (from request header). */
103   int sent;                /**< Number of requests sent. */
104   request_state state;     /**< State the resolver machine is in. */
105   char type;               /**< Current request type. */
106   char retries;            /**< Retry counter. */
107   char sends;              /**< Number of sends (>1 means resent). */
108   char resend;             /**< Send flag; 0 == dont resend. */
109   time_t sentat;           /**< Timestamp we last sent this request. */
110   time_t timeout;          /**< When this request times out. */
111   struct irc_in_addr addr; /**< Address for this request. */
112   char *name;              /**< Hostname for this request. */
113   struct DNSQuery query;   /**< Query callback for this request. */
114 };
115
116 /** Base of request list. */
117 static struct dlink request_list;
118
119 static void rem_request(struct reslist *request);
120 static struct reslist *make_request(const struct DNSQuery *query);
121 static void do_query_name(const struct DNSQuery *query,
122                           const char* name, struct reslist *request, int);
123 static void do_query_number(const struct DNSQuery *query,
124                             const struct irc_in_addr *,
125                             struct reslist *request);
126 static void query_name(const char *name, int query_class, int query_type,
127                        struct reslist *request);
128 static int send_res_msg(const char *buf, int len, int count);
129 static void resend_query(struct reslist *request);
130 static int proc_answer(struct reslist *request, HEADER *header, char *, char *);
131 static struct reslist *find_id(int id);
132 static struct DNSReply *make_dnsreply(struct reslist *request);
133 static void res_readreply(struct Event *ev);
134 static void timeout_resolver(struct Event *notused);
135
136 extern struct irc_sockaddr irc_nsaddr_list[IRCD_MAXNS];
137 extern int irc_nscount;
138 extern char irc_domain[HOSTLEN];
139
140 /** Check whether \a inp is a nameserver we use.
141  * @param[in] inp Nameserver address.
142  * @return Non-zero if we trust \a inp; zero if not.
143  */
144 static int
145 res_ourserver(const struct irc_sockaddr *inp)
146 {
147   int ns;
148
149   for (ns = 0;  ns < irc_nscount;  ns++)
150     if (!irc_in_addr_cmp(&inp->addr, &irc_nsaddr_list[ns].addr)
151         && inp->port == irc_nsaddr_list[ns].port)
152       return 1;
153
154   return(0);
155 }
156
157 /** Start (or re-start) resolver.
158  * This means read resolv.conf, initialize the list of pending
159  * requests, open the resolver socket and initialize its timeout.
160  */
161 void
162 restart_resolver(void)
163 {
164   irc_res_init();
165
166   if (!request_list.next)
167     request_list.next = request_list.prev = &request_list;
168
169   if (!s_active(&res_socket))
170   {
171     int fd;
172     fd = os_socket(NULL, SOCK_DGRAM, "Resolver UDP socket");
173     if (fd < 0) return;
174     if (!socket_add(&res_socket, res_readreply, NULL, SS_DATAGRAM,
175                     SOCK_EVENT_READABLE, fd)) return;
176     timer_init(&res_timeout);
177   }
178 }
179
180 /** Initialize resolver.
181  * This seends the pseudo-random number generator and calls
182  * restart_resolver().
183  * @return Resolver socket file descriptor.
184  */
185 int
186 init_resolver(void)
187 {
188   srand(CurrentTime);
189   restart_resolver();
190   return(s_fd(&res_socket));
191 }
192
193 /** Append local domain to hostname if needed.
194  * If \a hname does not contain any '.'s, append #irc_domain to it.
195  * @param[in,out] hname Hostname to check.
196  * @param[in] size Length of \a hname buffer.
197  */
198 void
199 add_local_domain(char* hname, size_t size)
200 {
201   /* try to fix up unqualified names 
202    */
203   if (strchr(hname, '.') == NULL)
204   {
205     if (irc_domain[0])
206     {
207       size_t len = strlen(hname);
208
209       if ((strlen(irc_domain) + len + 2) < size)
210       {
211         hname[len++] = '.';
212         strcpy(hname + len, irc_domain);
213       }
214     }
215   }
216 }
217
218 /** Add a node to a doubly linked list.
219  * @param[in,out] node Node to add to list.
220  * @param[in,out] next Add \a node before this one.
221  */
222 static void
223 add_dlink(struct dlink *node, struct dlink *next)
224 {
225     node->prev = next->prev;
226     node->next = next;
227     node->prev->next = node;
228     node->next->prev = node;
229 }
230
231 /** Remove a request from the list and free it.
232  * @param[in] request Node to free.
233  */
234 static void
235 rem_request(struct reslist *request)
236 {
237   /* remove from dlist */
238   request->node.prev->next = request->node.next;
239   request->node.next->prev = request->node.prev;
240   /* free memory */
241   MyFree(request->name);
242   MyFree(request);
243 }
244
245 /** Create a DNS request record for the server.
246  * @param[in] query Callback information for caller.
247  * @return Newly allocated and linked-in reslist.
248  */
249 static struct reslist *
250 make_request(const struct DNSQuery* query)
251 {
252   struct reslist *request;
253
254   request = (struct reslist *)MyMalloc(sizeof(struct reslist));
255   memset(request, 0, sizeof(struct reslist));
256
257   request->state   = REQ_IDLE;
258   request->sentat  = CurrentTime;
259   request->retries = feature_int(FEAT_IRCD_RES_RETRIES);
260   request->resend  = 1;
261   request->timeout = feature_int(FEAT_IRCD_RES_TIMEOUT);
262   memset(&request->addr, 0, sizeof(request->addr));
263   memcpy(&request->query, query, sizeof(request->query));
264
265   add_dlink(&request->node, &request_list);
266   return(request);
267 }
268
269 /** Make sure that a timeout event will happen by the given time.
270  * @param[in] when Latest time for timeout to run.
271  */
272 static void
273 check_resolver_timeout(time_t when)
274 {
275   if (when > CurrentTime + AR_TTL)
276     when = CurrentTime + AR_TTL;
277   if (!t_active(&res_timeout))
278     timer_add(&res_timeout, timeout_resolver, NULL, TT_ABSOLUTE, when);
279   else if (when < t_expire(&res_timeout))
280     timer_chg(&res_timeout, TT_ABSOLUTE, when);
281 }
282
283 /** Drop pending DNS lookups which have timed out.
284  * @param[in] notused Timer event data (ignored).
285  */
286 static void
287 timeout_resolver(struct Event *notused)
288 {
289   struct dlink *ptr, *next_ptr;
290   struct reslist *request;
291   time_t next_time = 0;
292   time_t timeout   = 0;
293
294   for (ptr = request_list.next; ptr != &request_list; ptr = next_ptr)
295   {
296     next_ptr = ptr->next;
297     request = (struct reslist*)ptr;
298     timeout = request->sentat + request->timeout;
299
300     if (CurrentTime >= timeout)
301     {
302       if (--request->retries <= 0)
303       {
304         Debug((DEBUG_DNS, "Request %p out of retries; destroying", request));
305         (*request->query.callback)(request->query.vptr, 0);
306         rem_request(request);
307         continue;
308       }
309       else
310       {
311         request->sentat = CurrentTime;
312         request->timeout += request->timeout;
313         resend_query(request);
314       }
315     }
316
317     if ((next_time == 0) || timeout < next_time)
318     {
319       next_time = timeout;
320     }
321   }
322
323   if (next_time <= CurrentTime)
324     next_time = CurrentTime + AR_TTL;
325   check_resolver_timeout(next_time);
326 }
327
328 /** Drop queries that are associated with a particular pointer.
329  * This is used to clean up lookups for clients or conf blocks
330  * that went away.
331  * @param[in] vptr User callback pointer to search for.
332  */
333 void
334 delete_resolver_queries(const void *vptr)
335 {
336   struct dlink *ptr, *next_ptr;
337   struct reslist *request;
338
339   for (ptr = request_list.next; ptr != &request_list; ptr = next_ptr)
340   {
341     next_ptr = ptr->next;
342     request = (struct reslist*)ptr;
343     if (vptr == request->query.vptr) {
344       Debug((DEBUG_DNS, "Removing request %p with vptr %p", request, vptr));
345       rem_request(request);
346     }
347   }
348 }
349
350 /** Send a message to all of our nameservers.
351  * @param[in] msg Message to send.
352  * @param[in] len Length of message.
353  * @param[in] rcount Maximum number of servers to ask.
354  * @return Number of servers that were successfully asked.
355  */
356 static int
357 send_res_msg(const char *msg, int len, int rcount)
358 {
359   int i;
360   int sent = 0;
361   int max_queries = IRCD_MIN(irc_nscount, rcount);
362
363   /* RES_PRIMARY option is not implemented
364    * if (res.options & RES_PRIMARY || 0 == max_queries)
365    */
366   if (max_queries == 0)
367     max_queries = 1;
368
369   for (i = 0; i < max_queries; i++)
370     if (os_sendto_nonb(s_fd(&res_socket), msg, len, NULL, 0, &irc_nsaddr_list[i]) == IO_SUCCESS)
371       ++sent;
372
373   return(sent);
374 }
375
376 /** Find a DNS request by ID.
377  * @param[in] id Identifier to find.
378  * @return Matching DNS request, or NULL if none are found.
379  */
380 static struct reslist *
381 find_id(int id)
382 {
383   struct dlink *ptr;
384   struct reslist *request;
385
386   for (ptr = request_list.next; ptr != &request_list; ptr = ptr->next)
387   {
388     request = (struct reslist*)ptr;
389
390     if (request->id == id) {
391       Debug((DEBUG_DNS, "find_id(%d) -> %p", id, request));
392       return(request);
393     }
394   }
395
396   Debug((DEBUG_DNS, "find_id(%d) -> NULL", id));
397   return(NULL);
398 }
399
400 /** Try to look up address for a hostname, trying IPv6 (T_AAAA) first.
401  * @param[in] name Hostname to look up.
402  * @param[in] query Callback information.
403  */
404 void
405 gethost_byname(const char *name, const struct DNSQuery *query)
406 {
407   do_query_name(query, name, NULL, T_AAAA);
408 }
409
410 /** Try to look up hostname for an address.
411  * @param[in] addr Address to look up.
412  * @param[in] query Callback information.
413  */
414 void
415 gethost_byaddr(const struct irc_in_addr *addr, const struct DNSQuery *query)
416 {
417   do_query_number(query, addr, NULL);
418 }
419
420 /** Send a query to look up the address for a name.
421  * @param[in] query Callback information.
422  * @param[in] name Hostname to look up.
423  * @param[in] request DNS lookup structure (may be NULL).
424  * @param[in] type Preferred request type.
425  */
426 static void
427 do_query_name(const struct DNSQuery *query, const char *name,
428               struct reslist *request, int type)
429 {
430   char host_name[HOSTLEN + 1];
431
432   ircd_strncpy(host_name, name, HOSTLEN);
433   add_local_domain(host_name, HOSTLEN);
434
435   if (request == NULL)
436   {
437     request       = make_request(query);
438     DupString(request->name, host_name);
439 #ifdef IPV6
440     if (type != T_A)
441       request->state = REQ_AAAA;
442     else
443 #endif
444     request->state = REQ_A;
445   }
446
447   request->type = type;
448   Debug((DEBUG_DNS, "Requesting DNS %s %s as %p", (request->state == REQ_AAAA ? "AAAA" : "A"), host_name, request));
449   query_name(host_name, C_IN, type, request);
450 }
451
452 /** Send a query to look up the name for an address.
453  * @param[in] query Callback information.
454  * @param[in] addr Address to look up.
455  * @param[in] request DNS lookup structure (may be NULL).
456  */
457 static void
458 do_query_number(const struct DNSQuery *query, const struct irc_in_addr *addr,
459                 struct reslist *request)
460 {
461   char ipbuf[128];
462   const unsigned char *cp;
463
464   if (irc_in_addr_is_ipv4(addr))
465   {
466     cp = (const unsigned char*)&addr->in6_16[6];
467     ircd_snprintf(NULL, ipbuf, sizeof(ipbuf), "%u.%u.%u.%u.in-addr.arpa.",
468                   (unsigned int)(cp[3]), (unsigned int)(cp[2]),
469                   (unsigned int)(cp[1]), (unsigned int)(cp[0]));
470   }
471   else
472   {
473     const char *intarpa;
474
475     if (request != NULL && request->state == REQ_INT)
476       intarpa = "int";
477     else
478       intarpa = "arpa";
479
480     cp = (const unsigned char *)&addr->in6_16[0];
481     ircd_snprintf(NULL, ipbuf, sizeof(ipbuf),
482                   "%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x."
483                   "%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.ip6.%s.",
484                   (unsigned int)(cp[15]&0xf), (unsigned int)(cp[15]>>4),
485                   (unsigned int)(cp[14]&0xf), (unsigned int)(cp[14]>>4),
486                   (unsigned int)(cp[13]&0xf), (unsigned int)(cp[13]>>4),
487                   (unsigned int)(cp[12]&0xf), (unsigned int)(cp[12]>>4),
488                   (unsigned int)(cp[11]&0xf), (unsigned int)(cp[11]>>4),
489                   (unsigned int)(cp[10]&0xf), (unsigned int)(cp[10]>>4),
490                   (unsigned int)(cp[9]&0xf), (unsigned int)(cp[9]>>4),
491                   (unsigned int)(cp[8]&0xf), (unsigned int)(cp[8]>>4),
492                   (unsigned int)(cp[7]&0xf), (unsigned int)(cp[7]>>4),
493                   (unsigned int)(cp[6]&0xf), (unsigned int)(cp[6]>>4),
494                   (unsigned int)(cp[5]&0xf), (unsigned int)(cp[5]>>4),
495                   (unsigned int)(cp[4]&0xf), (unsigned int)(cp[4]>>4),
496                   (unsigned int)(cp[3]&0xf), (unsigned int)(cp[3]>>4),
497                   (unsigned int)(cp[2]&0xf), (unsigned int)(cp[2]>>4),
498                   (unsigned int)(cp[1]&0xf), (unsigned int)(cp[1]>>4),
499                   (unsigned int)(cp[0]&0xf), (unsigned int)(cp[0]>>4), intarpa);
500   }
501   if (request == NULL)
502   {
503     request       = make_request(query);
504     request->type = T_PTR;
505     memcpy(&request->addr, addr, sizeof(request->addr));
506     request->name = (char *)MyMalloc(HOSTLEN + 1);
507   }
508   Debug((DEBUG_DNS, "Requesting DNS PTR %s as %p", ipbuf, request));
509   query_name(ipbuf, C_IN, T_PTR, request);
510 }
511
512 /** Generate a query based on class, type and name.
513  * @param[in] name Domain name to look up.
514  * @param[in] query_class Query class (see RFC 1035).
515  * @param[in] type Query type (see RFC 1035).
516  * @param[in] request DNS request structure.
517  */
518 static void
519 query_name(const char *name, int query_class, int type,
520            struct reslist *request)
521 {
522   char buf[MAXPACKET];
523   int request_len = 0;
524
525   memset(buf, 0, sizeof(buf));
526
527   if ((request_len = irc_res_mkquery(name, query_class, type,
528       (unsigned char *)buf, sizeof(buf))) > 0)
529   {
530     HEADER *header = (HEADER *)buf;
531
532     /*
533      * generate an unique id
534      * NOTE: we don't have to worry about converting this to and from
535      * network byte order, the nameserver does not interpret this value
536      * and returns it unchanged
537      */
538     do
539     {
540       header->id = (header->id + rand()) & 0xffff;
541     } while (find_id(header->id));
542     request->id = header->id;
543     ++request->sends;
544
545     request->sent += send_res_msg(buf, request_len, request->sends);
546     check_resolver_timeout(request->sentat + request->timeout);
547   }
548 }
549
550 /** Send a failed DNS lookup request again.
551  * @param[in] request Request to resend.
552  */
553 static void
554 resend_query(struct reslist *request)
555 {
556   if (request->resend == 0)
557     return;
558
559   switch(request->type)
560   {
561     case T_PTR:
562       do_query_number(NULL, &request->addr, request);
563       break;
564     case T_A:
565       do_query_name(NULL, request->name, request, request->type);
566       break;
567     case T_AAAA:
568       /* didnt work, try A */
569       if (request->state == REQ_AAAA)
570         do_query_name(NULL, request->name, request, T_A);
571     default:
572       break;
573   }
574 }
575
576 /** Process the answer for a lookup request.
577  * @param[in] request DNS request that got an answer.
578  * @param[in] header Header of DNS response.
579  * @param[in] buf DNS response body.
580  * @param[in] eob Pointer to end of DNS response.
581  * @return Number of answers read from \a buf.
582  */
583 static int
584 proc_answer(struct reslist *request, HEADER* header, char* buf, char* eob)
585 {
586   char hostbuf[HOSTLEN + 100]; /* working buffer */
587   unsigned char *current;      /* current position in buf */
588   int query_class;             /* answer class */
589   int type;                    /* answer type */
590   int n;                       /* temp count */
591   int rd_length;
592
593   current = (unsigned char *)buf + sizeof(HEADER);
594
595   for (; header->qdcount > 0; --header->qdcount)
596   {
597     if ((n = irc_dn_skipname(current, (unsigned char *)eob)) < 0)
598       break;
599
600     current += (size_t) n + QFIXEDSZ;
601   }
602
603   /*
604    * process each answer sent to us blech.
605    */
606   while (header->ancount > 0 && (char *)current < eob)
607   {
608     header->ancount--;
609
610     n = irc_dn_expand((unsigned char *)buf, (unsigned char *)eob, current,
611         hostbuf, sizeof(hostbuf));
612
613     if (n < 0)
614     {
615       /*
616        * broken message
617        */
618       return(0);
619     }
620     else if (n == 0)
621     {
622       /*
623        * no more answers left
624        */
625       return(0);
626     }
627
628     hostbuf[HOSTLEN] = '\0';
629
630     /* With Address arithmetic you have to be very anal
631      * this code was not working on alpha due to that
632      * (spotted by rodder/jailbird/dianora)
633      */
634     current += (size_t) n;
635
636     if (!(((char *)current + ANSWER_FIXED_SIZE) < eob))
637       break;
638
639     type = irc_ns_get16(current);
640     current += TYPE_SIZE;
641
642     query_class = irc_ns_get16(current);
643     current += CLASS_SIZE;
644
645     current += TTL_SIZE;
646
647     rd_length = irc_ns_get16(current);
648     current += RDLENGTH_SIZE;
649
650     /*
651      * Wait to set request->type until we verify this structure
652      */
653     switch (type)
654     {
655       case T_A:
656         if (request->type != T_A)
657           return(0);
658
659         /*
660          * check for invalid rd_length or too many addresses
661          */
662         if (rd_length != sizeof(struct in_addr))
663           return(0);
664         memset(&request->addr, 0, sizeof(request->addr));
665         memcpy(&request->addr.in6_16[6], current, sizeof(struct in_addr));
666         return(1);
667         break;
668       case T_AAAA:
669         if (request->type != T_AAAA)
670           return(0);
671         if (rd_length != sizeof(struct irc_in_addr))
672           return(0);
673         memcpy(&request->addr, current, sizeof(struct irc_in_addr));
674         return(1);
675         break;
676       case T_PTR:
677         if (request->type != T_PTR)
678           return(0);
679         n = irc_dn_expand((unsigned char *)buf, (unsigned char *)eob,
680             current, hostbuf, sizeof(hostbuf));
681         if (n < 0)
682           return(0); /* broken message */
683         else if (n == 0)
684           return(0); /* no more answers left */
685
686         ircd_strncpy(request->name, hostbuf, HOSTLEN);
687
688         return(1);
689         break;
690       case T_CNAME: /* first check we already havent started looking
691                        into a cname */
692         if (request->type != T_PTR)
693           return(0);
694
695         if (request->state == REQ_CNAME)
696         {
697           n = irc_dn_expand((unsigned char *)buf, (unsigned char *)eob,
698                             current, hostbuf, sizeof(hostbuf));
699
700           if (n < 0)
701             return(0);
702           return(1);
703         }
704
705         request->state = REQ_CNAME;
706         current += rd_length;
707         break;
708
709       default:
710         /* XXX I'd rather just throw away the entire bogus thing
711          * but its possible its just a broken nameserver with still
712          * valid answers. But lets do some rudimentary logging for now...
713          */
714           log_write(LS_RESOLVER, L_ERROR, 0, "irc_res.c bogus type %d", type);
715         break;
716     }
717   }
718
719   return(1);
720 }
721
722 /** Read a DNS reply from the nameserver and process it.
723  * @param[in] ev I/O activity event for resolver socket.
724  */
725 static void
726 res_readreply(struct Event *ev)
727 {
728   struct irc_sockaddr lsin;
729   struct Socket *sock;
730   char buf[sizeof(HEADER) + MAXPACKET];
731   HEADER *header;
732   struct reslist *request = NULL;
733   struct DNSReply *reply  = NULL;
734   unsigned int rc;
735   int answer_count;
736
737   assert(ev_socket(ev) == &res_socket);
738   sock = ev_socket(ev);
739
740   if (IO_SUCCESS != os_recvfrom_nonb(s_fd(sock), buf, sizeof(buf), &rc, &lsin)
741       || (rc <= sizeof(HEADER)))
742     return;
743
744   /*
745    * convert DNS reply reader from Network byte order to CPU byte order.
746    */
747   header = (HEADER *)buf;
748   header->ancount = ntohs(header->ancount);
749   header->qdcount = ntohs(header->qdcount);
750   header->nscount = ntohs(header->nscount);
751   header->arcount = ntohs(header->arcount);
752
753   /*
754    * response for an id which we have already received an answer for
755    * just ignore this response.
756    */
757   if (0 == (request = find_id(header->id)))
758     return;
759
760   /*
761    * check against possibly fake replies
762    */
763   if (!res_ourserver(&lsin))
764     return;
765
766   if ((header->rcode != NO_ERRORS) || (header->ancount == 0))
767   {
768     if (SERVFAIL == header->rcode)
769       resend_query(request);
770     else
771     {
772       /*
773        * If we havent already tried this, and we're looking up AAAA, try A
774        * now
775        */
776
777       if (request->state == REQ_AAAA && request->type == T_AAAA)
778       {
779         request->timeout += feature_int(FEAT_IRCD_RES_TIMEOUT);
780         resend_query(request);
781       }
782       else if (request->type == T_PTR && request->state != REQ_INT &&
783                !irc_in_addr_is_ipv4(&request->addr))
784       {
785         request->state = REQ_INT;
786         request->timeout += feature_int(FEAT_IRCD_RES_TIMEOUT);
787         resend_query(request);
788       }
789       else
790       {
791         /*
792          * If a bad error was returned, we stop here and dont send
793          * send any more (no retries granted).
794          */
795           Debug((DEBUG_DNS, "Request %p has bad response (state %d type %d)", request, request->state, request->type));
796         (*request->query.callback)(request->query.vptr, 0);
797         rem_request(request);
798       }
799     }
800
801     return;
802   }
803   /*
804    * If this fails there was an error decoding the received packet,
805    * try it again and hope it works the next time.
806    */
807   answer_count = proc_answer(request, header, buf, buf + rc);
808
809   if (answer_count)
810   {
811     if (request->type == T_PTR)
812     {
813       if (request->name == NULL)
814       {
815         /*
816          * got a PTR response with no name, something bogus is happening
817          * don't bother trying again, the client address doesn't resolve
818          */
819         Debug((DEBUG_DNS, "Request %p PTR had empty name", request));
820         (*request->query.callback)(request->query.vptr, reply);
821         rem_request(request);
822         return;
823       }
824
825       /*
826        * Lookup the 'authoritative' name that we were given for the
827        * ip#.
828        */
829 #ifdef IPV6
830       if (!irc_in_addr_is_ipv4(&request->addr))
831         do_query_name(&request->query, request->name, NULL, T_AAAA);
832       else
833 #endif
834       do_query_name(&request->query, request->name, NULL, T_A);
835       Debug((DEBUG_DNS, "Request %p switching to forward resolution", request));
836       rem_request(request);
837     }
838     else
839     {
840       /*
841        * got a name and address response, client resolved
842        */
843       reply = make_dnsreply(request);
844       (*request->query.callback)(request->query.vptr, (reply) ? reply : 0);
845       Debug((DEBUG_DNS, "Request %p got forward resolution", request));
846       rem_request(request);
847     }
848   }
849   else if (!request->sent)
850   {
851     /* XXX - we got a response for a query we didn't send with a valid id?
852      * this should never happen, bail here and leave the client unresolved
853      */
854     assert(0);
855
856     /* XXX don't leak it */
857     Debug((DEBUG_DNS, "Request %p was unexpected(!)", request));
858     rem_request(request);
859   }
860 }
861
862 /** Build a DNSReply for a completed request.
863  * @param[in] request Completed DNS request.
864  * @return Newly allocated DNSReply containing host name and address.
865  */
866 static struct DNSReply *
867 make_dnsreply(struct reslist *request)
868 {
869   struct DNSReply *cp;
870   assert(request != 0);
871
872   cp = (struct DNSReply *)MyMalloc(sizeof(struct DNSReply));
873
874   DupString(cp->h_name, request->name);
875   memcpy(&cp->addr, &request->addr, sizeof(cp->addr));
876   return(cp);
877 }
878
879 /** Statistics callback to list DNS servers.
880  * @param[in] source_p Client requesting statistics.
881  * @param[in] sd Stats descriptor for request (ignored).
882  * @param[in] param Extra parameter from user (ignored).
883  */
884 void
885 report_dns_servers(struct Client *source_p, const struct StatDesc *sd, char *param)
886 {
887   int i;
888   char ipaddr[128];
889
890   for (i = 0; i < irc_nscount; i++)
891   {
892     ircd_ntoa_r(ipaddr, &irc_nsaddr_list[i].addr);
893     send_reply(source_p, RPL_STATSALINE, ipaddr);
894   }
895 }
896
897 /** Report memory usage to a client.
898  * @param[in] sptr Client requesting information.
899  * @return Total memory used by pending requests.
900  */
901 size_t
902 cres_mem(struct Client* sptr)
903 {
904   struct dlink *dlink;
905   struct reslist *request;
906   size_t request_mem   = 0;
907   int    request_count = 0;
908
909   for (dlink = request_list.next; dlink != &request_list; dlink = dlink->next) {
910     request = (struct reslist*)dlink;
911     request_mem += sizeof(*request);
912     if (request->name)
913       request_mem += strlen(request->name) + 1;
914     ++request_count;
915   }
916
917   send_reply(sptr, SND_EXPLICIT | RPL_STATSDEBUG,
918              ":Resolver: requests %d(%d)", request_count, request_mem);
919   return request_mem;
920 }
921
922 /** Check whether an address looks valid.
923  * This means not all 0s and not all 1s.
924  * @param[in] addr Address to check for validity.
925  * @return Non-zero if the address looks valid.
926  */
927 int irc_in_addr_valid(const struct irc_in_addr *addr)
928 {
929   unsigned int ii;
930   unsigned short val;
931
932   val = addr->in6_16[0];
933   if (val != 0 && val != 0xffff)
934     return 1;
935   for (ii = 1; ii < 8; ii++)
936     if (addr->in6_16[ii] != val)
937       return 1;
938   return 0;
939 }
940
941 /** Compare two IP addresses.
942  * @param[in] a First address to compare.
943  * @param[in] b Second address to compare.
944  * @return Non-zero if the two addresses differ, zero if they are identical.
945  */
946 int irc_in_addr_cmp(const struct irc_in_addr *a, const struct irc_in_addr *b)
947 {
948   if (irc_in_addr_is_ipv4(a))
949     return a->in6_16[6] != b->in6_16[6]
950         || a->in6_16[7] != b->in6_16[7]
951         || !irc_in_addr_is_ipv4(b);
952   else
953     return memcmp(a, b, sizeof(*a));
954 }
955
956 /** Indicate whether an IP address is a loopback address.
957  * @param[in] addr Address to check.
958  * @return Non-zero if the address is loopback; zero if not.
959  */
960 int irc_in_addr_is_loopback(const struct irc_in_addr *addr)
961 {
962   if (addr->in6_16[0] != 0
963     || addr->in6_16[1] != 0
964     || addr->in6_16[2] != 0
965     || addr->in6_16[3] != 0
966     || addr->in6_16[4] != 0)
967     return 0;
968   if ((addr->in6_16[5] == 0xffff) || (addr->in6_16[5] == 0 && addr->in6_16[6] != 0))
969     return (ntohs(addr->in6_16[6]) & 0xff00) == 0x7f00;
970   else
971     return addr->in6_16[5] == 0 && addr->in6_16[6] == 0 && htons(addr->in6_16[7]) == 1;
972 }