1059 lines
22 KiB
C
1059 lines
22 KiB
C
/* $NetBSD: arlib.c,v 1.2 2003/12/04 16:23:34 drochner Exp $ */
|
|
|
|
/*
|
|
* arlib.c (C)opyright 1993 Darren Reed. All rights reserved.
|
|
* This file may not be distributed without the author's permission in any
|
|
* shape or form. The author takes no responsibility for any damage or loss
|
|
* of property which results from the use of this software.
|
|
*/
|
|
#ifndef lint
|
|
static char sccsid[] = "@(#)arlib.c 1.9 6/5/93 (C)opyright 1992 Darren \
|
|
Reed. ASYNC DNS";
|
|
#endif
|
|
|
|
#include <stdio.h>
|
|
#include <fcntl.h>
|
|
#include <signal.h>
|
|
#include <sys/types.h>
|
|
#include <sys/time.h>
|
|
#include <sys/socket.h>
|
|
#include <netinet/in.h>
|
|
#include "netdb.h"
|
|
#include "arpa/nameser.h"
|
|
#include <resolv.h>
|
|
#include "arlib.h"
|
|
#include "arplib.h"
|
|
|
|
extern int errno, h_errno;
|
|
static char ar_hostbuf[65], ar_domainname[65];
|
|
static char ar_dot[] = ".";
|
|
static int ar_resfd = -1, ar_vc = 0;
|
|
static struct reslist *ar_last, *ar_first;
|
|
|
|
/*
|
|
* Statistics structure.
|
|
*/
|
|
static struct resstats {
|
|
int re_errors;
|
|
int re_nu_look;
|
|
int re_na_look;
|
|
int re_replies;
|
|
int re_requests;
|
|
int re_resends;
|
|
int re_sent;
|
|
int re_timeouts;
|
|
} ar_reinfo;
|
|
|
|
static int do_query_name(/* struct resinfo *, char *, struct reslist * */);
|
|
static int do_query_number(/* struct resinfo *, char *, struct reslist * */);
|
|
static int ar_resend_query(/* struct reslist * */);
|
|
|
|
/*
|
|
* ar_init
|
|
*
|
|
* Initializes the various ARLIB internal varilables and related DNS
|
|
* options for res_init().
|
|
*
|
|
* Returns 0 or the socket opened for use with talking to name servers
|
|
* if 0 is passed or ARES_INITSOCK is set.
|
|
*/
|
|
int ar_init(op)
|
|
int op;
|
|
{
|
|
int ret = 0;
|
|
|
|
if (op & ARES_INITLIST)
|
|
{
|
|
bzero(&ar_reinfo, sizeof(ar_reinfo));
|
|
ar_first = ar_last = NULL;
|
|
}
|
|
|
|
if (op & ARES_CALLINIT && !(_res.options & RES_INIT))
|
|
{
|
|
ret = res_init();
|
|
(void)strcpy(ar_domainname, ar_dot);
|
|
(void)strncat(ar_domainname, _res.defdname,
|
|
sizeof(ar_domainname)-2);
|
|
}
|
|
|
|
if (op & ARES_INITSOCK)
|
|
ret = ar_resfd = ar_open();
|
|
|
|
if (op & ARES_INITDEBG)
|
|
_res.options |= RES_DEBUG;
|
|
|
|
if (op == 0)
|
|
ret = ar_resfd;
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
/*
|
|
* ar_open
|
|
*
|
|
* Open a socket to talk to a name server with.
|
|
* Check _res.options to see if we use a TCP or UDP socket.
|
|
*/
|
|
int ar_open()
|
|
{
|
|
if (ar_resfd == -1)
|
|
{
|
|
if (_res.options & RES_USEVC)
|
|
{
|
|
struct sockaddr_in *sip;
|
|
int i;
|
|
|
|
sip = _res.NS_ADDR_LIST; /* was _res.nsaddr_list */
|
|
ar_vc = 1;
|
|
ar_resfd = socket(AF_INET, SOCK_STREAM, 0);
|
|
|
|
/*
|
|
* Try each name server listed in sequence until we
|
|
* succeed or run out.
|
|
*/
|
|
while (connect(ar_resfd, (struct sockaddr *)sip++,
|
|
sizeof(struct sockaddr)))
|
|
{
|
|
(void)close(ar_resfd);
|
|
ar_resfd = -1;
|
|
if (i >= _res.nscount)
|
|
break;
|
|
ar_resfd = socket(AF_INET, SOCK_STREAM, 0);
|
|
}
|
|
}
|
|
else
|
|
ar_resfd = socket(AF_INET, SOCK_DGRAM, 0);
|
|
}
|
|
if (ar_resfd >= 0)
|
|
{ /* Need one of these two here - and it MUST work!! */
|
|
int flags;
|
|
|
|
if ((flags = fcntl(ar_resfd, F_GETFL, 0)) != -1)
|
|
#ifdef O_NONBLOCK
|
|
if (fcntl(ar_resfd, F_SETFL, flags|O_NONBLOCK) == -1)
|
|
#else
|
|
# ifdef O_NDELAY
|
|
if (fcntl(ar_resfd, F_SETFL, flags|O_NDELAY) == -1)
|
|
# else
|
|
# ifdef FNDELAY
|
|
if (fcntl(ar_resfd, F_SETFL, flags|FNDELAY) == -1)
|
|
# endif
|
|
# endif
|
|
#endif
|
|
{
|
|
(void)close(ar_resfd);
|
|
ar_resfd = -1;
|
|
}
|
|
}
|
|
return ar_resfd;
|
|
}
|
|
|
|
|
|
/*
|
|
* ar_close
|
|
*
|
|
* Closes and flags the ARLIB socket as closed.
|
|
*/
|
|
void ar_close()
|
|
{
|
|
(void)close(ar_resfd);
|
|
ar_resfd = -1;
|
|
return;
|
|
}
|
|
|
|
|
|
/*
|
|
* ar_add_request
|
|
*
|
|
* Add a new DNS query to the end of the query list.
|
|
*/
|
|
static int ar_add_request(new)
|
|
struct reslist *new;
|
|
{
|
|
if (!new)
|
|
return -1;
|
|
if (!ar_first)
|
|
ar_first = ar_last = new;
|
|
else {
|
|
ar_last->re_next = new;
|
|
ar_last = new;
|
|
}
|
|
new->re_next = NULL;
|
|
ar_reinfo.re_requests++;
|
|
return 0;
|
|
}
|
|
|
|
|
|
/*
|
|
* ar_remrequest
|
|
*
|
|
* Remove a request from the list. This must also free any memory that has
|
|
* been allocated for temporary storage of DNS results.
|
|
*
|
|
* Returns -1 if there are anyy problems removing the requested structure
|
|
* or 0 if the remove is successful.
|
|
*/
|
|
static int ar_remrequest(old)
|
|
struct reslist *old;
|
|
{
|
|
register struct reslist *rptr, *r2ptr;
|
|
register char **s;
|
|
|
|
if (!old)
|
|
return -1;
|
|
for (rptr = ar_first, r2ptr = NULL; rptr; rptr = rptr->re_next)
|
|
{
|
|
if (rptr == old)
|
|
break;
|
|
r2ptr = rptr;
|
|
}
|
|
|
|
if (!rptr)
|
|
return -1;
|
|
if (rptr == ar_first)
|
|
ar_first = ar_first->re_next;
|
|
else if (rptr == ar_last)
|
|
{
|
|
if (ar_last = r2ptr)
|
|
ar_last->re_next = NULL;
|
|
}
|
|
else
|
|
r2ptr->re_next = rptr->re_next;
|
|
|
|
if (!ar_first)
|
|
ar_last = ar_first;
|
|
|
|
#ifdef ARLIB_DEBUG
|
|
ar_dump_hostent("ar_remrequest:", rptr->re_he);
|
|
#endif
|
|
|
|
if (rptr->re_he.h_name)
|
|
(void)free(rptr->re_he.h_name);
|
|
if (s = rptr->re_he.h_aliases)
|
|
for (; *s; s++)
|
|
(void)free(*s);
|
|
if (rptr->re_rinfo.ri_ptr)
|
|
(void)free(rptr->re_rinfo.ri_ptr);
|
|
(void)free(rptr);
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
/*
|
|
* ar_make_request
|
|
*
|
|
* Create a DNS query recorded for the request being made and place it on the
|
|
* current list awaiting replies. Initialization of the record with set
|
|
* values should also be done.
|
|
*/
|
|
static struct reslist *ar_make_request(resi)
|
|
register struct resinfo *resi;
|
|
{
|
|
register struct reslist *rptr;
|
|
register struct resinfo *rp;
|
|
|
|
rptr = (struct reslist *)calloc(1, sizeof(struct reslist));
|
|
rp = &rptr->re_rinfo;
|
|
|
|
rptr->re_next = NULL; /* where NULL is non-zero ;) */
|
|
rptr->re_sentat = time(NULL);
|
|
rptr->re_retries = _res.retry;
|
|
rptr->re_sends = 1;
|
|
rptr->re_resend = 1;
|
|
rptr->re_timeout = rptr->re_sentat + _res.retrans;
|
|
rptr->re_he.h_name = NULL;
|
|
rptr->re_he.h_addrtype = AF_INET;
|
|
rptr->re_he.h_aliases[0] = NULL;
|
|
rp->ri_ptr = resi->ri_ptr;
|
|
rp->ri_size = resi->ri_size;
|
|
|
|
(void)ar_add_request(rptr);
|
|
|
|
return rptr;
|
|
}
|
|
|
|
|
|
/*
|
|
* ar_timeout
|
|
*
|
|
* Remove queries from the list which have been there too long without
|
|
* being resolved.
|
|
*/
|
|
long ar_timeout(now, info, size)
|
|
time_t now;
|
|
char *info;
|
|
int size;
|
|
{
|
|
register struct reslist *rptr, *r2ptr;
|
|
register long next = 0;
|
|
|
|
for (rptr = ar_first, r2ptr = NULL; rptr; rptr = r2ptr)
|
|
{
|
|
r2ptr = rptr->re_next;
|
|
if (now >= rptr->re_timeout)
|
|
{
|
|
/*
|
|
* If the timeout for the query has been exceeded,
|
|
* then resend the query if we still have some
|
|
* 'retry credit' and reset the timeout. If we have
|
|
* used it all up, then remove the request.
|
|
*/
|
|
if (--rptr->re_retries <= 0)
|
|
{
|
|
ar_reinfo.re_timeouts++;
|
|
if (info && rptr->re_rinfo.ri_ptr)
|
|
bcopy(rptr->re_rinfo.ri_ptr, info,
|
|
MIN(rptr->re_rinfo.ri_size,
|
|
size));
|
|
(void)ar_remrequest(rptr);
|
|
return now;
|
|
}
|
|
else
|
|
{
|
|
rptr->re_sends++;
|
|
rptr->re_sentat = now;
|
|
rptr->re_timeout = now + _res.retrans;
|
|
(void)ar_resend_query(rptr);
|
|
}
|
|
}
|
|
if (!next || rptr->re_timeout < next)
|
|
next = rptr->re_timeout;
|
|
}
|
|
return next;
|
|
}
|
|
|
|
|
|
/*
|
|
* ar_send_res_msg
|
|
*
|
|
* When sending queries to nameservers listed in the resolv.conf file,
|
|
* don't send a query to every one, but increase the number sent linearly
|
|
* to match the number of resends. This increase only occurs if there are
|
|
* multiple nameserver entries in the resolv.conf file.
|
|
* The return value is the number of messages successfully sent to
|
|
* nameservers or -1 if no successful sends.
|
|
*/
|
|
static int ar_send_res_msg(msg, len, rcount)
|
|
char *msg;
|
|
int len, rcount;
|
|
{
|
|
register int i;
|
|
int sent = 0;
|
|
|
|
if (!msg)
|
|
return -1;
|
|
|
|
rcount = (_res.nscount > rcount) ? rcount : _res.nscount;
|
|
if (_res.options & RES_PRIMARY)
|
|
rcount = 1;
|
|
|
|
if (ar_vc)
|
|
{
|
|
ar_reinfo.re_sent++;
|
|
sent++;
|
|
if (write(ar_resfd, msg, len) == -1)
|
|
{
|
|
int errtmp = errno;
|
|
(void)close(ar_resfd);
|
|
errno = errtmp;
|
|
ar_resfd = -1;
|
|
}
|
|
}
|
|
else
|
|
for (i = 0; i < rcount; i++)
|
|
{
|
|
if (sendto(ar_resfd, msg, len, 0,
|
|
(struct sockaddr *)&(_res.NS_ADDR_LIST[i]),
|
|
sizeof(struct sockaddr_in)) == len)
|
|
{
|
|
ar_reinfo.re_sent++;
|
|
sent++;
|
|
}
|
|
}
|
|
return (sent) ? sent : -1;
|
|
}
|
|
|
|
|
|
/*
|
|
* ar_find_id
|
|
*
|
|
* find a dns query record by the id (id is determined by dn_mkquery)
|
|
*/
|
|
static struct reslist *ar_find_id(id)
|
|
int id;
|
|
{
|
|
register struct reslist *rptr;
|
|
|
|
for (rptr = ar_first; rptr; rptr = rptr->re_next)
|
|
if (rptr->re_id == id)
|
|
return rptr;
|
|
return NULL;
|
|
}
|
|
|
|
|
|
/*
|
|
* ar_delete
|
|
*
|
|
* Delete a request from the waiting list if it has a data pointer which
|
|
* matches the one passed.
|
|
*/
|
|
int ar_delete(ptr, size)
|
|
char *ptr;
|
|
int size;
|
|
{
|
|
register struct reslist *rptr;
|
|
register struct reslist *r2ptr;
|
|
int removed = 0;
|
|
|
|
for (rptr = ar_first; rptr; rptr = r2ptr)
|
|
{
|
|
r2ptr = rptr->re_next;
|
|
if (rptr->re_rinfo.ri_ptr && ptr && size &&
|
|
bcmp(rptr->re_rinfo.ri_ptr, ptr, size) == 0)
|
|
{
|
|
(void)ar_remrequest(rptr);
|
|
removed++;
|
|
}
|
|
}
|
|
return removed;
|
|
}
|
|
|
|
|
|
/*
|
|
* ar_query_name
|
|
*
|
|
* generate a query based on class, type and name.
|
|
*/
|
|
static int ar_query_name(name, class, type, rptr)
|
|
char *name;
|
|
int class, type;
|
|
struct reslist *rptr;
|
|
{
|
|
static char buf[MAXPACKET];
|
|
int r,s,a;
|
|
HEADER *hptr;
|
|
|
|
bzero(buf, sizeof(buf));
|
|
r = res_mkquery(QUERY, name, class, type, NULL, 0, NULL,
|
|
buf, sizeof(buf));
|
|
if (r <= 0)
|
|
{
|
|
h_errno = NO_RECOVERY;
|
|
return r;
|
|
}
|
|
hptr = (HEADER *)buf;
|
|
rptr->re_id = ntohs(hptr->id);
|
|
|
|
s = ar_send_res_msg(buf, r, rptr->re_sends);
|
|
|
|
if (s == -1)
|
|
{
|
|
h_errno = TRY_AGAIN;
|
|
return -1;
|
|
}
|
|
else
|
|
rptr->re_sent += s;
|
|
return 0;
|
|
}
|
|
|
|
|
|
/*
|
|
* ar_gethostbyname
|
|
*
|
|
* Replacement library function call to gethostbyname(). This one, however,
|
|
* doesn't return the record being looked up but just places the query in the
|
|
* queue to await answers.
|
|
*/
|
|
int ar_gethostbyname(name, info, size)
|
|
char *name;
|
|
char *info;
|
|
int size;
|
|
{
|
|
char host[65];
|
|
struct resinfo resi;
|
|
register struct resinfo *rp = &resi;
|
|
|
|
if (size && info)
|
|
{
|
|
rp->ri_ptr = (char *)malloc(size);
|
|
bcopy(info, rp->ri_ptr, size);
|
|
rp->ri_size = size;
|
|
}
|
|
else
|
|
bzero((char *)rp, sizeof(resi));
|
|
ar_reinfo.re_na_look++;
|
|
(void)strncpy(host, name, 64);
|
|
host[64] = '\0';
|
|
|
|
return (do_query_name(rp, host, NULL));
|
|
}
|
|
|
|
|
|
static int do_query_name(resi, name, rptr)
|
|
struct resinfo *resi;
|
|
char *name;
|
|
register struct reslist *rptr;
|
|
{
|
|
char hname[65];
|
|
int len;
|
|
|
|
len = strlen((char *)strncpy(hname, name, sizeof(hname)-1));
|
|
|
|
if (rptr && (hname[len-1] != '.'))
|
|
{
|
|
(void)strncat(hname, ar_dot, sizeof(hname)-len-1);
|
|
/*
|
|
* NOTE: The logical relationship between DNSRCH and DEFNAMES
|
|
* is implies. ie no DEFNAES, no DNSRCH.
|
|
*/
|
|
if (_res.options & (RES_DEFNAMES|RES_DNSRCH) ==
|
|
(RES_DEFNAMES|RES_DNSRCH))
|
|
{
|
|
if (_res.dnsrch[rptr->re_srch])
|
|
(void)strncat(hname, _res.dnsrch[rptr->re_srch],
|
|
sizeof(hname) - ++len -1);
|
|
}
|
|
else if (_res.options & RES_DEFNAMES)
|
|
(void)strncat(hname, ar_domainname, sizeof(hname) - len -1);
|
|
}
|
|
|
|
/*
|
|
* Store the name passed as the one to lookup and generate other host
|
|
* names to pass onto the nameserver(s) for lookups.
|
|
*/
|
|
if (!rptr)
|
|
{
|
|
rptr = ar_make_request(resi);
|
|
rptr->re_type = T_A;
|
|
(void)strncpy(rptr->re_name, name, sizeof(rptr->re_name)-1);
|
|
}
|
|
return (ar_query_name(hname, C_IN, T_A, rptr));
|
|
}
|
|
|
|
|
|
/*
|
|
* ar_gethostbyaddr
|
|
*
|
|
* Generates a query for a given IP address.
|
|
*/
|
|
int ar_gethostbyaddr(addr, info, size)
|
|
char *addr;
|
|
char *info;
|
|
int size;
|
|
{
|
|
struct resinfo resi;
|
|
register struct resinfo *rp = &resi;
|
|
|
|
if (size && info)
|
|
{
|
|
rp->ri_ptr = (char *)malloc(size);
|
|
bcopy(info, rp->ri_ptr, size);
|
|
rp->ri_size = size;
|
|
}
|
|
else
|
|
bzero((char *)rp, sizeof(resi));
|
|
ar_reinfo.re_nu_look++;
|
|
return (do_query_number(rp, addr, NULL));
|
|
}
|
|
|
|
|
|
/*
|
|
* do_query_number
|
|
*
|
|
* Use this to do reverse IP# lookups.
|
|
*/
|
|
static int do_query_number(resi, numb, rptr)
|
|
struct resinfo *resi;
|
|
char *numb;
|
|
register struct reslist *rptr;
|
|
{
|
|
register unsigned char *cp;
|
|
static char ipbuf[32];
|
|
|
|
/*
|
|
* Generate name in the "in-addr.arpa" domain. No addings bits to this
|
|
* name to get more names to query!.
|
|
*/
|
|
cp = (unsigned char *)numb;
|
|
(void)sprintf(ipbuf,"%u.%u.%u.%u.in-addr.arpa.",
|
|
(unsigned int)(cp[3]), (unsigned int)(cp[2]),
|
|
(unsigned int)(cp[1]), (unsigned int)(cp[0]));
|
|
|
|
if (!rptr)
|
|
{
|
|
rptr = ar_make_request(resi);
|
|
rptr->re_type = T_PTR;
|
|
rptr->re_he.h_length = sizeof(struct in_addr);
|
|
bcopy(numb, (char *)&rptr->re_addr, rptr->re_he.h_length);
|
|
bcopy(numb, (char *)&rptr->re_he.h_addr_list[0].s_addr,
|
|
rptr->re_he.h_length);
|
|
}
|
|
return (ar_query_name(ipbuf, C_IN, T_PTR, rptr));
|
|
}
|
|
|
|
|
|
/*
|
|
* ar_resent_query
|
|
*
|
|
* resends a query.
|
|
*/
|
|
static int ar_resend_query(rptr)
|
|
struct reslist *rptr;
|
|
{
|
|
if (!rptr->re_resend)
|
|
return -1;
|
|
|
|
switch(rptr->re_type)
|
|
{
|
|
case T_PTR:
|
|
ar_reinfo.re_resends++;
|
|
return do_query_number(NULL, &rptr->re_addr, rptr);
|
|
case T_A:
|
|
ar_reinfo.re_resends++;
|
|
return do_query_name(NULL, rptr->re_name, rptr);
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
|
|
/*
|
|
* ar_procanswer
|
|
*
|
|
* process an answer received from a nameserver.
|
|
*/
|
|
static int ar_procanswer(rptr, hptr, buf, eob)
|
|
struct reslist *rptr;
|
|
char *buf, *eob;
|
|
HEADER *hptr;
|
|
{
|
|
char *cp, **alias, *s;
|
|
int class, type, dlen, len, ans = 0, n, i;
|
|
u_int32_t ttl, dr, *adr;
|
|
struct hent *hp;
|
|
|
|
cp = buf + sizeof(HEADER);
|
|
adr = (u_int32_t *)rptr->re_he.h_addr_list;
|
|
|
|
while (*adr)
|
|
adr++;
|
|
|
|
alias = rptr->re_he.h_aliases;
|
|
while (*alias)
|
|
alias++;
|
|
|
|
hp = &rptr->re_he;
|
|
|
|
|
|
/*
|
|
* Skip over the original question.
|
|
*/
|
|
while (hptr->qdcount-- > 0)
|
|
cp += dn_skipname(cp, eob) + QFIXEDSZ;
|
|
/*
|
|
* proccess each answer sent to us. blech.
|
|
*/
|
|
while (hptr->ancount-- > 0 && cp < eob) {
|
|
n = dn_expand(buf, eob, cp, ar_hostbuf, sizeof(ar_hostbuf));
|
|
cp += n;
|
|
if (n <= 0)
|
|
return ans;
|
|
|
|
ans++;
|
|
/*
|
|
* 'skip' past the general dns crap (ttl, class, etc) to get
|
|
* the pointer to the right spot. Some of thse are actually
|
|
* useful so its not a good idea to skip past in one big jump.
|
|
*/
|
|
type = (int)_getshort(cp);
|
|
cp += sizeof(short);
|
|
class = (int)_getshort(cp);
|
|
cp += sizeof(short);
|
|
ttl = (u_int32_t)_getlong(cp);
|
|
cp += sizeof(u_int32_t);
|
|
dlen = (int)_getshort(cp);
|
|
cp += sizeof(short);
|
|
rptr->re_type = type;
|
|
|
|
switch(type)
|
|
{
|
|
case T_A :
|
|
rptr->re_he.h_length = dlen;
|
|
if (ans == 1)
|
|
rptr->re_he.h_addrtype=(class == C_IN) ?
|
|
AF_INET : AF_UNSPEC;
|
|
if (dlen != sizeof(dr))
|
|
{
|
|
h_errno = TRY_AGAIN;
|
|
continue;
|
|
}
|
|
bcopy(cp, &dr, dlen);
|
|
*adr++ = dr;
|
|
*adr = 0;
|
|
cp += dlen;
|
|
len = strlen(ar_hostbuf);
|
|
if (!rptr->re_he.h_name)
|
|
{
|
|
rptr->re_he.h_name = (char *)malloc(len+1);
|
|
if (!rptr->re_he.h_name)
|
|
break;
|
|
(void)strcpy(rptr->re_he.h_name, ar_hostbuf);
|
|
}
|
|
break;
|
|
case T_PTR :
|
|
if ((n = dn_expand(buf, eob, cp, ar_hostbuf,
|
|
sizeof(ar_hostbuf) )) < 0)
|
|
{
|
|
cp += n;
|
|
continue;
|
|
}
|
|
cp += n;
|
|
len = strlen(ar_hostbuf)+1;
|
|
/*
|
|
* copy the returned hostname into the host name
|
|
* or alias field if there is a known hostname
|
|
* already.
|
|
*/
|
|
if (!rptr->re_he.h_name)
|
|
{
|
|
rptr->re_he.h_name = (char *)malloc(len);
|
|
if (!rptr->re_he.h_name)
|
|
break;
|
|
(void)strcpy(rptr->re_he.h_name, ar_hostbuf);
|
|
}
|
|
else
|
|
{
|
|
*alias = (char *)malloc(len);
|
|
if (!*alias)
|
|
return -1;
|
|
(void)strcpy(*alias++, ar_hostbuf);
|
|
*alias = NULL;
|
|
}
|
|
break;
|
|
case T_CNAME :
|
|
cp += dlen;
|
|
if (alias >= &(rptr->re_he.h_aliases[MAXALIASES-1]))
|
|
continue;
|
|
n = strlen(ar_hostbuf)+1;
|
|
*alias = (char *)malloc(n);
|
|
if (!*alias)
|
|
return -1;
|
|
(void)strcpy(*alias++, ar_hostbuf);
|
|
*alias = NULL;
|
|
break;
|
|
default :
|
|
break;
|
|
}
|
|
}
|
|
|
|
return ans;
|
|
}
|
|
|
|
|
|
/*
|
|
* ar_answer
|
|
*
|
|
* Get an answer from a DNS server and process it. If a query is found to
|
|
* which no answer has been given to yet, copy its 'info' structure back
|
|
* to where "reip" points and return a pointer to the hostent structure.
|
|
*/
|
|
struct hostent *ar_answer(reip, size)
|
|
char *reip;
|
|
int size;
|
|
{
|
|
static char ar_rcvbuf[sizeof(HEADER) + MAXPACKET];
|
|
static struct hostent ar_host;
|
|
|
|
register HEADER *hptr;
|
|
register struct reslist *rptr = NULL;
|
|
register struct hostent *hp;
|
|
register char **s;
|
|
unsigned long *adr;
|
|
int rc, i, n, a;
|
|
|
|
rc = recv(ar_resfd, ar_rcvbuf, sizeof(ar_rcvbuf), 0);
|
|
if (rc <= 0)
|
|
goto getres_err;
|
|
|
|
ar_reinfo.re_replies++;
|
|
hptr = (HEADER *)ar_rcvbuf;
|
|
/*
|
|
* convert things to be in the right order.
|
|
*/
|
|
hptr->id = ntohs(hptr->id);
|
|
hptr->ancount = ntohs(hptr->ancount);
|
|
hptr->arcount = ntohs(hptr->arcount);
|
|
hptr->nscount = ntohs(hptr->nscount);
|
|
hptr->qdcount = ntohs(hptr->qdcount);
|
|
/*
|
|
* response for an id which we have already received an answer for
|
|
* just ignore this response.
|
|
*/
|
|
rptr = ar_find_id(hptr->id);
|
|
if (!rptr)
|
|
goto getres_err;
|
|
|
|
if ((hptr->rcode != NOERROR) || (hptr->ancount == 0))
|
|
{
|
|
switch (hptr->rcode)
|
|
{
|
|
case NXDOMAIN:
|
|
h_errno = HOST_NOT_FOUND;
|
|
break;
|
|
case SERVFAIL:
|
|
h_errno = TRY_AGAIN;
|
|
break;
|
|
case NOERROR:
|
|
h_errno = NO_DATA;
|
|
break;
|
|
case FORMERR:
|
|
case NOTIMP:
|
|
case REFUSED:
|
|
default:
|
|
h_errno = NO_RECOVERY;
|
|
break;
|
|
}
|
|
ar_reinfo.re_errors++;
|
|
/*
|
|
** If a bad error was returned, we stop here and dont send
|
|
** send any more (no retries granted).
|
|
*/
|
|
if (h_errno != TRY_AGAIN)
|
|
{
|
|
rptr->re_resend = 0;
|
|
rptr->re_retries = 0;
|
|
}
|
|
goto getres_err;
|
|
}
|
|
|
|
a = ar_procanswer(rptr, hptr, ar_rcvbuf, ar_rcvbuf+rc);
|
|
|
|
if ((rptr->re_type == T_PTR) && (_res.options & RES_CHECKPTR))
|
|
{
|
|
/*
|
|
* For reverse lookups on IP#'s, lookup the name that is given
|
|
* for the ip# and return with that as the official result.
|
|
* -avalon
|
|
*/
|
|
rptr->re_type = T_A;
|
|
/*
|
|
* Clean out the list of addresses already set, even though
|
|
* there should only be one :)
|
|
*/
|
|
adr = (unsigned long *)rptr->re_he.h_addr_list;
|
|
while (*adr)
|
|
*adr++ = 0L;
|
|
/*
|
|
* Lookup the name that we were given for the ip#
|
|
*/
|
|
ar_reinfo.re_na_look++;
|
|
(void)strncpy(rptr->re_name, rptr->re_he.h_name,
|
|
sizeof(rptr->re_name)-1);
|
|
rptr->re_he.h_name = NULL;
|
|
rptr->re_retries = _res.retry;
|
|
rptr->re_sends = 1;
|
|
rptr->re_resend = 1;
|
|
rptr->re_he.h_name = NULL;
|
|
ar_reinfo.re_na_look++;
|
|
(void)ar_query_name(rptr->re_name, C_IN, T_A, rptr);
|
|
return NULL;
|
|
}
|
|
|
|
if (reip && rptr->re_rinfo.ri_ptr && size)
|
|
bcopy(rptr->re_rinfo.ri_ptr, reip,
|
|
MIN(rptr->re_rinfo.ri_size, size));
|
|
/*
|
|
* Clean up structure from previous usage.
|
|
*/
|
|
hp = &ar_host;
|
|
#ifdef ARLIB_DEBUG
|
|
ar_dump_hostent("ar_answer: previous usage", hp);
|
|
#endif
|
|
|
|
if (hp->h_name)
|
|
(void)free(hp->h_name);
|
|
if (s = hp->h_aliases)
|
|
{
|
|
while (*s)
|
|
(void)free(*s++);
|
|
(void)free(hp->h_aliases);
|
|
}
|
|
if (s = hp->h_addr_list)
|
|
{
|
|
/*
|
|
* Only free once since we allocated space for
|
|
* address in one big chunk.
|
|
*/
|
|
(void)free(*s);
|
|
(void)free(hp->h_addr_list);
|
|
}
|
|
bzero((char *)hp, sizeof(*hp));
|
|
|
|
/*
|
|
* Setup and copy details for the structure we return a pointer to.
|
|
*/
|
|
hp->h_addrtype = AF_INET;
|
|
hp->h_length = sizeof(struct in_addr);
|
|
if(rptr->re_he.h_name)
|
|
{
|
|
hp->h_name = (char *)malloc(strlen(rptr->re_he.h_name)+1);
|
|
if(!hp->h_name)
|
|
{
|
|
#ifdef ARLIB_DEBUG
|
|
fprintf(stderr, "no memory for hostname\n");
|
|
#endif
|
|
h_errno = TRY_AGAIN;
|
|
goto getres_err;
|
|
}
|
|
(void)strcpy(hp->h_name, rptr->re_he.h_name);
|
|
}
|
|
#ifdef ARLIB_DEBUG
|
|
ar_dump_hostent("ar_answer: (snap) store name", hp);
|
|
#endif
|
|
|
|
/*
|
|
* Count IP#'s.
|
|
*/
|
|
for (i = 0, n = 0; i < MAXADDRS; i++, n++)
|
|
if (!rptr->re_he.h_addr_list[i].s_addr)
|
|
break;
|
|
s = hp->h_addr_list = (char **)malloc((n + 1) * sizeof(char *));
|
|
if (n)
|
|
{
|
|
*s = (char *)malloc(n * sizeof(struct in_addr));
|
|
if(!*s)
|
|
{
|
|
#ifdef ARLIB_DEBUG
|
|
fprintf(stderr, "no memory for IP#'s (%d)\n", n);
|
|
#endif
|
|
h_errno = TRY_AGAIN;
|
|
goto getres_err;
|
|
}
|
|
bcopy((char *)&rptr->re_he.h_addr_list[0].s_addr, *s,
|
|
sizeof(struct in_addr));
|
|
s++;
|
|
for (i = 1; i < n; i++, s++)
|
|
{
|
|
*s = hp->h_addr + i * sizeof(struct in_addr);
|
|
bcopy((char *)&rptr->re_he.h_addr_list[i].s_addr, *s,
|
|
sizeof(struct in_addr));
|
|
}
|
|
}
|
|
*s = NULL;
|
|
#ifdef ARLIB_DEBUG
|
|
ar_dump_hostent("ar_answer: (snap) store IP#'s", hp);
|
|
#endif
|
|
|
|
/*
|
|
* Count CNAMEs
|
|
*/
|
|
for (i = 0, n = 0; i < MAXADDRS; i++, n++)
|
|
if (!rptr->re_he.h_aliases[i])
|
|
break;
|
|
s = hp->h_aliases = (char **)malloc((n + 1) * sizeof(char *));
|
|
if (!s)
|
|
{
|
|
#ifdef ARLIB_DEBUG
|
|
fprintf(stderr, "no memory for aliases (%d)\n", n);
|
|
#endif
|
|
h_errno = TRY_AGAIN;
|
|
goto getres_err;
|
|
}
|
|
for (i = 0; i < n; i++)
|
|
{
|
|
*s++ = rptr->re_he.h_aliases[i];
|
|
rptr->re_he.h_aliases[i] = NULL;
|
|
}
|
|
*s = NULL;
|
|
#ifdef ARLIB_DEBUG
|
|
ar_dump_hostent("ar_answer: (snap) store CNAMEs", hp);
|
|
ar_dump_hostent("ar_answer: new one", hp);
|
|
#endif
|
|
|
|
if (a > 0)
|
|
(void)ar_remrequest(rptr);
|
|
else
|
|
if (!rptr->re_sent)
|
|
(void)ar_remrequest(rptr);
|
|
return hp;
|
|
|
|
getres_err:
|
|
if (rptr)
|
|
{
|
|
if (reip && rptr->re_rinfo.ri_ptr && size)
|
|
bcopy(rptr->re_rinfo.ri_ptr, reip,
|
|
MIN(rptr->re_rinfo.ri_size, size));
|
|
if ((h_errno != TRY_AGAIN) &&
|
|
(_res.options & (RES_DNSRCH|RES_DEFNAMES) ==
|
|
(RES_DNSRCH|RES_DEFNAMES) ))
|
|
if (_res.dnsrch[rptr->re_srch])
|
|
{
|
|
rptr->re_retries = _res.retry;
|
|
rptr->re_sends = 1;
|
|
rptr->re_resend = 1;
|
|
(void)ar_resend_query(rptr);
|
|
rptr->re_srch++;
|
|
}
|
|
return NULL;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
|
|
#ifdef ARLIB_DEBUG
|
|
void ar_dump_hostent(prefix, hp)
|
|
char *prefix;
|
|
struct hostent *hp;
|
|
{
|
|
register char **s;
|
|
|
|
fflush(stdout);
|
|
|
|
fprintf(stderr, "%s\n", prefix);
|
|
fprintf(stderr, " hp %p\n", hp);
|
|
fprintf(stderr, " h_name %p '%s'\n",
|
|
hp->h_name, hp->h_name);
|
|
if (s = hp->h_aliases)
|
|
{
|
|
fprintf(stderr, " h_aliases %p\n",
|
|
hp->h_aliases);
|
|
while (*s)
|
|
{
|
|
fprintf(stderr, " element %p\n", *s);
|
|
s++;
|
|
}
|
|
}
|
|
if (s = hp->h_addr_list)
|
|
{
|
|
fprintf(stderr, " h_addr_list %p\n",
|
|
hp->h_addr_list);
|
|
while (*s)
|
|
{
|
|
fprintf(stderr, " element %p\n", *s);
|
|
s++;
|
|
}
|
|
}
|
|
|
|
fflush(stderr);
|
|
}
|
|
|
|
|
|
void ar_dump_reslist(FILE* fp)
|
|
{
|
|
register struct reslist *rptr;
|
|
int c;
|
|
|
|
c = 0;
|
|
for (rptr = ar_first; rptr; rptr = rptr->re_next)
|
|
{
|
|
fprintf(fp, "%4d [%p] %4d [%p]: %s\n", rptr->re_id, rptr,
|
|
*(rptr->re_rinfo.ri_ptr), rptr->re_rinfo.ri_ptr,
|
|
rptr->re_name);
|
|
}
|
|
}
|
|
#endif
|