/*
- * 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.
*
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)
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,
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);
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.
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_v4))
+ /* Check which address family (or families) our nameservers use. */
+ for (need_v4 = need_v6 = ns = 0; ns < irc_nscount; ns++)
{
- int fd = os_socket(&VirtualHost_v4, SOCK_DGRAM, "Resolver UDPv4 socket");
+ 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);
}
- if (!s_active(&res_socket_v6))
+#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_v6, SOCK_DGRAM, "Resolver UDPv6 socket");
+ 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);
* @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;
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);
}
/** 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 *ev)
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;
}
{
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);
}
* @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.
* @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.
* @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];
if (request == NULL)
{
- request = make_request(query);
+ request = make_request(callback, ctx);
DupString(request->name, host_name);
#ifdef IPV6
if (type != T_A)
* @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];
}
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));
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:
/* 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;
}
break;
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,
* 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;
}
}
char buf[sizeof(HEADER) + MAXPACKET];
HEADER *header;
struct reslist *request = NULL;
- struct DNSReply *reply = NULL;
unsigned int rc;
int answer_count;
|| (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.
*/
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
{
/*
request->timeout += feature_int(FEAT_IRCD_RES_TIMEOUT);
resend_query(request);
}
- else
- {
- /*
- * 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->query.callback)(request->query.vptr, 0);
- rem_request(request);
- }
}
return;
* 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;
}
*/
#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);
}
/*
* 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);
}
}
}
-/** 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).