X-Git-Url: https://jfr.im/git/solanum.git/blobdiff_plain/81af5bcb394280ff1af82486961d852b102b923b..0191e3d3ebaa8b58a2b232984fdadbccba732176:/src/res.c diff --git a/src/res.c b/src/res.c index 22f0993c..5fe6fc6d 100644 --- a/src/res.c +++ b/src/res.c @@ -15,7 +15,7 @@ * --Bleep (Thomas Helvey ) * * This was all needlessly complicated for irc. Simplified. No more hostent - * All we really care about is the IP -> hostname mappings. Thats all. + * All we really care about is the IP -> hostname mappings. Thats all. * * Apr 28, 2003 --cryogen and Dianora * @@ -23,6 +23,12 @@ * removed, various robustness fixes * * 2006 --jilles and nenolod + * + * Resend queries to other servers if the DNS server replies with an error or + * an invalid response. Also, avoid servers that return errors or invalid + * responses. + * + * October 2012 --mr_flea */ #include "stdinc.h" @@ -31,10 +37,12 @@ #include "ircd.h" #include "res.h" #include "reslib.h" -#include "irc_string.h" -#include "sprintf_irc.h" +#include "match.h" #include "numeric.h" #include "client.h" /* SNO_* */ +#include "s_assert.h" +#include "logger.h" +#include "send.h" #if (CHAR_BIT != 8) #error this code needs to be able to address individual octets @@ -57,40 +65,33 @@ static PF res_readreply; #define RDLENGTH_SIZE (size_t)2 #define ANSWER_FIXED_SIZE (TYPE_SIZE + CLASS_SIZE + TTL_SIZE + RDLENGTH_SIZE) -typedef enum -{ - REQ_IDLE, /* We're doing not much at all */ - REQ_PTR, /* Looking up a PTR */ - REQ_A, /* Looking up an A or AAAA */ - REQ_CNAME /* We got a CNAME in response, we better get a real answer next */ -} request_state; - struct reslist { rb_dlink_node node; int id; - int sent; /* number of requests sent */ - request_state state; /* State the resolver machine is in */ time_t ttl; char type; - char queryname[128]; /* name currently being queried */ + char queryname[IRCD_RES_HOSTLEN + 1]; /* name currently being queried */ char retries; /* retry counter */ char sends; /* number of sends (>1 means resent) */ - char resend; /* send flag. 0 == dont resend */ time_t sentat; time_t timeout; - struct irc_sockaddr_storage addr; + int lastns; /* index of last server sent to */ + struct rb_sockaddr_storage addr; char *name; struct DNSQuery *query; /* query callback for this request */ }; -static int res_fd; +static rb_fde_t *res_fd; static rb_dlink_list request_list = { NULL, NULL, 0 }; +static int ns_failure_count[IRCD_MAXNS]; /* timeouts and invalid/failed replies */ static void rem_request(struct reslist *request); static struct reslist *make_request(struct DNSQuery *query); +static void gethost_byname_type_fqdn(const char *name, struct DNSQuery *query, + int type); static void do_query_name(struct DNSQuery *query, const char *name, struct reslist *request, int); -static void do_query_number(struct DNSQuery *query, const struct irc_sockaddr_storage *, +static void do_query_number(struct DNSQuery *query, const struct rb_sockaddr_storage *, struct reslist *request); static void query_name(struct reslist *request); static int send_res_msg(const char *buf, int len, int count); @@ -100,73 +101,74 @@ static int proc_answer(struct reslist *request, HEADER * header, char *, char *) static struct reslist *find_id(int id); static struct DNSReply *make_dnsreply(struct reslist *request); -extern struct irc_sockaddr_storage irc_nsaddr_list[IRCD_MAXNS]; -extern int irc_nscount; -extern char irc_domain[HOSTLEN + 1]; - - /* * int * res_ourserver(inp) * looks up "inp" in irc_nsaddr_list[] * returns: - * 0 : not found - * >0 : found + * server ID or -1 for not found * author: * paul vixie, 29may94 * revised for ircd, cryogen(stu) may03 + * slightly modified for charybdis, mr_flea oct12 */ -static int res_ourserver(const struct irc_sockaddr_storage *inp) +static int res_ourserver(const struct rb_sockaddr_storage *inp) { -#ifdef IPV6 - struct sockaddr_in6 *v6; - struct sockaddr_in6 *v6in = (struct sockaddr_in6 *)inp; +#ifdef RB_IPV6 + const struct sockaddr_in6 *v6; + const struct sockaddr_in6 *v6in = (const struct sockaddr_in6 *)inp; #endif - struct sockaddr_in *v4; - struct sockaddr_in *v4in = (struct sockaddr_in *)inp; + const struct sockaddr_in *v4; + const struct sockaddr_in *v4in = (const struct sockaddr_in *)inp; int ns; for (ns = 0; ns < irc_nscount; ns++) { - const struct irc_sockaddr_storage *srv = &irc_nsaddr_list[ns]; -#ifdef IPV6 - v6 = (struct sockaddr_in6 *)srv; + const struct rb_sockaddr_storage *srv = &irc_nsaddr_list[ns]; + + if (srv->ss_family != inp->ss_family) + continue; + +#ifdef RB_IPV6 + v6 = (const struct sockaddr_in6 *)srv; #endif - v4 = (struct sockaddr_in *)srv; + v4 = (const struct sockaddr_in *)srv; /* could probably just memcmp(srv, inp, srv.ss_len) here - * but we'll air on the side of caution - stu + * but we'll err on the side of caution - stu */ switch (srv->ss_family) { -#ifdef IPV6 - case AF_INET6: - if (srv->ss_family == inp->ss_family) - if (v6->sin6_port == v6in->sin6_port) - if ((memcmp(&v6->sin6_addr.s6_addr, &v6in->sin6_addr.s6_addr, - sizeof(struct in6_addr)) == 0) || - (memcmp(&v6->sin6_addr.s6_addr, &in6addr_any, - sizeof(struct in6_addr)) == 0)) - return 1; - break; +#ifdef RB_IPV6 + case AF_INET6: + if (v6->sin6_port == v6in->sin6_port) + if ((memcmp(&v6->sin6_addr.s6_addr, &v6in->sin6_addr.s6_addr, + sizeof(struct in6_addr)) == 0) || + (memcmp(&v6->sin6_addr.s6_addr, &in6addr_any, + sizeof(struct in6_addr)) == 0)) + { + return ns; + } + break; #endif - case AF_INET: - if (srv->ss_family == inp->ss_family) - if (v4->sin_port == v4in->sin_port) - if ((v4->sin_addr.s_addr == INADDR_ANY) - || (v4->sin_addr.s_addr == v4in->sin_addr.s_addr)) - return 1; - break; - default: - break; + case AF_INET: + if (v4->sin_port == v4in->sin_port) + if ((v4->sin_addr.s_addr == INADDR_ANY) + || (v4->sin_addr.s_addr == v4in->sin_addr.s_addr)) + { + return ns; + } + break; + default: + break; } } - return 0; + return -1; } /* - * timeout_query_list - Remove queries from the list which have been + * timeout_query_list - Remove queries from the list which have been * there too long without being resolved. */ static time_t timeout_query_list(time_t now) @@ -184,18 +186,10 @@ static time_t timeout_query_list(time_t now) if (now >= timeout) { - if (--request->retries <= 0) - { - (*request->query->callback) (request->query->ptr, NULL); - rem_request(request); - continue; - } - else - { - request->sentat = now; - request->timeout += request->timeout; - resend_query(request); - } + ns_failure_count[request->lastns]++; + request->sentat = now; + request->timeout += request->timeout; + resend_query(request); } if ((next_time == 0) || timeout < next_time) @@ -212,26 +206,32 @@ static time_t timeout_query_list(time_t now) */ static void timeout_resolver(void *notused) { - timeout_query_list(CurrentTime); + timeout_query_list(rb_current_time()); } +static struct ev_entry *timeout_resolver_ev = NULL; + /* * start_resolver - do everything we need to read the resolv.conf file * and initialize the resolver file descriptor if needed */ static void start_resolver(void) { + int i; + irc_res_init(); + for (i = 0; i < irc_nscount; i++) + ns_failure_count[i] = 0; - if (res_fd <= 0) /* there isn't any such thing as fd 0, that's just a myth. */ + if (res_fd == NULL) { if ((res_fd = rb_socket(irc_nsaddr_list[0].ss_family, SOCK_DGRAM, 0, - "UDP resolver socket")) == -1) + "UDP resolver socket")) == NULL) return; /* At the moment, the resolver FD data is global .. */ - rb_setselect(res_fd, FDLIST_NONE, COMM_SELECT_READ, res_readreply, NULL, 0); - eventAdd("timeout_resolver", timeout_resolver, NULL, 1); + rb_setselect(res_fd, RB_SELECT_READ, res_readreply, NULL); + timeout_resolver_ev = rb_event_add("timeout_resolver", timeout_resolver, NULL, 1); } } @@ -241,7 +241,7 @@ static void start_resolver(void) void init_resolver(void) { #ifdef HAVE_SRAND48 - srand48(CurrentTime); + srand48(rb_current_time()); #endif start_resolver(); } @@ -252,8 +252,8 @@ void init_resolver(void) void restart_resolver(void) { rb_close(res_fd); - res_fd = -1; - eventDelete(timeout_resolver, NULL); /* -ddosen */ + res_fd = NULL; + rb_event_delete(timeout_resolver_ev); /* -ddosen */ start_resolver(); } @@ -280,15 +280,15 @@ void add_local_domain(char *hname, size_t size) } /* - * rem_request - remove a request from the list. - * This must also free any memory that has been allocated for + * rem_request - remove a request from the list. + * This must also free any memory that has been allocated for * temporary storage of DNS results. */ static void rem_request(struct reslist *request) { rb_dlinkDelete(&request->node, &request_list); - MyFree(request->name); - MyFree(request); + rb_free(request->name); + rb_free(request); } /* @@ -296,14 +296,37 @@ static void rem_request(struct reslist *request) */ static struct reslist *make_request(struct DNSQuery *query) { - struct reslist *request = MyMalloc(sizeof(struct reslist)); + struct reslist *request = rb_malloc(sizeof(struct reslist)); - request->sentat = CurrentTime; + request->sentat = rb_current_time(); request->retries = 3; - request->resend = 1; request->timeout = 4; /* start at 4 and exponential inc. */ request->query = query; - request->state = REQ_IDLE; + + /* + * generate a unique id + * NOTE: we don't have to worry about converting this to and from + * network byte order, the nameserver does not interpret this value + * and returns it unchanged + * + * we generate an id per request now (instead of per send) to allow + * late replies to be used. + */ +#ifdef HAVE_LRAND48 + do + { + request->id = (request->id + lrand48()) & 0xffff; + } while (find_id(request->id)); +#else + int k = 0; + struct timeval tv; + gettimeofday(&tv, NULL); + do + { + request->id = (request->id + k + tv.tv_usec) & 0xffff; + k++; + } while (find_id(request->id)); +#endif /* HAVE_LRAND48 */ rb_dlinkAdd(request, &request->node, &request_list); @@ -311,7 +334,7 @@ static struct reslist *make_request(struct DNSQuery *query) } /* - * delete_resolver_queries - cleanup outstanding queries + * delete_resolver_queries - cleanup outstanding queries * for which there no longer exist clients or conf lines. */ void delete_resolver_queries(const struct DNSQuery *query) @@ -331,33 +354,67 @@ void delete_resolver_queries(const struct DNSQuery *query) } /* - * send_res_msg - sends msg to all nameservers found in the "_res" structure. - * This should reflect /etc/resolv.conf. We will get responses - * which arent needed but is easier than checking to see if nameserver - * isnt present. Returns number of messages successfully sent to - * nameservers or -1 if no successful sends. + * retryfreq - determine how many queries to wait before resending + * if there have been that many consecutive timeouts + */ +static int retryfreq(int timeouts) +{ + switch (timeouts) + { + case 1: + return 3; + case 2: + return 9; + case 3: + return 27; + case 4: + return 81; + default: + return 243; + } +} + +/* + * send_res_msg - sends msg to a nameserver. + * This should reflect /etc/resolv.conf. + * Returns number of nameserver successfully sent to + * or -1 if no successful sends. */ static int send_res_msg(const char *msg, int len, int rcount) { int i; - int sent = 0; - int max_queries = IRCD_MIN(irc_nscount, rcount); + int ns; + static int retrycnt; - /* RES_PRIMARY option is not implemented - * if (res.options & RES_PRIMARY || 0 == max_queries) + retrycnt++; + /* First try a nameserver that seems to work. + * Every once in a while, try a possibly broken one to check + * if it is working again. */ - if (max_queries == 0) - max_queries = 1; + for (i = 0; i < irc_nscount; i++) + { + ns = (i + rcount - 1) % irc_nscount; + if (ns_failure_count[ns] && retrycnt % retryfreq(ns_failure_count[ns])) + continue; + if (sendto(rb_get_fd(res_fd), msg, len, 0, + (struct sockaddr *)&(irc_nsaddr_list[ns]), + GET_SS_LEN(&irc_nsaddr_list[ns])) == len) + return ns; + } - for (i = 0; sent < max_queries && i < irc_nscount; i++) + /* No known working nameservers, try some broken one. */ + for (i = 0; i < irc_nscount; i++) { - if (sendto(res_fd, msg, len, 0, - (struct sockaddr *)&(irc_nsaddr_list[i]), - GET_SS_LEN(irc_nsaddr_list[i])) == len) - ++sent; + ns = (i + rcount - 1) % irc_nscount; + if (!ns_failure_count[ns]) + continue; + if (sendto(rb_get_fd(res_fd), msg, len, 0, + (struct sockaddr *)&(irc_nsaddr_list[ns]), + GET_SS_LEN(&irc_nsaddr_list[ns])) == len) + return ns; } - return (sent); + return -1; } /* @@ -379,11 +436,24 @@ static struct reslist *find_id(int id) return (NULL); } -/* - * gethost_byname_type - get host address from name - * +/* + * gethost_byname_type - get host address from name, adding domain if needed */ void gethost_byname_type(const char *name, struct DNSQuery *query, int type) +{ + char fqdn[IRCD_RES_HOSTLEN + 1]; + assert(name != 0); + + rb_strlcpy(fqdn, name, sizeof fqdn); + add_local_domain(fqdn, IRCD_RES_HOSTLEN); + gethost_byname_type_fqdn(fqdn, query, type); +} + +/* + * gethost_byname_type_fqdn - get host address from fqdn + */ +static void gethost_byname_type_fqdn(const char *name, struct DNSQuery *query, + int type) { assert(name != 0); do_query_name(query, name, NULL, type); @@ -392,7 +462,7 @@ void gethost_byname_type(const char *name, struct DNSQuery *query, int type) /* * gethost_byaddr - get host name from address */ -void gethost_byaddr(const struct irc_sockaddr_storage *addr, struct DNSQuery *query) +void gethost_byaddr(const struct rb_sockaddr_storage *addr, struct DNSQuery *query) { do_query_number(query, addr, NULL); } @@ -403,20 +473,13 @@ void gethost_byaddr(const struct irc_sockaddr_storage *addr, struct DNSQuery *qu static void do_query_name(struct DNSQuery *query, const char *name, struct reslist *request, int type) { - char host_name[HOSTLEN + 1]; - - strlcpy(host_name, name, HOSTLEN + 1); - add_local_domain(host_name, HOSTLEN); - if (request == NULL) { request = make_request(query); - request->name = (char *)MyMalloc(strlen(host_name) + 1); - strcpy(request->name, host_name); - request->state = REQ_A; + request->name = rb_strdup(name); } - strlcpy(request->queryname, host_name, sizeof(request->queryname)); + rb_strlcpy(request->queryname, name, sizeof(request->queryname)); request->type = type; query_name(request); } @@ -424,7 +487,7 @@ static void do_query_name(struct DNSQuery *query, const char *name, struct resli /* * do_query_number - Use this to do reverse IP# lookups. */ -static void do_query_number(struct DNSQuery *query, const struct irc_sockaddr_storage *addr, +static void do_query_number(struct DNSQuery *query, const struct rb_sockaddr_storage *addr, struct reslist *request) { const unsigned char *cp; @@ -432,22 +495,22 @@ static void do_query_number(struct DNSQuery *query, const struct irc_sockaddr_st if (request == NULL) { request = make_request(query); - memcpy(&request->addr, addr, sizeof(struct irc_sockaddr_storage)); - request->name = (char *)MyMalloc(HOSTLEN + 1); + memcpy(&request->addr, addr, sizeof(struct rb_sockaddr_storage)); + request->name = (char *)rb_malloc(IRCD_RES_HOSTLEN + 1); } if (addr->ss_family == AF_INET) { - struct sockaddr_in *v4 = (struct sockaddr_in *)addr; + const struct sockaddr_in *v4 = (const struct sockaddr_in *)addr; cp = (const unsigned char *)&v4->sin_addr.s_addr; rb_sprintf(request->queryname, "%u.%u.%u.%u.in-addr.arpa", (unsigned int)(cp[3]), (unsigned int)(cp[2]), (unsigned int)(cp[1]), (unsigned int)(cp[0])); } -#ifdef IPV6 +#ifdef RB_IPV6 else if (addr->ss_family == AF_INET6) { - struct sockaddr_in6 *v6 = (struct sockaddr_in6 *)addr; + const struct sockaddr_in6 *v6 = (const struct sockaddr_in6 *)addr; cp = (const unsigned char *)&v6->sin6_addr.s6_addr; (void)sprintf(request->queryname, "%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x." @@ -482,47 +545,31 @@ static void query_name(struct reslist *request) { char buf[MAXPACKET]; int request_len = 0; + int ns; memset(buf, 0, sizeof(buf)); if ((request_len = irc_res_mkquery(request->queryname, C_IN, request->type, (unsigned char *)buf, sizeof(buf))) > 0) { - HEADER *header = (HEADER *) buf; -#ifndef HAVE_LRAND48 - int k = 0; - struct timeval tv; -#endif - /* - * generate an unique id - * NOTE: we don't have to worry about converting this to and from - * network byte order, the nameserver does not interpret this value - * and returns it unchanged - */ -#ifdef HAVE_LRAND48 - do - { - header->id = (header->id + lrand48()) & 0xffff; - } while (find_id(header->id)); -#else - gettimeofday(&tv, NULL); - do - { - header->id = (header->id + k + tv.tv_usec) & 0xffff; - k++; - } while (find_id(header->id)); -#endif /* HAVE_LRAND48 */ - request->id = header->id; + HEADER *header = (HEADER *)(void *)buf; + header->id = request->id; ++request->sends; - request->sent += send_res_msg(buf, request_len, request->sends); + ns = send_res_msg(buf, request_len, request->sends); + if (ns != -1) + request->lastns = ns; } } static void resend_query(struct reslist *request) { - if (request->resend == 0) + if (--request->retries <= 0) + { + (*request->query->callback) (request->query->ptr, NULL); + rem_request(request); return; + } switch (request->type) { @@ -530,7 +577,7 @@ static void resend_query(struct reslist *request) do_query_number(NULL, &request->addr, request); break; case T_A: -#ifdef IPV6 +#ifdef RB_IPV6 case T_AAAA: #endif do_query_name(NULL, request->name, request, request->type); @@ -547,7 +594,7 @@ static void resend_query(struct reslist *request) */ static int check_question(struct reslist *request, HEADER * header, char *buf, char *eob) { - char hostbuf[128]; /* working buffer */ + char hostbuf[IRCD_RES_HOSTLEN + 1]; /* working buffer */ unsigned char *current; /* current position in buf */ int n; /* temp count */ @@ -568,14 +615,13 @@ static int check_question(struct reslist *request, HEADER * header, char *buf, c */ static int proc_answer(struct reslist *request, HEADER * header, char *buf, char *eob) { - char hostbuf[HOSTLEN + 100]; /* working buffer */ + char hostbuf[IRCD_RES_HOSTLEN + 100]; /* working buffer */ unsigned char *current; /* current position in buf */ - int query_class; /* answer class */ int type; /* answer type */ int n; /* temp count */ int rd_length; struct sockaddr_in *v4; /* conversion */ -#ifdef IPV6 +#ifdef RB_IPV6 struct sockaddr_in6 *v6; #endif current = (unsigned char *)buf + sizeof(HEADER); @@ -613,7 +659,7 @@ static int proc_answer(struct reslist *request, HEADER * header, char *buf, char return (0); } - hostbuf[HOSTLEN] = '\0'; + hostbuf[IRCD_RES_HOSTLEN] = '\0'; /* With Address arithmetic you have to be very anal * this code was not working on alpha due to that @@ -627,7 +673,7 @@ static int proc_answer(struct reslist *request, HEADER * header, char *buf, char type = irc_ns_get16(current); current += TYPE_SIZE; - query_class = irc_ns_get16(current); + (void) irc_ns_get16(current); current += CLASS_SIZE; request->ttl = irc_ns_get32(current); @@ -636,8 +682,8 @@ static int proc_answer(struct reslist *request, HEADER * header, char *buf, char rd_length = irc_ns_get16(current); current += RDLENGTH_SIZE; - /* - * Wait to set request->type until we verify this structure + /* + * Wait to set request->type until we verify this structure */ switch (type) { @@ -651,18 +697,18 @@ static int proc_answer(struct reslist *request, HEADER * header, char *buf, char if (rd_length != sizeof(struct in_addr)) return (0); v4 = (struct sockaddr_in *)&request->addr; - SET_SS_LEN(request->addr, sizeof(struct sockaddr_in)); + SET_SS_LEN(&request->addr, sizeof(struct sockaddr_in)); v4->sin_family = AF_INET; memcpy(&v4->sin_addr, current, sizeof(struct in_addr)); return (1); break; -#ifdef IPV6 +#ifdef RB_IPV6 case T_AAAA: if (request->type != T_AAAA) return (0); if (rd_length != sizeof(struct in6_addr)) return (0); - SET_SS_LEN(request->addr, sizeof(struct sockaddr_in6)); + SET_SS_LEN(&request->addr, sizeof(struct sockaddr_in6)); v6 = (struct sockaddr_in6 *)&request->addr; v6->sin6_family = AF_INET6; memcpy(&v6->sin6_addr, current, sizeof(struct in6_addr)); @@ -679,26 +725,12 @@ static int proc_answer(struct reslist *request, HEADER * header, char *buf, char else if (n == 0) return (0); /* no more answers left */ - strlcpy(request->name, hostbuf, HOSTLEN + 1); + rb_strlcpy(request->name, hostbuf, IRCD_RES_HOSTLEN + 1); return (1); break; - case T_CNAME: /* first check we already havent started looking - into a cname */ - if (request->type != T_PTR) - return (0); - - if (request->state == REQ_CNAME) - { - n = irc_dn_expand((unsigned char *)buf, (unsigned char *)eob, - current, hostbuf, sizeof(hostbuf)); - - if (n < 0) - return (0); - return (1); - } - - request->state = REQ_CNAME; + case T_CNAME: + /* real answer will follow */ current += rd_length; break; @@ -716,13 +748,14 @@ static int proc_answer(struct reslist *request, HEADER * header, char *buf, char } /* - * res_readreply - read a dns reply from the nameserver and process it. + * res_read_single_reply - read a dns reply from the nameserver and process it. + * Return value: 1 if a packet was read, 0 otherwise */ -static void res_readreply(int fd, void *data) +static int res_read_single_reply(rb_fde_t *F, void *data) { char buf[sizeof(HEADER) + MAXPACKET] - /* Sparc and alpha need 16bit-alignment for accessing header->id - * (which is uint16_t). Because of the header = (HEADER*) buf; + /* Sparc and alpha need 16bit-alignment for accessing header->id + * (which is uint16_t). Because of the header = (HEADER*) buf; * lateron, this is neeeded. --FaUl */ #if defined(__sparc__) || defined(__alpha__) @@ -734,23 +767,24 @@ static void res_readreply(int fd, void *data) struct DNSReply *reply = NULL; int rc; int answer_count; - socklen_t len = sizeof(struct irc_sockaddr_storage); - struct irc_sockaddr_storage lsin; + socklen_t len = sizeof(struct rb_sockaddr_storage); + struct rb_sockaddr_storage lsin; + int ns; - rc = recvfrom(fd, buf, sizeof(buf), 0, (struct sockaddr *)&lsin, &len); + rc = recvfrom(rb_get_fd(F), buf, sizeof(buf), 0, (struct sockaddr *)&lsin, &len); - /* Re-schedule a read *after* recvfrom, or we'll be registering - * interest where it'll instantly be ready for read :-) -- adrian - */ - rb_setselect(fd, FDLIST_NONE, COMM_SELECT_READ, res_readreply, NULL, 0); - /* Better to cast the sizeof instead of rc */ + /* No packet */ + if (rc == 0 || rc == -1) + return 0; + + /* Too small */ if (rc <= (int)(sizeof(HEADER))) - return; + return 1; /* * convert DNS reply reader from Network byte order to CPU byte order. */ - header = (HEADER *) buf; + header = (HEADER *)(void *)buf; header->ancount = ntohs(header->ancount); header->qdcount = ntohs(header->qdcount); header->nscount = ntohs(header->nscount); @@ -761,38 +795,61 @@ static void res_readreply(int fd, void *data) * just ignore this response. */ if (0 == (request = find_id(header->id))) - return; + return 1; /* * check against possibly fake replies */ - if (!res_ourserver(&lsin)) - return; + ns = res_ourserver(&lsin); + if (ns == -1) + return 1; + + if (ns != request->lastns) + { + /* + * We'll accept the late reply, but penalize it a little more to make + * sure a laggy server doesn't end up favored. + */ + ns_failure_count[ns] += 3; + } + if (!check_question(request, header, buf, buf + rc)) - return; + return 1; if ((header->rcode != NO_ERRORS) || (header->ancount == 0)) { - if (NXDOMAIN == header->rcode) + /* + * RFC 2136 states that in the event of a server returning SERVFAIL + * or NOTIMP, the request should be resent to the next server. + * Additionally, if the server refuses our query, resend it as well. + * -- mr_flea + */ + if (SERVFAIL == header->rcode || NOTIMP == header->rcode || + REFUSED == header->rcode) { - (*request->query->callback) (request->query->ptr, NULL); - rem_request(request); + ns_failure_count[ns]++; + resend_query(request); } else { /* - * If a bad error was returned, we stop here and dont send - * send any more (no retries granted). + * Either a fatal error was returned or no answer. Cancel the + * request. */ + if (NXDOMAIN == header->rcode) + { + /* If the rcode is NXDOMAIN, treat it as a good response. */ + ns_failure_count[ns] /= 4; + } (*request->query->callback) (request->query->ptr, NULL); rem_request(request); } - return; + return 1; } /* - * If this fails there was an error decoding the received packet, - * give up. -- jilles + * If this fails there was an error decoding the received packet. + * -- jilles */ answer_count = proc_answer(request, header, buf, buf + rc); @@ -803,25 +860,24 @@ static void res_readreply(int fd, void *data) if (request->name == NULL) { /* - * got a PTR response with no name, something bogus is happening - * don't bother trying again, the client address doesn't resolve + * Got a PTR response with no name, something strange is + * happening. Try another DNS server. */ - (*request->query->callback) (request->query->ptr, reply); - rem_request(request); - return; + ns_failure_count[ns]++; + resend_query(request); + return 1; } /* * Lookup the 'authoritative' name that we were given for the - * ip#. - * + * ip#. */ -#ifdef IPV6 +#ifdef RB_IPV6 if (request->addr.ss_family == AF_INET6) - gethost_byname_type(request->name, request->query, T_AAAA); + gethost_byname_type_fqdn(request->name, request->query, T_AAAA); else #endif - gethost_byname_type(request->name, request->query, T_A); + gethost_byname_type_fqdn(request->name, request->query, T_A); rem_request(request); } else @@ -831,16 +887,26 @@ static void res_readreply(int fd, void *data) */ reply = make_dnsreply(request); (*request->query->callback) (request->query->ptr, reply); - MyFree(reply); + rb_free(reply); rem_request(request); } + + ns_failure_count[ns] /= 4; } else { - /* couldn't decode, give up -- jilles */ - (*request->query->callback) (request->query->ptr, NULL); - rem_request(request); + /* Invalid or corrupt reply - try another resolver. */ + ns_failure_count[ns]++; + resend_query(request); } + return 1; +} + +static void res_readreply(rb_fde_t *F, void *data) +{ + while (res_read_single_reply(F, data)) + ; + rb_setselect(F, RB_SELECT_READ, res_readreply, NULL); } static struct DNSReply *make_dnsreply(struct reslist *request) @@ -848,7 +914,7 @@ static struct DNSReply *make_dnsreply(struct reslist *request) struct DNSReply *cp; s_assert(request != 0); - cp = (struct DNSReply *)MyMalloc(sizeof(struct DNSReply)); + cp = (struct DNSReply *)rb_malloc(sizeof(struct DNSReply)); cp->h_name = request->name; memcpy(&cp->addr, &request->addr, sizeof(cp->addr)); @@ -862,10 +928,10 @@ void report_dns_servers(struct Client *source_p) for (i = 0; i < irc_nscount; i++) { - if (!inetntop_sock((struct sockaddr *)&(irc_nsaddr_list[i]), + if (!rb_inet_ntop_sock((struct sockaddr *)&(irc_nsaddr_list[i]), ipaddr, sizeof ipaddr)) - strlcpy(ipaddr, "?", sizeof ipaddr); + rb_strlcpy(ipaddr, "?", sizeof ipaddr); sendto_one_numeric(source_p, RPL_STATSDEBUG, - "A %s", ipaddr); + "A %s %d", ipaddr, ns_failure_count[i]); } }