4 * arlib.c (C)opyright 1993 Darren Reed. All rights reserved.
5 * This file may not be distributed without the author's permission in any
6 * shape or form. The author takes no responsibility for any damage or loss
7 * of property which results from the use of this software.
10 static char sccsid
[] = "@(#)arlib.c 1.9 6/5/93 (C)opyright 1992 Darren \
17 #include <sys/types.h>
19 #include <sys/socket.h>
20 #include <netinet/in.h>
22 #include "arpa/nameser.h"
27 extern int errno
, h_errno
;
28 static char ar_hostbuf
[65], ar_domainname
[65];
29 static char ar_dot
[] = ".";
30 static int ar_resfd
= -1, ar_vc
= 0;
31 static struct reslist
*ar_last
, *ar_first
;
34 * Statistics structure.
36 static struct resstats
{
47 static int do_query_name(/* struct resinfo *, char *, struct reslist * */);
48 static int do_query_number(/* struct resinfo *, char *, struct reslist * */);
49 static int ar_resend_query(/* struct reslist * */);
54 * Initializes the various ARLIB internal varilables and related DNS
55 * options for res_init().
57 * Returns 0 or the socket opened for use with talking to name servers
58 * if 0 is passed or ARES_INITSOCK is set.
65 if (op
& ARES_INITLIST
)
67 bzero(&ar_reinfo
, sizeof(ar_reinfo
));
68 ar_first
= ar_last
= NULL
;
71 if (op
& ARES_CALLINIT
&& !(_res
.options
& RES_INIT
))
74 (void)strcpy(ar_domainname
, ar_dot
);
75 (void)strncat(ar_domainname
, _res
.defdname
,
76 sizeof(ar_domainname
)-2);
79 if (op
& ARES_INITSOCK
)
80 ret
= ar_resfd
= ar_open();
82 if (op
& ARES_INITDEBG
)
83 _res
.options
|= RES_DEBUG
;
95 * Open a socket to talk to a name server with.
96 * Check _res.options to see if we use a TCP or UDP socket.
102 if (_res
.options
& RES_USEVC
)
104 struct sockaddr_in
*sip
;
107 sip
= _res
.NS_ADDR_LIST
; /* was _res.nsaddr_list */
109 ar_resfd
= socket(AF_INET
, SOCK_STREAM
, 0);
112 * Try each name server listed in sequence until we
113 * succeed or run out.
115 while (connect(ar_resfd
, (struct sockaddr
*)sip
++,
116 sizeof(struct sockaddr
)))
118 (void)close(ar_resfd
);
120 if (i
>= _res
.nscount
)
122 ar_resfd
= socket(AF_INET
, SOCK_STREAM
, 0);
126 ar_resfd
= socket(AF_INET
, SOCK_DGRAM
, 0);
129 { /* Need one of these two here - and it MUST work!! */
132 if ((flags
= fcntl(ar_resfd
, F_GETFL
, 0)) != -1)
134 if (fcntl(ar_resfd
, F_SETFL
, flags
|O_NONBLOCK
) == -1)
137 if (fcntl(ar_resfd
, F_SETFL
, flags
|O_NDELAY
) == -1)
140 if (fcntl(ar_resfd
, F_SETFL
, flags
|FNDELAY
) == -1)
145 (void)close(ar_resfd
);
156 * Closes and flags the ARLIB socket as closed.
160 (void)close(ar_resfd
);
169 * Add a new DNS query to the end of the query list.
171 static int ar_add_request(new)
177 ar_first
= ar_last
= new;
179 ar_last
->re_next
= new;
183 ar_reinfo
.re_requests
++;
191 * Remove a request from the list. This must also free any memory that has
192 * been allocated for temporary storage of DNS results.
194 * Returns -1 if there are anyy problems removing the requested structure
195 * or 0 if the remove is successful.
197 static int ar_remrequest(old
)
200 register struct reslist
*rptr
, *r2ptr
;
205 for (rptr
= ar_first
, r2ptr
= NULL
; rptr
; rptr
= rptr
->re_next
)
214 if (rptr
== ar_first
)
215 ar_first
= ar_first
->re_next
;
216 else if (rptr
== ar_last
)
219 ar_last
->re_next
= NULL
;
222 r2ptr
->re_next
= rptr
->re_next
;
228 ar_dump_hostent("ar_remrequest:", rptr
->re_he
);
231 if (rptr
->re_he
.h_name
)
232 (void)free(rptr
->re_he
.h_name
);
233 if (s
= rptr
->re_he
.h_aliases
)
236 if (rptr
->re_rinfo
.ri_ptr
)
237 (void)free(rptr
->re_rinfo
.ri_ptr
);
247 * Create a DNS query recorded for the request being made and place it on the
248 * current list awaiting replies. Initialization of the record with set
249 * values should also be done.
251 static struct reslist
*ar_make_request(resi
)
252 register struct resinfo
*resi
;
254 register struct reslist
*rptr
;
255 register struct resinfo
*rp
;
257 rptr
= (struct reslist
*)calloc(1, sizeof(struct reslist
));
258 rp
= &rptr
->re_rinfo
;
260 rptr
->re_next
= NULL
; /* where NULL is non-zero ;) */
261 rptr
->re_sentat
= time(NULL
);
262 rptr
->re_retries
= _res
.retry
;
265 rptr
->re_timeout
= rptr
->re_sentat
+ _res
.retrans
;
266 rptr
->re_he
.h_name
= NULL
;
267 rptr
->re_he
.h_addrtype
= AF_INET
;
268 rptr
->re_he
.h_aliases
[0] = NULL
;
269 rp
->ri_ptr
= resi
->ri_ptr
;
270 rp
->ri_size
= resi
->ri_size
;
272 (void)ar_add_request(rptr
);
281 * Remove queries from the list which have been there too long without
284 long ar_timeout(now
, info
, size
)
289 register struct reslist
*rptr
, *r2ptr
;
290 register long next
= 0;
292 for (rptr
= ar_first
, r2ptr
= NULL
; rptr
; rptr
= r2ptr
)
294 r2ptr
= rptr
->re_next
;
295 if (now
>= rptr
->re_timeout
)
298 * If the timeout for the query has been exceeded,
299 * then resend the query if we still have some
300 * 'retry credit' and reset the timeout. If we have
301 * used it all up, then remove the request.
303 if (--rptr
->re_retries
<= 0)
305 ar_reinfo
.re_timeouts
++;
306 if (info
&& rptr
->re_rinfo
.ri_ptr
)
307 bcopy(rptr
->re_rinfo
.ri_ptr
, info
,
308 MIN(rptr
->re_rinfo
.ri_size
,
310 (void)ar_remrequest(rptr
);
316 rptr
->re_sentat
= now
;
317 rptr
->re_timeout
= now
+ _res
.retrans
;
318 (void)ar_resend_query(rptr
);
321 if (!next
|| rptr
->re_timeout
< next
)
322 next
= rptr
->re_timeout
;
331 * When sending queries to nameservers listed in the resolv.conf file,
332 * don't send a query to every one, but increase the number sent linearly
333 * to match the number of resends. This increase only occurs if there are
334 * multiple nameserver entries in the resolv.conf file.
335 * The return value is the number of messages successfully sent to
336 * nameservers or -1 if no successful sends.
338 static int ar_send_res_msg(msg
, len
, rcount
)
348 rcount
= (_res
.nscount
> rcount
) ? rcount
: _res
.nscount
;
349 if (_res
.options
& RES_PRIMARY
)
356 if (write(ar_resfd
, msg
, len
) == -1)
359 (void)close(ar_resfd
);
365 for (i
= 0; i
< rcount
; i
++)
367 if (sendto(ar_resfd
, msg
, len
, 0,
368 (struct sockaddr
*)&(_res
.NS_ADDR_LIST
[i
]),
369 sizeof(struct sockaddr_in
)) == len
)
375 return (sent
) ? sent
: -1;
382 * find a dns query record by the id (id is determined by dn_mkquery)
384 static struct reslist
*ar_find_id(id
)
387 register struct reslist
*rptr
;
389 for (rptr
= ar_first
; rptr
; rptr
= rptr
->re_next
)
390 if (rptr
->re_id
== id
)
399 * Delete a request from the waiting list if it has a data pointer which
400 * matches the one passed.
402 int ar_delete(ptr
, size
)
406 register struct reslist
*rptr
;
407 register struct reslist
*r2ptr
;
410 for (rptr
= ar_first
; rptr
; rptr
= r2ptr
)
412 r2ptr
= rptr
->re_next
;
413 if (rptr
->re_rinfo
.ri_ptr
&& ptr
&& size
&&
414 bcmp(rptr
->re_rinfo
.ri_ptr
, ptr
, size
) == 0)
416 (void)ar_remrequest(rptr
);
427 * generate a query based on class, type and name.
429 static int ar_query_name(name
, class, type
, rptr
)
432 struct reslist
*rptr
;
434 static char buf
[MAXPACKET
];
438 bzero(buf
, sizeof(buf
));
439 r
= res_mkquery(QUERY
, name
, class, type
, NULL
, 0, NULL
,
443 h_errno
= NO_RECOVERY
;
446 hptr
= (HEADER
*)buf
;
447 rptr
->re_id
= ntohs(hptr
->id
);
449 s
= ar_send_res_msg(buf
, r
, rptr
->re_sends
);
465 * Replacement library function call to gethostbyname(). This one, however,
466 * doesn't return the record being looked up but just places the query in the
467 * queue to await answers.
469 int ar_gethostbyname(name
, info
, size
)
476 register struct resinfo
*rp
= &resi
;
480 rp
->ri_ptr
= (char *)malloc(size
);
481 bcopy(info
, rp
->ri_ptr
, size
);
485 bzero((char *)rp
, sizeof(resi
));
486 ar_reinfo
.re_na_look
++;
487 (void)strncpy(host
, name
, 64);
490 return (do_query_name(rp
, host
, NULL
));
494 static int do_query_name(resi
, name
, rptr
)
495 struct resinfo
*resi
;
497 register struct reslist
*rptr
;
502 len
= strlen((char *)strncpy(hname
, name
, sizeof(hname
)-1));
504 if (rptr
&& (hname
[len
-1] != '.'))
506 (void)strncat(hname
, ar_dot
, sizeof(hname
)-len
-1);
508 * NOTE: The logical relationship between DNSRCH and DEFNAMES
509 * is implies. ie no DEFNAES, no DNSRCH.
511 if (_res
.options
& (RES_DEFNAMES
|RES_DNSRCH
) ==
512 (RES_DEFNAMES
|RES_DNSRCH
))
514 if (_res
.dnsrch
[rptr
->re_srch
])
515 (void)strncat(hname
, _res
.dnsrch
[rptr
->re_srch
],
516 sizeof(hname
) - ++len
-1);
518 else if (_res
.options
& RES_DEFNAMES
)
519 (void)strncat(hname
, ar_domainname
, sizeof(hname
) - len
-1);
523 * Store the name passed as the one to lookup and generate other host
524 * names to pass onto the nameserver(s) for lookups.
528 rptr
= ar_make_request(resi
);
530 (void)strncpy(rptr
->re_name
, name
, sizeof(rptr
->re_name
)-1);
532 return (ar_query_name(hname
, C_IN
, T_A
, rptr
));
539 * Generates a query for a given IP address.
541 int ar_gethostbyaddr(addr
, info
, size
)
547 register struct resinfo
*rp
= &resi
;
551 rp
->ri_ptr
= (char *)malloc(size
);
552 bcopy(info
, rp
->ri_ptr
, size
);
556 bzero((char *)rp
, sizeof(resi
));
557 ar_reinfo
.re_nu_look
++;
558 return (do_query_number(rp
, addr
, NULL
));
565 * Use this to do reverse IP# lookups.
567 static int do_query_number(resi
, numb
, rptr
)
568 struct resinfo
*resi
;
570 register struct reslist
*rptr
;
572 register unsigned char *cp
;
573 static char ipbuf
[32];
576 * Generate name in the "in-addr.arpa" domain. No addings bits to this
577 * name to get more names to query!.
579 cp
= (unsigned char *)numb
;
580 (void)sprintf(ipbuf
,"%u.%u.%u.%u.in-addr.arpa.",
581 (unsigned int)(cp
[3]), (unsigned int)(cp
[2]),
582 (unsigned int)(cp
[1]), (unsigned int)(cp
[0]));
586 rptr
= ar_make_request(resi
);
587 rptr
->re_type
= T_PTR
;
588 rptr
->re_he
.h_length
= sizeof(struct in_addr
);
589 bcopy(numb
, (char *)&rptr
->re_addr
, rptr
->re_he
.h_length
);
590 bcopy(numb
, (char *)&rptr
->re_he
.h_addr_list
[0].s_addr
,
591 rptr
->re_he
.h_length
);
593 return (ar_query_name(ipbuf
, C_IN
, T_PTR
, rptr
));
602 static int ar_resend_query(rptr
)
603 struct reslist
*rptr
;
605 if (!rptr
->re_resend
)
608 switch(rptr
->re_type
)
611 ar_reinfo
.re_resends
++;
612 return do_query_number(NULL
, &rptr
->re_addr
, rptr
);
614 ar_reinfo
.re_resends
++;
615 return do_query_name(NULL
, rptr
->re_name
, rptr
);
627 * process an answer received from a nameserver.
629 static int ar_procanswer(rptr
, hptr
, buf
, eob
)
630 struct reslist
*rptr
;
634 char *cp
, **alias
, *s
;
635 int class, type
, dlen
, len
, ans
= 0, n
, i
;
636 u_int32_t ttl
, dr
, *adr
;
639 cp
= buf
+ sizeof(HEADER
);
640 adr
= (u_int32_t
*)rptr
->re_he
.h_addr_list
;
645 alias
= rptr
->re_he
.h_aliases
;
653 * Skip over the original question.
655 while (hptr
->qdcount
-- > 0)
656 cp
+= dn_skipname(cp
, eob
) + QFIXEDSZ
;
658 * proccess each answer sent to us. blech.
660 while (hptr
->ancount
-- > 0 && cp
< eob
) {
661 n
= dn_expand(buf
, eob
, cp
, ar_hostbuf
, sizeof(ar_hostbuf
));
668 * 'skip' past the general dns crap (ttl, class, etc) to get
669 * the pointer to the right spot. Some of thse are actually
670 * useful so its not a good idea to skip past in one big jump.
672 type
= (int)_getshort(cp
);
674 class = (int)_getshort(cp
);
676 ttl
= (u_int32_t
)_getlong(cp
);
677 cp
+= sizeof(u_int32_t
);
678 dlen
= (int)_getshort(cp
);
680 rptr
->re_type
= type
;
685 rptr
->re_he
.h_length
= dlen
;
687 rptr
->re_he
.h_addrtype
=(class == C_IN
) ?
689 if (dlen
!= sizeof(dr
))
694 bcopy(cp
, &dr
, dlen
);
698 len
= strlen(ar_hostbuf
);
699 if (!rptr
->re_he
.h_name
)
701 rptr
->re_he
.h_name
= (char *)malloc(len
+1);
702 if (!rptr
->re_he
.h_name
)
704 (void)strcpy(rptr
->re_he
.h_name
, ar_hostbuf
);
708 if ((n
= dn_expand(buf
, eob
, cp
, ar_hostbuf
,
709 sizeof(ar_hostbuf
) )) < 0)
715 len
= strlen(ar_hostbuf
)+1;
717 * copy the returned hostname into the host name
718 * or alias field if there is a known hostname
721 if (!rptr
->re_he
.h_name
)
723 rptr
->re_he
.h_name
= (char *)malloc(len
);
724 if (!rptr
->re_he
.h_name
)
726 (void)strcpy(rptr
->re_he
.h_name
, ar_hostbuf
);
730 *alias
= (char *)malloc(len
);
733 (void)strcpy(*alias
++, ar_hostbuf
);
739 if (alias
>= &(rptr
->re_he
.h_aliases
[MAXALIASES
-1]))
741 n
= strlen(ar_hostbuf
)+1;
742 *alias
= (char *)malloc(n
);
745 (void)strcpy(*alias
++, ar_hostbuf
);
760 * Get an answer from a DNS server and process it. If a query is found to
761 * which no answer has been given to yet, copy its 'info' structure back
762 * to where "reip" points and return a pointer to the hostent structure.
764 struct hostent
*ar_answer(reip
, size
)
768 static char ar_rcvbuf
[sizeof(HEADER
) + MAXPACKET
];
769 static struct hostent ar_host
;
771 register HEADER
*hptr
;
772 register struct reslist
*rptr
= NULL
;
773 register struct hostent
*hp
;
778 rc
= recv(ar_resfd
, ar_rcvbuf
, sizeof(ar_rcvbuf
), 0);
782 ar_reinfo
.re_replies
++;
783 hptr
= (HEADER
*)ar_rcvbuf
;
785 * convert things to be in the right order.
787 hptr
->id
= ntohs(hptr
->id
);
788 hptr
->ancount
= ntohs(hptr
->ancount
);
789 hptr
->arcount
= ntohs(hptr
->arcount
);
790 hptr
->nscount
= ntohs(hptr
->nscount
);
791 hptr
->qdcount
= ntohs(hptr
->qdcount
);
793 * response for an id which we have already received an answer for
794 * just ignore this response.
796 rptr
= ar_find_id(hptr
->id
);
800 if ((hptr
->rcode
!= NOERROR
) || (hptr
->ancount
== 0))
805 h_errno
= HOST_NOT_FOUND
;
817 h_errno
= NO_RECOVERY
;
820 ar_reinfo
.re_errors
++;
822 ** If a bad error was returned, we stop here and dont send
823 ** send any more (no retries granted).
825 if (h_errno
!= TRY_AGAIN
)
828 rptr
->re_retries
= 0;
833 a
= ar_procanswer(rptr
, hptr
, ar_rcvbuf
, ar_rcvbuf
+rc
);
835 if ((rptr
->re_type
== T_PTR
) && (_res
.options
& RES_CHECKPTR
))
838 * For reverse lookups on IP#'s, lookup the name that is given
839 * for the ip# and return with that as the official result.
844 * Clean out the list of addresses already set, even though
845 * there should only be one :)
847 adr
= (unsigned long *)rptr
->re_he
.h_addr_list
;
851 * Lookup the name that we were given for the ip#
853 ar_reinfo
.re_na_look
++;
854 (void)strncpy(rptr
->re_name
, rptr
->re_he
.h_name
,
855 sizeof(rptr
->re_name
)-1);
856 rptr
->re_he
.h_name
= NULL
;
857 rptr
->re_retries
= _res
.retry
;
860 rptr
->re_he
.h_name
= NULL
;
861 ar_reinfo
.re_na_look
++;
862 (void)ar_query_name(rptr
->re_name
, C_IN
, T_A
, rptr
);
866 if (reip
&& rptr
->re_rinfo
.ri_ptr
&& size
)
867 bcopy(rptr
->re_rinfo
.ri_ptr
, reip
,
868 MIN(rptr
->re_rinfo
.ri_size
, size
));
870 * Clean up structure from previous usage.
874 ar_dump_hostent("ar_answer: previous usage", hp
);
878 (void)free(hp
->h_name
);
879 if (s
= hp
->h_aliases
)
883 (void)free(hp
->h_aliases
);
885 if (s
= hp
->h_addr_list
)
888 * Only free once since we allocated space for
889 * address in one big chunk.
892 (void)free(hp
->h_addr_list
);
894 bzero((char *)hp
, sizeof(*hp
));
897 * Setup and copy details for the structure we return a pointer to.
899 hp
->h_addrtype
= AF_INET
;
900 hp
->h_length
= sizeof(struct in_addr
);
901 if(rptr
->re_he
.h_name
)
903 hp
->h_name
= (char *)malloc(strlen(rptr
->re_he
.h_name
)+1);
907 fprintf(stderr
, "no memory for hostname\n");
912 (void)strcpy(hp
->h_name
, rptr
->re_he
.h_name
);
915 ar_dump_hostent("ar_answer: (snap) store name", hp
);
921 for (i
= 0, n
= 0; i
< MAXADDRS
; i
++, n
++)
922 if (!rptr
->re_he
.h_addr_list
[i
].s_addr
)
924 s
= hp
->h_addr_list
= (char **)malloc((n
+ 1) * sizeof(char *));
927 *s
= (char *)malloc(n
* sizeof(struct in_addr
));
931 fprintf(stderr
, "no memory for IP#'s (%d)\n", n
);
936 bcopy((char *)&rptr
->re_he
.h_addr_list
[0].s_addr
, *s
,
937 sizeof(struct in_addr
));
939 for (i
= 1; i
< n
; i
++, s
++)
941 *s
= hp
->h_addr
+ i
* sizeof(struct in_addr
);
942 bcopy((char *)&rptr
->re_he
.h_addr_list
[i
].s_addr
, *s
,
943 sizeof(struct in_addr
));
948 ar_dump_hostent("ar_answer: (snap) store IP#'s", hp
);
954 for (i
= 0, n
= 0; i
< MAXADDRS
; i
++, n
++)
955 if (!rptr
->re_he
.h_aliases
[i
])
957 s
= hp
->h_aliases
= (char **)malloc((n
+ 1) * sizeof(char *));
961 fprintf(stderr
, "no memory for aliases (%d)\n", n
);
966 for (i
= 0; i
< n
; i
++)
968 *s
++ = rptr
->re_he
.h_aliases
[i
];
969 rptr
->re_he
.h_aliases
[i
] = NULL
;
973 ar_dump_hostent("ar_answer: (snap) store CNAMEs", hp
);
974 ar_dump_hostent("ar_answer: new one", hp
);
978 (void)ar_remrequest(rptr
);
981 (void)ar_remrequest(rptr
);
987 if (reip
&& rptr
->re_rinfo
.ri_ptr
&& size
)
988 bcopy(rptr
->re_rinfo
.ri_ptr
, reip
,
989 MIN(rptr
->re_rinfo
.ri_size
, size
));
990 if ((h_errno
!= TRY_AGAIN
) &&
991 (_res
.options
& (RES_DNSRCH
|RES_DEFNAMES
) ==
992 (RES_DNSRCH
|RES_DEFNAMES
) ))
993 if (_res
.dnsrch
[rptr
->re_srch
])
995 rptr
->re_retries
= _res
.retry
;
998 (void)ar_resend_query(rptr
);
1008 void ar_dump_hostent(prefix
, hp
)
1016 fprintf(stderr
, "%s\n", prefix
);
1017 fprintf(stderr
, " hp %p\n", hp
);
1018 fprintf(stderr
, " h_name %p '%s'\n",
1019 hp
->h_name
, hp
->h_name
);
1020 if (s
= hp
->h_aliases
)
1022 fprintf(stderr
, " h_aliases %p\n",
1026 fprintf(stderr
, " element %p\n", *s
);
1030 if (s
= hp
->h_addr_list
)
1032 fprintf(stderr
, " h_addr_list %p\n",
1036 fprintf(stderr
, " element %p\n", *s
);
1045 void ar_dump_reslist(FILE* fp
)
1047 register struct reslist
*rptr
;
1051 for (rptr
= ar_first
; rptr
; rptr
= rptr
->re_next
)
1053 fprintf(fp
, "%4d [%p] %4d [%p]: %s\n", rptr
->re_id
, rptr
,
1054 *(rptr
->re_rinfo
.ri_ptr
), rptr
->re_rinfo
.ri_ptr
,