2 * arlib.c (C)opyright 1993 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.
8 static char sccsid[] = "@(#)arlib.c 1.9 6/5/93 (C)opyright 1992 Darren \
15 #include <sys/types.h>
17 #include <sys/socket.h>
18 #include <netinet/in.h>
20 #include "arpa/nameser.h"
25 extern int errno, h_errno;
26 static char ar_hostbuf[65], ar_domainname[65];
27 static char ar_dot[] = ".";
28 static int ar_resfd = -1, ar_vc = 0;
29 static struct reslist *ar_last, *ar_first;
32 * Statistics structure.
34 static struct resstats {
45 static int do_query_name(/* struct resinfo *, char *, struct reslist * */);
46 static int do_query_number(/* struct resinfo *, char *, struct reslist * */);
47 static int ar_resend_query(/* struct reslist * */);
52 * Initializes the various ARLIB internal varilables and related DNS
53 * options for res_init().
55 * Returns 0 or the socket opened for use with talking to name servers
56 * if 0 is passed or ARES_INITSOCK is set.
63 if (op & ARES_INITLIST)
65 bzero(&ar_reinfo, sizeof(ar_reinfo));
66 ar_first = ar_last = NULL;
69 if (op & ARES_CALLINIT && !(_res.options & RES_INIT))
72 (void)strcpy(ar_domainname, ar_dot);
73 (void)strncat(ar_domainname, _res.defdname,
74 sizeof(ar_domainname)-2);
77 if (op & ARES_INITSOCK)
78 ret = ar_resfd = ar_open();
80 if (op & ARES_INITDEBG)
81 _res.options |= RES_DEBUG;
93 * Open a socket to talk to a name server with.
94 * Check _res.options to see if we use a TCP or UDP socket.
100 if (_res.options & RES_USEVC)
102 struct sockaddr_in *sip;
105 sip = _res.NS_ADDR_LIST; /* was _res.nsaddr_list */
107 ar_resfd = socket(AF_INET, SOCK_STREAM, 0);
110 * Try each name server listed in sequence until we
111 * succeed or run out.
113 while (connect(ar_resfd, (struct sockaddr *)sip++,
114 sizeof(struct sockaddr)))
116 (void)close(ar_resfd);
118 if (i >= _res.nscount)
120 ar_resfd = socket(AF_INET, SOCK_STREAM, 0);
124 ar_resfd = socket(AF_INET, SOCK_DGRAM, 0);
127 { /* Need one of these two here - and it MUST work!! */
130 if ((flags = fcntl(ar_resfd, F_GETFL, 0)) != -1)
132 if (fcntl(ar_resfd, F_SETFL, flags|O_NONBLOCK) == -1)
135 if (fcntl(ar_resfd, F_SETFL, flags|O_NDELAY) == -1)
138 if (fcntl(ar_resfd, F_SETFL, flags|FNDELAY) == -1)
143 (void)close(ar_resfd);
154 * Closes and flags the ARLIB socket as closed.
158 (void)close(ar_resfd);
167 * Add a new DNS query to the end of the query list.
169 static int ar_add_request(new)
175 ar_first = ar_last = new;
177 ar_last->re_next = new;
181 ar_reinfo.re_requests++;
189 * Remove a request from the list. This must also free any memory that has
190 * been allocated for temporary storage of DNS results.
192 * Returns -1 if there are anyy problems removing the requested structure
193 * or 0 if the remove is successful.
195 static int ar_remrequest(old)
198 register struct reslist *rptr, *r2ptr;
203 for (rptr = ar_first, r2ptr = NULL; rptr; rptr = rptr->re_next)
212 if (rptr == ar_first)
213 ar_first = ar_first->re_next;
214 else if (rptr == ar_last)
217 ar_last->re_next = NULL;
220 r2ptr->re_next = rptr->re_next;
226 ar_dump_hostent("ar_remrequest:", rptr->re_he);
229 if (rptr->re_he.h_name)
230 (void)free(rptr->re_he.h_name);
231 if (s = rptr->re_he.h_aliases)
234 if (rptr->re_rinfo.ri_ptr)
235 (void)free(rptr->re_rinfo.ri_ptr);
245 * Create a DNS query recorded for the request being made and place it on the
246 * current list awaiting replies. Initialization of the record with set
247 * values should also be done.
249 static struct reslist *ar_make_request(resi)
250 register struct resinfo *resi;
252 register struct reslist *rptr;
253 register struct resinfo *rp;
255 rptr = (struct reslist *)calloc(1, sizeof(struct reslist));
256 rp = &rptr->re_rinfo;
258 rptr->re_next = NULL; /* where NULL is non-zero ;) */
259 rptr->re_sentat = time(NULL);
260 rptr->re_retries = _res.retry;
263 rptr->re_timeout = rptr->re_sentat + _res.retrans;
264 rptr->re_he.h_name = NULL;
265 rptr->re_he.h_addrtype = AF_INET;
266 rptr->re_he.h_aliases[0] = NULL;
267 rp->ri_ptr = resi->ri_ptr;
268 rp->ri_size = resi->ri_size;
270 (void)ar_add_request(rptr);
279 * Remove queries from the list which have been there too long without
282 long ar_timeout(now, info, size)
287 register struct reslist *rptr, *r2ptr;
288 register long next = 0;
290 for (rptr = ar_first, r2ptr = NULL; rptr; rptr = r2ptr)
292 r2ptr = rptr->re_next;
293 if (now >= rptr->re_timeout)
296 * If the timeout for the query has been exceeded,
297 * then resend the query if we still have some
298 * 'retry credit' and reset the timeout. If we have
299 * used it all up, then remove the request.
301 if (--rptr->re_retries <= 0)
303 ar_reinfo.re_timeouts++;
304 if (info && rptr->re_rinfo.ri_ptr)
305 bcopy(rptr->re_rinfo.ri_ptr, info,
306 MIN(rptr->re_rinfo.ri_size,
308 (void)ar_remrequest(rptr);
314 rptr->re_sentat = now;
315 rptr->re_timeout = now + _res.retrans;
316 (void)ar_resend_query(rptr);
319 if (!next || rptr->re_timeout < next)
320 next = rptr->re_timeout;
329 * When sending queries to nameservers listed in the resolv.conf file,
330 * don't send a query to every one, but increase the number sent linearly
331 * to match the number of resends. This increase only occurs if there are
332 * multiple nameserver entries in the resolv.conf file.
333 * The return value is the number of messages successfully sent to
334 * nameservers or -1 if no successful sends.
336 static int ar_send_res_msg(msg, len, rcount)
346 rcount = (_res.nscount > rcount) ? rcount : _res.nscount;
347 if (_res.options & RES_PRIMARY)
354 if (write(ar_resfd, msg, len) == -1)
357 (void)close(ar_resfd);
363 for (i = 0; i < rcount; i++)
365 if (sendto(ar_resfd, msg, len, 0,
366 (struct sockaddr *)&(_res.NS_ADDR_LIST[i]),
367 sizeof(struct sockaddr_in)) == len)
373 return (sent) ? sent : -1;
380 * find a dns query record by the id (id is determined by dn_mkquery)
382 static struct reslist *ar_find_id(id)
385 register struct reslist *rptr;
387 for (rptr = ar_first; rptr; rptr = rptr->re_next)
388 if (rptr->re_id == id)
397 * Delete a request from the waiting list if it has a data pointer which
398 * matches the one passed.
400 int ar_delete(ptr, size)
404 register struct reslist *rptr;
405 register struct reslist *r2ptr;
408 for (rptr = ar_first; rptr; rptr = r2ptr)
410 r2ptr = rptr->re_next;
411 if (rptr->re_rinfo.ri_ptr && ptr && size &&
412 bcmp(rptr->re_rinfo.ri_ptr, ptr, size) == 0)
414 (void)ar_remrequest(rptr);
425 * generate a query based on class, type and name.
427 static int ar_query_name(name, class, type, rptr)
430 struct reslist *rptr;
432 static char buf[MAXPACKET];
436 bzero(buf, sizeof(buf));
437 r = res_mkquery(QUERY, name, class, type, NULL, 0, NULL,
441 h_errno = NO_RECOVERY;
444 hptr = (HEADER *)buf;
445 rptr->re_id = ntohs(hptr->id);
447 s = ar_send_res_msg(buf, r, rptr->re_sends);
463 * Replacement library function call to gethostbyname(). This one, however,
464 * doesn't return the record being looked up but just places the query in the
465 * queue to await answers.
467 int ar_gethostbyname(name, info, size)
474 register struct resinfo *rp = &resi;
478 rp->ri_ptr = (char *)malloc(size);
479 bcopy(info, rp->ri_ptr, size);
483 bzero((char *)rp, sizeof(resi));
484 ar_reinfo.re_na_look++;
485 (void)strncpy(host, name, 64);
488 return (do_query_name(rp, host, NULL));
492 static int do_query_name(resi, name, rptr)
493 struct resinfo *resi;
495 register struct reslist *rptr;
500 len = strlen((char *)strncpy(hname, name, sizeof(hname)-1));
502 if (rptr && (hname[len-1] != '.'))
504 (void)strncat(hname, ar_dot, sizeof(hname)-len-1);
506 * NOTE: The logical relationship between DNSRCH and DEFNAMES
507 * is implies. ie no DEFNAES, no DNSRCH.
509 if (_res.options & (RES_DEFNAMES|RES_DNSRCH) ==
510 (RES_DEFNAMES|RES_DNSRCH))
512 if (_res.dnsrch[rptr->re_srch])
513 (void)strncat(hname, _res.dnsrch[rptr->re_srch],
514 sizeof(hname) - ++len -1);
516 else if (_res.options & RES_DEFNAMES)
517 (void)strncat(hname, ar_domainname, sizeof(hname) - len -1);
521 * Store the name passed as the one to lookup and generate other host
522 * names to pass onto the nameserver(s) for lookups.
526 rptr = ar_make_request(resi);
528 (void)strncpy(rptr->re_name, name, sizeof(rptr->re_name)-1);
530 return (ar_query_name(hname, C_IN, T_A, rptr));
537 * Generates a query for a given IP address.
539 int ar_gethostbyaddr(addr, info, size)
545 register struct resinfo *rp = &resi;
549 rp->ri_ptr = (char *)malloc(size);
550 bcopy(info, rp->ri_ptr, size);
554 bzero((char *)rp, sizeof(resi));
555 ar_reinfo.re_nu_look++;
556 return (do_query_number(rp, addr, NULL));
563 * Use this to do reverse IP# lookups.
565 static int do_query_number(resi, numb, rptr)
566 struct resinfo *resi;
568 register struct reslist *rptr;
570 register unsigned char *cp;
571 static char ipbuf[32];
574 * Generate name in the "in-addr.arpa" domain. No addings bits to this
575 * name to get more names to query!.
577 cp = (unsigned char *)numb;
578 (void)sprintf(ipbuf,"%u.%u.%u.%u.in-addr.arpa.",
579 (unsigned int)(cp[3]), (unsigned int)(cp[2]),
580 (unsigned int)(cp[1]), (unsigned int)(cp[0]));
584 rptr = ar_make_request(resi);
585 rptr->re_type = T_PTR;
586 rptr->re_he.h_length = sizeof(struct in_addr);
587 bcopy(numb, (char *)&rptr->re_addr, rptr->re_he.h_length);
588 bcopy(numb, (char *)&rptr->re_he.h_addr_list[0].s_addr,
589 rptr->re_he.h_length);
591 return (ar_query_name(ipbuf, C_IN, T_PTR, rptr));
600 static int ar_resend_query(rptr)
601 struct reslist *rptr;
603 if (!rptr->re_resend)
606 switch(rptr->re_type)
609 ar_reinfo.re_resends++;
610 return do_query_number(NULL, &rptr->re_addr, rptr);
612 ar_reinfo.re_resends++;
613 return do_query_name(NULL, rptr->re_name, rptr);
625 * process an answer received from a nameserver.
627 static int ar_procanswer(rptr, hptr, buf, eob)
628 struct reslist *rptr;
632 char *cp, **alias, *s;
633 int class, type, dlen, len, ans = 0, n, i;
634 u_int32_t ttl, dr, *adr;
637 cp = buf + sizeof(HEADER);
638 adr = (u_int32_t *)rptr->re_he.h_addr_list;
643 alias = rptr->re_he.h_aliases;
651 * Skip over the original question.
653 while (hptr->qdcount-- > 0)
654 cp += dn_skipname(cp, eob) + QFIXEDSZ;
656 * proccess each answer sent to us. blech.
658 while (hptr->ancount-- > 0 && cp < eob) {
659 n = dn_expand(buf, eob, cp, ar_hostbuf, sizeof(ar_hostbuf));
666 * 'skip' past the general dns crap (ttl, class, etc) to get
667 * the pointer to the right spot. Some of thse are actually
668 * useful so its not a good idea to skip past in one big jump.
670 type = (int)_getshort(cp);
672 class = (int)_getshort(cp);
674 ttl = (u_int32_t)_getlong(cp);
675 cp += sizeof(u_int32_t);
676 dlen = (int)_getshort(cp);
678 rptr->re_type = type;
683 rptr->re_he.h_length = dlen;
685 rptr->re_he.h_addrtype=(class == C_IN) ?
687 if (dlen != sizeof(dr))
692 bcopy(cp, &dr, dlen);
696 len = strlen(ar_hostbuf);
697 if (!rptr->re_he.h_name)
699 rptr->re_he.h_name = (char *)malloc(len+1);
700 if (!rptr->re_he.h_name)
702 (void)strcpy(rptr->re_he.h_name, ar_hostbuf);
706 if ((n = dn_expand(buf, eob, cp, ar_hostbuf,
707 sizeof(ar_hostbuf) )) < 0)
713 len = strlen(ar_hostbuf)+1;
715 * copy the returned hostname into the host name
716 * or alias field if there is a known hostname
719 if (!rptr->re_he.h_name)
721 rptr->re_he.h_name = (char *)malloc(len);
722 if (!rptr->re_he.h_name)
724 (void)strcpy(rptr->re_he.h_name, ar_hostbuf);
728 *alias = (char *)malloc(len);
731 (void)strcpy(*alias++, ar_hostbuf);
737 if (alias >= &(rptr->re_he.h_aliases[MAXALIASES-1]))
739 n = strlen(ar_hostbuf)+1;
740 *alias = (char *)malloc(n);
743 (void)strcpy(*alias++, ar_hostbuf);
758 * Get an answer from a DNS server and process it. If a query is found to
759 * which no answer has been given to yet, copy its 'info' structure back
760 * to where "reip" points and return a pointer to the hostent structure.
762 struct hostent *ar_answer(reip, size)
766 static char ar_rcvbuf[sizeof(HEADER) + MAXPACKET];
767 static struct hostent ar_host;
769 register HEADER *hptr;
770 register struct reslist *rptr = NULL;
771 register struct hostent *hp;
776 rc = recv(ar_resfd, ar_rcvbuf, sizeof(ar_rcvbuf), 0);
780 ar_reinfo.re_replies++;
781 hptr = (HEADER *)ar_rcvbuf;
783 * convert things to be in the right order.
785 hptr->id = ntohs(hptr->id);
786 hptr->ancount = ntohs(hptr->ancount);
787 hptr->arcount = ntohs(hptr->arcount);
788 hptr->nscount = ntohs(hptr->nscount);
789 hptr->qdcount = ntohs(hptr->qdcount);
791 * response for an id which we have already received an answer for
792 * just ignore this response.
794 rptr = ar_find_id(hptr->id);
798 if ((hptr->rcode != NOERROR) || (hptr->ancount == 0))
803 h_errno = HOST_NOT_FOUND;
815 h_errno = NO_RECOVERY;
818 ar_reinfo.re_errors++;
820 ** If a bad error was returned, we stop here and dont send
821 ** send any more (no retries granted).
823 if (h_errno != TRY_AGAIN)
826 rptr->re_retries = 0;
831 a = ar_procanswer(rptr, hptr, ar_rcvbuf, ar_rcvbuf+rc);
833 if ((rptr->re_type == T_PTR) && (_res.options & RES_CHECKPTR))
836 * For reverse lookups on IP#'s, lookup the name that is given
837 * for the ip# and return with that as the official result.
842 * Clean out the list of addresses already set, even though
843 * there should only be one :)
845 adr = (unsigned long *)rptr->re_he.h_addr_list;
849 * Lookup the name that we were given for the ip#
851 ar_reinfo.re_na_look++;
852 (void)strncpy(rptr->re_name, rptr->re_he.h_name,
853 sizeof(rptr->re_name)-1);
854 rptr->re_he.h_name = NULL;
855 rptr->re_retries = _res.retry;
858 rptr->re_he.h_name = NULL;
859 ar_reinfo.re_na_look++;
860 (void)ar_query_name(rptr->re_name, C_IN, T_A, rptr);
864 if (reip && rptr->re_rinfo.ri_ptr && size)
865 bcopy(rptr->re_rinfo.ri_ptr, reip,
866 MIN(rptr->re_rinfo.ri_size, size));
868 * Clean up structure from previous usage.
872 ar_dump_hostent("ar_answer: previous usage", hp);
876 (void)free(hp->h_name);
877 if (s = hp->h_aliases)
881 (void)free(hp->h_aliases);
883 if (s = hp->h_addr_list)
886 * Only free once since we allocated space for
887 * address in one big chunk.
890 (void)free(hp->h_addr_list);
892 bzero((char *)hp, sizeof(*hp));
895 * Setup and copy details for the structure we return a pointer to.
897 hp->h_addrtype = AF_INET;
898 hp->h_length = sizeof(struct in_addr);
899 if(rptr->re_he.h_name)
901 hp->h_name = (char *)malloc(strlen(rptr->re_he.h_name)+1);
905 fprintf(stderr, "no memory for hostname\n");
910 (void)strcpy(hp->h_name, rptr->re_he.h_name);
913 ar_dump_hostent("ar_answer: (snap) store name", hp);
919 for (i = 0, n = 0; i < MAXADDRS; i++, n++)
920 if (!rptr->re_he.h_addr_list[i].s_addr)
922 s = hp->h_addr_list = (char **)malloc((n + 1) * sizeof(char *));
925 *s = (char *)malloc(n * sizeof(struct in_addr));
929 fprintf(stderr, "no memory for IP#'s (%d)\n", n);
934 bcopy((char *)&rptr->re_he.h_addr_list[0].s_addr, *s,
935 sizeof(struct in_addr));
937 for (i = 1; i < n; i++, s++)
939 *s = hp->h_addr + i * sizeof(struct in_addr);
940 bcopy((char *)&rptr->re_he.h_addr_list[i].s_addr, *s,
941 sizeof(struct in_addr));
946 ar_dump_hostent("ar_answer: (snap) store IP#'s", hp);
952 for (i = 0, n = 0; i < MAXADDRS; i++, n++)
953 if (!rptr->re_he.h_aliases[i])
955 s = hp->h_aliases = (char **)malloc((n + 1) * sizeof(char *));
959 fprintf(stderr, "no memory for aliases (%d)\n", n);
964 for (i = 0; i < n; i++)
966 *s++ = rptr->re_he.h_aliases[i];
967 rptr->re_he.h_aliases[i] = NULL;
971 ar_dump_hostent("ar_answer: (snap) store CNAMEs", hp);
972 ar_dump_hostent("ar_answer: new one", hp);
976 (void)ar_remrequest(rptr);
979 (void)ar_remrequest(rptr);
985 if (reip && rptr->re_rinfo.ri_ptr && size)
986 bcopy(rptr->re_rinfo.ri_ptr, reip,
987 MIN(rptr->re_rinfo.ri_size, size));
988 if ((h_errno != TRY_AGAIN) &&
989 (_res.options & (RES_DNSRCH|RES_DEFNAMES) ==
990 (RES_DNSRCH|RES_DEFNAMES) ))
991 if (_res.dnsrch[rptr->re_srch])
993 rptr->re_retries = _res.retry;
996 (void)ar_resend_query(rptr);
1006 void ar_dump_hostent(prefix, hp)
1014 fprintf(stderr, "%s\n", prefix);
1015 fprintf(stderr, " hp %p\n", hp);
1016 fprintf(stderr, " h_name %p '%s'\n",
1017 hp->h_name, hp->h_name);
1018 if (s = hp->h_aliases)
1020 fprintf(stderr, " h_aliases %p\n",
1024 fprintf(stderr, " element %p\n", *s);
1028 if (s = hp->h_addr_list)
1030 fprintf(stderr, " h_addr_list %p\n",
1034 fprintf(stderr, " element %p\n", *s);
1043 void ar_dump_reslist(FILE* fp)
1045 register struct reslist *rptr;
1049 for (rptr = ar_first; rptr; rptr = rptr->re_next)
1051 fprintf(fp, "%4d [%p] %4d [%p]: %s\n", rptr->re_id, rptr,
1052 *(rptr->re_rinfo.ri_ptr), rptr->re_rinfo.ri_ptr,