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 {
219 /* Socket callback for resolver */
220 static void res_socket_callback(struct Event* ev)
224 assert(ev_type(ev) == ET_READ || ev_type(ev) == ET_ERROR);
226 tv.tv_sec = CurrentTime;
228 adns_processreadable(adns, ResolverFileDescriptor, &tv);
232 * start_resolver - do everything we need to read the resolv.conf file
233 * and initialize the resolver file descriptor if needed
235 static void start_resolver(void)
239 Debug((DEBUG_DNS, "Resolver: start_resolver"));
241 * close the spare file descriptor so res_init can read resolv.conf
242 * successfully. Needed on Solaris
248 * make sure we have a valid file descriptor below 256 so we can
249 * do this again. Needed on Solaris
251 spare_fd = open("/dev/null",O_RDONLY,0);
252 if ((spare_fd < 0) || (spare_fd > 255)) {
254 ircd_snprintf(0, sparemsg, sizeof(sparemsg), "invalid spare_fd %d",
256 server_restart(sparemsg);
262 if ((res = adns_init(&adns, adns_if_debug /*| adns_if_noautosys*/, NULL))) {
263 report_error("Resolver: error initializing adns for %s: %s",
270 ResolverFileDescriptor = adns_get_fd(adns);
272 if (!socket_add(&resSock, res_socket_callback, 0, SS_DATAGRAM,
273 SOCK_EVENT_READABLE, ResolverFileDescriptor))
274 report_error("Resolver: unable to queue resolver file descriptor for %s",
275 cli_name(&me), ENFILE);
278 /* Call the query timeout function */
279 static void expire_DNS_callback(struct Event* ev)
283 next = timeout_query_list(CurrentTime);
285 timer_add(&resExpireDNS, expire_DNS_callback, 0, TT_ABSOLUTE, next);
289 * init_resolver - initialize resolver and resolver library
291 int init_resolver(void)
293 Debug((DEBUG_DNS, "Resolver: init_resolver"));
295 srand48(CurrentTime);
297 memset(&reinfo, 0, sizeof(reinfo));
299 requestListHead = requestListTail = 0;
301 /* initiate the resolver timers */
302 timer_add(timer_init(&resExpireDNS), expire_DNS_callback, 0,
308 Debug((DEBUG_DNS, "Resolver: fd %d errno: %d h_errno: %d: %s",
309 ResolverFileDescriptor, errno, h_errno,
310 (strerror(errno)) ? strerror(errno) : "Unknown"));
311 return ResolverFileDescriptor;
315 * restart_resolver - flush the cache, reread resolv.conf, reopen socket
317 void restart_resolver(void)
323 * add_request - place a new request in the request list
325 static void add_request(struct ResRequest* request)
327 assert(0 != request);
328 if (!requestListHead)
329 requestListHead = requestListTail = request;
331 requestListTail->next = request;
332 requestListTail = request;
334 request->next = NULL;
335 ++reinfo.re_requests;
339 * rem_request - remove a request from the list.
340 * This must also free any memory that has been allocated for
341 * temporary storage of DNS results.
343 static void rem_request(struct ResRequest* request)
345 struct ResRequest** current;
346 struct ResRequest* prev = NULL;
348 assert(0 != request);
349 for (current = &requestListHead; *current; ) {
350 if (*current == request) {
351 *current = request->next;
352 if (requestListTail == request)
353 requestListTail = prev;
357 current = &(*current)->next;
360 adns_cancel(request->aq);
361 MyFree(request->buf);
362 MyFree(request->name);
367 * make_request - Create a DNS request record for the server.
369 static struct ResRequest* make_request(const struct DNSQuery* query)
371 struct ResRequest* request;
373 request = (struct ResRequest*) MyMalloc(sizeof(struct ResRequest));
374 memset(request, 0, sizeof(struct ResRequest));
376 request->addr.s_addr = INADDR_NONE;
377 request->he.h_addrtype = AF_INET;
378 request->he.h_length = sizeof(struct in_addr);
379 request->query.vptr = query->vptr;
380 request->query.callback = query->callback;
382 add_request(request);
387 * timeout_query_list - Remove queries from the list which have been
388 * there too long without being resolved.
390 static time_t timeout_query_list(time_t now)
392 struct timeval tv, tv_buf, *tv_mod = NULL;
395 Debug((DEBUG_DNS, "Resolver: timeout_query_list at %s", myctime(now)));
399 adns_processtimeouts(adns, &tv);
400 adns_firsttimeout(adns, &tv_mod, &tv_buf, tv);
401 next = tv_mod ? tv_mod->tv_sec : AR_TTL;
405 Debug((DEBUG_DNS, "Resolver: next timeout_query_list in %d seconds", next));
410 * delete_resolver_queries - cleanup outstanding queries
411 * for which there no longer exist clients or conf lines.
413 void delete_resolver_queries(const void* vptr)
415 struct ResRequest* request;
416 struct ResRequest* next_request;
418 for (request = requestListHead; request; request = next_request) {
419 next_request = request->next;
420 if (vptr == request->query.vptr)
421 rem_request(request);
426 * gethost_byname - get host address from name
428 void gethost_byname(const char* name, const struct DNSQuery* query)
432 Debug((DEBUG_DNS, "Resolver: gethost_byname %s", name));
434 do_query_name(query, name, NULL);
438 * gethost_byaddr - get host name from address
440 void gethost_byaddr(const char* addr, const struct DNSQuery* query)
443 Debug((DEBUG_DNS, "Resolver: gethost_byaddr %s", ircd_ntoa(addr)));
445 do_query_number(query, (const struct in_addr*) addr, NULL);
449 * do_query_name - nameserver lookup name
451 static void do_query_name(const struct DNSQuery* query,
452 const char* name, struct ResRequest* request)
454 char hname[HOSTLEN + 1];
458 ircd_strncpy(hname, name, HOSTLEN);
459 hname[HOSTLEN] = '\0';
462 request = make_request(query);
463 request->type = adns_r_a;
464 request->name = (char*) MyMalloc(strlen(hname) + 1);
465 strcpy(request->name, hname);
467 res = adns_submit_callback(adns, hname, adns_r_a, adns_qf_owner,
468 request, &request->aq, res_adns_callback);
470 timer_chg(&resExpireDNS, TT_RELATIVE, 1);
474 * do_query_number - Use this to do reverse IP# lookups.
476 static void do_query_number(const struct DNSQuery* query,
477 const struct in_addr* addr,
478 struct ResRequest* request)
481 const unsigned char* cp;
485 cp = (const unsigned char*) &addr->s_addr;
486 ircd_snprintf(0, ipbuf, sizeof(ipbuf), "%u.%u.%u.%u.in-addr.arpa.",
487 (unsigned int)(cp[3]), (unsigned int)(cp[2]),
488 (unsigned int)(cp[1]), (unsigned int)(cp[0]));
491 request = make_request(query);
492 request->type = adns_r_ptr;
493 request->addr.s_addr = addr->s_addr;
495 res = adns_submit_callback(adns, ipbuf, adns_r_ptr, adns_qf_owner,
496 request, &request->aq, res_adns_callback);
498 timer_chg(&resExpireDNS, TT_RELATIVE, 1);
502 * dup_hostent - Duplicate a hostent struct, allocate only enough memory for
503 * the data we're putting in it.
505 static struct hostent* dup_hostent(struct hostent* hp)
512 size_t bytes_needed = 0;
513 struct Hostent* new_hp = 0;
517 /* how much buffer do we need? */
518 bytes_needed += (strlen(hp->h_name) + 1);
522 bytes_needed += (strlen(*pp++) + 1 + sizeof(char*));
525 pp = hp->h_addr_list;
527 bytes_needed += (hp->h_length + sizeof(char*));
530 /* Reserve space for 2 nulls to terminate h_aliases and h_addr_list */
531 bytes_needed += (2 * sizeof(char*));
533 /* Allocate memory */
534 new_hp = (struct Hostent*) MyMalloc(sizeof(struct Hostent) + bytes_needed);
536 new_hp->h.h_addrtype = hp->h_addrtype;
537 new_hp->h.h_length = hp->h_length;
539 /* first write the address list */
540 pp = hp->h_addr_list;
541 ap = new_hp->h.h_addr_list =
542 (char**)(new_hp->buf + ((alias_count + 1) * sizeof(char*)));
543 p = (char*)ap + ((addr_count + 1) * sizeof(char*));
547 memcpy(p, *pp++, hp->h_length);
551 /* next write the name */
552 new_hp->h.h_name = p;
553 strcpy(p, hp->h_name);
554 p += (strlen(p) + 1);
556 /* last write the alias list */
558 ap = new_hp->h.h_aliases = (char**) new_hp->buf;
562 p += (strlen(p) + 1);
565 return (struct hostent*) new_hp;
568 static void res_adns_callback(adns_state state, adns_query q, void *context)
570 struct ResRequest *request = (struct ResRequest *) context;
571 adns_answer *answer = NULL;
573 struct hostent* hp; /* hostent getting filled */
574 char** alias; /* alias list */
575 char** addr; /* address list */
576 char** base_addr; /* original pointer to address list */
577 char* name; /* pointer to name string */
578 char* address; /* pointer to address */
579 char* base_address; /* original pointer to address */
580 char* endp; /* end of our buffer */
581 int addr_count = 0; /* number of addresses in hostent */
582 int alias_count = 0; /* number of aliases in hostent */
586 assert(request->aq == q);
587 res = adns_check(adns, &q, &answer, NULL);
591 /* adns_check returned an error, bail */
592 Debug((DEBUG_DNS, "Resolver: adns_check result %d", res));
594 (*request->query.callback)(request->query.vptr, 0);
595 rem_request(request);
599 Debug((DEBUG_DNS, "Resolver: adns_check status %d nrrs %d",
600 answer->status, answer->nrrs));
602 /* No error, we have a valid answer structure */
603 if (answer->status != adns_s_ok || !answer->nrrs) {
604 /* Status is not 'ok', or there were no RRs found */
606 (*request->query.callback)(request->query.vptr, 0);
611 request->buf = (char*) MyMalloc(MAXGETHOSTLEN + 1);
612 request->buf[MAXGETHOSTLEN] = '\0';
614 * array of alias list pointers starts at beginning of buf
616 hp->h_aliases = (char**) request->buf;
617 hp->h_aliases[0] = NULL;
619 * array of address list pointers starts after alias list pointers
620 * the actual addresses follow the the address list pointers
622 hp->h_addr_list = (char**)(request->buf + ALIAS_BLEN);
624 * don't copy the host address to the beginning of h_addr_list
626 hp->h_addr_list[0] = NULL;
629 hp->h_addrtype = AF_INET;
631 endp = request->buf + MAXGETHOSTLEN;
633 * find the end of the address list
635 addr = hp->h_addr_list;
642 * make address point to first available address slot
644 address = request->buf + ADDRS_OFFSET +
645 (sizeof(struct in_addr) * addr_count);
646 base_address = address;
649 * find the end of the alias list
651 alias = hp->h_aliases;
657 * make name point to first available space in request->buf
659 if (alias_count > 0) {
660 name = hp->h_aliases[alias_count - 1];
661 name += (strlen(name) + 1);
664 name = hp->h_name + strlen(hp->h_name) + 1;
666 name = request->buf + ADDRS_OFFSET + ADDRS_DLEN;
668 switch (request->type) {
670 for (k = 0; k < answer->nrrs; k++) {
671 if (++addr_count < RES_MAXADDRS) {
672 memcpy(address, &answer->rrs.inaddr[k], sizeof(struct in_addr));
675 address += sizeof(struct in_addr);
677 Debug((DEBUG_DNS, "Resolver: A %s for %s",
678 ircd_ntoa((char*) &answer->rrs.inaddr[k]), answer->owner));
681 strcpy(name, answer->owner);
683 name += strlen(name) + 1;
687 strcpy(name, answer->rrs.str[0]);
689 name += strlen(name) + 1;
691 Debug((DEBUG_DNS, "Resolver: PTR %s for %s", hp->h_name, answer->owner));
700 ircd_strncpy(name, answer->cname, endp - name);
703 name += strlen(name) + 1;
704 Debug((DEBUG_DNS, "Resolver: CNAME %s for %s",
705 answer->cname, answer->owner));
708 if (request->type == adns_r_ptr) {
710 Debug((DEBUG_DNS, "relookup %s <-> %s",
711 request->he.h_name, ircd_ntoa((char*) &request->addr)));
713 * Lookup the 'authoritive' name that we were given for the
714 * ip#. By using this call rather than regenerating the
715 * type we automatically gain the use of the cache with no
718 gethost_byname(request->he.h_name, &request->query);
720 * If name wasn't found, a request has been queued and it will
721 * be the last one queued. This is rather nasty way to keep
722 * a host alias with the query. -avalon
724 MyFree(requestListTail->buf);
725 requestListTail->buf = request->buf;
727 memcpy(&requestListTail->he, &request->he, sizeof(struct hostent));
730 * got a name and address response, client resolved
731 * XXX - Bug found here by Dianora -
732 * make_cache() occasionally returns a NULL pointer when a
733 * PTR returned a CNAME, cp was not checked before so the
734 * callback was being called with a value of 0x2C != NULL.
736 struct hostent* he = dup_hostent(&request->he);
737 (*request->query.callback)(request->query.vptr, he);
741 rem_request(request);
744 * adns doesn't use MyMalloc, so we don't use MyFree
750 * m_dns - dns status query
752 int m_dns(struct Client *cptr, struct Client *sptr, int parc, char *parv[])
755 sendcmdto_one(&me, CMD_NOTICE, sptr,"%C :Errors %d Lookups %d/%d Replies %d Requests %d",
756 sptr, reinfo.re_errors, reinfo.re_nu_look,
757 reinfo.re_na_look, reinfo.re_replies, reinfo.re_requests);
758 sendcmdto_one(&me, CMD_NOTICE, sptr, "%C :Unknown Reply %d Short TTL(<10m) %d Sent %d Resends %d Timeouts %d", sptr,
759 reinfo.re_unkrep, reinfo.re_shortttl, reinfo.re_sent,
760 reinfo.re_resends, reinfo.re_timeouts);
761 sendcmdto_one(&me, CMD_NOTICE, sptr, "%C :ResolverFileDescriptor = %d",
762 sptr, ResolverFileDescriptor);
767 size_t cres_mem(struct Client* sptr)
769 struct ResRequest* request;
770 size_t request_mem = 0;
771 int request_count = 0;
773 for (request = requestListHead; request; request = request->next) {
774 request_mem += sizeof(struct ResRequest);
776 request_mem += strlen(request->name) + 1;
778 request_mem += MAXGETHOSTLEN + 1;
782 send_reply(sptr, SND_EXPLICIT | RPL_STATSDEBUG,
783 ":Resolver: requests %d(%d)", request_count, request_mem);