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>)
16 #include "ircd_alloc.h"
18 #include "ircd_osdep.h"
19 #include "ircd_string.h"
25 #include "sprintf_irc.h"
35 #include <sys/socket.h>
39 #include <arpa/nameser.h>
42 #include <arpa/inet.h>
46 #error this code needs to be able to address individual octets
50 * Some systems do not define INADDR_NONE (255.255.255.255)
51 * INADDR_NONE is actually a valid address, but it should never
52 * be returned from any nameserver.
53 * NOTE: The bit pattern for INADDR_NONE and INADDR_ANY (0.0.0.0) should be
54 * the same on all hosts so we shouldn't need to use htonl or ntohl to
55 * compare or set the values.
58 #define INADDR_NONE ((unsigned int) 0xffffffff)
61 #define MAXPACKET 1024 /* rfc sez 512 but we expand names so ... */
62 #define RES_MAXALIASES 35 /* maximum aliases allowed */
63 #define RES_MAXADDRS 35 /* maximum addresses allowed */
66 * macros used to calulate offsets into fixed query buffer
68 #define ALIAS_BLEN (size_t) ((RES_MAXALIASES + 1) * sizeof(char*))
69 #define ADDRS_BLEN (size_t) ((RES_MAXADDRS + 1) * sizeof(struct in_addr*))
71 #define ADDRS_OFFSET (size_t) (ALIAS_BLEN + ADDRS_BLEN)
72 #define ADDRS_DLEN (size_t) (RES_MAXADDRS * sizeof(struct in_addr))
73 #define NAMES_OFFSET (size_t) (ADDRS_OFFSET + ADDRS_DLEN)
74 #define MAXGETHOSTLEN (size_t) (NAMES_OFFSET + MAXPACKET)
76 #define AR_TTL 600 /* TTL in seconds for dns cache entries */
79 * the following values should be prime
81 #define ARES_CACSIZE 307
85 * RFC 1104/1105 wasn't very helpful about what these fields
86 * should be named, so for now, we'll just name them this way.
87 * we probably should look at what named calls them or something.
89 #define TYPE_SIZE (size_t) 2
90 #define CLASS_SIZE (size_t) 2
91 #define TTL_SIZE (size_t) 4
92 #define RDLENGTH_SIZE (size_t) 2
93 #define ANSWER_FIXED_SIZE (TYPE_SIZE + CLASS_SIZE + TTL_SIZE + RDLENGTH_SIZE)
96 * Building the Hostent
97 * The Hostent struct is arranged like this:
98 * +-------------------------------+
99 * Hostent: | struct hostent h |
100 * |-------------------------------|
102 * +-------------------------------+
106 * +-------------------------------+
107 * buf: | h_aliases pointer array | Max size: ALIAS_BLEN;
108 * | NULL | contains `char *'s
109 * |-------------------------------|
110 * | h_addr_list pointer array | Max size: ADDRS_BLEN;
111 * | NULL | contains `struct in_addr *'s
112 * |-------------------------------|
113 * | h_addr_list addresses | Max size: ADDRS_DLEN;
114 * | | contains `struct in_addr's
115 * |-------------------------------|
116 * | storage for hostname strings | Max size: ALIAS_DLEN;
117 * +-------------------------------+ contains `char's
119 * For requests the size of the h_aliases, and h_addr_list pointer
120 * array sizes are set to MAXALISES and MAXADDRS respectively, and
121 * buf is a fixed size with enough space to hold the largest expected
122 * reply from a nameserver, see RFC 1034 and RFC 1035.
123 * For cached entries the sizes are dependent on the actual number
124 * of aliases and addresses. If new aliases and addresses are found
125 * for cached entries, the buffer is grown and the new entries are added.
126 * The hostent struct is filled in with the addresses of the entries in
127 * the Hostent buf as follows:
128 * h_name - contains a pointer to the start of the hostname string area,
129 * or NULL if none is set. The h_name is followed by the
130 * aliases, in the storage for hostname strings area.
131 * h_aliases - contains a pointer to the start of h_aliases pointer array.
132 * This array contains pointers to the storage for hostname
133 * strings area and is terminated with a NULL. The first alias
134 * is stored directly after the h_name.
135 * h_addr_list - contains a pointer to the start of h_addr_list pointer array.
136 * This array contains pointers to in_addr structures in the
137 * h_addr_list addresses area and is terminated with a NULL.
139 * Filling the buffer this way allows for proper alignment of the h_addr_list
142 * This arrangement allows us to alias a Hostent struct pointer as a
143 * real struct hostent* without lying. It also allows us to change the
144 * values contained in the cached entries and requests without changing
145 * the actual hostent pointer, which is saved in a client struct and can't
146 * be changed without blowing things up or a lot more fiddling around.
147 * It also allows for defered allocation of the fixed size buffers until
148 * they are really needed.
149 * Nov. 17, 1997 --Bleep
152 typedef struct Hostent {
153 struct hostent h; /* the hostent struct we are passing around */
154 char* buf; /* buffer for data pointed to from hostent */
158 struct ResRequest* next;
160 int sent; /* number of requests sent */
163 char retries; /* retry counter */
164 char sends; /* number of sends (>1 means resent) */
165 char resend; /* send flag. 0 == dont resend */
170 struct DNSQuery query; /* query callback for this request */
175 struct CacheEntry* hname_next;
176 struct CacheEntry* hnum_next;
177 struct CacheEntry* list_next;
181 struct DNSReply reply;
185 struct CacheEntry* num_list;
186 struct CacheEntry* name_list;
190 int ResolverFileDescriptor = -1; /* GLOBAL - used in s_bsd.c */
192 static time_t nextDNSCheck = 0;
193 static time_t nextCacheExpire = 1;
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 void rem_cache(struct CacheEntry*);
215 static void do_query_name(const struct DNSQuery* query,
217 struct ResRequest* request);
218 static void do_query_number(const struct DNSQuery* query,
219 const struct in_addr*,
220 struct ResRequest* request);
221 static void query_name(const char* name,
224 struct ResRequest* request);
225 static void resend_query(struct ResRequest* request);
226 static struct CacheEntry* make_cache(struct ResRequest* request);
227 static struct CacheEntry* find_cache_name(const char* name);
228 static struct CacheEntry* find_cache_number(struct ResRequest* request,
230 static struct ResRequest* find_id(int);
232 static struct cacheinfo {
242 static struct resinfo {
257 * From bind 8.3, these aren't declared in earlier versions of bind
259 extern u_short _getshort(const u_char *);
260 extern u_int _getlong(const u_char *);
263 * res_isourserver(ina)
264 * looks up "ina" in _res.ns_addr_list[]
269 * paul vixie, 29may94
272 res_ourserver(const struct __res_state* statp, const struct sockaddr_in *inp)
274 struct sockaddr_in ina;
278 for (ns = 0; ns < statp->nscount; ns++) {
279 const struct sockaddr_in *srv = &statp->nsaddr_list[ns];
281 if (srv->sin_family == ina.sin_family &&
282 srv->sin_port == ina.sin_port &&
283 (srv->sin_addr.s_addr == INADDR_ANY ||
284 srv->sin_addr.s_addr == ina.sin_addr.s_addr))
291 * start_resolver - do everything we need to read the resolv.conf file
292 * and initialize the resolver file descriptor if needed
294 static void start_resolver(void)
296 Debug((DEBUG_DNS, "Resolver: start_resolver"));
298 * close the spare file descriptor so res_init can read resolv.conf
299 * successfully. Needed on Solaris
304 res_init(); /* res_init always returns 0 */
306 * make sure we have a valid file descriptor below 256 so we can
307 * do this again. Needed on Solaris
309 spare_fd = open("/dev/null",O_RDONLY,0);
310 if ((spare_fd < 0) || (spare_fd > 255)) {
312 sprintf_irc(sparemsg, "invalid spare_fd %d", spare_fd);
313 server_restart(sparemsg);
318 _res.nsaddr_list[0].sin_addr.s_addr = inet_addr("127.0.0.1");
320 _res.options |= RES_NOALIASES;
322 if (ResolverFileDescriptor < 0) {
323 ResolverFileDescriptor = socket(AF_INET, SOCK_DGRAM, 0);
324 if (-1 == ResolverFileDescriptor) {
325 report_error("Resolver: error creating socket for %s: %s",
329 if (!os_set_nonblocking(ResolverFileDescriptor))
330 report_error("Resolver: error setting non-blocking for %s: %s",
336 * init_resolver - initialize resolver and resolver library
338 int init_resolver(void)
340 Debug((DEBUG_DNS, "Resolver: init_resolver"));
342 srand48(CurrentTime);
344 memset(&cainfo, 0, sizeof(cainfo));
345 memset(hashtable, 0, sizeof(hashtable));
346 memset(&reinfo, 0, sizeof(reinfo));
348 requestListHead = requestListTail = NULL;
352 Debug((DEBUG_DNS, "Resolver: fd %d errno: %d h_errno: %d: %s",
353 ResolverFileDescriptor, errno, h_errno, strerror(errno)));
354 return ResolverFileDescriptor;
358 * restart_resolver - flush the cache, reread resolv.conf, reopen socket
360 void restart_resolver(void)
362 /* flush_cache(); flush the dns cache */
367 * add_local_domain - Add the domain to hostname, if it is missing
368 * (as suggested by eps@TOASTER.SFSU.EDU)
370 void add_local_domain(char* hname, size_t size)
374 * try to fix up unqualified names
376 if ((_res.options & RES_DEFNAMES) && !strchr(hname, '.')) {
377 if (_res.defdname[0]) {
378 size_t len = strlen(hname);
379 if ((strlen(_res.defdname) + len + 2) < size) {
381 strcpy(hname + len, _res.defdname);
388 * add_request - place a new request in the request list
390 static void add_request(struct ResRequest* request)
392 assert(0 != request);
393 if (!requestListHead)
394 requestListHead = requestListTail = request;
396 requestListTail->next = request;
397 requestListTail = request;
399 request->next = NULL;
400 ++reinfo.re_requests;
404 * rem_request - remove a request from the list.
405 * This must also free any memory that has been allocated for
406 * temporary storage of DNS results.
408 static void rem_request(struct ResRequest* request)
410 struct ResRequest** current;
411 struct ResRequest* prev = NULL;
413 assert(0 != request);
414 for (current = &requestListHead; *current; ) {
415 if (*current == request) {
416 *current = request->next;
417 if (requestListTail == request)
418 requestListTail = prev;
422 current = &(*current)->next;
424 MyFree(request->he.buf);
425 MyFree(request->name);
430 * make_request - Create a DNS request record for the server.
432 static struct ResRequest* make_request(const struct DNSQuery* query)
434 struct ResRequest* request;
436 request = (struct ResRequest*) MyMalloc(sizeof(struct ResRequest));
437 memset(request, 0, sizeof(struct ResRequest));
439 request->sentat = CurrentTime;
440 request->retries = 3;
442 request->timeout = 5; /* start at 5 per RFC1123 */
443 request->addr.s_addr = INADDR_NONE;
444 request->he.h.h_addrtype = AF_INET;
445 request->he.h.h_length = sizeof(struct in_addr);
446 request->query.vptr = query->vptr;
447 request->query.callback = query->callback;
449 #if defined(NULL_POINTER_NOT_ZERO)
450 request->next = NULL;
451 request->he.buf = NULL;
452 request->he.h.h_name = NULL;
453 request->he.h.h_aliases = NULL;
454 request->he.h.h_addr_list = NULL;
456 add_request(request);
461 * timeout_query_list - Remove queries from the list which have been
462 * there too long without being resolved.
464 static time_t timeout_query_list(time_t now)
466 struct ResRequest* request;
467 struct ResRequest* next_request = 0;
468 time_t next_time = 0;
471 Debug((DEBUG_DNS, "Resolver: timeout_query_list at %s", myctime(now)));
472 for (request = requestListHead; request; request = next_request) {
473 next_request = request->next;
474 timeout = request->sentat + request->timeout;
476 if (--request->retries <= 0) {
477 ++reinfo.re_timeouts;
478 (*request->query.callback)(request->query.vptr, 0);
479 rem_request(request);
483 request->sentat = now;
484 request->timeout += request->timeout;
485 resend_query(request);
488 if (!next_time || timeout < next_time) {
492 return (next_time > now) ? next_time : (now + AR_TTL);
496 * expire_cache - removes entries from the cache which are older
497 * than their expiry times. returns the time at which the server
498 * should next poll the cache.
500 static time_t expire_cache(time_t now)
502 struct CacheEntry* cp;
503 struct CacheEntry* cp_next;
506 Debug((DEBUG_DNS, "Resolver: expire_cache at %s", myctime(now)));
507 for (cp = cacheTop; cp; cp = cp_next) {
508 cp_next = cp->list_next;
509 if (cp->expireat < now) {
513 else if (!expire || expire > cp->expireat)
514 expire = cp->expireat;
516 return (expire > now) ? expire : (now + AR_TTL);
520 * timeout_resolver - check request list and cache for expired entries
522 time_t timeout_resolver(time_t now)
524 if (nextDNSCheck < now)
525 nextDNSCheck = timeout_query_list(now);
526 if (nextCacheExpire < now)
527 nextCacheExpire = expire_cache(now);
528 return IRCD_MIN(nextDNSCheck, nextCacheExpire);
533 * delete_resolver_queries - cleanup outstanding queries
534 * for which there no longer exist clients or conf lines.
536 void delete_resolver_queries(const void* vptr)
538 struct ResRequest* request;
539 struct ResRequest* next_request;
541 for (request = requestListHead; request; request = next_request) {
542 next_request = request->next;
543 if (vptr == request->query.vptr)
544 rem_request(request);
549 * send_res_msg - sends msg to all nameservers found in the "_res" structure.
550 * This should reflect /etc/resolv.conf. We will get responses
551 * which arent needed but is easier than checking to see if nameserver
552 * isnt present. Returns number of messages successfully sent to
553 * nameservers or -1 if no successful sends.
555 static int send_res_msg(const u_char* msg, int len, int rcount)
559 int max_queries = IRCD_MIN(_res.nscount, rcount);
563 * RES_PRIMARY option is not implemented
564 * if (_res.options & RES_PRIMARY || 0 == max_queries)
566 if (0 == max_queries)
569 Debug((DEBUG_DNS, "Resolver: sendto %d", max_queries));
571 for (i = 0; i < max_queries; i++) {
572 if (sendto(ResolverFileDescriptor, msg, len, 0,
573 (struct sockaddr*) &(_res.nsaddr_list[i]),
574 sizeof(struct sockaddr_in)) == len) {
579 ircd_log(L_ERROR, "Resolver: send failed %s", strerror(errno));
585 * find_id - find a dns request id (id is determined by dn_mkquery)
587 static struct ResRequest* find_id(int id)
589 struct ResRequest* request;
591 for (request = requestListHead; request; request = request->next) {
592 if (request->id == id)
599 * gethost_byname - get host address from name
601 struct DNSReply* gethost_byname(const char* name,
602 const struct DNSQuery* query)
604 struct CacheEntry* cp;
607 Debug((DEBUG_DNS, "Resolver: gethost_byname %s", name));
609 if ((cp = find_cache_name(name)))
612 do_query_name(query, name, NULL);
618 * gethost_byaddr - get host name from address
620 struct DNSReply* gethost_byaddr(const char* addr,
621 const struct DNSQuery* query)
623 struct CacheEntry *cp;
627 Debug((DEBUG_DNS, "Resolver: gethost_byaddr %s", ircd_ntoa(addr)));
630 if ((cp = find_cache_number(NULL, addr)))
633 do_query_number(query, (const struct in_addr*) addr, NULL);
639 * do_query_name - nameserver lookup name
641 static void do_query_name(const struct DNSQuery* query,
642 const char* name, struct ResRequest* request)
644 char hname[HOSTLEN + 1];
647 ircd_strncpy(hname, name, HOSTLEN);
648 hname[HOSTLEN] = '\0';
651 * removed, this is incorrect for anything it's used for
653 add_local_domain(hname, HOSTLEN);
657 request = make_request(query);
659 request->name = (char*) MyMalloc(strlen(hname) + 1);
660 strcpy(request->name, hname);
662 query_name(hname, C_IN, T_A, request);
666 * do_query_number - Use this to do reverse IP# lookups.
668 static void do_query_number(const struct DNSQuery* query,
669 const struct in_addr* addr,
670 struct ResRequest* request)
673 const unsigned char* cp;
676 cp = (const unsigned char*) &addr->s_addr;
677 sprintf_irc(ipbuf, "%u.%u.%u.%u.in-addr.arpa.",
678 (unsigned int)(cp[3]), (unsigned int)(cp[2]),
679 (unsigned int)(cp[1]), (unsigned int)(cp[0]));
682 request = make_request(query);
683 request->type = T_PTR;
684 request->addr.s_addr = addr->s_addr;
686 query_name(ipbuf, C_IN, T_PTR, request);
690 * query_name - generate a query based on class, type and name.
692 static void query_name(const char* name, int query_class,
693 int type, struct ResRequest* request)
699 assert(0 != request);
701 Debug((DEBUG_DNS, "Resolver: query_name: %s %d %d", name, query_class, type));
702 memset(buf, 0, sizeof(buf));
703 if ((request_len = res_mkquery(QUERY, name, query_class, type,
704 NULL, 0, NULL, buf, sizeof(buf))) > 0) {
705 HEADER* header = (HEADER*) buf;
711 * generate a unique id
712 * NOTE: we don't have to worry about converting this to and from
713 * network byte order, the nameserver does not interpret this value
714 * and returns it unchanged
718 header->id = (header->id + lrand48()) & 0xffff;
719 } while (find_id(header->id));
721 gettimeofday(&tv, NULL);
723 header->id = (header->id + k + tv.tv_usec) & 0xffff;
725 } while (find_id(header->id));
727 request->id = header->id;
729 Debug((DEBUG_DNS, "Resolver: query_name %d: %s %d %d", request->id,
730 name, query_class, type));
731 request->sent += send_res_msg(buf, request_len, request->sends);
735 static void resend_query(struct ResRequest* request)
737 assert(0 != request);
739 if (request->resend == 0)
742 switch(request->type) {
744 do_query_number(NULL, &request->addr, request);
747 do_query_name(NULL, request->name, request);
755 * proc_answer - process name server reply
756 * build a hostent struct in the passed request
758 static int proc_answer(struct ResRequest* request, HEADER* header,
759 u_char* buf, u_char* eob)
761 char hostbuf[HOSTLEN + 1]; /* working buffer */
762 u_char* current; /* current position in buf */
763 char** alias; /* alias list */
764 char** addr; /* address list */
765 char* name; /* pointer to name string */
766 char* address; /* pointer to address */
767 char* endp; /* end of our buffer */
768 int query_class; /* answer class */
769 int type; /* answer type */
770 int rd_length; /* record data length */
771 int answer_count = 0; /* answer counter */
772 int n; /* temp count */
773 int addr_count = 0; /* number of addresses in hostent */
774 int alias_count = 0; /* number of aliases in hostent */
775 struct hostent* hp; /* hostent getting filled */
777 assert(0 != request);
782 current = buf + sizeof(HEADER);
783 hp = &(request->he.h);
785 * lazy allocation of request->he.buf, we don't allocate a buffer
786 * unless there is something to put in it.
788 if (!request->he.buf) {
789 request->he.buf = (char*) MyMalloc(MAXGETHOSTLEN + 1);
790 request->he.buf[MAXGETHOSTLEN] = '\0';
792 * array of alias list pointers starts at beginning of buf
794 hp->h_aliases = (char**) request->he.buf;
795 hp->h_aliases[0] = NULL;
797 * array of address list pointers starts after alias list pointers
798 * the actual addresses follow the the address list pointers
800 hp->h_addr_list = (char**)(request->he.buf + ALIAS_BLEN);
802 * don't copy the host address to the beginning of h_addr_list
804 hp->h_addr_list[0] = NULL;
806 endp = request->he.buf + MAXGETHOSTLEN;
808 * find the end of the address list
810 addr = hp->h_addr_list;
816 * make address point to first available address slot
818 address = request->he.buf + ADDRS_OFFSET +
819 (sizeof(struct in_addr) * addr_count);
821 * find the end of the alias list
823 alias = hp->h_aliases;
829 * make name point to first available space in request->buf
831 if (alias_count > 0) {
832 name = hp->h_aliases[alias_count - 1];
833 name += (strlen(name) + 1);
836 name = hp->h_name + strlen(hp->h_name) + 1;
838 name = request->he.buf + ADDRS_OFFSET + ADDRS_DLEN;
843 while (header->qdcount-- > 0) {
844 if ((n = dn_skipname(current, eob)) < 0)
846 current += (n + QFIXEDSZ);
849 * process each answer sent to us blech.
851 while (header->ancount-- > 0 && current < eob && name < endp) {
852 n = dn_expand(buf, eob, current, hostbuf, sizeof(hostbuf));
855 * no more answers left
859 hostbuf[HOSTLEN] = '\0';
861 * With Address arithmetic you have to be very anal
862 * this code was not working on alpha due to that
863 * (spotted by rodder/jailbird/dianora)
865 current += (size_t) n;
867 if (!((current + ANSWER_FIXED_SIZE) < eob))
870 type = _getshort(current);
871 current += TYPE_SIZE;
873 query_class = _getshort(current);
874 current += CLASS_SIZE;
876 request->ttl = _getlong(current);
879 rd_length = _getshort(current);
880 current += RDLENGTH_SIZE;
883 * Wait to set request->type until we verify this structure
886 add_local_domain(hostbuf, HOSTLEN);
892 * check for invalid rd_length or too many addresses
894 if (rd_length != sizeof(struct in_addr))
896 if (++addr_count < RES_MAXADDRS) {
897 if (answer_count == 1)
898 hp->h_addrtype = (query_class == C_IN) ? AF_INET : AF_UNSPEC;
900 memcpy(address, current, sizeof(struct in_addr));
903 address += sizeof(struct in_addr);
906 strcpy(name, hostbuf);
908 name += strlen(name) + 1;
910 Debug((DEBUG_DNS, "Resolver: A %s for %s",
911 ircd_ntoa((char*) hp->h_addr_list[addr_count - 1]), hostbuf));
913 current += rd_length;
917 n = dn_expand(buf, eob, current, hostbuf, sizeof(hostbuf));
926 * no more answers left
931 * This comment is based on analysis by Shadowfax, Wohali and johan,
932 * not me. (Dianora) I am only commenting it.
934 * dn_expand is guaranteed to not return more than sizeof(hostbuf)
935 * but do all implementations of dn_expand also guarantee
936 * buffer is terminated with null byte? Lets not take chances.
939 hostbuf[HOSTLEN] = '\0';
940 current += (size_t) n;
942 Debug((DEBUG_DNS, "Resolver: PTR %s", hostbuf));
944 * copy the returned hostname into the host name
945 * ignore duplicate ptr records
948 strcpy(name, hostbuf);
950 name += strlen(name) + 1;
955 Debug((DEBUG_DNS, "Resolver: CNAME %s", hostbuf));
956 if (++alias_count < RES_MAXALIASES) {
957 ircd_strncpy(name, hostbuf, endp - name);
960 name += strlen(name) + 1;
962 current += rd_length;
966 Debug((DEBUG_DNS,"Resolver: proc_answer type: %d for: %s", type, hostbuf));
974 * resolver_read - read a dns reply from the nameserver and process it.
975 * return 0 if nothing was read from the socket, otherwise return 1
977 int resolver_read(void)
979 u_char buf[sizeof(HEADER) + MAXPACKET];
981 struct ResRequest* request = 0;
982 struct CacheEntry* cp = 0;
984 int answer_count = 0;
985 struct sockaddr_in sin;
987 Debug((DEBUG_DNS, "Resolver: read"));
988 if (IO_SUCCESS != os_recvfrom_nonb(ResolverFileDescriptor,
989 buf, sizeof(buf), &rc, &sin)) {
992 if (rc < sizeof(HEADER)) {
993 Debug((DEBUG_DNS, "Resolver: short reply %d: %s", rc, strerror(errno)));
997 * convert DNS reply reader from Network byte order to CPU byte order.
999 header = (HEADER*) buf;
1000 /* header->id = ntohs(header->id); */
1001 header->ancount = ntohs(header->ancount);
1002 header->qdcount = ntohs(header->qdcount);
1003 header->nscount = ntohs(header->nscount);
1004 header->arcount = ntohs(header->arcount);
1005 ++reinfo.re_replies;
1007 * response for an id which we have already received an answer for
1008 * just ignore this response.
1010 if (0 == (request = find_id(header->id))) {
1011 Debug((DEBUG_DNS, "Resolver: can't find request id: %d", header->id));
1015 * check against possibly fake replies
1017 if (!res_ourserver(&_res, &sin)) {
1018 Debug((DEBUG_DNS, "Resolver: fake reply from: %s",
1019 (const char*) &sin.sin_addr));
1024 if ((header->rcode != NOERROR) || (header->ancount == 0)) {
1026 if (SERVFAIL == header->rcode)
1027 resend_query(request);
1030 * If a bad error was returned, we stop here and dont send
1031 * send any more (no retries granted).
1032 * Isomer: Perhaps we should return these error messages back to
1036 switch (header->rcode) {
1038 Debug((DEBUG_DNS, "Fatal DNS error: No Error"));
1041 Debug((DEBUG_DNS, "Fatal DNS error: Format Error"));
1044 Debug((DEBUG_DNS, "Fatal DNS error: Server Failure"));
1047 Debug((DEBUG_DNS, "DNS error: Non Existant Domain"));
1050 Debug((DEBUG_DNS, "Fatal DNS error: Not Implemented"));
1053 Debug((DEBUG_DNS, "Fatal DNS error: Query Refused"));
1056 Debug((DEBUG_DNS, "Unassigned fatal DNS error: %i", header->rcode));
1059 #endif /* DEBUGMODE */
1060 (*request->query.callback)(request->query.vptr, 0);
1061 rem_request(request);
1066 * If this fails there was an error decoding the received packet,
1067 * try it again and hope it works the next time.
1069 answer_count = proc_answer(request, header, buf, buf + rc);
1071 if (T_PTR == request->type) {
1072 struct DNSReply* reply = NULL;
1073 if (0 == request->he.h.h_name) {
1075 * got a PTR response with no name, something bogus is happening
1076 * don't bother trying again, the client address doesn't resolve
1078 (*request->query.callback)(request->query.vptr, reply);
1079 rem_request(request);
1082 Debug((DEBUG_DNS, "relookup %s <-> %s",
1083 request->he.h.h_name, ircd_ntoa((char*) &request->addr)));
1085 * Lookup the 'authoritive' name that we were given for the
1086 * ip#. By using this call rather than regenerating the
1087 * type we automatically gain the use of the cache with no
1090 reply = gethost_byname(request->he.h.h_name, &request->query);
1093 * If name wasn't found, a request has been queued and it will
1094 * be the last one queued. This is rather nasty way to keep
1095 * a host alias with the query. -avalon
1097 MyFree(requestListTail->he.buf);
1098 requestListTail->he.buf = request->he.buf;
1099 request->he.buf = 0;
1100 memcpy(&requestListTail->he.h, &request->he.h, sizeof(struct hostent));
1103 (*request->query.callback)(request->query.vptr, reply);
1104 rem_request(request);
1108 * got a name and address response, client resolved
1109 * XXX - Bug found here by Dianora -
1110 * make_cache() occasionally returns a NULL pointer when a
1111 * PTR returned a CNAME, cp was not checked before so the
1112 * callback was being called with a value of 0x2C != NULL.
1114 cp = make_cache(request);
1115 (*request->query.callback)(request->query.vptr,
1116 (cp) ? &cp->reply : 0);
1117 rem_request(request);
1120 else if (!request->sent) {
1122 * XXX - we got a response for a query we didn't send with a valid id?
1123 * this should never happen, bail here and leave the client unresolved
1125 (*request->query.callback)(request->query.vptr, 0);
1126 rem_request(request);
1132 * resolver_read_multiple - process up to count reads
1134 void resolver_read_multiple(int count)
1137 for ( ; i < count; ++i) {
1138 if (0 == resolver_read())
1143 static size_t calc_hostent_buffer_size(const struct hostent* hp)
1152 count += (strlen(hp->h_name) + 1);
1156 for (p = hp->h_aliases; *p; ++p)
1157 count += (strlen(*p) + 1 + sizeof(char*));
1159 * space for addresses
1161 for (p = hp->h_addr_list; *p; ++p)
1162 count += (hp->h_length + sizeof(char*));
1164 * space for 2 nulls to terminate h_aliases and h_addr_list
1166 count += (2 * sizeof(char*));
1172 * dup_hostent - Duplicate a hostent struct, allocate only enough memory for
1173 * the data we're putting in it.
1175 static void dup_hostent(aHostent* new_hp, struct hostent* hp)
1180 int alias_count = 0;
1182 size_t bytes_needed = 0;
1184 assert(0 != new_hp);
1187 /* how much buffer do we need? */
1188 bytes_needed += (strlen(hp->h_name) + 1);
1192 bytes_needed += (strlen(*pp++) + 1 + sizeof(char*));
1195 pp = hp->h_addr_list;
1197 bytes_needed += (hp->h_length + sizeof(char*));
1200 /* Reserve space for 2 nulls to terminate h_aliases and h_addr_list */
1201 bytes_needed += (2 * sizeof(char*));
1203 /* Allocate memory */
1204 new_hp->buf = (char*) MyMalloc(bytes_needed);
1206 new_hp->h.h_addrtype = hp->h_addrtype;
1207 new_hp->h.h_length = hp->h_length;
1209 /* first write the address list */
1210 pp = hp->h_addr_list;
1211 ap = new_hp->h.h_addr_list =
1212 (char**)(new_hp->buf + ((alias_count + 1) * sizeof(char*)));
1213 p = (char*)ap + ((addr_count + 1) * sizeof(char*));
1217 memcpy(p, *pp++, hp->h_length);
1221 /* next write the name */
1222 new_hp->h.h_name = p;
1223 strcpy(p, hp->h_name);
1224 p += (strlen(p) + 1);
1226 /* last write the alias list */
1228 ap = new_hp->h.h_aliases = (char**) new_hp->buf;
1232 p += (strlen(p) + 1);
1238 * update_hostent - Add records to a Hostent struct in place.
1240 static void update_hostent(aHostent* hp, char** addr, char** alias)
1245 int alias_count = 0;
1248 size_t bytes_needed = 0;
1250 if (!hp || !hp->buf)
1253 /* how much buffer do we need? */
1254 bytes_needed = strlen(hp->h.h_name) + 1;
1255 pp = hp->h.h_aliases;
1257 bytes_needed += (strlen(*pp++) + 1 + sizeof(char*));
1263 bytes_needed += (strlen(*pp++) + 1 + sizeof(char*));
1267 pp = hp->h.h_addr_list;
1269 bytes_needed += (hp->h.h_length + sizeof(char*));
1275 bytes_needed += (hp->h.h_length + sizeof(char*));
1279 /* Reserve space for 2 nulls to terminate h_aliases and h_addr_list */
1280 bytes_needed += 2 * sizeof(char*);
1282 /* Allocate memory */
1283 buf = (char*) MyMalloc(bytes_needed);
1286 /* first write the address list */
1287 pp = hp->h.h_addr_list;
1288 ap = hp->h.h_addr_list =
1289 (char**)(buf + ((alias_count + 1) * sizeof(char*)));
1290 p = (char*)ap + ((addr_count + 1) * sizeof(char*));
1292 memcpy(p, *pp++, hp->h.h_length);
1294 p += hp->h.h_length;
1298 memcpy(p, *addr++, hp->h.h_length);
1300 p += hp->h.h_length;
1305 /* next write the name */
1306 strcpy(p, hp->h.h_name);
1308 p += (strlen(p) + 1);
1310 /* last write the alias list */
1311 pp = hp->h.h_aliases;
1312 ap = hp->h.h_aliases = (char**) buf;
1316 p += (strlen(p) + 1);
1320 strcpy(p, *alias++);
1322 p += (strlen(p) + 1);
1326 /* release the old buffer */
1333 * hash_number - IP address hash function
1335 static int hash_number(const unsigned char* ip)
1337 /* could use loop but slower */
1339 const u_char* p = (const u_char*) ip;
1344 hashv += hashv + *p++;
1345 hashv += hashv + *p++;
1346 hashv += hashv + *p;
1347 hashv %= ARES_CACSIZE;
1352 * hash_name - hostname hash function
1354 static int hash_name(const char* name)
1356 unsigned int hashv = 0;
1357 const u_char* p = (const u_char*) name;
1361 for (; *p && *p != '.'; ++p)
1363 hashv %= ARES_CACSIZE;
1368 * add_to_cache - Add a new cache item to the queue and hash table.
1370 static struct CacheEntry* add_to_cache(struct CacheEntry* ocp)
1376 ocp->list_next = cacheTop;
1379 hashv = hash_name(ocp->he.h.h_name);
1381 ocp->hname_next = hashtable[hashv].name_list;
1382 hashtable[hashv].name_list = ocp;
1384 hashv = hash_number(ocp->he.h.h_addr);
1386 ocp->hnum_next = hashtable[hashv].num_list;
1387 hashtable[hashv].num_list = ocp;
1390 * LRU deletion of excessive cache entries.
1392 if (++cachedCount > MAXCACHED) {
1393 struct CacheEntry* cp;
1394 struct CacheEntry* cp_next;
1395 for (cp = ocp->list_next; cp; cp = cp_next) {
1396 cp_next = cp->list_next;
1405 * update_list - does not alter the cache structure passed. It is assumed that
1406 * it already contains the correct expire time, if it is a new entry. Old
1407 * entries have the expirey time updated.
1409 static void update_list(struct ResRequest* request, struct CacheEntry* cachep)
1412 struct CacheEntry** cpp;
1414 struct CacheEntry* cp = cachep;
1420 char* addrs[RES_MAXADDRS + 1];
1421 char* aliases[RES_MAXALIASES + 1];
1424 * search for the new cache item in the cache list by hostname.
1425 * If found, move the entry to the top of the list and return.
1427 ++cainfo.ca_updates;
1429 for (cpp = &cacheTop; *cpp; cpp = &((*cpp)->list_next)) {
1435 *cpp = cp->list_next;
1436 cp->list_next = cacheTop;
1443 * Compare the cache entry against the new record. Add any
1444 * previously missing names for this entry.
1448 for (i = 0, s = request->he.h.h_name; s; s = request->he.h.h_aliases[i++]) {
1449 for (j = 0, t = cp->he.h.h_name; t; t = cp->he.h.h_aliases[j++]) {
1450 if (0 == ircd_strcmp(t, s))
1459 * Do the same again for IP#'s.
1463 for (i = 0; (s = request->he.h.h_addr_list[i]); i++) {
1464 for (j = 0; (t = cp->he.h.h_addr_list[j]); j++) {
1465 if (!memcmp(t, s, sizeof(struct in_addr)))
1473 if (*addrs || *aliases)
1474 update_hostent(&cp->he, addrs, aliases);
1478 * find_cache_name - find name in nameserver cache
1480 static struct CacheEntry* find_cache_name(const char* name)
1482 struct CacheEntry* cp;
1488 hashv = hash_name(name);
1490 cp = hashtable[hashv].name_list;
1492 for (; cp; cp = cp->hname_next) {
1493 for (i = 0, s = cp->he.h.h_name; s; s = cp->he.h.h_aliases[i++]) {
1494 if (0 == ircd_strcmp(s, name)) {
1495 ++cainfo.ca_na_hits;
1501 for (cp = cacheTop; cp; cp = cp->list_next) {
1503 * if no aliases or the hash value matches, we've already
1504 * done this entry and all possiblilities concerning it.
1506 if (!cp->he.h.h_name || hashv == hash_name(cp->he.h.h_name))
1508 for (i = 0, s = cp->he.h.h_aliases[i]; s; s = cp->he.h.h_aliases[++i]) {
1509 if (0 == ircd_strcmp(name, s)) {
1510 ++cainfo.ca_na_hits;
1519 * find_cache_number - find a cache entry by ip# and update its expire time
1521 static struct CacheEntry* find_cache_number(struct ResRequest* request,
1524 struct CacheEntry* cp;
1529 hashv = hash_number(addr);
1530 cp = hashtable[hashv].num_list;
1532 for (; cp; cp = cp->hnum_next) {
1533 for (i = 0; cp->he.h.h_addr_list[i]; ++i) {
1534 if (!memcmp(cp->he.h.h_addr_list[i], addr, sizeof(struct in_addr))) {
1535 ++cainfo.ca_nu_hits;
1540 for (cp = cacheTop; cp; cp = cp->list_next) {
1542 * single address entry...would have been done by hashed
1544 * if the first IP# has the same hashnumber as the IP# we
1545 * are looking for, its been done already.
1547 if (!cp->he.h.h_addr_list[1] ||
1548 hashv == hash_number(cp->he.h.h_addr_list[0]))
1550 for (i = 1; cp->he.h.h_addr_list[i]; ++i) {
1551 if (!memcmp(cp->he.h.h_addr_list[i], addr, sizeof(struct in_addr))) {
1552 ++cainfo.ca_nu_hits;
1560 static struct CacheEntry* make_cache(struct ResRequest* request)
1562 struct CacheEntry* cp;
1565 assert(0 != request);
1567 hp = &request->he.h;
1569 * shouldn't happen but it just might...
1571 assert(0 != hp->h_name);
1572 assert(0 != hp->h_addr_list[0]);
1573 if (!hp->h_name || !hp->h_addr_list[0])
1576 * Make cache entry. First check to see if the cache already exists
1577 * and if so, return a pointer to it.
1579 for (i = 0; hp->h_addr_list[i]; ++i) {
1580 if ((cp = find_cache_number(request, hp->h_addr_list[i]))) {
1581 update_list(request, cp);
1586 * a matching entry wasnt found in the cache so go and make one up.
1588 cp = (struct CacheEntry*) MyMalloc(sizeof(struct CacheEntry));
1591 memset(cp, 0, sizeof(struct CacheEntry));
1592 dup_hostent(&cp->he, hp);
1593 cp->reply.hp = &cp->he.h;
1595 * hmmm... we could time out the cache after 10 minutes regardless
1596 * would that be reasonable since we don't save the reply?
1598 if (request->ttl < AR_TTL) {
1599 ++reinfo.re_shortttl;
1603 cp->ttl = request->ttl;
1604 cp->expireat = CurrentTime + cp->ttl;
1605 return add_to_cache(cp);
1609 * rem_cache - delete a cache entry from the cache structures
1610 * and lists and return all memory used for the cache back to the memory pool.
1612 static void rem_cache(struct CacheEntry* ocp)
1614 struct CacheEntry** cp;
1620 if (0 < ocp->reply.ref_count) {
1621 if (ocp->expireat < CurrentTime) {
1622 ocp->expireat = CurrentTime + AR_TTL;
1623 Debug((DEBUG_DNS, "Resolver: referenced cache entry not removed for: %s",
1629 * remove cache entry from linked list
1631 for (cp = &cacheTop; *cp; cp = &((*cp)->list_next)) {
1633 *cp = ocp->list_next;
1639 * remove cache entry from hashed name list
1641 assert(0 != hp->h_name);
1642 hashv = hash_name(hp->h_name);
1644 for (cp = &hashtable[hashv].name_list; *cp; cp = &((*cp)->hname_next)) {
1646 *cp = ocp->hname_next;
1651 * remove cache entry from hashed number list
1653 hashv = hash_number(hp->h_addr);
1656 for (cp = &hashtable[hashv].num_list; *cp; cp = &((*cp)->hnum_next)) {
1658 *cp = ocp->hnum_next;
1663 * free memory used to hold the various host names and the array
1664 * of alias pointers.
1666 MyFree(ocp->he.buf);
1672 void flush_resolver_cache(void)
1675 * stubbed - iterate cache and remove everything that isn't referenced
1680 * m_dns - dns status query
1682 int m_dns(struct Client *cptr, struct Client *sptr, int parc, char *parv[])
1684 #if !defined(NDEBUG)
1685 struct CacheEntry* cp;
1689 if (parv[1] && *parv[1] == 'l') {
1690 for(cp = cacheTop; cp; cp = cp->list_next) {
1692 sendto_one(sptr, "NOTICE %s :Ex %d ttl %d host %s(%s)",
1693 parv[0], cp->expireat - CurrentTime, cp->ttl,
1694 hp->h_name, ircd_ntoa(hp->h_addr));
1695 for (i = 0; hp->h_aliases[i]; i++)
1696 sendto_one(sptr,"NOTICE %s : %s = %s (CN)",
1697 parv[0], hp->h_name, hp->h_aliases[i]);
1698 for (i = 1; hp->h_addr_list[i]; i++)
1699 sendto_one(sptr,"NOTICE %s : %s = %s (IP)",
1700 parv[0], hp->h_name, ircd_ntoa(hp->h_addr_list[i]));
1704 if (parv[1] && *parv[1] == 'd') {
1705 sendto_one(sptr, "NOTICE %s :ResolverFileDescriptor = %d",
1706 parv[0], ResolverFileDescriptor);
1709 sendto_one(sptr,"NOTICE %s :Ca %d Cd %d Ce %d Cl %d Ch %d:%d Cu %d",
1711 cainfo.ca_adds, cainfo.ca_dels, cainfo.ca_expires,
1712 cainfo.ca_lookups, cainfo.ca_na_hits, cainfo.ca_nu_hits,
1715 sendto_one(sptr,"NOTICE %s :Re %d Rl %d/%d Rp %d Rq %d",
1716 sptr->name, reinfo.re_errors, reinfo.re_nu_look,
1717 reinfo.re_na_look, reinfo.re_replies, reinfo.re_requests);
1718 sendto_one(sptr,"NOTICE %s :Ru %d Rsh %d Rs %d(%d) Rt %d", sptr->name,
1719 reinfo.re_unkrep, reinfo.re_shortttl, reinfo.re_sent,
1720 reinfo.re_resends, reinfo.re_timeouts);
1725 unsigned long cres_mem(struct Client* sptr)
1727 struct CacheEntry* entry;
1728 struct ResRequest* request;
1729 size_t cache_mem = 0;
1730 int cache_count = 0;
1731 size_t request_mem = 0;
1732 int request_count = 0;
1734 for (entry = cacheTop; entry; entry = entry->list_next) {
1735 cache_mem += sizeof(struct CacheEntry);
1736 cache_mem += calc_hostent_buffer_size(&entry->he.h);
1739 for (request = requestListHead; request; request = request->next) {
1740 request_mem += sizeof(struct ResRequest);
1742 request_mem += strlen(request->name) + 1;
1743 if (request->he.buf)
1744 request_mem += MAXGETHOSTLEN + 1;
1747 if (cachedCount != cache_count) {
1749 ":%s %d %s :Resolver: cache count mismatch: %d != %d",
1750 me.name, RPL_STATSDEBUG, sptr->name, cachedCount, cache_count);
1751 assert(cachedCount == cache_count);
1753 sendto_one(sptr, ":%s %d %s :Resolver: cache %d(%d) requests %d(%d)",
1754 me.name, RPL_STATSDEBUG, sptr->name, cache_count, cache_mem,
1755 request_count, request_mem);
1756 return cache_mem + request_mem;