2 * src/res.c (C)opyright 1992 Darren Reed. All rights reserved.
3 * This file may not be distributed without the author's permission in any
4 * shape or form. The author takes no responsibility for any damage or loss
5 * of property which results from the use of this software.
9 * July 1999 - Rewrote a bunch of stuff here. Change hostent builder code,
10 * added callbacks and reference counting of returned hostents.
11 * --Bleep (Thomas Helvey <tomh@inxpress.net>)
19 #include "ircd_alloc.h"
20 #include "ircd_events.h"
22 #include "ircd_osdep.h"
23 #include "ircd_reply.h"
24 #include "ircd_snprintf.h"
25 #include "ircd_string.h"
41 #include <sys/socket.h>
46 #include <arpa/nameser.h>
49 #include <arpa/inet.h>
53 #error this code needs to be able to address individual octets
57 * Some systems do not define INADDR_NONE (255.255.255.255)
58 * INADDR_NONE is actually a valid address, but it should never
59 * be returned from any nameserver.
60 * NOTE: The bit pattern for INADDR_NONE and INADDR_ANY (0.0.0.0) should be
61 * the same on all hosts so we shouldn't need to use htonl or ntohl to
62 * compare or set the values.
65 #define INADDR_NONE ((unsigned int) 0xffffffff)
68 #define MAXPACKET 1024 /* rfc sez 512 but we expand names so ... */
69 #define RES_MAXALIASES 35 /* maximum aliases allowed */
70 #define RES_MAXADDRS 35 /* maximum addresses allowed */
72 * OSF1 doesn't have RES_NOALIASES
75 #define RES_NOALIASES 0
79 * macros used to calulate offsets into fixed query buffer
81 #define ALIAS_BLEN ((RES_MAXALIASES + 1) * sizeof(char*))
82 #define ADDRS_BLEN ((RES_MAXADDRS + 1) * sizeof(struct in_addr*))
84 #define ADDRS_OFFSET (ALIAS_BLEN + ADDRS_BLEN)
85 #define ADDRS_DLEN (RES_MAXADDRS * sizeof(struct in_addr))
86 #define NAMES_OFFSET (ADDRS_OFFSET + ADDRS_DLEN)
87 #define MAXGETHOSTLEN (NAMES_OFFSET + MAXPACKET)
89 #define AR_TTL 600 /* TTL in seconds for dns cache entries */
92 * the following values should be prime
94 #define ARES_CACSIZE 307
96 //#define MAXCACHED 281
99 * Building the Hostent
100 * The Hostent struct is arranged like this:
101 * +-------------------------------+
102 * Hostent: | struct hostent h |
103 * |-------------------------------|
105 * +-------------------------------+
109 * +-------------------------------+
110 * buf: | h_aliases pointer array | Max size: ALIAS_BLEN;
111 * | NULL | contains `char *'s
112 * |-------------------------------|
113 * | h_addr_list pointer array | Max size: ADDRS_BLEN;
114 * | NULL | contains `struct in_addr *'s
115 * |-------------------------------|
116 * | h_addr_list addresses | Max size: ADDRS_DLEN;
117 * | | contains `struct in_addr's
118 * |-------------------------------|
119 * | storage for hostname strings | Max size: ALIAS_DLEN;
120 * +-------------------------------+ contains `char's
122 * For requests the size of the h_aliases, and h_addr_list pointer
123 * array sizes are set to MAXALISES and MAXADDRS respectively, and
124 * buf is a fixed size with enough space to hold the largest expected
125 * reply from a nameserver, see RFC 1034 and RFC 1035.
126 * For cached entries the sizes are dependent on the actual number
127 * of aliases and addresses. If new aliases and addresses are found
128 * for cached entries, the buffer is grown and the new entries are added.
129 * The hostent struct is filled in with the addresses of the entries in
130 * the Hostent buf as follows:
131 * h_name - contains a pointer to the start of the hostname string area,
132 * or NULL if none is set. The h_name is followed by the
133 * aliases, in the storage for hostname strings area.
134 * h_aliases - contains a pointer to the start of h_aliases pointer array.
135 * This array contains pointers to the storage for hostname
136 * strings area and is terminated with a NULL. The first alias
137 * is stored directly after the h_name.
138 * h_addr_list - contains a pointer to the start of h_addr_list pointer array.
139 * This array contains pointers to in_addr structures in the
140 * h_addr_list addresses area and is terminated with a NULL.
142 * Filling the buffer this way allows for proper alignment of the h_addr_list
145 * This arrangement allows us to alias a Hostent struct pointer as a
146 * real struct hostent* without lying. It also allows us to change the
147 * values contained in the cached entries and requests without changing
148 * the actual hostent pointer, which is saved in a client struct and can't
149 * be changed without blowing things up or a lot more fiddling around.
150 * It also allows for defered allocation of the fixed size buffers until
151 * they are really needed.
152 * Nov. 17, 1997 --Bleep
156 struct hostent h; /* the hostent struct we are passing around */
157 char buf[1]; /* buffer for data pointed to from hostent */
161 struct ResRequest* next;
168 struct DNSQuery query; /* query callback for this request */
173 int ResolverFileDescriptor = -1; /* GLOBAL - used in s_bsd.c */
175 static struct Socket resSock; /* Socket describing resolver */
176 static struct Timer resExpireDNS; /* Timer for DNS expiration */
177 static adns_state adns = NULL;
180 * Keep a spare file descriptor open. res_init calls fopen to read the
181 * resolv.conf file. If ircd is hogging all the file descriptors below 256,
182 * on systems with crippled FILE structures this will cause wierd bugs.
183 * This is definitely needed for Solaris which uses an unsigned char to
184 * hold the file descriptor. --Dianora
186 static int spare_fd = -1;
188 static struct ResRequest* requestListHead; /* head of resolver request list */
189 static struct ResRequest* requestListTail; /* tail of resolver request list */
192 static void add_request(struct ResRequest* request);
193 static void rem_request(struct ResRequest* request);
194 static struct ResRequest* make_request(const struct DNSQuery* query);
195 static time_t timeout_query_list(time_t now);
196 static void do_query_name(const struct DNSQuery* query,
198 struct ResRequest* request);
199 static void do_query_number(const struct DNSQuery* query,
200 const struct in_addr*,
201 struct ResRequest* request);
202 static void res_adns_callback(adns_state state, adns_query q, void *context);
204 static struct resinfo {
220 * res_isourserver(ina)
221 * looks up "ina" in _res.ns_addr_list[]
226 * paul vixie, 29may94
229 res_ourserver(const struct __res_state* statp, const struct sockaddr_in* inp)
231 struct sockaddr_in ina;
235 for (ns = 0; ns < statp->nscount; ns++) {
236 const struct sockaddr_in *srv = &statp->nsaddr_list[ns];
238 if (srv->sin_family == ina.sin_family &&
239 srv->sin_port == ina.sin_port &&
240 (srv->sin_addr.s_addr == INADDR_ANY ||
241 srv->sin_addr.s_addr == ina.sin_addr.s_addr))
247 /* Socket callback for resolver */
248 static void res_socket_callback(struct Event* ev)
252 assert(ev_type(ev) == ET_READ || ev_type(ev) == ET_ERROR);
254 tv.tv_sec = CurrentTime;
256 adns_processreadable(adns, ResolverFileDescriptor, &tv);
260 * start_resolver - do everything we need to read the resolv.conf file
261 * and initialize the resolver file descriptor if needed
263 static void start_resolver(void)
267 Debug((DEBUG_DNS, "Resolver: start_resolver"));
269 * close the spare file descriptor so res_init can read resolv.conf
270 * successfully. Needed on Solaris
276 * make sure we have a valid file descriptor below 256 so we can
277 * do this again. Needed on Solaris
279 spare_fd = open("/dev/null",O_RDONLY,0);
280 if ((spare_fd < 0) || (spare_fd > 255)) {
282 ircd_snprintf(0, sparemsg, sizeof(sparemsg), "invalid spare_fd %d",
284 server_restart(sparemsg);
290 if ((res = adns_init(&adns, adns_if_debug /*| adns_if_noautosys*/, NULL))) {
291 report_error("Resolver: error initializing adns for %s: %s",
298 ResolverFileDescriptor = adns_get_fd(adns);
300 if (!socket_add(&resSock, res_socket_callback, 0, SS_DATAGRAM,
301 SOCK_EVENT_READABLE, ResolverFileDescriptor))
302 report_error("Resolver: unable to queue resolver file descriptor for %s",
303 cli_name(&me), ENFILE);
306 /* Call the query timeout function */
307 static void expire_DNS_callback(struct Event* ev)
311 next = timeout_query_list(CurrentTime);
313 timer_add(&resExpireDNS, expire_DNS_callback, 0, TT_ABSOLUTE, next);
317 * init_resolver - initialize resolver and resolver library
319 int init_resolver(void)
321 Debug((DEBUG_DNS, "Resolver: init_resolver"));
323 srand48(CurrentTime);
325 memset(&reinfo, 0, sizeof(reinfo));
327 requestListHead = requestListTail = 0;
329 /* initiate the resolver timers */
330 timer_add(timer_init(&resExpireDNS), expire_DNS_callback, 0,
336 Debug((DEBUG_DNS, "Resolver: fd %d errno: %d h_errno: %d: %s",
337 ResolverFileDescriptor, errno, h_errno,
338 (strerror(errno)) ? strerror(errno) : "Unknown"));
339 return ResolverFileDescriptor;
343 * restart_resolver - flush the cache, reread resolv.conf, reopen socket
345 void restart_resolver(void)
351 * add_request - place a new request in the request list
353 static void add_request(struct ResRequest* request)
355 assert(0 != request);
356 if (!requestListHead)
357 requestListHead = requestListTail = request;
359 requestListTail->next = request;
360 requestListTail = request;
362 request->next = NULL;
363 ++reinfo.re_requests;
367 * rem_request - remove a request from the list.
368 * This must also free any memory that has been allocated for
369 * temporary storage of DNS results.
371 static void rem_request(struct ResRequest* request)
373 struct ResRequest** current;
374 struct ResRequest* prev = NULL;
376 assert(0 != request);
377 for (current = &requestListHead; *current; ) {
378 if (*current == request) {
379 *current = request->next;
380 if (requestListTail == request)
381 requestListTail = prev;
385 current = &(*current)->next;
388 adns_cancel(request->aq);
389 MyFree(request->buf);
390 MyFree(request->name);
395 * make_request - Create a DNS request record for the server.
397 static struct ResRequest* make_request(const struct DNSQuery* query)
399 struct ResRequest* request;
401 request = (struct ResRequest*) MyMalloc(sizeof(struct ResRequest));
402 memset(request, 0, sizeof(struct ResRequest));
404 request->addr.s_addr = INADDR_NONE;
405 request->he.h_addrtype = AF_INET;
406 request->he.h_length = sizeof(struct in_addr);
407 request->query.vptr = query->vptr;
408 request->query.callback = query->callback;
410 add_request(request);
415 * timeout_query_list - Remove queries from the list which have been
416 * there too long without being resolved.
418 static time_t timeout_query_list(time_t now)
420 struct timeval tv, tv_buf, *tv_mod = NULL;
423 Debug((DEBUG_DNS, "Resolver: timeout_query_list at %s", myctime(now)));
427 adns_processtimeouts(adns, &tv);
428 adns_firsttimeout(adns, &tv_mod, &tv_buf, tv);
429 next = tv_mod ? tv_mod->tv_sec : AR_TTL;
433 Debug((DEBUG_DNS, "Resolver: next timeout_query_list in %d seconds", next));
438 * delete_resolver_queries - cleanup outstanding queries
439 * for which there no longer exist clients or conf lines.
441 void delete_resolver_queries(const void* vptr)
443 struct ResRequest* request;
444 struct ResRequest* next_request;
446 for (request = requestListHead; request; request = next_request) {
447 next_request = request->next;
448 if (vptr == request->query.vptr)
449 rem_request(request);
454 * gethost_byname - get host address from name
456 void gethost_byname(const char* name, const struct DNSQuery* query)
460 Debug((DEBUG_DNS, "Resolver: gethost_byname %s", name));
462 do_query_name(query, name, NULL);
466 * gethost_byaddr - get host name from address
468 void gethost_byaddr(const char* addr, const struct DNSQuery* query)
471 Debug((DEBUG_DNS, "Resolver: gethost_byaddr %s", ircd_ntoa(addr)));
473 do_query_number(query, (const struct in_addr*) addr, NULL);
477 * do_query_name - nameserver lookup name
479 static void do_query_name(const struct DNSQuery* query,
480 const char* name, struct ResRequest* request)
482 char hname[HOSTLEN + 1];
486 ircd_strncpy(hname, name, HOSTLEN);
487 hname[HOSTLEN] = '\0';
490 request = make_request(query);
491 request->type = adns_r_a;
492 request->name = (char*) MyMalloc(strlen(hname) + 1);
493 strcpy(request->name, hname);
495 res = adns_submit_callback(adns, hname, adns_r_a, adns_qf_owner,
496 request, &request->aq, res_adns_callback);
498 timer_chg(&resExpireDNS, TT_RELATIVE, 1);
502 * do_query_number - Use this to do reverse IP# lookups.
504 static void do_query_number(const struct DNSQuery* query,
505 const struct in_addr* addr,
506 struct ResRequest* request)
509 const unsigned char* cp;
513 cp = (const unsigned char*) &addr->s_addr;
514 ircd_snprintf(0, ipbuf, sizeof(ipbuf), "%u.%u.%u.%u.in-addr.arpa.",
515 (unsigned int)(cp[3]), (unsigned int)(cp[2]),
516 (unsigned int)(cp[1]), (unsigned int)(cp[0]));
519 request = make_request(query);
520 request->type = adns_r_ptr;
521 request->addr.s_addr = addr->s_addr;
523 res = adns_submit_callback(adns, ipbuf, adns_r_ptr, adns_qf_owner,
524 request, &request->aq, res_adns_callback);
526 timer_chg(&resExpireDNS, TT_RELATIVE, 1);
529 /* Returns true if this is a valid name */
530 static int validate_name(char *name)
533 if ((*name<'A' || *name>'Z')
534 && (*name<'a' || *name>'z')
535 && (*name<'0' || *name>'9')
545 * dup_hostent - Duplicate a hostent struct, allocate only enough memory for
546 * the data we're putting in it.
548 static struct hostent* dup_hostent(struct hostent* hp)
555 size_t bytes_needed = 0;
556 struct Hostent* new_hp = 0;
560 /* how much buffer do we need? */
561 bytes_needed += (strlen(hp->h_name) + 1);
565 bytes_needed += (strlen(*pp++) + 1 + sizeof(char*));
568 pp = hp->h_addr_list;
570 bytes_needed += (hp->h_length + sizeof(char*));
573 /* Reserve space for 2 nulls to terminate h_aliases and h_addr_list */
574 bytes_needed += (2 * sizeof(char*));
576 /* Allocate memory */
577 new_hp = (struct Hostent*) MyMalloc(sizeof(struct Hostent) + bytes_needed);
579 new_hp->h.h_addrtype = hp->h_addrtype;
580 new_hp->h.h_length = hp->h_length;
582 /* first write the address list */
583 pp = hp->h_addr_list;
584 ap = new_hp->h.h_addr_list =
585 (char**)(new_hp->buf + ((alias_count + 1) * sizeof(char*)));
586 p = (char*)ap + ((addr_count + 1) * sizeof(char*));
590 memcpy(p, *pp++, hp->h_length);
594 /* next write the name */
595 new_hp->h.h_name = p;
596 strcpy(p, hp->h_name);
597 p += (strlen(p) + 1);
599 /* last write the alias list */
601 ap = new_hp->h.h_aliases = (char**) new_hp->buf;
605 p += (strlen(p) + 1);
608 return (struct hostent*) new_hp;
611 static void res_adns_callback(adns_state state, adns_query q, void *context)
613 struct ResRequest *request = (struct ResRequest *) context;
614 adns_answer *answer = NULL;
616 struct hostent* hp; /* hostent getting filled */
617 char** alias; /* alias list */
618 char** addr; /* address list */
619 char** base_addr; /* original pointer to address list */
620 char* name; /* pointer to name string */
621 char* address; /* pointer to address */
622 char* base_address; /* original pointer to address */
623 char* endp; /* end of our buffer */
624 int addr_count = 0; /* number of addresses in hostent */
625 int alias_count = 0; /* number of aliases in hostent */
629 assert(request->aq == q);
630 res = adns_check(adns, &q, &answer, NULL);
634 /* adns_check returned an error, bail */
635 Debug((DEBUG_DNS, "Resolver: adns_check result %d", res));
637 (*request->query.callback)(request->query.vptr, 0);
638 rem_request(request);
642 Debug((DEBUG_DNS, "Resolver: adns_check status %d nrrs %d",
643 answer->status, answer->nrrs));
645 /* No error, we have a valid answer structure */
646 if (answer->status != adns_s_ok || !answer->nrrs) {
647 /* Status is not 'ok', or there were no RRs found */
649 (*request->query.callback)(request->query.vptr, 0);
654 request->buf = (char*) MyMalloc(MAXGETHOSTLEN + 1);
655 request->buf[MAXGETHOSTLEN] = '\0';
657 * array of alias list pointers starts at beginning of buf
659 hp->h_aliases = (char**) request->buf;
660 hp->h_aliases[0] = NULL;
662 * array of address list pointers starts after alias list pointers
663 * the actual addresses follow the the address list pointers
665 hp->h_addr_list = (char**)(request->buf + ALIAS_BLEN);
667 * don't copy the host address to the beginning of h_addr_list
669 hp->h_addr_list[0] = NULL;
672 hp->h_addrtype = AF_INET;
674 endp = request->buf + MAXGETHOSTLEN;
676 * find the end of the address list
678 addr = hp->h_addr_list;
685 * make address point to first available address slot
687 address = request->buf + ADDRS_OFFSET +
688 (sizeof(struct in_addr) * addr_count);
689 base_address = address;
692 * find the end of the alias list
694 alias = hp->h_aliases;
700 * make name point to first available space in request->buf
702 if (alias_count > 0) {
703 name = hp->h_aliases[alias_count - 1];
704 name += (strlen(name) + 1);
707 name = hp->h_name + strlen(hp->h_name) + 1;
709 name = request->buf + ADDRS_OFFSET + ADDRS_DLEN;
711 switch (request->type) {
713 for (k = 0; k < answer->nrrs; k++) {
714 if (++addr_count < RES_MAXADDRS) {
715 memcpy(address, &answer->rrs.inaddr[k], sizeof(struct in_addr));
718 address += sizeof(struct in_addr);
720 Debug((DEBUG_DNS, "Resolver: A %s for %s",
721 ircd_ntoa((char*) &answer->rrs.inaddr[k]), answer->owner));
724 strcpy(name, answer->owner);
726 name += strlen(name) + 1;
730 strcpy(name, answer->rrs.str[0]);
732 name += strlen(name) + 1;
734 Debug((DEBUG_DNS, "Resolver: PTR %s for %s", hp->h_name, answer->owner));
743 ircd_strncpy(name, answer->cname, endp - name);
746 name += strlen(name) + 1;
747 Debug((DEBUG_DNS, "Resolver: CNAME %s for %s",
748 answer->cname, answer->owner));
751 if (request->type == adns_r_ptr) {
753 Debug((DEBUG_DNS, "relookup %s <-> %s",
754 request->he.h_name, ircd_ntoa((char*) &request->addr)));
756 * Lookup the 'authoritive' name that we were given for the
757 * ip#. By using this call rather than regenerating the
758 * type we automatically gain the use of the cache with no
761 gethost_byname(request->he.h_name, &request->query);
763 * If name wasn't found, a request has been queued and it will
764 * be the last one queued. This is rather nasty way to keep
765 * a host alias with the query. -avalon
767 MyFree(requestListTail->buf);
768 requestListTail->buf = request->buf;
770 memcpy(&requestListTail->he, &request->he, sizeof(struct hostent));
773 * got a name and address response, client resolved
774 * XXX - Bug found here by Dianora -
775 * make_cache() occasionally returns a NULL pointer when a
776 * PTR returned a CNAME, cp was not checked before so the
777 * callback was being called with a value of 0x2C != NULL.
779 struct hostent* he = dup_hostent(&request->he);
780 (*request->query.callback)(request->query.vptr, he);
784 rem_request(request);
787 * adns doesn't use MyMalloc, so we don't use MyFree
793 * m_dns - dns status query
795 int m_dns(struct Client *cptr, struct Client *sptr, int parc, char *parv[])
798 if (parv[1] && *parv[1] == 'd') {
799 sendcmdto_one(&me, CMD_NOTICE, sptr, "%C :ResolverFileDescriptor = %d",
800 sptr, ResolverFileDescriptor);
803 sendcmdto_one(&me, CMD_NOTICE, sptr,"%C :Re %d Rl %d/%d Rp %d Rq %d",
804 sptr, reinfo.re_errors, reinfo.re_nu_look,
805 reinfo.re_na_look, reinfo.re_replies, reinfo.re_requests);
806 sendcmdto_one(&me, CMD_NOTICE, sptr,"%C :Ru %d Rsh %d Rs %d(%d) Rt %d", sptr,
807 reinfo.re_unkrep, reinfo.re_shortttl, reinfo.re_sent,
808 reinfo.re_resends, reinfo.re_timeouts);
813 size_t cres_mem(struct Client* sptr)
815 struct ResRequest* request;
816 size_t request_mem = 0;
817 int request_count = 0;
819 for (request = requestListHead; request; request = request->next) {
820 request_mem += sizeof(struct ResRequest);
822 request_mem += strlen(request->name) + 1;
824 request_mem += MAXGETHOSTLEN + 1;
828 send_reply(sptr, SND_EXPLICIT | RPL_STATSDEBUG,
829 ":Resolver: requests %d(%d)", request_count, request_mem);