]> jfr.im git - solanum.git/blame - authd/res.c
Mailmap and copyright update for Ariadne
[solanum.git] / authd / res.c
CommitLineData
ed62c46b
AC
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 *
ed62c46b
AC
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 * DNS server flooding lessened, AAAA-or-A lookup removed, ip6.int support
20 * removed, various robustness fixes
21 *
22 * 2006 --jilles and nenolod
23 *
24 * Resend queries to other servers if the DNS server replies with an error or
25 * an invalid response. Also, avoid servers that return errors or invalid
26 * responses.
27 *
28 * October 2012 --mr_flea
29 *
30 * ircd-ratbox changes for random IDs merged back in.
31 *
32 * January 2016 --kaniini
33 */
34
fe037171 35#include <rb_lib.h>
ed62c46b
AC
36#include "setup.h"
37#include "res.h"
38#include "reslib.h"
39
40#if (CHAR_BIT != 8)
41#error this code needs to be able to address individual octets
42#endif
43
44static PF res_readreply;
45
46#define MAXPACKET 1024 /* rfc sez 512 but we expand names so ... */
ed62c46b
AC
47#define AR_TTL 600 /* TTL in seconds for dns cache entries */
48
49/* RFC 1104/1105 wasn't very helpful about what these fields
50 * should be named, so for now, we'll just name them this way.
51 * we probably should look at what named calls them or something.
52 */
53#define TYPE_SIZE (size_t)2
54#define CLASS_SIZE (size_t)2
55#define TTL_SIZE (size_t)4
56#define RDLENGTH_SIZE (size_t)2
57#define ANSWER_FIXED_SIZE (TYPE_SIZE + CLASS_SIZE + TTL_SIZE + RDLENGTH_SIZE)
58
3f6cbacc 59struct in6_addr ipv6_addr;
3f6cbacc 60struct in_addr ipv4_addr;
ed62c46b
AC
61
62struct reslist
63{
64 rb_dlink_node node;
65 int id;
66 time_t ttl;
67 char type;
68 char queryname[IRCD_RES_HOSTLEN + 1]; /* name currently being queried */
69 char retries; /* retry counter */
70 char sends; /* number of sends (>1 means resent) */
71 time_t sentat;
72 time_t timeout;
73 int lastns; /* index of last server sent to */
74 struct rb_sockaddr_storage addr;
75 char *name;
76 struct DNSQuery *query; /* query callback for this request */
77};
78
79static rb_fde_t *res_fd;
80static rb_dlink_list request_list = { NULL, NULL, 0 };
81static int ns_failure_count[IRCD_MAXNS]; /* timeouts and invalid/failed replies */
82
83static void rem_request(struct reslist *request);
84static struct reslist *make_request(struct DNSQuery *query);
85static void gethost_byname_type_fqdn(const char *name, struct DNSQuery *query,
86 int type);
87static void do_query_name(struct DNSQuery *query, const char *name, struct reslist *request, int);
88static void do_query_number(struct DNSQuery *query, const struct rb_sockaddr_storage *,
89 struct reslist *request);
90static void query_name(struct reslist *request);
91static int send_res_msg(const char *buf, int len, int count);
92static void resend_query(struct reslist *request);
93static int check_question(struct reslist *request, HEADER * header, char *buf, char *eob);
94static int proc_answer(struct reslist *request, HEADER * header, char *, char *);
95static struct reslist *find_id(int id);
96static struct DNSReply *make_dnsreply(struct reslist *request);
ed62c46b
AC
97static uint16_t generate_random_id(void);
98
ed62c46b
AC
99/*
100 * int
101 * res_ourserver(inp)
102 * looks up "inp" in irc_nsaddr_list[]
103 * returns:
104 * server ID or -1 for not found
105 * author:
106 * paul vixie, 29may94
107 * revised for ircd, cryogen(stu) may03
6ee7c05c 108 * slightly modified for charybdis, mr_flea oct12
ed62c46b 109 */
c99ae190
AC
110static int
111res_ourserver(const struct rb_sockaddr_storage *inp)
ed62c46b 112{
ed62c46b
AC
113 const struct sockaddr_in6 *v6;
114 const struct sockaddr_in6 *v6in = (const struct sockaddr_in6 *)inp;
ed62c46b
AC
115 const struct sockaddr_in *v4;
116 const struct sockaddr_in *v4in = (const struct sockaddr_in *)inp;
117 int ns;
118
c99ae190 119 for(ns = 0; ns < irc_nscount; ns++)
ed62c46b
AC
120 {
121 const struct rb_sockaddr_storage *srv = &irc_nsaddr_list[ns];
ed62c46b 122 v6 = (const struct sockaddr_in6 *)srv;
ed62c46b
AC
123 v4 = (const struct sockaddr_in *)srv;
124
125 /* could probably just memcmp(srv, inp, srv.ss_len) here
c99ae190 126 * but we'll air on the side of caution - stu
ed62c46b 127 */
c99ae190 128 switch (GET_SS_FAMILY(srv))
ed62c46b 129 {
c99ae190
AC
130 case AF_INET6:
131 if(GET_SS_FAMILY(srv) == GET_SS_FAMILY(inp))
132 if(v6->sin6_port == v6in->sin6_port)
133 if((memcmp(&v6->sin6_addr.s6_addr, &v6in->sin6_addr.s6_addr,
134 sizeof(struct in6_addr)) == 0) ||
135 (memcmp(&v6->sin6_addr.s6_addr, &in6addr_any,
136 sizeof(struct in6_addr)) == 0))
137 return 1;
138 break;
c99ae190
AC
139 case AF_INET:
140 if(GET_SS_FAMILY(srv) == GET_SS_FAMILY(inp))
141 if(v4->sin_port == v4in->sin_port)
142 if((v4->sin_addr.s_addr == INADDR_ANY)
143 || (v4->sin_addr.s_addr == v4in->sin_addr.s_addr))
144 return 1;
145 break;
146 default:
147 break;
ed62c46b
AC
148 }
149 }
150
c99ae190 151 return 0;
ed62c46b
AC
152}
153
154/*
155 * timeout_query_list - Remove queries from the list which have been
156 * there too long without being resolved.
157 */
158static time_t timeout_query_list(time_t now)
159{
160 rb_dlink_node *ptr;
161 rb_dlink_node *next_ptr;
162 struct reslist *request;
163 time_t next_time = 0;
164 time_t timeout = 0;
165
166 RB_DLINK_FOREACH_SAFE(ptr, next_ptr, request_list.head)
167 {
168 request = ptr->data;
169 timeout = request->sentat + request->timeout;
170
171 if (now >= timeout)
172 {
173 ns_failure_count[request->lastns]++;
174 request->sentat = now;
175 request->timeout += request->timeout;
176 resend_query(request);
177 }
178
179 if ((next_time == 0) || timeout < next_time)
180 {
181 next_time = timeout;
182 }
183 }
184
185 return (next_time > now) ? next_time : (now + AR_TTL);
186}
187
188/*
189 * timeout_resolver - check request list
190 */
191static void timeout_resolver(void *notused)
192{
193 timeout_query_list(rb_current_time());
194}
195
196static struct ev_entry *timeout_resolver_ev = NULL;
197
198/*
199 * start_resolver - do everything we need to read the resolv.conf file
200 * and initialize the resolver file descriptor if needed
201 */
202static void start_resolver(void)
203{
204 int i;
205
206 irc_res_init();
207 for (i = 0; i < irc_nscount; i++)
208 ns_failure_count[i] = 0;
209
210 if (res_fd == NULL)
211 {
c99ae190 212 if ((res_fd = rb_socket(GET_SS_FAMILY(&irc_nsaddr_list[0]), SOCK_DGRAM, 0,
ed62c46b
AC
213 "UDP resolver socket")) == NULL)
214 return;
215
216 /* At the moment, the resolver FD data is global .. */
217 rb_setselect(res_fd, RB_SELECT_READ, res_readreply, NULL);
218 timeout_resolver_ev = rb_event_add("timeout_resolver", timeout_resolver, NULL, 1);
219 }
220}
221
222/*
223 * init_resolver - initialize resolver and resolver library
224 */
225void init_resolver(void)
226{
227#ifdef HAVE_SRAND48
228 srand48(rb_current_time());
229#endif
230 start_resolver();
231}
232
233/*
234 * restart_resolver - reread resolv.conf, reopen socket
235 */
236void restart_resolver(void)
237{
238 rb_close(res_fd);
239 res_fd = NULL;
240 rb_event_delete(timeout_resolver_ev); /* -ddosen */
241 start_resolver();
242}
243
244/*
245 * add_local_domain - Add the domain to hostname, if it is missing
246 * (as suggested by eps@TOASTER.SFSU.EDU)
247 */
856440bc 248static void add_local_domain(char *hname, size_t size)
ed62c46b
AC
249{
250 /* try to fix up unqualified names */
251 if (strchr(hname, '.') == NULL)
252 {
253 if (irc_domain[0])
254 {
255 size_t len = strlen(hname);
256
257 if ((strlen(irc_domain) + len + 2) < size)
258 {
259 hname[len++] = '.';
260 strcpy(hname + len, irc_domain);
261 }
262 }
263 }
264}
265
266/*
267 * rem_request - remove a request from the list.
268 * This must also free any memory that has been allocated for
269 * temporary storage of DNS results.
270 */
271static void rem_request(struct reslist *request)
272{
273 rb_dlinkDelete(&request->node, &request_list);
274 rb_free(request->name);
275 rb_free(request);
276}
277
278/*
279 * make_request - Create a DNS request record for the server.
280 */
281static struct reslist *make_request(struct DNSQuery *query)
282{
283 struct reslist *request = rb_malloc(sizeof(struct reslist));
284
285 request->sentat = rb_current_time();
286 request->retries = 3;
287 request->timeout = 4; /* start at 4 and exponential inc. */
288 request->query = query;
289
290 /*
291 * generate a unique id
292 * NOTE: we don't have to worry about converting this to and from
293 * network byte order, the nameserver does not interpret this value
294 * and returns it unchanged
295 *
296 * we generate an id per request now (instead of per send) to allow
297 * late replies to be used.
298 */
299 request->id = generate_random_id();
300
301 rb_dlinkAdd(request, &request->node, &request_list);
302
303 return request;
304}
305
306/*
307 * retryfreq - determine how many queries to wait before resending
308 * if there have been that many consecutive timeouts
fa2d5b83
EM
309 *
310 * This is a cubic backoff btw, if anyone didn't pick up on it. --Elizafox
ed62c46b
AC
311 */
312static int retryfreq(int timeouts)
313{
314 switch (timeouts)
315 {
fa2d5b83
EM
316 case 1:
317 return 3;
318 case 2:
319 return 9;
320 case 3:
321 return 27;
322 case 4:
323 return 81;
324 default:
325 return 243;
ed62c46b
AC
326 }
327}
328
329/*
330 * send_res_msg - sends msg to a nameserver.
331 * This should reflect /etc/resolv.conf.
332 * Returns number of nameserver successfully sent to
333 * or -1 if no successful sends.
334 */
335static int send_res_msg(const char *msg, int len, int rcount)
336{
337 int i;
338 int ns;
339 static int retrycnt;
340
341 retrycnt++;
342 /* First try a nameserver that seems to work.
343 * Every once in a while, try a possibly broken one to check
344 * if it is working again.
345 */
346 for (i = 0; i < irc_nscount; i++)
347 {
348 ns = (i + rcount - 1) % irc_nscount;
349 if (ns_failure_count[ns] && retrycnt % retryfreq(ns_failure_count[ns]))
350 continue;
351 if (sendto(rb_get_fd(res_fd), msg, len, 0,
352 (struct sockaddr *)&(irc_nsaddr_list[ns]),
353 GET_SS_LEN(&irc_nsaddr_list[ns])) == len)
354 return ns;
355 }
356
357 /* No known working nameservers, try some broken one. */
358 for (i = 0; i < irc_nscount; i++)
359 {
360 ns = (i + rcount - 1) % irc_nscount;
361 if (!ns_failure_count[ns])
362 continue;
363 if (sendto(rb_get_fd(res_fd), msg, len, 0,
364 (struct sockaddr *)&(irc_nsaddr_list[ns]),
365 GET_SS_LEN(&irc_nsaddr_list[ns])) == len)
366 return ns;
367 }
368
369 return -1;
370}
371
372/*
373 * find_id - find a dns request id (id is determined by dn_mkquery)
374 */
375static struct reslist *find_id(int id)
376{
377 rb_dlink_node *ptr;
378 struct reslist *request;
379
380 RB_DLINK_FOREACH(ptr, request_list.head)
381 {
382 request = ptr->data;
383
384 if (request->id == id)
385 return (request);
386 }
387
388 return (NULL);
389}
390
391static uint16_t
392generate_random_id(void)
393{
394 uint16_t id;
395
396 do
397 {
398 rb_get_random(&id, sizeof(id));
399 if(id == 0xffff)
400 continue;
401 }
402 while(find_id(id));
403 return id;
404}
405
ed62c46b
AC
406/*
407 * gethost_byname_type - get host address from name, adding domain if needed
408 */
409void gethost_byname_type(const char *name, struct DNSQuery *query, int type)
410{
411 char fqdn[IRCD_RES_HOSTLEN + 1];
412 assert(name != 0);
413
414 rb_strlcpy(fqdn, name, sizeof fqdn);
415 add_local_domain(fqdn, IRCD_RES_HOSTLEN);
416 gethost_byname_type_fqdn(fqdn, query, type);
417}
418
419/*
420 * gethost_byname_type_fqdn - get host address from fqdn
421 */
422static void gethost_byname_type_fqdn(const char *name, struct DNSQuery *query,
423 int type)
424{
425 assert(name != 0);
426 do_query_name(query, name, NULL, type);
427}
428
429/*
430 * gethost_byaddr - get host name from address
431 */
432void gethost_byaddr(const struct rb_sockaddr_storage *addr, struct DNSQuery *query)
433{
434 do_query_number(query, addr, NULL);
435}
436
437/*
438 * do_query_name - nameserver lookup name
439 */
440static void do_query_name(struct DNSQuery *query, const char *name, struct reslist *request,
441 int type)
442{
443 if (request == NULL)
444 {
445 request = make_request(query);
446 request->name = rb_strdup(name);
447 }
448
449 rb_strlcpy(request->queryname, name, sizeof(request->queryname));
450 request->type = type;
451 query_name(request);
452}
453
8ed8e5ca
EM
454/* Build an rDNS style query - if suffix is NULL, use the appropriate .arpa zone */
455void build_rdns(char *buf, size_t size, const struct rb_sockaddr_storage *addr, const char *suffix)
ed62c46b
AC
456{
457 const unsigned char *cp;
458
c99ae190 459 if (GET_SS_FAMILY(addr) == AF_INET)
ed62c46b
AC
460 {
461 const struct sockaddr_in *v4 = (const struct sockaddr_in *)addr;
462 cp = (const unsigned char *)&v4->sin_addr.s_addr;
463
8ed8e5ca
EM
464 (void) snprintf(buf, size, "%u.%u.%u.%u.%s",
465 (unsigned int)(cp[3]),
466 (unsigned int)(cp[2]),
467 (unsigned int)(cp[1]),
468 (unsigned int)(cp[0]),
469 suffix == NULL ? "in-addr.arpa" : suffix);
ed62c46b 470 }
c99ae190 471 else if (GET_SS_FAMILY(addr) == AF_INET6)
ed62c46b
AC
472 {
473 const struct sockaddr_in6 *v6 = (const struct sockaddr_in6 *)addr;
474 cp = (const unsigned char *)&v6->sin6_addr.s6_addr;
475
d52762b2
EM
476#define HI_NIBBLE(x) (unsigned int)((x) >> 4)
477#define LO_NIBBLE(x) (unsigned int)((x) & 0xf)
478
8ed8e5ca
EM
479 (void) snprintf(buf, size,
480 "%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%s",
d52762b2
EM
481 LO_NIBBLE(cp[15]), HI_NIBBLE(cp[15]),
482 LO_NIBBLE(cp[14]), HI_NIBBLE(cp[14]),
483 LO_NIBBLE(cp[13]), HI_NIBBLE(cp[13]),
484 LO_NIBBLE(cp[12]), HI_NIBBLE(cp[12]),
485 LO_NIBBLE(cp[11]), HI_NIBBLE(cp[11]),
486 LO_NIBBLE(cp[10]), HI_NIBBLE(cp[10]),
487 LO_NIBBLE(cp[9]), HI_NIBBLE(cp[9]),
488 LO_NIBBLE(cp[8]), HI_NIBBLE(cp[8]),
489 LO_NIBBLE(cp[7]), HI_NIBBLE(cp[7]),
490 LO_NIBBLE(cp[6]), HI_NIBBLE(cp[6]),
491 LO_NIBBLE(cp[5]), HI_NIBBLE(cp[5]),
492 LO_NIBBLE(cp[4]), HI_NIBBLE(cp[4]),
493 LO_NIBBLE(cp[3]), HI_NIBBLE(cp[3]),
494 LO_NIBBLE(cp[2]), HI_NIBBLE(cp[2]),
495 LO_NIBBLE(cp[1]), HI_NIBBLE(cp[1]),
496 LO_NIBBLE(cp[0]), HI_NIBBLE(cp[0]),
8ed8e5ca 497 suffix == NULL ? "ip6.arpa" : suffix);
ed62c46b 498 }
d52762b2
EM
499
500#undef HI_NIBBLE
501#undef LO_NIBBLE
8ed8e5ca
EM
502}
503
504/*
505 * do_query_number - Use this to do reverse IP# lookups.
506 */
507static void do_query_number(struct DNSQuery *query, const struct rb_sockaddr_storage *addr,
508 struct reslist *request)
509{
8ed8e5ca
EM
510 if (request == NULL)
511 {
512 request = make_request(query);
513 memcpy(&request->addr, addr, sizeof(struct rb_sockaddr_storage));
514 request->name = (char *)rb_malloc(IRCD_RES_HOSTLEN + 1);
515 }
516
517 build_rdns(request->queryname, IRCD_RES_HOSTLEN + 1, addr, NULL);
ed62c46b
AC
518
519 request->type = T_PTR;
520 query_name(request);
521}
522
523/*
524 * query_name - generate a query based on class, type and name.
525 */
526static void query_name(struct reslist *request)
527{
528 char buf[MAXPACKET];
529 int request_len = 0;
530 int ns;
531
532 memset(buf, 0, sizeof(buf));
533
534 if ((request_len =
535 irc_res_mkquery(request->queryname, C_IN, request->type, (unsigned char *)buf, sizeof(buf))) > 0)
536 {
537 HEADER *header = (HEADER *)(void *)buf;
538 header->id = request->id;
539 ++request->sends;
540
541 ns = send_res_msg(buf, request_len, request->sends);
542 if (ns != -1)
543 request->lastns = ns;
544 }
545}
546
547static void resend_query(struct reslist *request)
548{
549 if (--request->retries <= 0)
550 {
551 (*request->query->callback) (request->query->ptr, NULL);
552 rem_request(request);
553 return;
554 }
555
556 switch (request->type)
557 {
fa2d5b83
EM
558 case T_PTR:
559 do_query_number(NULL, &request->addr, request);
560 break;
561 case T_A:
fa2d5b83 562 case T_AAAA:
fa2d5b83
EM
563 do_query_name(NULL, request->name, request, request->type);
564 break;
565 default:
566 break;
ed62c46b
AC
567 }
568}
569
570/*
571 * check_question - check if the reply really belongs to the
572 * name we queried (to guard against late replies from previous
573 * queries with the same id).
574 */
575static int check_question(struct reslist *request, HEADER * header, char *buf, char *eob)
576{
577 char hostbuf[IRCD_RES_HOSTLEN + 1]; /* working buffer */
578 unsigned char *current; /* current position in buf */
579 int n; /* temp count */
580
581 current = (unsigned char *)buf + sizeof(HEADER);
582 if (header->qdcount != 1)
583 return 0;
584 n = irc_dn_expand((unsigned char *)buf, (unsigned char *)eob, current, hostbuf,
585 sizeof(hostbuf));
586 if (n <= 0)
587 return 0;
f956cb0f 588 if (rb_strcasecmp(hostbuf, request->queryname))
ed62c46b
AC
589 return 0;
590 return 1;
591}
592
593/*
594 * proc_answer - process name server reply
595 */
596static int proc_answer(struct reslist *request, HEADER * header, char *buf, char *eob)
597{
598 char hostbuf[IRCD_RES_HOSTLEN + 100]; /* working buffer */
599 unsigned char *current; /* current position in buf */
600 int type; /* answer type */
601 int n; /* temp count */
602 int rd_length;
603 struct sockaddr_in *v4; /* conversion */
ed62c46b 604 struct sockaddr_in6 *v6;
ed62c46b
AC
605 current = (unsigned char *)buf + sizeof(HEADER);
606
607 for (; header->qdcount > 0; --header->qdcount)
608 {
609 if ((n = irc_dn_skipname(current, (unsigned char *)eob)) < 0)
610 return 0;
611
612 current += (size_t) n + QFIXEDSZ;
613 }
614
615 /*
616 * process each answer sent to us blech.
617 */
618 while (header->ancount > 0 && (char *)current < eob)
619 {
620 header->ancount--;
621
622 n = irc_dn_expand((unsigned char *)buf, (unsigned char *)eob, current, hostbuf,
623 sizeof(hostbuf));
624
625 if (n < 0)
626 {
627 /*
628 * broken message
629 */
630 return (0);
631 }
632 else if (n == 0)
633 {
634 /*
635 * no more answers left
636 */
637 return (0);
638 }
639
640 hostbuf[IRCD_RES_HOSTLEN] = '\0';
641
642 /* With Address arithmetic you have to be very anal
643 * this code was not working on alpha due to that
644 * (spotted by rodder/jailbird/dianora)
645 */
646 current += (size_t) n;
647
648 if (!(((char *)current + ANSWER_FIXED_SIZE) < eob))
649 break;
650
651 type = irc_ns_get16(current);
652 current += TYPE_SIZE;
653
654 (void) irc_ns_get16(current);
655 current += CLASS_SIZE;
656
657 request->ttl = irc_ns_get32(current);
658 current += TTL_SIZE;
659
660 rd_length = irc_ns_get16(current);
661 current += RDLENGTH_SIZE;
662
663 /*
664 * Wait to set request->type until we verify this structure
665 */
666 switch (type)
667 {
fa2d5b83
EM
668 case T_A:
669 if (request->type != T_A)
670 return (0);
671
672 /*
673 * check for invalid rd_length or too many addresses
674 */
675 if (rd_length != sizeof(struct in_addr))
676 return (0);
677 v4 = (struct sockaddr_in *)&request->addr;
678 SET_SS_LEN(&request->addr, sizeof(struct sockaddr_in));
679 v4->sin_family = AF_INET;
680 memcpy(&v4->sin_addr, current, sizeof(struct in_addr));
681 return (1);
fa2d5b83
EM
682 case T_AAAA:
683 if (request->type != T_AAAA)
684 return (0);
685 if (rd_length != sizeof(struct in6_addr))
686 return (0);
687 SET_SS_LEN(&request->addr, sizeof(struct sockaddr_in6));
688 v6 = (struct sockaddr_in6 *)&request->addr;
689 v6->sin6_family = AF_INET6;
690 memcpy(&v6->sin6_addr, current, sizeof(struct in6_addr));
691 return (1);
fa2d5b83
EM
692 case T_PTR:
693 if (request->type != T_PTR)
694 return (0);
695 n = irc_dn_expand((unsigned char *)buf, (unsigned char *)eob, current,
696 hostbuf, sizeof(hostbuf));
697 if (n < 0)
698 return (0); /* broken message */
699 else if (n == 0)
700 return (0); /* no more answers left */
701
702 rb_strlcpy(request->name, hostbuf, IRCD_RES_HOSTLEN + 1);
703
704 return (1);
ed62c46b 705 }
1f9bc066
MS
706 /* skip any other record type e.g. CNAME, DNAME; real answer should follow */
707 current += rd_length;
ed62c46b
AC
708 }
709
710 return (1);
711}
712
713/*
714 * res_read_single_reply - read a dns reply from the nameserver and process it.
715 * Return value: 1 if a packet was read, 0 otherwise
716 */
717static int res_read_single_reply(rb_fde_t *F, void *data)
718{
719 char buf[sizeof(HEADER) + MAXPACKET]
720 /* Sparc and alpha need 16bit-alignment for accessing header->id
721 * (which is uint16_t). Because of the header = (HEADER*) buf;
722 * lateron, this is neeeded. --FaUl
723 */
724#if defined(__sparc__) || defined(__alpha__)
725 __attribute__ ((aligned(16)))
726#endif
727 ;
728 HEADER *header;
729 struct reslist *request = NULL;
730 struct DNSReply *reply = NULL;
731 int rc;
732 int answer_count;
733 socklen_t len = sizeof(struct rb_sockaddr_storage);
734 struct rb_sockaddr_storage lsin;
735 int ns;
736
737 rc = recvfrom(rb_get_fd(F), buf, sizeof(buf), 0, (struct sockaddr *)&lsin, &len);
738
739 /* No packet */
740 if (rc == 0 || rc == -1)
741 return 0;
742
743 /* Too small */
744 if (rc <= (int)(sizeof(HEADER)))
745 return 1;
746
747 /*
748 * convert DNS reply reader from Network byte order to CPU byte order.
749 */
750 header = (HEADER *)(void *)buf;
751 header->ancount = ntohs(header->ancount);
752 header->qdcount = ntohs(header->qdcount);
753 header->nscount = ntohs(header->nscount);
754 header->arcount = ntohs(header->arcount);
755
756 /*
757 * response for an id which we have already received an answer for
758 * just ignore this response.
759 */
760 if (0 == (request = find_id(header->id)))
761 return 1;
762
763 /*
764 * check against possibly fake replies
765 */
766 ns = res_ourserver(&lsin);
767 if (ns == -1)
768 return 1;
769
770 if (ns != request->lastns)
771 {
772 /*
773 * We'll accept the late reply, but penalize it a little more to make
774 * sure a laggy server doesn't end up favored.
775 */
776 ns_failure_count[ns] += 3;
777 }
778
779
780 if (!check_question(request, header, buf, buf + rc))
781 return 1;
782
783 if ((header->rcode != NO_ERRORS) || (header->ancount == 0))
784 {
785 /*
786 * RFC 2136 states that in the event of a server returning SERVFAIL
787 * or NOTIMP, the request should be resent to the next server.
788 * Additionally, if the server refuses our query, resend it as well.
789 * -- mr_flea
790 */
791 if (SERVFAIL == header->rcode || NOTIMP == header->rcode ||
792 REFUSED == header->rcode)
793 {
794 ns_failure_count[ns]++;
795 resend_query(request);
796 }
797 else
798 {
799 /*
800 * Either a fatal error was returned or no answer. Cancel the
801 * request.
802 */
803 if (NXDOMAIN == header->rcode)
804 {
805 /* If the rcode is NXDOMAIN, treat it as a good response. */
806 ns_failure_count[ns] /= 4;
807 }
808 (*request->query->callback) (request->query->ptr, NULL);
809 rem_request(request);
810 }
811 return 1;
812 }
813 /*
814 * If this fails there was an error decoding the received packet.
815 * -- jilles
816 */
817 answer_count = proc_answer(request, header, buf, buf + rc);
818
819 if (answer_count)
820 {
821 if (request->type == T_PTR)
822 {
823 if (request->name == NULL)
824 {
825 /*
826 * Got a PTR response with no name, something strange is
827 * happening. Try another DNS server.
828 */
829 ns_failure_count[ns]++;
830 resend_query(request);
831 return 1;
832 }
833
834 /*
835 * Lookup the 'authoritative' name that we were given for the
836 * ip#.
837 */
9783438e 838 if (GET_SS_FAMILY(&request->addr) == AF_INET6)
ed62c46b
AC
839 gethost_byname_type_fqdn(request->name, request->query, T_AAAA);
840 else
ed62c46b
AC
841 gethost_byname_type_fqdn(request->name, request->query, T_A);
842 rem_request(request);
843 }
844 else
845 {
846 /*
847 * got a name and address response, client resolved
848 */
849 reply = make_dnsreply(request);
850 (*request->query->callback) (request->query->ptr, reply);
851 rb_free(reply);
852 rem_request(request);
853 }
854
855 ns_failure_count[ns] /= 4;
856 }
857 else
858 {
859 /* Invalid or corrupt reply - try another resolver. */
860 ns_failure_count[ns]++;
861 resend_query(request);
862 }
863 return 1;
864}
865
866static void
867res_readreply(rb_fde_t *F, void *data)
868{
869 while (res_read_single_reply(F, data))
870 ;
871 rb_setselect(F, RB_SELECT_READ, res_readreply, NULL);
872}
873
874static struct DNSReply *
875make_dnsreply(struct reslist *request)
876{
877 struct DNSReply *cp;
878 lrb_assert(request != 0);
879
880 cp = (struct DNSReply *)rb_malloc(sizeof(struct DNSReply));
881
882 cp->h_name = request->name;
883 memcpy(&cp->addr, &request->addr, sizeof(cp->addr));
884 return (cp);
885}