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.
7 * The authors takes no responsibility for any damage or loss
8 * of property which results from the use of this software.
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>)
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.
17 * Apr 28, 2003 --cryogen and Dianora
19 * DNS server flooding lessened, AAAA-or-A lookup removed, ip6.int support
20 * removed, various robustness fixes
22 * 2006 --jilles and nenolod
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
28 * October 2012 --mr_flea
30 * ircd-ratbox changes for random IDs merged back in.
32 * January 2016 --kaniini
41 #error this code needs to be able to address individual octets
44 static PF res_readreply
;
46 #define MAXPACKET 1024 /* rfc sez 512 but we expand names so ... */
47 #define AR_TTL 600 /* TTL in seconds for dns cache entries */
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.
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)
59 struct in6_addr ipv6_addr
;
60 struct in_addr ipv4_addr
;
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) */
73 int lastns
; /* index of last server sent to */
74 struct rb_sockaddr_storage addr
;
76 struct DNSQuery
*query
; /* query callback for this request */
79 static rb_fde_t
*res_fd
;
80 static rb_dlink_list request_list
= { NULL
, NULL
, 0 };
81 static int ns_failure_count
[IRCD_MAXNS
]; /* timeouts and invalid/failed replies */
83 static void rem_request(struct reslist
*request
);
84 static struct reslist
*make_request(struct DNSQuery
*query
);
85 static void gethost_byname_type_fqdn(const char *name
, struct DNSQuery
*query
,
87 static void do_query_name(struct DNSQuery
*query
, const char *name
, struct reslist
*request
, int);
88 static void do_query_number(struct DNSQuery
*query
, const struct rb_sockaddr_storage
*,
89 struct reslist
*request
);
90 static void query_name(struct reslist
*request
);
91 static int send_res_msg(const char *buf
, int len
, int count
);
92 static void resend_query(struct reslist
*request
);
93 static int check_question(struct reslist
*request
, HEADER
* header
, char *buf
, char *eob
);
94 static int proc_answer(struct reslist
*request
, HEADER
* header
, char *, char *);
95 static struct reslist
*find_id(int id
);
96 static struct DNSReply
*make_dnsreply(struct reslist
*request
);
97 static uint16_t generate_random_id(void);
102 * looks up "inp" in irc_nsaddr_list[]
104 * server ID or -1 for not found
106 * paul vixie, 29may94
107 * revised for ircd, cryogen(stu) may03
108 * slightly modified for charybdis, mr_flea oct12
111 res_ourserver(const struct rb_sockaddr_storage
*inp
)
113 const struct sockaddr_in6
*v6
;
114 const struct sockaddr_in6
*v6in
= (const struct sockaddr_in6
*)inp
;
115 const struct sockaddr_in
*v4
;
116 const struct sockaddr_in
*v4in
= (const struct sockaddr_in
*)inp
;
119 for(ns
= 0; ns
< irc_nscount
; ns
++)
121 const struct rb_sockaddr_storage
*srv
= &irc_nsaddr_list
[ns
];
122 v6
= (const struct sockaddr_in6
*)srv
;
123 v4
= (const struct sockaddr_in
*)srv
;
125 /* could probably just memcmp(srv, inp, srv.ss_len) here
126 * but we'll air on the side of caution - stu
128 switch (GET_SS_FAMILY(srv
))
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))
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
))
155 * timeout_query_list - Remove queries from the list which have been
156 * there too long without being resolved.
158 static time_t timeout_query_list(time_t now
)
161 rb_dlink_node
*next_ptr
;
162 struct reslist
*request
;
163 time_t next_time
= 0;
166 RB_DLINK_FOREACH_SAFE(ptr
, next_ptr
, request_list
.head
)
169 timeout
= request
->sentat
+ request
->timeout
;
173 ns_failure_count
[request
->lastns
]++;
174 request
->sentat
= now
;
175 request
->timeout
+= request
->timeout
;
176 resend_query(request
);
179 if ((next_time
== 0) || timeout
< next_time
)
185 return (next_time
> now
) ? next_time
: (now
+ AR_TTL
);
189 * timeout_resolver - check request list
191 static void timeout_resolver(void *notused
)
193 timeout_query_list(rb_current_time());
196 static struct ev_entry
*timeout_resolver_ev
= NULL
;
199 * start_resolver - do everything we need to read the resolv.conf file
200 * and initialize the resolver file descriptor if needed
202 static void start_resolver(void)
207 for (i
= 0; i
< irc_nscount
; i
++)
208 ns_failure_count
[i
] = 0;
212 if ((res_fd
= rb_socket(GET_SS_FAMILY(&irc_nsaddr_list
[0]), SOCK_DGRAM
, 0,
213 "UDP resolver socket")) == NULL
)
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);
223 * init_resolver - initialize resolver and resolver library
225 void init_resolver(void)
228 srand48(rb_current_time());
234 * restart_resolver - reread resolv.conf, reopen socket
236 void restart_resolver(void)
240 rb_event_delete(timeout_resolver_ev
); /* -ddosen */
245 * add_local_domain - Add the domain to hostname, if it is missing
246 * (as suggested by eps@TOASTER.SFSU.EDU)
248 static void add_local_domain(char *hname
, size_t size
)
250 /* try to fix up unqualified names */
251 if (strchr(hname
, '.') == NULL
)
255 size_t len
= strlen(hname
);
257 if ((strlen(irc_domain
) + len
+ 2) < size
)
260 strcpy(hname
+ len
, irc_domain
);
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.
271 static void rem_request(struct reslist
*request
)
273 rb_dlinkDelete(&request
->node
, &request_list
);
274 rb_free(request
->name
);
279 * make_request - Create a DNS request record for the server.
281 static struct reslist
*make_request(struct DNSQuery
*query
)
283 struct reslist
*request
= rb_malloc(sizeof(struct reslist
));
285 request
->sentat
= rb_current_time();
286 request
->retries
= 3;
287 request
->timeout
= 4; /* start at 4 and exponential inc. */
288 request
->query
= query
;
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
296 * we generate an id per request now (instead of per send) to allow
297 * late replies to be used.
299 request
->id
= generate_random_id();
301 rb_dlinkAdd(request
, &request
->node
, &request_list
);
307 * retryfreq - determine how many queries to wait before resending
308 * if there have been that many consecutive timeouts
310 * This is a cubic backoff btw, if anyone didn't pick up on it. --Elizafox
312 static int retryfreq(int timeouts
)
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.
335 static int send_res_msg(const char *msg
, int len
, int rcount
)
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.
346 for (i
= 0; i
< irc_nscount
; i
++)
348 ns
= (i
+ rcount
- 1) % irc_nscount
;
349 if (ns_failure_count
[ns
] && retrycnt
% retryfreq(ns_failure_count
[ns
]))
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
)
357 /* No known working nameservers, try some broken one. */
358 for (i
= 0; i
< irc_nscount
; i
++)
360 ns
= (i
+ rcount
- 1) % irc_nscount
;
361 if (!ns_failure_count
[ns
])
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
)
373 * find_id - find a dns request id (id is determined by dn_mkquery)
375 static struct reslist
*find_id(int id
)
378 struct reslist
*request
;
380 RB_DLINK_FOREACH(ptr
, request_list
.head
)
384 if (request
->id
== id
)
392 generate_random_id(void)
398 rb_get_random(&id
, sizeof(id
));
407 * gethost_byname_type - get host address from name, adding domain if needed
409 void gethost_byname_type(const char *name
, struct DNSQuery
*query
, int type
)
411 char fqdn
[IRCD_RES_HOSTLEN
+ 1];
414 rb_strlcpy(fqdn
, name
, sizeof fqdn
);
415 add_local_domain(fqdn
, IRCD_RES_HOSTLEN
);
416 gethost_byname_type_fqdn(fqdn
, query
, type
);
420 * gethost_byname_type_fqdn - get host address from fqdn
422 static void gethost_byname_type_fqdn(const char *name
, struct DNSQuery
*query
,
426 do_query_name(query
, name
, NULL
, type
);
430 * gethost_byaddr - get host name from address
432 void gethost_byaddr(const struct rb_sockaddr_storage
*addr
, struct DNSQuery
*query
)
434 do_query_number(query
, addr
, NULL
);
438 * do_query_name - nameserver lookup name
440 static void do_query_name(struct DNSQuery
*query
, const char *name
, struct reslist
*request
,
445 request
= make_request(query
);
446 request
->name
= rb_strdup(name
);
449 rb_strlcpy(request
->queryname
, name
, sizeof(request
->queryname
));
450 request
->type
= type
;
454 /* Build an rDNS style query - if suffix is NULL, use the appropriate .arpa zone */
455 void build_rdns(char *buf
, size_t size
, const struct rb_sockaddr_storage
*addr
, const char *suffix
)
457 const unsigned char *cp
;
459 if (GET_SS_FAMILY(addr
) == AF_INET
)
461 const struct sockaddr_in
*v4
= (const struct sockaddr_in
*)addr
;
462 cp
= (const unsigned char *)&v4
->sin_addr
.s_addr
;
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
);
471 else if (GET_SS_FAMILY(addr
) == AF_INET6
)
473 const struct sockaddr_in6
*v6
= (const struct sockaddr_in6
*)addr
;
474 cp
= (const unsigned char *)&v6
->sin6_addr
.s6_addr
;
476 #define HI_NIBBLE(x) (unsigned int)((x) >> 4)
477 #define LO_NIBBLE(x) (unsigned int)((x) & 0xf)
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",
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]),
497 suffix
== NULL
? "ip6.arpa" : suffix
);
505 * do_query_number - Use this to do reverse IP# lookups.
507 static void do_query_number(struct DNSQuery
*query
, const struct rb_sockaddr_storage
*addr
,
508 struct reslist
*request
)
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);
517 build_rdns(request
->queryname
, IRCD_RES_HOSTLEN
+ 1, addr
, NULL
);
519 request
->type
= T_PTR
;
524 * query_name - generate a query based on class, type and name.
526 static void query_name(struct reslist
*request
)
532 memset(buf
, 0, sizeof(buf
));
535 irc_res_mkquery(request
->queryname
, C_IN
, request
->type
, (unsigned char *)buf
, sizeof(buf
))) > 0)
537 HEADER
*header
= (HEADER
*)(void *)buf
;
538 header
->id
= request
->id
;
541 ns
= send_res_msg(buf
, request_len
, request
->sends
);
543 request
->lastns
= ns
;
547 static void resend_query(struct reslist
*request
)
549 if (--request
->retries
<= 0)
551 (*request
->query
->callback
) (request
->query
->ptr
, NULL
);
552 rem_request(request
);
556 switch (request
->type
)
559 do_query_number(NULL
, &request
->addr
, request
);
563 do_query_name(NULL
, request
->name
, request
, request
->type
);
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).
575 static int check_question(struct reslist
*request
, HEADER
* header
, char *buf
, char *eob
)
577 char hostbuf
[IRCD_RES_HOSTLEN
+ 1]; /* working buffer */
578 unsigned char *current
; /* current position in buf */
579 int n
; /* temp count */
581 current
= (unsigned char *)buf
+ sizeof(HEADER
);
582 if (header
->qdcount
!= 1)
584 n
= irc_dn_expand((unsigned char *)buf
, (unsigned char *)eob
, current
, hostbuf
,
588 if (rb_strcasecmp(hostbuf
, request
->queryname
))
594 * proc_answer - process name server reply
596 static int proc_answer(struct reslist
*request
, HEADER
* header
, char *buf
, char *eob
)
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 */
603 struct sockaddr_in
*v4
; /* conversion */
604 struct sockaddr_in6
*v6
;
605 current
= (unsigned char *)buf
+ sizeof(HEADER
);
607 for (; header
->qdcount
> 0; --header
->qdcount
)
609 if ((n
= irc_dn_skipname(current
, (unsigned char *)eob
)) < 0)
612 current
+= (size_t) n
+ QFIXEDSZ
;
616 * process each answer sent to us blech.
618 while (header
->ancount
> 0 && (char *)current
< eob
)
622 n
= irc_dn_expand((unsigned char *)buf
, (unsigned char *)eob
, current
, hostbuf
,
635 * no more answers left
640 hostbuf
[IRCD_RES_HOSTLEN
] = '\0';
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)
646 current
+= (size_t) n
;
648 if (!(((char *)current
+ ANSWER_FIXED_SIZE
) < eob
))
651 type
= irc_ns_get16(current
);
652 current
+= TYPE_SIZE
;
654 (void) irc_ns_get16(current
);
655 current
+= CLASS_SIZE
;
657 request
->ttl
= irc_ns_get32(current
);
660 rd_length
= irc_ns_get16(current
);
661 current
+= RDLENGTH_SIZE
;
664 * Wait to set request->type until we verify this structure
669 if (request
->type
!= T_A
)
673 * check for invalid rd_length or too many addresses
675 if (rd_length
!= sizeof(struct in_addr
))
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
));
683 if (request
->type
!= T_AAAA
)
685 if (rd_length
!= sizeof(struct in6_addr
))
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
));
693 if (request
->type
!= T_PTR
)
695 n
= irc_dn_expand((unsigned char *)buf
, (unsigned char *)eob
, current
,
696 hostbuf
, sizeof(hostbuf
));
698 return (0); /* broken message */
700 return (0); /* no more answers left */
702 rb_strlcpy(request
->name
, hostbuf
, IRCD_RES_HOSTLEN
+ 1);
706 /* skip any other record type e.g. CNAME, DNAME; real answer should follow */
707 current
+= rd_length
;
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
717 static int res_read_single_reply(rb_fde_t
*F
, void *data
)
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
724 #if defined(__sparc__) || defined(__alpha__)
725 __attribute__ ((aligned(16)))
729 struct reslist
*request
= NULL
;
730 struct DNSReply
*reply
= NULL
;
733 socklen_t len
= sizeof(struct rb_sockaddr_storage
);
734 struct rb_sockaddr_storage lsin
;
737 rc
= recvfrom(rb_get_fd(F
), buf
, sizeof(buf
), 0, (struct sockaddr
*)&lsin
, &len
);
740 if (rc
== 0 || rc
== -1)
744 if (rc
<= (int)(sizeof(HEADER
)))
748 * convert DNS reply reader from Network byte order to CPU byte order.
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
);
757 * response for an id which we have already received an answer for
758 * just ignore this response.
760 if (0 == (request
= find_id(header
->id
)))
764 * check against possibly fake replies
766 ns
= res_ourserver(&lsin
);
770 if (ns
!= request
->lastns
)
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.
776 ns_failure_count
[ns
] += 3;
780 if (!check_question(request
, header
, buf
, buf
+ rc
))
783 if ((header
->rcode
!= NO_ERRORS
) || (header
->ancount
== 0))
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.
791 if (SERVFAIL
== header
->rcode
|| NOTIMP
== header
->rcode
||
792 REFUSED
== header
->rcode
)
794 ns_failure_count
[ns
]++;
795 resend_query(request
);
800 * Either a fatal error was returned or no answer. Cancel the
803 if (NXDOMAIN
== header
->rcode
)
805 /* If the rcode is NXDOMAIN, treat it as a good response. */
806 ns_failure_count
[ns
] /= 4;
808 (*request
->query
->callback
) (request
->query
->ptr
, NULL
);
809 rem_request(request
);
814 * If this fails there was an error decoding the received packet.
817 answer_count
= proc_answer(request
, header
, buf
, buf
+ rc
);
821 if (request
->type
== T_PTR
)
823 if (request
->name
== NULL
)
826 * Got a PTR response with no name, something strange is
827 * happening. Try another DNS server.
829 ns_failure_count
[ns
]++;
830 resend_query(request
);
835 * Lookup the 'authoritative' name that we were given for the
838 if (GET_SS_FAMILY(&request
->addr
) == AF_INET6
)
839 gethost_byname_type_fqdn(request
->name
, request
->query
, T_AAAA
);
841 gethost_byname_type_fqdn(request
->name
, request
->query
, T_A
);
842 rem_request(request
);
847 * got a name and address response, client resolved
849 reply
= make_dnsreply(request
);
850 (*request
->query
->callback
) (request
->query
->ptr
, reply
);
852 rem_request(request
);
855 ns_failure_count
[ns
] /= 4;
859 /* Invalid or corrupt reply - try another resolver. */
860 ns_failure_count
[ns
]++;
861 resend_query(request
);
867 res_readreply(rb_fde_t
*F
, void *data
)
869 while (res_read_single_reply(F
, data
))
871 rb_setselect(F
, RB_SELECT_READ
, res_readreply
, NULL
);
874 static struct DNSReply
*
875 make_dnsreply(struct reslist
*request
)
878 lrb_assert(request
!= 0);
880 cp
= (struct DNSReply
*)rb_malloc(sizeof(struct DNSReply
));
882 cp
->h_name
= request
->name
;
883 memcpy(&cp
->addr
, &request
->addr
, sizeof(cp
->addr
));