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