X-Git-Url: https://jfr.im/git/irc/rqf/shadowircd.git/blobdiff_plain/907468c485f837cb8442c32509c8efec8f3f43b1..133755685ef38d8f9ac79ac27c4505b9e96f81fd:/src/res.c diff --git a/src/res.c b/src/res.c index aff4bd8..2dac47f 100644 --- a/src/res.c +++ b/src/res.c @@ -7,8 +7,6 @@ * The authors takes no responsibility for any damage or loss * of property which results from the use of this software. * - * $Id: res.c 3301 2007-03-28 15:04:06Z jilles $ - * from Hybrid Id: res.c 459 2006-02-12 22:21:37Z db $ * * July 1999 - Rewrote a bunch of stuff here. Change hostent builder code, * added callbacks and reference counting of returned hostents. @@ -31,8 +29,7 @@ #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_* */ @@ -57,28 +54,18 @@ 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; + unsigned int lastns; /* index of last server sent to */ struct rb_sockaddr_storage addr; char *name; struct DNSQuery *query; /* query callback for this request */ @@ -86,6 +73,7 @@ struct reslist static rb_fde_t *res_fd; static rb_dlink_list request_list = { NULL, NULL, 0 }; +static int ns_timeout_count[IRCD_MAXNS]; static void rem_request(struct reslist *request); static struct reslist *make_request(struct DNSQuery *query); @@ -100,11 +88,6 @@ 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 rb_sockaddr_storage irc_nsaddr_list[IRCD_MAXNS]; -extern int irc_nscount; -extern char irc_domain[HOSTLEN + 1]; - - /* * int * res_ourserver(inp) @@ -147,7 +130,10 @@ static int res_ourserver(const struct rb_sockaddr_storage *inp) sizeof(struct in6_addr)) == 0) || (memcmp(&v6->sin6_addr.s6_addr, &in6addr_any, sizeof(struct in6_addr)) == 0)) + { + ns_timeout_count[ns] = 0; return 1; + } break; #endif case AF_INET: @@ -155,7 +141,10 @@ static int res_ourserver(const struct rb_sockaddr_storage *inp) 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)) + { + ns_timeout_count[ns] = 0; return 1; + } break; default: break; @@ -192,6 +181,7 @@ static time_t timeout_query_list(time_t now) } else { + ns_timeout_count[request->lastns]++; request->sentat = now; request->timeout += request->timeout; resend_query(request); @@ -223,7 +213,11 @@ static struct ev_entry *timeout_resolver_ev = NULL; */ static void start_resolver(void) { + int i; + irc_res_init(); + for (i = 0; i < irc_nscount; i++) + ns_timeout_count[i] = 0; if (res_fd == NULL) { @@ -302,10 +296,8 @@ static struct reslist *make_request(struct DNSQuery *query) 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; rb_dlinkAdd(request, &request->node, &request_list); @@ -333,33 +325,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_timeout_count[ns] && retrycnt % retryfreq(ns_timeout_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++) { + ns = (i + rcount - 1) % irc_nscount; + if (!ns_timeout_count[ns]) + continue; if (sendto(rb_get_fd(res_fd), msg, len, 0, - (struct sockaddr *)&(irc_nsaddr_list[i]), - GET_SS_LEN(&irc_nsaddr_list[i])) == len) - ++sent; + (struct sockaddr *)&(irc_nsaddr_list[ns]), + GET_SS_LEN(&irc_nsaddr_list[ns])) == len) + return ns; } - return (sent); + return -1; } /* @@ -405,17 +431,16 @@ void gethost_byaddr(const struct rb_sockaddr_storage *addr, struct DNSQuery *que static void do_query_name(struct DNSQuery *query, const char *name, struct reslist *request, int type) { - char host_name[HOSTLEN + 1]; + char host_name[IRCD_RES_HOSTLEN + 1]; - rb_strlcpy(host_name, name, HOSTLEN + 1); - add_local_domain(host_name, HOSTLEN); + rb_strlcpy(host_name, name, IRCD_RES_HOSTLEN + 1); + add_local_domain(host_name, IRCD_RES_HOSTLEN); if (request == NULL) { request = make_request(query); request->name = (char *)rb_malloc(strlen(host_name) + 1); strcpy(request->name, host_name); - request->state = REQ_A; } rb_strlcpy(request->queryname, host_name, sizeof(request->queryname)); @@ -435,7 +460,7 @@ static void do_query_number(struct DNSQuery *query, const struct rb_sockaddr_sto { request = make_request(query); memcpy(&request->addr, addr, sizeof(struct rb_sockaddr_storage)); - request->name = (char *)rb_malloc(HOSTLEN + 1); + request->name = (char *)rb_malloc(IRCD_RES_HOSTLEN + 1); } if (addr->ss_family == AF_INET) @@ -484,6 +509,7 @@ static void query_name(struct reslist *request) { char buf[MAXPACKET]; int request_len = 0; + int ns; memset(buf, 0, sizeof(buf)); @@ -517,15 +543,14 @@ static void query_name(struct reslist *request) request->id = header->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) - return; - switch (request->type) { case T_PTR: @@ -549,7 +574,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 */ @@ -570,7 +595,7 @@ 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 */ @@ -615,7 +640,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 @@ -681,26 +706,12 @@ static int proc_answer(struct reslist *request, HEADER * header, char *buf, char else if (n == 0) return (0); /* no more answers left */ - rb_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; @@ -718,9 +729,10 @@ 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(rb_fde_t *F, 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 @@ -741,13 +753,13 @@ static void res_readreply(rb_fde_t *F, void *data) 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(F, RB_SELECT_READ, res_readreply, NULL); - /* 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. @@ -763,16 +775,16 @@ static void res_readreply(rb_fde_t *F, 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; + return 1; if (!check_question(request, header, buf, buf + rc)) - return; + return 1; if ((header->rcode != NO_ERRORS) || (header->ancount == 0)) { @@ -790,7 +802,7 @@ static void res_readreply(rb_fde_t *F, void *data) (*request->query->callback) (request->query->ptr, NULL); rem_request(request); } - return; + return 1; } /* * If this fails there was an error decoding the received packet, @@ -810,7 +822,7 @@ static void res_readreply(rb_fde_t *F, void *data) */ (*request->query->callback) (request->query->ptr, reply); rem_request(request); - return; + return 1; } /* @@ -843,6 +855,14 @@ static void res_readreply(rb_fde_t *F, void *data) (*request->query->callback) (request->query->ptr, NULL); rem_request(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) @@ -864,10 +884,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)) rb_strlcpy(ipaddr, "?", sizeof ipaddr); sendto_one_numeric(source_p, RPL_STATSDEBUG, - "A %s", ipaddr); + "A %s %d", ipaddr, ns_timeout_count[i]); } }