X-Git-Url: http://git.pk910.de/?a=blobdiff_plain;f=ircd%2Fircd_res.c;h=627e12e826fa813361d0a2737b7e8543aa383ec9;hb=refs%2Fheads%2Fupstream-ssl;hp=feb1d82b8c04f512ed1e105141b84ae616b1d350;hpb=482194058738a873e2a92144e7bd8a36bc0cba81;p=ircu2.10.12-pk.git diff --git a/ircd/ircd_res.c b/ircd/ircd_res.c index feb1d82..627e12e 100644 --- a/ircd/ircd_res.c +++ b/ircd/ircd_res.c @@ -1,6 +1,6 @@ /* - * A rewrite of Darren Reeds original res.c As there is nothing - * left of Darrens original code, this is now licensed by the hybrid group. + * A rewrite of Darren Reed's original res.c As there is nothing + * left of Darren's original code, this is now licensed by the hybrid group. * (Well, some of the function names are the same, and bits of the structs..) * You can use it where it is useful, free even. Buy us a beer and stuff. * @@ -50,10 +50,16 @@ #error this code needs to be able to address individual octets #endif -/** Resolver UDP socket. */ -static struct Socket res_socket; +/** IPv4 resolver UDP socket. */ +static struct Socket res_socket_v4; +/** IPv6 resolver UDP socket. */ +static struct Socket res_socket_v6; /** Next DNS lookup timeout. */ static struct Timer res_timeout; +/** Local address for IPv4 DNS lookups. */ +struct irc_sockaddr VirtualHost_dns_v4; +/** Local address for IPv6 DNS lookups. */ +struct irc_sockaddr VirtualHost_dns_v6; /** Check for whether the resolver has been initialized yet. */ #define resolver_started() (request_list.next != NULL) @@ -108,22 +114,23 @@ struct reslist char type; /**< Current request type. */ char retries; /**< Retry counter. */ char sends; /**< Number of sends (>1 means resent). */ - char resend; /**< Send flag; 0 == dont resend. */ + char resend; /**< Send flag; 0 == don't resend. */ time_t sentat; /**< Timestamp we last sent this request. */ time_t timeout; /**< When this request times out. */ struct irc_in_addr addr; /**< Address for this request. */ char *name; /**< Hostname for this request. */ - struct DNSQuery query; /**< Query callback for this request. */ + dns_callback_f callback; /**< Callback function on completion. */ + void *callback_ctx; /**< Context pointer for callback. */ }; /** Base of request list. */ static struct dlink request_list; static void rem_request(struct reslist *request); -static struct reslist *make_request(const struct DNSQuery *query); -static void do_query_name(const struct DNSQuery *query, +static struct reslist *make_request(dns_callback_f callback, void *ctx); +static void do_query_name(dns_callback_f callback, void *ctx, const char* name, struct reslist *request, int); -static void do_query_number(const struct DNSQuery *query, +static void do_query_number(dns_callback_f callback, void *ctx, const struct irc_in_addr *, struct reslist *request); static void query_name(const char *name, int query_class, int query_type, @@ -132,7 +139,6 @@ static int send_res_msg(const char *buf, int len, int count); static void resend_query(struct reslist *request); 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); static void res_readreply(struct Event *ev); static void timeout_resolver(struct Event *notused); @@ -140,6 +146,16 @@ extern struct irc_sockaddr irc_nsaddr_list[IRCD_MAXNS]; extern int irc_nscount; extern char irc_domain[HOSTLEN]; +/** Prepare the resolver library to (optionally) accept a list of + * DNS servers through add_dns_server(). + */ +void clear_nameservers(void) +{ + irc_nscount = 0; + memset(&VirtualHost_dns_v4, 0, sizeof(VirtualHost_dns_v4)); + memset(&VirtualHost_dns_v6, 0, sizeof(VirtualHost_dns_v6)); +} + /** Check whether \a inp is a nameserver we use. * @param[in] inp Nameserver address. * @return Non-zero if we trust \a inp; zero if not. @@ -164,20 +180,46 @@ res_ourserver(const struct irc_sockaddr *inp) void restart_resolver(void) { + int need_v4; + int need_v6; + int ns; + irc_res_init(); if (!request_list.next) request_list.next = request_list.prev = &request_list; - if (!s_active(&res_socket)) + /* Check which address family (or families) our nameservers use. */ + for (need_v4 = need_v6 = ns = 0; ns < irc_nscount; ns++) { - int fd; - fd = os_socket(&VirtualHost, SOCK_DGRAM, "Resolver UDP socket"); - if (fd < 0) return; - if (!socket_add(&res_socket, res_readreply, NULL, SS_DATAGRAM, - SOCK_EVENT_READABLE, fd)) return; - timer_init(&res_timeout); + if (irc_in_addr_is_ipv4(&irc_nsaddr_list[ns].addr)) + need_v4 = 1; + else + need_v6 = 1; } + + /* If we need an IPv4 socket, and don't have one, open it. */ + if (need_v4 && !s_active(&res_socket_v4)) + { + int fd = os_socket(&VirtualHost_dns_v4, SOCK_DGRAM, "Resolver UDPv4 socket", AF_INET); + if (fd >= 0) + socket_add(&res_socket_v4, res_readreply, NULL, + SS_DATAGRAM, SOCK_EVENT_READABLE, fd); + } + +#ifdef AF_INET6 + /* If we need an IPv6 socket, and don't have one, open it. */ + if (need_v6 && !s_active(&res_socket_v6)) + { + int fd = os_socket(&VirtualHost_dns_v6, SOCK_DGRAM, "Resolver UDPv6 socket", AF_INET6); + if (fd >= 0) + socket_add(&res_socket_v6, res_readreply, NULL, + SS_DATAGRAM, SOCK_EVENT_READABLE, fd); + } +#endif + + if (s_active(&res_socket_v4) || s_active(&res_socket_v6)) + timer_init(&res_timeout); } /** Append local domain to hostname if needed. @@ -237,7 +279,7 @@ rem_request(struct reslist *request) * @return Newly allocated and linked-in reslist. */ static struct reslist * -make_request(const struct DNSQuery* query) +make_request(dns_callback_f callback, void *ctx) { struct reslist *request; @@ -253,7 +295,8 @@ make_request(const struct DNSQuery* query) request->resend = 1; request->timeout = feature_int(FEAT_IRCD_RES_TIMEOUT); memset(&request->addr, 0, sizeof(request->addr)); - memcpy(&request->query, query, sizeof(request->query)); + request->callback = callback; + request->callback_ctx = ctx; add_dlink(&request->node, &request_list); return(request); @@ -267,23 +310,30 @@ check_resolver_timeout(time_t when) { if (when > CurrentTime + AR_TTL) when = CurrentTime + AR_TTL; - if (!t_active(&res_timeout)) - timer_add(&res_timeout, timeout_resolver, NULL, TT_ABSOLUTE, when); - else if (when < t_expire(&res_timeout)) + /* TODO after 2.10.12: Rewrite the timer API because there should be + * no need for clients to know this kind of implementation detail. */ + if (when > t_expire(&res_timeout)) + /* do nothing */; + else if (t_onqueue(&res_timeout) && !(res_timeout.t_header.gh_flags & GEN_MARKED)) timer_chg(&res_timeout, TT_ABSOLUTE, when); + else + timer_add(&res_timeout, timeout_resolver, NULL, TT_ABSOLUTE, when); } /** Drop pending DNS lookups which have timed out. - * @param[in] notused Timer event data (ignored). + * @param[in] ev Timer event data (ignored). */ static void -timeout_resolver(struct Event *notused) +timeout_resolver(struct Event *ev) { struct dlink *ptr, *next_ptr; struct reslist *request; time_t next_time = 0; time_t timeout = 0; + if (ev_type(ev) != ET_EXPIRE) + return; + for (ptr = request_list.next; ptr != &request_list; ptr = next_ptr) { next_ptr = ptr->next; @@ -295,7 +345,7 @@ timeout_resolver(struct Event *notused) if (--request->retries <= 0) { Debug((DEBUG_DNS, "Request %p out of retries; destroying", request)); - (*request->query.callback)(request->query.vptr, 0); + (*request->callback)(request->callback_ctx, NULL, NULL); rem_request(request); continue; } @@ -334,7 +384,7 @@ delete_resolver_queries(const void *vptr) { next_ptr = ptr->next; request = (struct reslist*)ptr; - if (vptr == request->query.vptr) { + if (vptr == request->callback_ctx) { Debug((DEBUG_DNS, "Removing request %p with vptr %p", request, vptr)); rem_request(request); } @@ -361,9 +411,11 @@ send_res_msg(const char *msg, int len, int rcount) if (max_queries == 0) max_queries = 1; - for (i = 0; i < max_queries; i++) - if (os_sendto_nonb(s_fd(&res_socket), msg, len, NULL, 0, &irc_nsaddr_list[i]) == IO_SUCCESS) + for (i = 0; i < max_queries; i++) { + int fd = irc_in_addr_is_ipv4(&irc_nsaddr_list[i].addr) ? s_fd(&res_socket_v4) : s_fd(&res_socket_v6); + if (os_sendto_nonb(fd, msg, len, NULL, 0, &irc_nsaddr_list[i]) == IO_SUCCESS) ++sent; + } return(sent); } @@ -397,9 +449,9 @@ find_id(int id) * @param[in] query Callback information. */ void -gethost_byname(const char *name, const struct DNSQuery *query) +gethost_byname(const char *name, dns_callback_f callback, void *ctx) { - do_query_name(query, name, NULL, T_AAAA); + do_query_name(callback, ctx, name, NULL, T_AAAA); } /** Try to look up hostname for an address. @@ -407,9 +459,9 @@ gethost_byname(const char *name, const struct DNSQuery *query) * @param[in] query Callback information. */ void -gethost_byaddr(const struct irc_in_addr *addr, const struct DNSQuery *query) +gethost_byaddr(const struct irc_in_addr *addr, dns_callback_f callback, void *ctx) { - do_query_number(query, addr, NULL); + do_query_number(callback, ctx, addr, NULL); } /** Send a query to look up the address for a name. @@ -419,7 +471,7 @@ gethost_byaddr(const struct irc_in_addr *addr, const struct DNSQuery *query) * @param[in] type Preferred request type. */ static void -do_query_name(const struct DNSQuery *query, const char *name, +do_query_name(dns_callback_f callback, void *ctx, const char *name, struct reslist *request, int type) { char host_name[HOSTLEN + 1]; @@ -429,7 +481,7 @@ do_query_name(const struct DNSQuery *query, const char *name, if (request == NULL) { - request = make_request(query); + request = make_request(callback, ctx); DupString(request->name, host_name); #ifdef IPV6 if (type != T_A) @@ -450,7 +502,7 @@ do_query_name(const struct DNSQuery *query, const char *name, * @param[in] request DNS lookup structure (may be NULL). */ static void -do_query_number(const struct DNSQuery *query, const struct irc_in_addr *addr, +do_query_number(dns_callback_f callback, void *ctx, const struct irc_in_addr *addr, struct reslist *request) { char ipbuf[128]; @@ -495,7 +547,8 @@ do_query_number(const struct DNSQuery *query, const struct irc_in_addr *addr, } if (request == NULL) { - request = make_request(query); + request = make_request(callback, ctx); + request->state= REQ_PTR; request->type = T_PTR; memcpy(&request->addr, addr, sizeof(request->addr)); request->name = (char *)MyMalloc(HOSTLEN + 1); @@ -554,15 +607,15 @@ resend_query(struct reslist *request) switch(request->type) { case T_PTR: - do_query_number(NULL, &request->addr, request); + do_query_number(NULL, NULL, &request->addr, request); break; case T_A: - do_query_name(NULL, request->name, request, request->type); + do_query_name(NULL, NULL, request->name, request, request->type); break; case T_AAAA: - /* didnt work, try A */ + /* didn't work, try A */ if (request->state == REQ_AAAA) - do_query_name(NULL, request->name, request, T_A); + do_query_name(NULL, NULL, request->name, request, T_A); default: break; } @@ -580,7 +633,6 @@ proc_answer(struct reslist *request, HEADER* header, char* buf, char* eob) { char hostbuf[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; @@ -634,9 +686,8 @@ proc_answer(struct reslist *request, HEADER* header, char* buf, char* eob) type = irc_ns_get16(current); current += TYPE_SIZE; - query_class = irc_ns_get16(current); + /* We do not use the class or TTL values. */ current += CLASS_SIZE; - current += TTL_SIZE; rd_length = irc_ns_get16(current); @@ -682,11 +733,8 @@ proc_answer(struct reslist *request, HEADER* header, char* buf, char* eob) return(1); break; - case T_CNAME: /* first check we already havent started looking + case T_CNAME: /* first check we already haven't 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, @@ -706,7 +754,13 @@ proc_answer(struct reslist *request, HEADER* header, char* buf, char* eob) * but its possible its just a broken nameserver with still * valid answers. But lets do some rudimentary logging for now... */ - log_write(LS_RESOLVER, L_ERROR, 0, "irc_res.c bogus type %d", type); + log_write(LS_RESOLVER, L_ERROR, 0, "irc_res.c bogus type %d", type); + + if ((char*)current + rd_length >= (char*)current) + current += rd_length; + else + return(0); + break; } } @@ -725,17 +779,22 @@ res_readreply(struct Event *ev) char buf[sizeof(HEADER) + MAXPACKET]; HEADER *header; struct reslist *request = NULL; - struct DNSReply *reply = NULL; unsigned int rc; int answer_count; - assert(ev_socket(ev) == &res_socket); + assert((ev_socket(ev) == &res_socket_v4) || (ev_socket(ev) == &res_socket_v6)); sock = ev_socket(ev); if (IO_SUCCESS != os_recvfrom_nonb(s_fd(sock), buf, sizeof(buf), &rc, &lsin) || (rc <= sizeof(HEADER))) return; + /* + * check against possibly fake replies + */ + if (!res_ourserver(&lsin)) + return; + /* * convert DNS reply reader from Network byte order to CPU byte order. */ @@ -752,20 +811,22 @@ res_readreply(struct Event *ev) if (0 == (request = find_id(header->id))) return; - /* - * check against possibly fake replies - */ - if (!res_ourserver(&lsin)) - return; - if ((header->rcode != NO_ERRORS) || (header->ancount == 0)) { - if (SERVFAIL == header->rcode) - resend_query(request); + if (SERVFAIL == header->rcode || NXDOMAIN == header->rcode) + { + /* + * If a bad error was returned, we stop here and don't send + * send any more (no retries granted). + */ + Debug((DEBUG_DNS, "Request %p has bad response (state %d type %d rcode %d)", request, request->state, request->type, header->rcode)); + (*request->callback)(request->callback_ctx, NULL, NULL); + rem_request(request); + } else { /* - * If we havent already tried this, and we're looking up AAAA, try A + * If we haven't already tried this, and we're looking up AAAA, try A * now */ @@ -781,16 +842,6 @@ res_readreply(struct Event *ev) request->timeout += feature_int(FEAT_IRCD_RES_TIMEOUT); resend_query(request); } - else - { - /* - * If a bad error was returned, we stop here and dont send - * send any more (no retries granted). - */ - Debug((DEBUG_DNS, "Request %p has bad response (state %d type %d)", request, request->state, request->type)); - (*request->query.callback)(request->query.vptr, 0); - rem_request(request); - } } return; @@ -812,7 +863,7 @@ res_readreply(struct Event *ev) * don't bother trying again, the client address doesn't resolve */ Debug((DEBUG_DNS, "Request %p PTR had empty name", request)); - (*request->query.callback)(request->query.vptr, reply); + (*request->callback)(request->callback_ctx, NULL, NULL); rem_request(request); return; } @@ -823,10 +874,10 @@ res_readreply(struct Event *ev) */ #ifdef IPV6 if (!irc_in_addr_is_ipv4(&request->addr)) - do_query_name(&request->query, request->name, NULL, T_AAAA); + do_query_name(request->callback, request->callback_ctx, request->name, NULL, T_AAAA); else #endif - do_query_name(&request->query, request->name, NULL, T_A); + do_query_name(request->callback, request->callback_ctx, request->name, NULL, T_A); Debug((DEBUG_DNS, "Request %p switching to forward resolution", request)); rem_request(request); } @@ -835,8 +886,7 @@ res_readreply(struct Event *ev) /* * got a name and address response, client resolved */ - reply = make_dnsreply(request); - (*request->query.callback)(request->query.vptr, (reply) ? reply : 0); + (*request->callback)(request->callback_ctx, &request->addr, request->name); Debug((DEBUG_DNS, "Request %p got forward resolution", request)); rem_request(request); } @@ -854,23 +904,6 @@ res_readreply(struct Event *ev) } } -/** Build a DNSReply for a completed request. - * @param[in] request Completed DNS request. - * @return Newly allocated DNSReply containing host name and address. - */ -static struct DNSReply * -make_dnsreply(struct reslist *request) -{ - struct DNSReply *cp; - assert(request != 0); - - cp = (struct DNSReply *)MyMalloc(sizeof(struct DNSReply)); - - DupString(cp->h_name, request->name); - memcpy(&cp->addr, &request->addr, sizeof(cp->addr)); - return(cp); -} - /** Statistics callback to list DNS servers. * @param[in] source_p Client requesting statistics. * @param[in] sd Stats descriptor for request (ignored). @@ -915,55 +948,3 @@ cres_mem(struct Client* sptr) ":Resolver: requests %d(%d)", request_count, request_mem); return request_mem; } - -/** Check whether an address looks valid. - * This means not all 0s and not all 1s. - * @param[in] addr Address to check for validity. - * @return Non-zero if the address looks valid. - */ -int irc_in_addr_valid(const struct irc_in_addr *addr) -{ - unsigned int ii; - unsigned short val; - - val = addr->in6_16[0]; - if (val != 0 && val != 0xffff) - return 1; - for (ii = 1; ii < 8; ii++) - if (addr->in6_16[ii] != val) - return 1; - return 0; -} - -/** Compare two IP addresses. - * @param[in] a First address to compare. - * @param[in] b Second address to compare. - * @return Non-zero if the two addresses differ, zero if they are identical. - */ -int irc_in_addr_cmp(const struct irc_in_addr *a, const struct irc_in_addr *b) -{ - if (irc_in_addr_is_ipv4(a)) - return a->in6_16[6] != b->in6_16[6] - || a->in6_16[7] != b->in6_16[7] - || !irc_in_addr_is_ipv4(b); - else - return memcmp(a, b, sizeof(*a)); -} - -/** Indicate whether an IP address is a loopback address. - * @param[in] addr Address to check. - * @return Non-zero if the address is loopback; zero if not. - */ -int irc_in_addr_is_loopback(const struct irc_in_addr *addr) -{ - if (addr->in6_16[0] != 0 - || addr->in6_16[1] != 0 - || addr->in6_16[2] != 0 - || addr->in6_16[3] != 0 - || addr->in6_16[4] != 0) - return 0; - if ((addr->in6_16[5] == 0xffff) || (addr->in6_16[5] == 0 && addr->in6_16[6] != 0)) - return (ntohs(addr->in6_16[6]) & 0xff00) == 0x7f00; - else - return addr->in6_16[5] == 0 && addr->in6_16[6] == 0 && htons(addr->in6_16[7]) == 1; -}