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/types.h>
42 #include <sys/socket.h>
47 #include <arpa/nameser.h>
50 #include <arpa/inet.h>
54 #error this code needs to be able to address individual octets
58 * Some systems do not define INADDR_NONE (255.255.255.255)
59 * INADDR_NONE is actually a valid address, but it should never
60 * be returned from any nameserver.
61 * NOTE: The bit pattern for INADDR_NONE and INADDR_ANY (0.0.0.0) should be
62 * the same on all hosts so we shouldn't need to use htonl or ntohl to
63 * compare or set the values.
66 #define INADDR_NONE ((unsigned int) 0xffffffff)
69 #define MAXPACKET 1024 /* rfc sez 512 but we expand names so ... */
70 #define RES_MAXALIASES 35 /* maximum aliases allowed */
71 #define RES_MAXADDRS 35 /* maximum addresses allowed */
73 * OSF1 doesn't have RES_NOALIASES
76 #define RES_NOALIASES 0
80 * macros used to calulate offsets into fixed query buffer
82 #define ALIAS_BLEN ((RES_MAXALIASES + 1) * sizeof(char*))
83 #define ADDRS_BLEN ((RES_MAXADDRS + 1) * sizeof(struct in_addr*))
85 #define ADDRS_OFFSET (ALIAS_BLEN + ADDRS_BLEN)
86 #define ADDRS_DLEN (RES_MAXADDRS * sizeof(struct in_addr))
87 #define NAMES_OFFSET (ADDRS_OFFSET + ADDRS_DLEN)
88 #define MAXGETHOSTLEN (NAMES_OFFSET + MAXPACKET)
90 #define AR_TTL 600 /* TTL in seconds for dns cache entries */
93 * the following values should be prime
95 #define ARES_CACSIZE 307
97 //#define MAXCACHED 281
100 * Building the Hostent
101 * The Hostent struct is arranged like this:
102 * +-------------------------------+
103 * Hostent: | struct hostent h |
104 * |-------------------------------|
106 * +-------------------------------+
110 * +-------------------------------+
111 * buf: | h_aliases pointer array | Max size: ALIAS_BLEN;
112 * | NULL | contains `char *'s
113 * |-------------------------------|
114 * | h_addr_list pointer array | Max size: ADDRS_BLEN;
115 * | NULL | contains `struct in_addr *'s
116 * |-------------------------------|
117 * | h_addr_list addresses | Max size: ADDRS_DLEN;
118 * | | contains `struct in_addr's
119 * |-------------------------------|
120 * | storage for hostname strings | Max size: ALIAS_DLEN;
121 * +-------------------------------+ contains `char's
123 * For requests the size of the h_aliases, and h_addr_list pointer
124 * array sizes are set to MAXALISES and MAXADDRS respectively, and
125 * buf is a fixed size with enough space to hold the largest expected
126 * reply from a nameserver, see RFC 1034 and RFC 1035.
127 * For cached entries the sizes are dependent on the actual number
128 * of aliases and addresses. If new aliases and addresses are found
129 * for cached entries, the buffer is grown and the new entries are added.
130 * The hostent struct is filled in with the addresses of the entries in
131 * the Hostent buf as follows:
132 * h_name - contains a pointer to the start of the hostname string area,
133 * or NULL if none is set. The h_name is followed by the
134 * aliases, in the storage for hostname strings area.
135 * h_aliases - contains a pointer to the start of h_aliases pointer array.
136 * This array contains pointers to the storage for hostname
137 * strings area and is terminated with a NULL. The first alias
138 * is stored directly after the h_name.
139 * h_addr_list - contains a pointer to the start of h_addr_list pointer array.
140 * This array contains pointers to in_addr structures in the
141 * h_addr_list addresses area and is terminated with a NULL.
143 * Filling the buffer this way allows for proper alignment of the h_addr_list
146 * This arrangement allows us to alias a Hostent struct pointer as a
147 * real struct hostent* without lying. It also allows us to change the
148 * values contained in the cached entries and requests without changing
149 * the actual hostent pointer, which is saved in a client struct and can't
150 * be changed without blowing things up or a lot more fiddling around.
151 * It also allows for defered allocation of the fixed size buffers until
152 * they are really needed.
153 * Nov. 17, 1997 --Bleep
157 struct hostent h; /* the hostent struct we are passing around */
158 char buf[1]; /* buffer for data pointed to from hostent */
162 struct ResRequest* next;
169 struct DNSQuery query; /* query callback for this request */
174 int ResolverFileDescriptor = -1; /* GLOBAL - used in s_bsd.c */
176 static struct Socket resSock; /* Socket describing resolver */
177 static struct Timer resExpireDNS; /* Timer for DNS expiration */
178 static adns_state adns = NULL;
181 * Keep a spare file descriptor open. res_init calls fopen to read the
182 * resolv.conf file. If ircd is hogging all the file descriptors below 256,
183 * on systems with crippled FILE structures this will cause wierd bugs.
184 * This is definitely needed for Solaris which uses an unsigned char to
185 * hold the file descriptor. --Dianora
187 static int spare_fd = -1;
189 static struct ResRequest* requestListHead; /* head of resolver request list */
190 static struct ResRequest* requestListTail; /* tail of resolver request list */
193 static void add_request(struct ResRequest* request);
194 static void rem_request(struct ResRequest* request);
195 static struct ResRequest* make_request(const struct DNSQuery* query);
196 static time_t timeout_query_list(time_t now);
197 static void do_query_name(const struct DNSQuery* query,
199 struct ResRequest* request);
200 static void do_query_number(const struct DNSQuery* query,
201 const struct in_addr*,
202 struct ResRequest* request);
203 static void res_adns_callback(adns_state state, adns_query q, void *context);
205 static struct resinfo {
221 * res_isourserver(ina)
222 * looks up "ina" in _res.ns_addr_list[]
227 * paul vixie, 29may94
230 res_ourserver(const struct __res_state* statp, const struct sockaddr_in* inp)
232 struct sockaddr_in ina;
236 for (ns = 0; ns < statp->nscount; ns++) {
237 const struct sockaddr_in *srv = &statp->nsaddr_list[ns];
239 if (srv->sin_family == ina.sin_family &&
240 srv->sin_port == ina.sin_port &&
241 (srv->sin_addr.s_addr == INADDR_ANY ||
242 srv->sin_addr.s_addr == ina.sin_addr.s_addr))
248 /* Socket callback for resolver */
249 static void res_socket_callback(struct Event* ev)
253 assert(ev_type(ev) == ET_READ || ev_type(ev) == ET_ERROR);
255 tv.tv_sec = CurrentTime;
257 adns_processreadable(adns, ResolverFileDescriptor, &tv);
261 * start_resolver - do everything we need to read the resolv.conf file
262 * and initialize the resolver file descriptor if needed
264 static void start_resolver(void)
268 Debug((DEBUG_DNS, "Resolver: start_resolver"));
270 * close the spare file descriptor so res_init can read resolv.conf
271 * successfully. Needed on Solaris
277 * make sure we have a valid file descriptor below 256 so we can
278 * do this again. Needed on Solaris
280 spare_fd = open("/dev/null",O_RDONLY,0);
281 if ((spare_fd < 0) || (spare_fd > 255)) {
283 ircd_snprintf(0, sparemsg, sizeof(sparemsg), "invalid spare_fd %d",
285 server_restart(sparemsg);
291 if ((res = adns_init(&adns, adns_if_debug /*| adns_if_noautosys*/, NULL))) {
292 report_error("Resolver: error initializing adns for %s: %s",
299 ResolverFileDescriptor = adns_get_fd(adns);
301 if (!socket_add(&resSock, res_socket_callback, 0, SS_DATAGRAM,
302 SOCK_EVENT_READABLE, ResolverFileDescriptor))
303 report_error("Resolver: unable to queue resolver file descriptor for %s",
304 cli_name(&me), ENFILE);
307 /* Call the query timeout function */
308 static void expire_DNS_callback(struct Event* ev)
312 next = timeout_query_list(CurrentTime);
314 timer_add(&resExpireDNS, expire_DNS_callback, 0, TT_ABSOLUTE, next);
318 * init_resolver - initialize resolver and resolver library
320 int init_resolver(void)
322 Debug((DEBUG_DNS, "Resolver: init_resolver"));
324 srand48(CurrentTime);
326 memset(&reinfo, 0, sizeof(reinfo));
328 requestListHead = requestListTail = 0;
330 /* initiate the resolver timers */
331 timer_add(timer_init(&resExpireDNS), expire_DNS_callback, 0,
337 Debug((DEBUG_DNS, "Resolver: fd %d errno: %d h_errno: %d: %s",
338 ResolverFileDescriptor, errno, h_errno,
339 (strerror(errno)) ? strerror(errno) : "Unknown"));
340 return ResolverFileDescriptor;
344 * restart_resolver - flush the cache, reread resolv.conf, reopen socket
346 void restart_resolver(void)
352 * add_request - place a new request in the request list
354 static void add_request(struct ResRequest* request)
356 assert(0 != request);
357 if (!requestListHead)
358 requestListHead = requestListTail = request;
360 requestListTail->next = request;
361 requestListTail = request;
363 request->next = NULL;
364 ++reinfo.re_requests;
368 * rem_request - remove a request from the list.
369 * This must also free any memory that has been allocated for
370 * temporary storage of DNS results.
372 static void rem_request(struct ResRequest* request)
374 struct ResRequest** current;
375 struct ResRequest* prev = NULL;
377 assert(0 != request);
378 for (current = &requestListHead; *current; ) {
379 if (*current == request) {
380 *current = request->next;
381 if (requestListTail == request)
382 requestListTail = prev;
386 current = &(*current)->next;
389 adns_cancel(request->aq);
390 MyFree(request->buf);
391 MyFree(request->name);
396 * make_request - Create a DNS request record for the server.
398 static struct ResRequest* make_request(const struct DNSQuery* query)
400 struct ResRequest* request;
402 request = (struct ResRequest*) MyMalloc(sizeof(struct ResRequest));
403 memset(request, 0, sizeof(struct ResRequest));
405 request->addr.s_addr = INADDR_NONE;
406 request->he.h_addrtype = AF_INET;
407 request->he.h_length = sizeof(struct in_addr);
408 request->query.vptr = query->vptr;
409 request->query.callback = query->callback;
411 add_request(request);
416 * timeout_query_list - Remove queries from the list which have been
417 * there too long without being resolved.
419 static time_t timeout_query_list(time_t now)
421 struct timeval tv, tv_buf, *tv_mod = NULL;
424 Debug((DEBUG_DNS, "Resolver: timeout_query_list at %s", myctime(now)));
428 adns_processtimeouts(adns, &tv);
429 adns_firsttimeout(adns, &tv_mod, &tv_buf, tv);
430 next = tv_mod ? tv_mod->tv_sec : AR_TTL;
434 Debug((DEBUG_DNS, "Resolver: next timeout_query_list in %d seconds", next));
439 * delete_resolver_queries - cleanup outstanding queries
440 * for which there no longer exist clients or conf lines.
442 void delete_resolver_queries(const void* vptr)
444 struct ResRequest* request;
445 struct ResRequest* next_request;
447 for (request = requestListHead; request; request = next_request) {
448 next_request = request->next;
449 if (vptr == request->query.vptr)
450 rem_request(request);
455 * gethost_byname - get host address from name
457 void gethost_byname(const char* name, const struct DNSQuery* query)
461 Debug((DEBUG_DNS, "Resolver: gethost_byname %s", name));
463 do_query_name(query, name, NULL);
467 * gethost_byaddr - get host name from address
469 void gethost_byaddr(const char* addr, const struct DNSQuery* query)
472 Debug((DEBUG_DNS, "Resolver: gethost_byaddr %s", ircd_ntoa(addr)));
474 do_query_number(query, (const struct in_addr*) addr, NULL);
478 * do_query_name - nameserver lookup name
480 static void do_query_name(const struct DNSQuery* query,
481 const char* name, struct ResRequest* request)
483 char hname[HOSTLEN + 1];
487 ircd_strncpy(hname, name, HOSTLEN);
488 hname[HOSTLEN] = '\0';
491 request = make_request(query);
492 request->type = adns_r_a;
493 request->name = (char*) MyMalloc(strlen(hname) + 1);
494 strcpy(request->name, hname);
496 res = adns_submit_callback(adns, hname, adns_r_a, adns_qf_owner,
497 request, &request->aq, res_adns_callback);
499 timer_chg(&resExpireDNS, TT_RELATIVE, 1);
503 * do_query_number - Use this to do reverse IP# lookups.
505 static void do_query_number(const struct DNSQuery* query,
506 const struct in_addr* addr,
507 struct ResRequest* request)
510 const unsigned char* cp;
514 cp = (const unsigned char*) &addr->s_addr;
515 ircd_snprintf(0, ipbuf, sizeof(ipbuf), "%u.%u.%u.%u.in-addr.arpa.",
516 (unsigned int)(cp[3]), (unsigned int)(cp[2]),
517 (unsigned int)(cp[1]), (unsigned int)(cp[0]));
520 request = make_request(query);
521 request->type = adns_r_ptr;
522 request->addr.s_addr = addr->s_addr;
524 res = adns_submit_callback(adns, ipbuf, adns_r_ptr, adns_qf_owner,
525 request, &request->aq, res_adns_callback);
527 timer_chg(&resExpireDNS, TT_RELATIVE, 1);
530 /* Returns true if this is a valid name */
531 static int validate_name(char *name)
534 if ((*name<'A' || *name>'Z')
535 && (*name<'a' || *name>'z')
536 && (*name<'0' || *name>'9')
546 * dup_hostent - Duplicate a hostent struct, allocate only enough memory for
547 * the data we're putting in it.
549 static struct hostent* dup_hostent(struct hostent* hp)
556 size_t bytes_needed = 0;
557 struct Hostent* new_hp = 0;
561 /* how much buffer do we need? */
562 bytes_needed += (strlen(hp->h_name) + 1);
566 bytes_needed += (strlen(*pp++) + 1 + sizeof(char*));
569 pp = hp->h_addr_list;
571 bytes_needed += (hp->h_length + sizeof(char*));
574 /* Reserve space for 2 nulls to terminate h_aliases and h_addr_list */
575 bytes_needed += (2 * sizeof(char*));
577 /* Allocate memory */
578 new_hp = (struct Hostent*) MyMalloc(sizeof(struct Hostent) + bytes_needed);
580 new_hp->h.h_addrtype = hp->h_addrtype;
581 new_hp->h.h_length = hp->h_length;
583 /* first write the address list */
584 pp = hp->h_addr_list;
585 ap = new_hp->h.h_addr_list =
586 (char**)(new_hp->buf + ((alias_count + 1) * sizeof(char*)));
587 p = (char*)ap + ((addr_count + 1) * sizeof(char*));
591 memcpy(p, *pp++, hp->h_length);
595 /* next write the name */
596 new_hp->h.h_name = p;
597 strcpy(p, hp->h_name);
598 p += (strlen(p) + 1);
600 /* last write the alias list */
602 ap = new_hp->h.h_aliases = (char**) new_hp->buf;
606 p += (strlen(p) + 1);
609 return (struct hostent*) new_hp;
612 static void res_adns_callback(adns_state state, adns_query q, void *context)
614 struct ResRequest *request = (struct ResRequest *) context;
615 adns_answer *answer = NULL;
617 struct hostent* hp; /* hostent getting filled */
618 char** alias; /* alias list */
619 char** addr; /* address list */
620 char** base_addr; /* original pointer to address list */
621 char* name; /* pointer to name string */
622 char* address; /* pointer to address */
623 char* base_address; /* original pointer to address */
624 char* endp; /* end of our buffer */
625 int addr_count = 0; /* number of addresses in hostent */
626 int alias_count = 0; /* number of aliases in hostent */
630 assert(request->aq == q);
631 res = adns_check(adns, &q, &answer, NULL);
635 /* adns_check returned an error, bail */
636 Debug((DEBUG_DNS, "Resolver: adns_check result %d", res));
638 (*request->query.callback)(request->query.vptr, 0);
639 rem_request(request);
643 Debug((DEBUG_DNS, "Resolver: adns_check status %d nrrs %d",
644 answer->status, answer->nrrs));
646 /* No error, we have a valid answer structure */
647 if (answer->status != adns_s_ok || !answer->nrrs) {
648 /* Status is not 'ok', or there were no RRs found */
650 (*request->query.callback)(request->query.vptr, 0);
655 request->buf = (char*) MyMalloc(MAXGETHOSTLEN + 1);
656 request->buf[MAXGETHOSTLEN] = '\0';
658 * array of alias list pointers starts at beginning of buf
660 hp->h_aliases = (char**) request->buf;
661 hp->h_aliases[0] = NULL;
663 * array of address list pointers starts after alias list pointers
664 * the actual addresses follow the the address list pointers
666 hp->h_addr_list = (char**)(request->buf + ALIAS_BLEN);
668 * don't copy the host address to the beginning of h_addr_list
670 hp->h_addr_list[0] = NULL;
673 hp->h_addrtype = AF_INET;
675 endp = request->buf + MAXGETHOSTLEN;
677 * find the end of the address list
679 addr = hp->h_addr_list;
686 * make address point to first available address slot
688 address = request->buf + ADDRS_OFFSET +
689 (sizeof(struct in_addr) * addr_count);
690 base_address = address;
693 * find the end of the alias list
695 alias = hp->h_aliases;
701 * make name point to first available space in request->buf
703 if (alias_count > 0) {
704 name = hp->h_aliases[alias_count - 1];
705 name += (strlen(name) + 1);
708 name = hp->h_name + strlen(hp->h_name) + 1;
710 name = request->buf + ADDRS_OFFSET + ADDRS_DLEN;
712 switch (request->type) {
714 for (k = 0; k < answer->nrrs; k++) {
715 if (++addr_count < RES_MAXADDRS) {
716 memcpy(address, &answer->rrs.inaddr[k], sizeof(struct in_addr));
719 address += sizeof(struct in_addr);
721 Debug((DEBUG_DNS, "Resolver: A %s for %s",
722 ircd_ntoa((char*) &answer->rrs.inaddr[k]), answer->owner));
725 strcpy(name, answer->owner);
727 name += strlen(name) + 1;
731 strcpy(name, answer->rrs.str[0]);
733 name += strlen(name) + 1;
735 Debug((DEBUG_DNS, "Resolver: PTR %s for %s", hp->h_name, answer->owner));
744 ircd_strncpy(name, answer->cname, endp - name);
747 name += strlen(name) + 1;
748 Debug((DEBUG_DNS, "Resolver: CNAME %s for %s",
749 answer->cname, answer->owner));
752 if (request->type == adns_r_ptr) {
754 Debug((DEBUG_DNS, "relookup %s <-> %s",
755 request->he.h_name, ircd_ntoa((char*) &request->addr)));
757 * Lookup the 'authoritive' name that we were given for the
758 * ip#. By using this call rather than regenerating the
759 * type we automatically gain the use of the cache with no
762 gethost_byname(request->he.h_name, &request->query);
764 * If name wasn't found, a request has been queued and it will
765 * be the last one queued. This is rather nasty way to keep
766 * a host alias with the query. -avalon
768 MyFree(requestListTail->buf);
769 requestListTail->buf = request->buf;
771 memcpy(&requestListTail->he, &request->he, sizeof(struct hostent));
774 * got a name and address response, client resolved
775 * XXX - Bug found here by Dianora -
776 * make_cache() occasionally returns a NULL pointer when a
777 * PTR returned a CNAME, cp was not checked before so the
778 * callback was being called with a value of 0x2C != NULL.
780 struct hostent* he = dup_hostent(&request->he);
781 (*request->query.callback)(request->query.vptr, he);
785 rem_request(request);
788 * adns doesn't use MyMalloc, so we don't use MyFree
794 * m_dns - dns status query
796 int m_dns(struct Client *cptr, struct Client *sptr, int parc, char *parv[])
799 if (parv[1] && *parv[1] == 'd') {
800 sendcmdto_one(&me, CMD_NOTICE, sptr, "%C :ResolverFileDescriptor = %d",
801 sptr, ResolverFileDescriptor);
804 sendcmdto_one(&me, CMD_NOTICE, sptr,"%C :Re %d Rl %d/%d Rp %d Rq %d",
805 sptr, reinfo.re_errors, reinfo.re_nu_look,
806 reinfo.re_na_look, reinfo.re_replies, reinfo.re_requests);
807 sendcmdto_one(&me, CMD_NOTICE, sptr,"%C :Ru %d Rsh %d Rs %d(%d) Rt %d", sptr,
808 reinfo.re_unkrep, reinfo.re_shortttl, reinfo.re_sent,
809 reinfo.re_resends, reinfo.re_timeouts);
814 size_t cres_mem(struct Client* sptr)
816 struct ResRequest* request;
817 size_t request_mem = 0;
818 int request_count = 0;
820 for (request = requestListHead; request; request = request->next) {
821 request_mem += sizeof(struct ResRequest);
823 request_mem += strlen(request->name) + 1;
825 request_mem += MAXGETHOSTLEN + 1;
829 send_reply(sptr, SND_EXPLICIT | RPL_STATSDEBUG,
830 ":Resolver: requests %d(%d)", request_count, request_mem);