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; /* buffer for data pointed to from hostent */
161 struct ResRequest* next;
168 struct DNSQuery query; /* query callback for this request */
173 struct CacheEntry* hname_next;
174 struct CacheEntry* hnum_next;
175 struct CacheEntry* list_next;
179 struct DNSReply reply;
183 struct CacheEntry* num_list;
184 struct CacheEntry* name_list;
188 int ResolverFileDescriptor = -1; /* GLOBAL - used in s_bsd.c */
190 static struct Socket resSock; /* Socket describing resolver */
191 static struct Timer resExpireDNS; /* Timer for DNS expiration */
192 static struct Timer resExpireCache; /* Timer for cache expiration */
193 static adns_state adns = NULL;
196 * Keep a spare file descriptor open. res_init calls fopen to read the
197 * resolv.conf file. If ircd is hogging all the file descriptors below 256,
198 * on systems with crippled FILE structures this will cause wierd bugs.
199 * This is definitely needed for Solaris which uses an unsigned char to
200 * hold the file descriptor. --Dianora
202 static int spare_fd = -1;
204 static int cachedCount = 0;
205 static struct CacheTable hashtable[ARES_CACSIZE];
206 static struct CacheEntry* cacheTop;
207 static struct ResRequest* requestListHead; /* head of resolver request list */
208 static struct ResRequest* requestListTail; /* tail of resolver request list */
211 static void add_request(struct ResRequest* request);
212 static void rem_request(struct ResRequest* request);
213 static struct ResRequest* make_request(const struct DNSQuery* query);
214 static time_t timeout_query_list(time_t now);
215 static time_t expire_cache(time_t now);
216 static void rem_cache(struct CacheEntry*);
217 static void do_query_name(const struct DNSQuery* query,
219 struct ResRequest* request);
220 static void do_query_number(const struct DNSQuery* query,
221 const struct in_addr*,
222 struct ResRequest* request);
223 static struct CacheEntry* make_cache(struct ResRequest* request);
224 static struct CacheEntry* find_cache_name(const char* name);
225 static struct CacheEntry* find_cache_number(struct ResRequest* request,
227 static void res_adns_callback(adns_state state, adns_query q, void *context);
229 static struct cacheinfo {
239 static struct resinfo {
255 * res_isourserver(ina)
256 * looks up "ina" in _res.ns_addr_list[]
261 * paul vixie, 29may94
264 res_ourserver(const struct __res_state* statp, const struct sockaddr_in* inp)
266 struct sockaddr_in ina;
270 for (ns = 0; ns < statp->nscount; ns++) {
271 const struct sockaddr_in *srv = &statp->nsaddr_list[ns];
273 if (srv->sin_family == ina.sin_family &&
274 srv->sin_port == ina.sin_port &&
275 (srv->sin_addr.s_addr == INADDR_ANY ||
276 srv->sin_addr.s_addr == ina.sin_addr.s_addr))
282 /* Socket callback for resolver */
283 static void res_socket_callback(struct Event* ev)
287 assert(ev_type(ev) == ET_READ || ev_type(ev) == ET_ERROR);
289 tv.tv_sec = CurrentTime;
291 adns_processreadable(adns, ResolverFileDescriptor, &tv);
295 * start_resolver - do everything we need to read the resolv.conf file
296 * and initialize the resolver file descriptor if needed
298 static void start_resolver(void)
302 Debug((DEBUG_DNS, "Resolver: start_resolver"));
304 * close the spare file descriptor so res_init can read resolv.conf
305 * successfully. Needed on Solaris
311 * make sure we have a valid file descriptor below 256 so we can
312 * do this again. Needed on Solaris
314 spare_fd = open("/dev/null",O_RDONLY,0);
315 if ((spare_fd < 0) || (spare_fd > 255)) {
317 ircd_snprintf(0, sparemsg, sizeof(sparemsg), "invalid spare_fd %d",
319 server_restart(sparemsg);
325 if ((res = adns_init(&adns, adns_if_debug /*| adns_if_noautosys*/, NULL))) {
326 report_error("Resolver: error initializing adns for %s: %s", cli_name(&me), res);
332 ResolverFileDescriptor = adns_get_fd(adns);
334 if (!socket_add(&resSock, res_socket_callback, 0, SS_DATAGRAM,
335 SOCK_EVENT_READABLE, ResolverFileDescriptor))
336 report_error("Resolver: unable to queue resolver file descriptor for %s",
337 cli_name(&me), ENFILE);
340 /* Call the query timeout function */
341 static void expire_DNS_callback(struct Event* ev)
345 next = timeout_query_list(CurrentTime);
347 timer_add(&resExpireDNS, expire_DNS_callback, 0, TT_ABSOLUTE, next);
350 /* Call the cache expire function */
351 static void expire_cache_callback(struct Event* ev)
355 next = expire_cache(CurrentTime);
357 timer_add(&resExpireCache, expire_cache_callback, 0, TT_ABSOLUTE, next);
361 * init_resolver - initialize resolver and resolver library
363 int init_resolver(void)
365 Debug((DEBUG_DNS, "Resolver: init_resolver"));
367 srand48(CurrentTime);
369 memset(&cainfo, 0, sizeof(cainfo));
370 memset(hashtable, 0, sizeof(hashtable));
371 memset(&reinfo, 0, sizeof(reinfo));
373 requestListHead = requestListTail = 0;
375 /* initiate the resolver timers */
376 timer_add(timer_init(&resExpireDNS), expire_DNS_callback, 0,
378 timer_add(timer_init(&resExpireCache), expire_cache_callback, 0,
384 Debug((DEBUG_DNS, "Resolver: fd %d errno: %d h_errno: %d: %s",
385 ResolverFileDescriptor, errno, h_errno,
386 (strerror(errno)) ? strerror(errno) : "Unknown"));
387 return ResolverFileDescriptor;
391 * restart_resolver - flush the cache, reread resolv.conf, reopen socket
393 void restart_resolver(void)
395 /* flush_cache(); flush the dns cache */
400 * add_request - place a new request in the request list
402 static void add_request(struct ResRequest* request)
404 assert(0 != request);
405 if (!requestListHead)
406 requestListHead = requestListTail = request;
408 requestListTail->next = request;
409 requestListTail = request;
411 request->next = NULL;
412 ++reinfo.re_requests;
416 * rem_request - remove a request from the list.
417 * This must also free any memory that has been allocated for
418 * temporary storage of DNS results.
420 static void rem_request(struct ResRequest* request)
422 struct ResRequest** current;
423 struct ResRequest* prev = NULL;
425 assert(0 != request);
426 for (current = &requestListHead; *current; ) {
427 if (*current == request) {
428 *current = request->next;
429 if (requestListTail == request)
430 requestListTail = prev;
434 current = &(*current)->next;
437 adns_cancel(request->aq);
438 MyFree(request->he.buf);
439 MyFree(request->name);
444 * make_request - Create a DNS request record for the server.
446 static struct ResRequest* make_request(const struct DNSQuery* query)
448 struct ResRequest* request;
450 request = (struct ResRequest*) MyMalloc(sizeof(struct ResRequest));
451 memset(request, 0, sizeof(struct ResRequest));
453 request->addr.s_addr = INADDR_NONE;
454 request->he.h.h_addrtype = AF_INET;
455 request->he.h.h_length = sizeof(struct in_addr);
456 request->query.vptr = query->vptr;
457 request->query.callback = query->callback;
459 #if defined(NULL_POINTER_NOT_ZERO)
460 request->next = NULL;
461 request->he.buf = NULL;
462 request->he.h.h_name = NULL;
463 request->he.h.h_aliases = NULL;
464 request->he.h.h_addr_list = NULL;
466 add_request(request);
471 * timeout_query_list - Remove queries from the list which have been
472 * there too long without being resolved.
474 static time_t timeout_query_list(time_t now)
476 struct timeval tv, tv_buf, *tv_mod = NULL;
479 Debug((DEBUG_DNS, "Resolver: timeout_query_list at %s", myctime(now)));
483 adns_processtimeouts(adns, &tv);
484 adns_firsttimeout(adns, &tv_mod, &tv_buf, tv);
485 next = tv_mod ? tv_mod->tv_sec : AR_TTL;
489 Debug((DEBUG_DNS, "Resolver: next timeout_query_list in %d seconds", next));
494 * expire_cache - removes entries from the cache which are older
495 * than their expiry times. returns the time at which the server
496 * should next poll the cache.
498 static time_t expire_cache(time_t now)
500 struct CacheEntry* cp;
501 struct CacheEntry* cp_next;
504 Debug((DEBUG_DNS, "Resolver: expire_cache at %s", myctime(now)));
505 for (cp = cacheTop; cp; cp = cp_next) {
506 cp_next = cp->list_next;
507 if (cp->expireat < now) {
511 else if (!expire || expire > cp->expireat)
512 expire = cp->expireat;
514 return (expire > now) ? expire : (now + AR_TTL);
518 * delete_resolver_queries - cleanup outstanding queries
519 * for which there no longer exist clients or conf lines.
521 void delete_resolver_queries(const void* vptr)
523 struct ResRequest* request;
524 struct ResRequest* next_request;
526 for (request = requestListHead; request; request = next_request) {
527 next_request = request->next;
528 if (vptr == request->query.vptr)
529 rem_request(request);
534 * gethost_byname - get host address from name
536 struct DNSReply* gethost_byname(const char* name,
537 const struct DNSQuery* query)
539 struct CacheEntry* cp;
542 Debug((DEBUG_DNS, "Resolver: gethost_byname %s", name));
544 if ((cp = find_cache_name(name)))
547 do_query_name(query, name, NULL);
552 * gethost_byaddr - get host name from address
554 struct DNSReply* gethost_byaddr(const char* addr,
555 const struct DNSQuery* query)
557 struct CacheEntry *cp;
561 Debug((DEBUG_DNS, "Resolver: gethost_byaddr %s", ircd_ntoa(addr)));
564 if ((cp = find_cache_number(NULL, addr)))
567 do_query_number(query, (const struct in_addr*) addr, NULL);
572 * do_query_name - nameserver lookup name
574 static void do_query_name(const struct DNSQuery* query,
575 const char* name, struct ResRequest* request)
577 char hname[HOSTLEN + 1];
581 ircd_strncpy(hname, name, HOSTLEN);
582 hname[HOSTLEN] = '\0';
585 request = make_request(query);
586 request->type = adns_r_a;
587 request->name = (char*) MyMalloc(strlen(hname) + 1);
588 strcpy(request->name, hname);
590 res = adns_submit_callback(adns, hname, adns_r_a, adns_qf_owner, request, &request->aq, res_adns_callback);
592 timer_chg(&resExpireDNS, TT_RELATIVE, 1);
596 * do_query_number - Use this to do reverse IP# lookups.
598 static void do_query_number(const struct DNSQuery* query,
599 const struct in_addr* addr,
600 struct ResRequest* request)
603 const unsigned char* cp;
607 cp = (const unsigned char*) &addr->s_addr;
608 ircd_snprintf(0, ipbuf, sizeof(ipbuf), "%u.%u.%u.%u.in-addr.arpa.",
609 (unsigned int)(cp[3]), (unsigned int)(cp[2]),
610 (unsigned int)(cp[1]), (unsigned int)(cp[0]));
613 request = make_request(query);
614 request->type = adns_r_ptr;
615 request->addr.s_addr = addr->s_addr;
617 res = adns_submit_callback(adns, ipbuf, adns_r_ptr, adns_qf_owner, request, &request->aq, res_adns_callback);
619 timer_chg(&resExpireDNS, TT_RELATIVE, 1);
622 /* Returns true if this is a valid name */
623 static int validate_name(char *name)
626 if ((*name<'A' || *name>'Z')
627 && (*name<'a' || *name>'z')
628 && (*name<'0' || *name>'9')
637 static void res_adns_callback(adns_state state, adns_query q, void *context)
639 struct ResRequest *request = (struct ResRequest *) context;
640 adns_answer *answer = NULL;
642 struct hostent* hp; /* hostent getting filled */
643 char** alias; /* alias list */
644 char** addr; /* address list */
645 char** base_addr; /* original pointer to address list */
646 char* name; /* pointer to name string */
647 char* address; /* pointer to address */
648 char* base_address; /* original pointer to address */
649 char* endp; /* end of our buffer */
650 int addr_count = 0; /* number of addresses in hostent */
651 int alias_count = 0; /* number of aliases in hostent */
652 struct CacheEntry* cp = 0;
656 assert(request->aq == q);
657 res = adns_check(adns, &q, &answer, NULL);
661 /* adns_check returned an error, bail */
662 Debug((DEBUG_DNS, "Resolver: adns_check result %d", res));
664 (*request->query.callback)(request->query.vptr, 0);
665 rem_request(request);
669 Debug((DEBUG_DNS, "Resolver: adns_check status %d nrrs %d", answer->status, answer->nrrs));
671 /* No error, we have a valid answer structure */
672 if (answer->status != adns_s_ok || !answer->nrrs) {
673 /* Status is not 'ok', or there were no RRs found */
675 (*request->query.callback)(request->query.vptr, 0);
678 hp = &(request->he.h);
679 if (!request->he.buf) {
680 request->he.buf = (char*) MyMalloc(MAXGETHOSTLEN + 1);
681 request->he.buf[MAXGETHOSTLEN] = '\0';
683 * array of alias list pointers starts at beginning of buf
685 hp->h_aliases = (char**) request->he.buf;
686 hp->h_aliases[0] = NULL;
688 * array of address list pointers starts after alias list pointers
689 * the actual addresses follow the the address list pointers
691 hp->h_addr_list = (char**)(request->he.buf + ALIAS_BLEN);
693 * don't copy the host address to the beginning of h_addr_list
695 hp->h_addr_list[0] = NULL;
698 hp->h_addrtype = AF_INET;
700 endp = request->he.buf + MAXGETHOSTLEN;
702 * find the end of the address list
704 addr = hp->h_addr_list;
711 * make address point to first available address slot
713 address = request->he.buf + ADDRS_OFFSET +
714 (sizeof(struct in_addr) * addr_count);
715 base_address = address;
718 * find the end of the alias list
720 alias = hp->h_aliases;
726 * make name point to first available space in request->buf
728 if (alias_count > 0) {
729 name = hp->h_aliases[alias_count - 1];
730 name += (strlen(name) + 1);
733 name = hp->h_name + strlen(hp->h_name) + 1;
735 name = request->he.buf + ADDRS_OFFSET + ADDRS_DLEN;
737 switch (request->type) {
739 for (k = 0; k < answer->nrrs; k++) {
740 if (++addr_count < RES_MAXADDRS) {
741 memcpy(address, &answer->rrs.inaddr[k], sizeof(struct in_addr));
744 address += sizeof(struct in_addr);
746 Debug((DEBUG_DNS, "Resolver: A %s for %s", ircd_ntoa((char*) &answer->rrs.inaddr[k]), answer->owner));
749 strcpy(name, answer->owner);
751 name += strlen(name) + 1;
755 strcpy(name, answer->rrs.str[0]);
757 name += strlen(name) + 1;
759 Debug((DEBUG_DNS, "Resolver: PTR %s for %s", hp->h_name, answer->owner));
768 ircd_strncpy(name, answer->cname, endp - name);
771 name += strlen(name) + 1;
772 Debug((DEBUG_DNS, "Resolver: CNAME %s for %s", answer->cname, answer->owner));
775 if (request->type == adns_r_ptr) {
776 struct DNSReply* reply;
778 Debug((DEBUG_DNS, "relookup %s <-> %s",
779 request->he.h.h_name, ircd_ntoa((char*) &request->addr)));
781 * Lookup the 'authoritive' name that we were given for the
782 * ip#. By using this call rather than regenerating the
783 * type we automatically gain the use of the cache with no
786 reply = gethost_byname(request->he.h.h_name, &request->query);
788 (*request->query.callback)(request->query.vptr, reply);
791 * If name wasn't found, a request has been queued and it will
792 * be the last one queued. This is rather nasty way to keep
793 * a host alias with the query. -avalon
795 MyFree(requestListTail->he.buf);
796 requestListTail->he.buf = request->he.buf;
798 memcpy(&requestListTail->he.h, &request->he.h, sizeof(struct hostent));
802 * got a name and address response, client resolved
803 * XXX - Bug found here by Dianora -
804 * make_cache() occasionally returns a NULL pointer when a
805 * PTR returned a CNAME, cp was not checked before so the
806 * callback was being called with a value of 0x2C != NULL.
808 struct DNSReply* reply = 0;
809 if ((cp = make_cache(request)))
811 (*request->query.callback)(request->query.vptr, reply);
815 rem_request(request);
818 * adns doesn't use MyMalloc, so we don't use MyFree
824 static size_t calc_hostent_buffer_size(const struct hostent* hp)
833 count += (strlen(hp->h_name) + 1);
837 for (p = hp->h_aliases; *p; ++p)
838 count += (strlen(*p) + 1 + sizeof(char*));
840 * space for addresses
842 for (p = hp->h_addr_list; *p; ++p)
843 count += (hp->h_length + sizeof(char*));
845 * space for 2 nulls to terminate h_aliases and h_addr_list
847 count += (2 * sizeof(char*));
853 * dup_hostent - Duplicate a hostent struct, allocate only enough memory for
854 * the data we're putting in it.
856 static void dup_hostent(struct Hostent* new_hp, struct hostent* hp)
863 size_t bytes_needed = 0;
868 /* how much buffer do we need? */
869 bytes_needed += (strlen(hp->h_name) + 1);
873 bytes_needed += (strlen(*pp++) + 1 + sizeof(char*));
876 pp = hp->h_addr_list;
878 bytes_needed += (hp->h_length + sizeof(char*));
881 /* Reserve space for 2 nulls to terminate h_aliases and h_addr_list */
882 bytes_needed += (2 * sizeof(char*));
884 /* Allocate memory */
885 new_hp->buf = (char*) MyMalloc(bytes_needed);
887 new_hp->h.h_addrtype = hp->h_addrtype;
888 new_hp->h.h_length = hp->h_length;
890 /* first write the address list */
891 pp = hp->h_addr_list;
892 ap = new_hp->h.h_addr_list =
893 (char**)(new_hp->buf + ((alias_count + 1) * sizeof(char*)));
894 p = (char*)ap + ((addr_count + 1) * sizeof(char*));
898 memcpy(p, *pp++, hp->h_length);
902 /* next write the name */
903 new_hp->h.h_name = p;
904 strcpy(p, hp->h_name);
905 p += (strlen(p) + 1);
907 /* last write the alias list */
909 ap = new_hp->h.h_aliases = (char**) new_hp->buf;
913 p += (strlen(p) + 1);
919 * update_hostent - Add records to a Hostent struct in place.
921 static void update_hostent(struct Hostent* hp, char** addr, char** alias)
929 size_t bytes_needed = 0;
934 /* how much buffer do we need? */
935 bytes_needed = strlen(hp->h.h_name) + 1;
936 pp = hp->h.h_aliases;
938 bytes_needed += (strlen(*pp++) + 1 + sizeof(char*));
944 bytes_needed += (strlen(*pp++) + 1 + sizeof(char*));
948 pp = hp->h.h_addr_list;
950 bytes_needed += (hp->h.h_length + sizeof(char*));
956 bytes_needed += (hp->h.h_length + sizeof(char*));
960 /* Reserve space for 2 nulls to terminate h_aliases and h_addr_list */
961 bytes_needed += 2 * sizeof(char*);
963 /* Allocate memory */
964 buf = (char*) MyMalloc(bytes_needed);
967 /* first write the address list */
968 pp = hp->h.h_addr_list;
969 ap = hp->h.h_addr_list =
970 (char**)(buf + ((alias_count + 1) * sizeof(char*)));
971 p = (char*)ap + ((addr_count + 1) * sizeof(char*));
973 memcpy(p, *pp++, hp->h.h_length);
979 memcpy(p, *addr++, hp->h.h_length);
986 /* next write the name */
987 strcpy(p, hp->h.h_name);
989 p += (strlen(p) + 1);
991 /* last write the alias list */
992 pp = hp->h.h_aliases;
993 ap = hp->h.h_aliases = (char**) buf;
997 p += (strlen(p) + 1);
1001 strcpy(p, *alias++);
1003 p += (strlen(p) + 1);
1007 /* release the old buffer */
1014 * hash_number - IP address hash function
1016 static int hash_number(const unsigned char* ip)
1018 /* could use loop but slower */
1020 const u_char* p = (const u_char*) ip;
1025 hashv += hashv + *p++;
1026 hashv += hashv + *p++;
1027 hashv += hashv + *p;
1028 hashv %= ARES_CACSIZE;
1033 * hash_name - hostname hash function
1035 static int hash_name(const char* name)
1037 unsigned int hashv = 0;
1038 const u_char* p = (const u_char*) name;
1042 for (; *p && *p != '.'; ++p)
1044 hashv %= ARES_CACSIZE;
1049 * add_to_cache - Add a new cache item to the queue and hash table.
1051 static struct CacheEntry* add_to_cache(struct CacheEntry* ocp)
1057 ocp->list_next = cacheTop;
1060 hashv = hash_name(ocp->he.h.h_name);
1062 ocp->hname_next = hashtable[hashv].name_list;
1063 hashtable[hashv].name_list = ocp;
1065 hashv = hash_number((const unsigned char*) ocp->he.h.h_addr);
1067 ocp->hnum_next = hashtable[hashv].num_list;
1068 hashtable[hashv].num_list = ocp;
1071 * LRU deletion of excessive cache entries.
1073 if (++cachedCount > MAXCACHED) {
1074 struct CacheEntry* cp;
1075 struct CacheEntry* cp_next;
1076 for (cp = ocp->list_next; cp; cp = cp_next) {
1077 cp_next = cp->list_next;
1086 * update_list - does not alter the cache structure passed. It is assumed that
1087 * it already contains the correct expire time, if it is a new entry. Old
1088 * entries have the expirey time updated.
1090 static void update_list(struct ResRequest* request, struct CacheEntry* cachep)
1092 struct CacheEntry* cp = cachep;
1098 char* addrs[RES_MAXADDRS + 1];
1099 char* aliases[RES_MAXALIASES + 1];
1102 * search for the new cache item in the cache list by hostname.
1103 * If found, move the entry to the top of the list and return.
1105 ++cainfo.ca_updates;
1110 * Compare the cache entry against the new record. Add any
1111 * previously missing names for this entry.
1115 for (i = 0, s = request->he.h.h_name; s; s = request->he.h.h_aliases[i++]) {
1116 for (j = 0, t = cp->he.h.h_name; t; t = cp->he.h.h_aliases[j++]) {
1117 if (0 == ircd_strcmp(t, s))
1126 * Do the same again for IP#'s.
1130 for (i = 0; (s = request->he.h.h_addr_list[i]); i++) {
1131 for (j = 0; (t = cp->he.h.h_addr_list[j]); j++) {
1132 if (!memcmp(t, s, sizeof(struct in_addr)))
1140 if (*addrs || *aliases)
1141 update_hostent(&cp->he, addrs, aliases);
1145 * find_cache_name - find name in nameserver cache
1147 static struct CacheEntry* find_cache_name(const char* name)
1149 struct CacheEntry* cp;
1155 hashv = hash_name(name);
1157 cp = hashtable[hashv].name_list;
1159 for (; cp; cp = cp->hname_next) {
1160 for (i = 0, s = cp->he.h.h_name; s; s = cp->he.h.h_aliases[i++]) {
1161 if (0 == ircd_strcmp(s, name)) {
1162 ++cainfo.ca_na_hits;
1168 for (cp = cacheTop; cp; cp = cp->list_next) {
1170 * if no aliases or the hash value matches, we've already
1171 * done this entry and all possiblilities concerning it.
1173 if (!cp->he.h.h_name || hashv == hash_name(cp->he.h.h_name))
1175 for (i = 0, s = cp->he.h.h_aliases[i]; s; s = cp->he.h.h_aliases[++i]) {
1176 if (0 == ircd_strcmp(name, s)) {
1177 ++cainfo.ca_na_hits;
1186 * find_cache_number - find a cache entry by ip# and update its expire time
1188 static struct CacheEntry* find_cache_number(struct ResRequest* request,
1191 struct CacheEntry* cp;
1196 hashv = hash_number((const unsigned char*) addr);
1197 cp = hashtable[hashv].num_list;
1199 for (; cp; cp = cp->hnum_next) {
1200 for (i = 0; cp->he.h.h_addr_list[i]; ++i) {
1201 if (!memcmp(cp->he.h.h_addr_list[i], addr, sizeof(struct in_addr))) {
1202 ++cainfo.ca_nu_hits;
1207 for (cp = cacheTop; cp; cp = cp->list_next) {
1209 * single address entry...would have been done by hashed
1211 * if the first IP# has the same hashnumber as the IP# we
1212 * are looking for, its been done already.
1214 if (!cp->he.h.h_addr_list[1] ||
1215 hashv == hash_number((const unsigned char*) cp->he.h.h_addr_list[0]))
1217 for (i = 1; cp->he.h.h_addr_list[i]; ++i) {
1218 if (!memcmp(cp->he.h.h_addr_list[i], addr, sizeof(struct in_addr))) {
1219 ++cainfo.ca_nu_hits;
1227 static struct CacheEntry* make_cache(struct ResRequest* request)
1229 struct CacheEntry* cp;
1232 assert(0 != request);
1234 hp = &request->he.h;
1236 * shouldn't happen but it just might...
1238 assert(0 != hp->h_name);
1239 /* assert(0 != hp->h_addr_list[0]); */
1240 if (!hp->h_name || !hp->h_addr_list[0])
1243 * Make cache entry. First check to see if the cache already exists
1244 * and if so, return a pointer to it.
1246 for (i = 0; hp->h_addr_list[i]; ++i) {
1247 if ((cp = find_cache_number(request, hp->h_addr_list[i]))) {
1248 update_list(request, cp);
1253 * a matching entry wasnt found in the cache so go and make one up.
1255 cp = (struct CacheEntry*) MyMalloc(sizeof(struct CacheEntry));
1258 memset(cp, 0, sizeof(struct CacheEntry));
1259 dup_hostent(&cp->he, hp);
1260 cp->reply.hp = &cp->he.h;
1262 * hmmm... we could time out the cache after 10 minutes regardless
1263 * would that be reasonable since we don't save the reply?
1266 if (request->ttl < AR_TTL) {
1267 ++reinfo.re_shortttl;
1271 cp->ttl = request->ttl;
1275 cp->expireat = CurrentTime + cp->ttl;
1276 return add_to_cache(cp);
1280 * rem_cache - delete a cache entry from the cache structures
1281 * and lists and return all memory used for the cache back to the memory pool.
1283 static void rem_cache(struct CacheEntry* ocp)
1285 struct CacheEntry** cp;
1291 if (0 < ocp->reply.ref_count) {
1292 if (ocp->expireat < CurrentTime) {
1293 ocp->expireat = CurrentTime + AR_TTL;
1294 Debug((DEBUG_DNS, "Resolver: referenced cache entry not removed for: %s",
1300 * remove cache entry from linked list
1302 for (cp = &cacheTop; *cp; cp = &((*cp)->list_next)) {
1304 *cp = ocp->list_next;
1310 * remove cache entry from hashed name list
1312 assert(0 != hp->h_name);
1313 hashv = hash_name(hp->h_name);
1315 for (cp = &hashtable[hashv].name_list; *cp; cp = &((*cp)->hname_next)) {
1317 *cp = ocp->hname_next;
1322 * remove cache entry from hashed number list
1324 hashv = hash_number((const unsigned char*) hp->h_addr);
1327 for (cp = &hashtable[hashv].num_list; *cp; cp = &((*cp)->hnum_next)) {
1329 *cp = ocp->hnum_next;
1334 * free memory used to hold the various host names and the array
1335 * of alias pointers.
1337 MyFree(ocp->he.buf);
1343 void flush_resolver_cache(void)
1346 * stubbed - iterate cache and remove everything that isn't referenced
1351 * m_dns - dns status query
1353 int m_dns(struct Client *cptr, struct Client *sptr, int parc, char *parv[])
1355 #if !defined(NDEBUG)
1356 struct CacheEntry* cp;
1360 if (parv[1] && *parv[1] == 'l') {
1361 for(cp = cacheTop; cp; cp = cp->list_next) {
1363 sendcmdto_one(&me, CMD_NOTICE, sptr, "%C :Ex %d ttl %d host %s(%s)",
1364 sptr, cp->expireat - CurrentTime, cp->ttl,
1365 hp->h_name, ircd_ntoa(hp->h_addr));
1366 for (i = 0; hp->h_aliases[i]; i++)
1367 sendcmdto_one(&me, CMD_NOTICE, sptr, "%C : %s = %s (CN)", sptr,
1368 hp->h_name, hp->h_aliases[i]);
1369 for (i = 1; hp->h_addr_list[i]; i++)
1370 sendcmdto_one(&me, CMD_NOTICE, sptr, "%C : %s = %s (IP)", sptr,
1371 hp->h_name, ircd_ntoa(hp->h_addr_list[i]));
1375 if (parv[1] && *parv[1] == 'd') {
1376 sendcmdto_one(&me, CMD_NOTICE, sptr, "%C :ResolverFileDescriptor = %d",
1377 sptr, ResolverFileDescriptor);
1380 sendcmdto_one(&me, CMD_NOTICE, sptr,"%C :Ca %d Cd %d Ce %d Cl %d Ch %d:%d "
1382 cainfo.ca_adds, cainfo.ca_dels, cainfo.ca_expires,
1383 cainfo.ca_lookups, cainfo.ca_na_hits, cainfo.ca_nu_hits,
1386 sendcmdto_one(&me, CMD_NOTICE, sptr,"%C :Re %d Rl %d/%d Rp %d Rq %d",
1387 sptr, reinfo.re_errors, reinfo.re_nu_look,
1388 reinfo.re_na_look, reinfo.re_replies, reinfo.re_requests);
1389 sendcmdto_one(&me, CMD_NOTICE, sptr,"%C :Ru %d Rsh %d Rs %d(%d) Rt %d", sptr,
1390 reinfo.re_unkrep, reinfo.re_shortttl, reinfo.re_sent,
1391 reinfo.re_resends, reinfo.re_timeouts);
1396 size_t cres_mem(struct Client* sptr)
1398 struct CacheEntry* entry;
1399 struct ResRequest* request;
1400 size_t cache_mem = 0;
1401 size_t request_mem = 0;
1402 int cache_count = 0;
1403 int request_count = 0;
1405 for (entry = cacheTop; entry; entry = entry->list_next) {
1406 cache_mem += sizeof(struct CacheEntry);
1407 cache_mem += calc_hostent_buffer_size(&entry->he.h);
1410 for (request = requestListHead; request; request = request->next) {
1411 request_mem += sizeof(struct ResRequest);
1413 request_mem += strlen(request->name) + 1;
1414 if (request->he.buf)
1415 request_mem += MAXGETHOSTLEN + 1;
1419 if (cachedCount != cache_count) {
1420 send_reply(sptr, SND_EXPLICIT | RPL_STATSDEBUG,
1421 ":Resolver: cache count mismatch: %d != %d", cachedCount,
1423 assert(cachedCount == cache_count);
1425 send_reply(sptr, SND_EXPLICIT | RPL_STATSDEBUG,
1426 ":Resolver: cache %d(%d) requests %d(%d)", cache_count,
1427 cache_mem, request_count, request_mem);
1428 return cache_mem + request_mem;