* added callbacks and reference counting of returned hostents.
* --Bleep (Thomas Helvey <tomh@inxpress.net>)
*/
+#include "config.h"
+
#include "res.h"
#include "client.h"
#include "ircd.h"
#include "ircd_alloc.h"
+#include "ircd_events.h"
#include "ircd_log.h"
#include "ircd_osdep.h"
+#include "ircd_reply.h"
+#include "ircd_snprintf.h"
#include "ircd_string.h"
#include "msg.h"
#include "numeric.h"
#include "s_debug.h"
#include "s_misc.h"
#include "send.h"
-#include "sprintf_irc.h"
#include "struct.h"
#include "support.h"
#include "sys.h"
#include <sys/socket.h>
#include <fcntl.h>
#include <unistd.h>
+#include <regex.h>
#include <arpa/nameser.h>
#include <resolv.h>
* Nov. 17, 1997 --Bleep
*/
-typedef struct Hostent {
+struct Hostent {
struct hostent h; /* the hostent struct we are passing around */
char* buf; /* buffer for data pointed to from hostent */
-} aHostent;
+};
struct ResRequest {
struct ResRequest* next;
struct in_addr addr;
char* name;
struct DNSQuery query; /* query callback for this request */
- aHostent he;
+ struct Hostent he;
};
struct CacheEntry {
int ResolverFileDescriptor = -1; /* GLOBAL - used in s_bsd.c */
+static struct Socket resSock; /* Socket describing resolver */
+static struct Timer resExpireDNS; /* Timer for DNS expiration */
+static struct Timer resExpireCache; /* Timer for cache expiration */
+
static time_t nextDNSCheck = 0;
static time_t nextCacheExpire = 1;
static void add_request(struct ResRequest* request);
static void rem_request(struct ResRequest* request);
static struct ResRequest* make_request(const struct DNSQuery* query);
+static time_t timeout_query_list(time_t now);
+static time_t expire_cache(time_t now);
static void rem_cache(struct CacheEntry*);
static void do_query_name(const struct DNSQuery* query,
const char* name,
const char* addr);
static struct ResRequest* find_id(int);
-static struct cacheinfo {
+static struct cacheinfo {
int ca_adds;
int ca_dels;
int ca_expires;
* paul vixie, 29may94
*/
static int
-res_ourserver(const struct __res_state* statp, const struct sockaddr_in *inp)
+res_ourserver(const struct __res_state* statp, const struct sockaddr_in* inp)
{
struct sockaddr_in ina;
int ns;
return (0);
}
+/* Socket callback for resolver */
+static void res_callback(struct Event* ev)
+{
+ assert(ev_type(ev) == ET_READ || ev_type(ev) == ET_ERROR);
+
+ resolver_read();
+}
+
/*
* start_resolver - do everything we need to read the resolv.conf file
* and initialize the resolver file descriptor if needed
spare_fd = open("/dev/null",O_RDONLY,0);
if ((spare_fd < 0) || (spare_fd > 255)) {
char sparemsg[80];
- sprintf_irc(sparemsg, "invalid spare_fd %d", spare_fd);
+ ircd_snprintf(0, sparemsg, sizeof(sparemsg), "invalid spare_fd %d",
+ spare_fd);
server_restart(sparemsg);
}
ResolverFileDescriptor = socket(AF_INET, SOCK_DGRAM, 0);
if (-1 == ResolverFileDescriptor) {
report_error("Resolver: error creating socket for %s: %s",
- me.name, errno);
+ cli_name(&me), errno);
return;
}
if (!os_set_nonblocking(ResolverFileDescriptor))
report_error("Resolver: error setting non-blocking for %s: %s",
- me.name, errno);
+ cli_name(&me), errno);
+ if (!socket_add(&resSock, res_callback, 0, SS_DATAGRAM,
+ SOCK_EVENT_READABLE, ResolverFileDescriptor))
+ report_error("Resolver: unable to queue resolver file descriptor for %s",
+ cli_name(&me), ENFILE);
}
}
+/* Call the query timeout function */
+static void expire_DNS_callback(struct Event* ev)
+{
+ time_t next;
+
+ next = timeout_query_list(CurrentTime);
+
+ timer_add(&resExpireDNS, expire_DNS_callback, 0, TT_ABSOLUTE, next);
+}
+
+/* Call the cache expire function */
+static void expire_cache_callback(struct Event* ev)
+{
+ time_t next;
+
+ next = expire_cache(CurrentTime);
+
+ timer_add(&resExpireCache, expire_cache_callback, 0, TT_ABSOLUTE, next);
+}
+
/*
* init_resolver - initialize resolver and resolver library
*/
memset(hashtable, 0, sizeof(hashtable));
memset(&reinfo, 0, sizeof(reinfo));
- requestListHead = requestListTail = NULL;
+ requestListHead = requestListTail = 0;
+
+ /* initiate the resolver timers */
+ timer_add(timer_init(&resExpireDNS), expire_DNS_callback, 0,
+ TT_RELATIVE, 1);
+ timer_add(timer_init(&resExpireCache), expire_cache_callback, 0,
+ TT_RELATIVE, 1);
errno = h_errno = 0;
+
start_resolver();
Debug((DEBUG_DNS, "Resolver: fd %d errno: %d h_errno: %d: %s",
ResolverFileDescriptor, errno, h_errno,
start_resolver();
}
+static int validate_hostent(const struct hostent* hp)
+{
+ const char* name;
+ int i = 0;
+ assert(0 != hp);
+ for (name = hp->h_name; name; name = hp->h_aliases[i++]) {
+ if (!string_is_hostname(name))
+ return 0;
+ }
+ return 1;
+}
+
/*
* add_request - place a new request in the request list
*/
++sent;
}
else
- ircd_log(L_ERROR, "Resolver: send failed %s",
- (strerror(errno)) ? strerror(errno) : "Unknown");
+ log_write(LS_RESOLVER, L_ERROR, 0, "Resolver: send failed %m");
}
return sent;
}
assert(0 != addr);
cp = (const unsigned char*) &addr->s_addr;
- sprintf_irc(ipbuf, "%u.%u.%u.%u.in-addr.arpa.",
- (unsigned int)(cp[3]), (unsigned int)(cp[2]),
- (unsigned int)(cp[1]), (unsigned int)(cp[0]));
+ ircd_snprintf(0, ipbuf, sizeof(ipbuf), "%u.%u.%u.%u.in-addr.arpa.",
+ (unsigned int)(cp[3]), (unsigned int)(cp[2]),
+ (unsigned int)(cp[1]), (unsigned int)(cp[0]));
if (!request) {
request = make_request(query);
u_char* current; /* current position in buf */
char** alias; /* alias list */
char** addr; /* address list */
+ char** base_addr; /* original pointer to address list */
char* name; /* pointer to name string */
char* address; /* pointer to address */
+ char* base_address; /* original pointer to address */
char* endp; /* end of our buffer */
int query_class; /* answer class */
int type; /* answer type */
int n; /* temp count */
int addr_count = 0; /* number of addresses in hostent */
int alias_count = 0; /* number of aliases in hostent */
+ int t_ptr_seen = 0; /* Seen a T_PTR in proc_answer? */
struct hostent* hp; /* hostent getting filled */
assert(0 != request);
++addr;
++addr_count;
}
+ base_addr = addr;
/*
* make address point to first available address slot
*/
address = request->he.buf + ADDRS_OFFSET +
(sizeof(struct in_addr) * addr_count);
+ base_address = address;
+
/*
* find the end of the alias list
*/
case T_A:
/*
* check for invalid rd_length or too many addresses
+ * ignore T_A relies if looking for a T_PTR
*/
+ if (t_ptr_seen)
+ return answer_count;
if (rd_length != sizeof(struct in_addr))
return answer_count;
if (++addr_count < RES_MAXADDRS) {
++answer_count;
break;
case T_PTR:
+ t_ptr_seen = 1;
+ addr_count = 0;
+ addr = base_addr;
+ *addr = 0;
+ address = base_address;
n = dn_expand(buf, eob, current, hostbuf, sizeof(hostbuf));
if (n < 0) {
/*
* check against possibly fake replies
*/
if (!res_ourserver(&_res, &sin)) {
- Debug((DEBUG_DNS, "Resolver: fake reply from: %s",
- (const char*) &sin.sin_addr));
+ Debug((DEBUG_DNS, "Resolver: fake reply from: %s", (const char*) &sin.sin_addr));
++reinfo.re_unkrep;
return 1;
}
answer_count = proc_answer(request, header, buf, buf + rc);
if (answer_count) {
if (T_PTR == request->type) {
- struct DNSReply* reply = NULL;
+ struct DNSReply* reply = 0;
if (0 == request->he.h.h_name) {
/*
* got a PTR response with no name, something bogus is happening
* extra kludges.
*/
reply = gethost_byname(request->he.h.h_name, &request->query);
- if (0 == reply) {
+ if (reply) {
+ (*request->query.callback)(request->query.vptr, reply);
+ }
+ else {
/*
* If name wasn't found, a request has been queued and it will
* be the last one queued. This is rather nasty way to keep
request->he.buf = 0;
memcpy(&requestListTail->he.h, &request->he.h, sizeof(struct hostent));
}
- else
- (*request->query.callback)(request->query.vptr, reply);
rem_request(request);
}
else {
* PTR returned a CNAME, cp was not checked before so the
* callback was being called with a value of 0x2C != NULL.
*/
- cp = make_cache(request);
- (*request->query.callback)(request->query.vptr,
- (cp) ? &cp->reply : 0);
+ struct DNSReply* reply = 0;
+ if (validate_hostent(&request->he.h)) {
+ if ((cp = make_cache(request)))
+ reply = &cp->reply;
+ }
+ (*request->query.callback)(request->query.vptr, reply);
rem_request(request);
}
}
* dup_hostent - Duplicate a hostent struct, allocate only enough memory for
* the data we're putting in it.
*/
-static void dup_hostent(aHostent* new_hp, struct hostent* hp)
+static void dup_hostent(struct Hostent* new_hp, struct hostent* hp)
{
char* p;
char** ap;
/*
* update_hostent - Add records to a Hostent struct in place.
*/
-static void update_hostent(aHostent* hp, char** addr, char** alias)
+static void update_hostent(struct Hostent* hp, char** addr, char** alias)
{
char* p;
char** ap;
*/
static void update_list(struct ResRequest* request, struct CacheEntry* cachep)
{
-#if 0
- struct CacheEntry** cpp;
-#endif
struct CacheEntry* cp = cachep;
char* s;
char* t;
* If found, move the entry to the top of the list and return.
*/
++cainfo.ca_updates;
-#if 0
- for (cpp = &cacheTop; *cpp; cpp = &((*cpp)->list_next)) {
- if (cp == *cpp)
- break;
- }
- if (!*cpp)
- return;
- *cpp = cp->list_next;
- cp->list_next = cacheTop;
- cacheTop = cp;
-#endif
if (!request)
return;
* shouldn't happen but it just might...
*/
assert(0 != hp->h_name);
- assert(0 != hp->h_addr_list[0]);
+/* assert(0 != hp->h_addr_list[0]); */
if (!hp->h_name || !hp->h_addr_list[0])
return NULL;
/*
request_mem += MAXGETHOSTLEN + 1;
++request_count;
}
- /* XXX sendto_one used to send STATSDEBUG */
+
if (cachedCount != cache_count) {
- sendto_one(sptr,
- ":%s %d %s :Resolver: cache count mismatch: %d != %d",
- me.name, RPL_STATSDEBUG, sptr->name, cachedCount, cache_count);
+ send_reply(sptr, SND_EXPLICIT | RPL_STATSDEBUG,
+ ":Resolver: cache count mismatch: %d != %d", cachedCount,
+ cache_count);
assert(cachedCount == cache_count);
}
- sendto_one(sptr, ":%s %d %s :Resolver: cache %d(%d) requests %d(%d)",
- me.name, RPL_STATSDEBUG, sptr->name, cache_count, cache_mem,
- request_count, request_mem);
+ send_reply(sptr, SND_EXPLICIT | RPL_STATSDEBUG,
+ ":Resolver: cache %d(%d) requests %d(%d)", cache_count,
+ cache_mem, request_count, request_mem);
return cache_mem + request_mem;
}