1132451Sroberto/*
2132451Sroberto * arlib.c (C)opyright 1993 Darren Reed. All rights reserved.
3132451Sroberto * This file may not be distributed without the author's permission in any
4132451Sroberto * shape or form. The author takes no responsibility for any damage or loss
5132451Sroberto * of property which results from the use of this software.
6132451Sroberto */
7132451Sroberto#ifndef lint
8132451Srobertostatic	char	sccsid[] = "@(#)arlib.c	1.9 6/5/93 (C)opyright 1992 Darren \
9132451SrobertoReed. ASYNC DNS";
10132451Sroberto#endif
11132451Sroberto
12132451Sroberto#include <stdio.h>
13132451Sroberto#include <fcntl.h>
14132451Sroberto#include <signal.h>
15132451Sroberto#include <sys/types.h>
16132451Sroberto#include <sys/time.h>
17132451Sroberto#include <sys/socket.h>
18132451Sroberto#include <netinet/in.h>
19132451Sroberto#include "netdb.h"
20132451Sroberto#include "arpa/nameser.h"
21132451Sroberto#include <resolv.h>
22132451Sroberto#include "arlib.h"
23132451Sroberto#include "arplib.h"
24132451Sroberto
25132451Srobertoextern	int	errno, h_errno;
26132451Srobertostatic	char	ar_hostbuf[65], ar_domainname[65];
27132451Srobertostatic	char	ar_dot[] = ".";
28132451Srobertostatic	int	ar_resfd = -1, ar_vc = 0;
29132451Srobertostatic	struct	reslist	*ar_last, *ar_first;
30132451Sroberto
31132451Sroberto/*
32132451Sroberto * Statistics structure.
33132451Sroberto */
34132451Srobertostatic	struct	resstats {
35132451Sroberto	int	re_errors;
36132451Sroberto	int	re_nu_look;
37132451Sroberto	int	re_na_look;
38132451Sroberto	int	re_replies;
39132451Sroberto	int	re_requests;
40132451Sroberto	int	re_resends;
41132451Sroberto	int	re_sent;
42132451Sroberto	int	re_timeouts;
43132451Sroberto} ar_reinfo;
44132451Sroberto
45132451Srobertostatic int do_query_name(/* struct resinfo *, char *, struct reslist * */);
46132451Srobertostatic int do_query_number(/* struct resinfo *, char *, struct reslist * */);
47132451Srobertostatic int ar_resend_query(/* struct reslist * */);
48132451Sroberto
49132451Sroberto/*
50132451Sroberto * ar_init
51132451Sroberto *
52132451Sroberto * Initializes the various ARLIB internal varilables and related DNS
53132451Sroberto * options for res_init().
54132451Sroberto *
55132451Sroberto * Returns 0 or the socket opened for use with talking to name servers
56132451Sroberto * if 0 is passed or ARES_INITSOCK is set.
57132451Sroberto */
58132451Srobertoint	ar_init(op)
59132451Srobertoint	op;
60132451Sroberto{
61132451Sroberto	int	ret = 0;
62132451Sroberto
63132451Sroberto	if (op & ARES_INITLIST)
64132451Sroberto	    {
65132451Sroberto		bzero(&ar_reinfo, sizeof(ar_reinfo));
66132451Sroberto		ar_first = ar_last = NULL;
67132451Sroberto	    }
68132451Sroberto
69132451Sroberto	if (op & ARES_CALLINIT && !(_res.options & RES_INIT))
70132451Sroberto	    {
71132451Sroberto		ret = res_init();
72132451Sroberto		(void)strcpy(ar_domainname, ar_dot);
73132451Sroberto		(void)strncat(ar_domainname, _res.defdname,
74132451Sroberto				sizeof(ar_domainname)-2);
75132451Sroberto	    }
76132451Sroberto
77132451Sroberto	if (op & ARES_INITSOCK)
78132451Sroberto		ret = ar_resfd = ar_open();
79132451Sroberto
80132451Sroberto	if (op & ARES_INITDEBG)
81132451Sroberto		_res.options |= RES_DEBUG;
82132451Sroberto
83132451Sroberto	if (op == 0)
84132451Sroberto		ret = ar_resfd;
85132451Sroberto
86132451Sroberto	return ret;
87132451Sroberto}
88132451Sroberto
89132451Sroberto
90132451Sroberto/*
91132451Sroberto * ar_open
92132451Sroberto *
93132451Sroberto * Open a socket to talk to a name server with.
94132451Sroberto * Check _res.options to see if we use a TCP or UDP socket.
95132451Sroberto */
96132451Srobertoint	ar_open()
97132451Sroberto{
98132451Sroberto	if (ar_resfd == -1)
99132451Sroberto	    {
100132451Sroberto		if (_res.options & RES_USEVC)
101132451Sroberto		    {
102132451Sroberto			struct	sockaddr_in	*sip;
103132451Sroberto			int	i;
104132451Sroberto
105132451Sroberto			sip = _res.NS_ADDR_LIST;	/* was _res.nsaddr_list */
106132451Sroberto			ar_vc = 1;
107132451Sroberto			ar_resfd = socket(AF_INET, SOCK_STREAM, 0);
108132451Sroberto
109132451Sroberto			/*
110132451Sroberto			 * Try each name server listed in sequence until we
111132451Sroberto			 * succeed or run out.
112132451Sroberto			 */
113132451Sroberto			while (connect(ar_resfd, (struct sockaddr *)sip++,
114132451Sroberto					sizeof(struct sockaddr)))
115132451Sroberto			    {
116132451Sroberto				(void)close(ar_resfd);
117132451Sroberto				ar_resfd = -1;
118132451Sroberto				if (i >= _res.nscount)
119132451Sroberto					break;
120132451Sroberto				ar_resfd = socket(AF_INET, SOCK_STREAM, 0);
121132451Sroberto			    }
122132451Sroberto		    }
123132451Sroberto		else
124132451Sroberto			ar_resfd = socket(AF_INET, SOCK_DGRAM, 0);
125132451Sroberto	    }
126132451Sroberto	if (ar_resfd >= 0)
127132451Sroberto	    {	/* Need one of these two here - and it MUST work!! */
128132451Sroberto		int flags;
129132451Sroberto
130132451Sroberto		if ((flags = fcntl(ar_resfd, F_GETFL, 0)) != -1)
131132451Sroberto#ifdef	O_NONBLOCK
132132451Sroberto			 if (fcntl(ar_resfd, F_SETFL, flags|O_NONBLOCK) == -1)
133132451Sroberto#else
134132451Sroberto# ifdef	O_NDELAY
135132451Sroberto			 if (fcntl(ar_resfd, F_SETFL, flags|O_NDELAY) == -1)
136132451Sroberto# else
137132451Sroberto#  ifdef	FNDELAY
138132451Sroberto			 if (fcntl(ar_resfd, F_SETFL, flags|FNDELAY) == -1)
139132451Sroberto#  endif
140132451Sroberto# endif
141132451Sroberto#endif
142132451Sroberto		    {
143132451Sroberto			(void)close(ar_resfd);
144132451Sroberto			ar_resfd = -1;
145132451Sroberto		    }
146132451Sroberto	    }
147132451Sroberto	return ar_resfd;
148132451Sroberto}
149132451Sroberto
150132451Sroberto
151132451Sroberto/*
152132451Sroberto * ar_close
153132451Sroberto *
154132451Sroberto * Closes and flags the ARLIB socket as closed.
155132451Sroberto */
156132451Srobertovoid	ar_close()
157132451Sroberto{
158132451Sroberto	(void)close(ar_resfd);
159132451Sroberto	ar_resfd = -1;
160132451Sroberto	return;
161132451Sroberto}
162132451Sroberto
163132451Sroberto
164132451Sroberto/*
165132451Sroberto * ar_add_request
166132451Sroberto *
167132451Sroberto * Add a new DNS query to the end of the query list.
168132451Sroberto */
169132451Srobertostatic	int	ar_add_request(new)
170132451Srobertostruct	reslist *new;
171132451Sroberto{
172132451Sroberto	if (!new)
173132451Sroberto		return -1;
174132451Sroberto	if (!ar_first)
175132451Sroberto		ar_first = ar_last = new;
176132451Sroberto	else {
177132451Sroberto		ar_last->re_next = new;
178132451Sroberto		ar_last = new;
179132451Sroberto	}
180132451Sroberto	new->re_next = NULL;
181132451Sroberto	ar_reinfo.re_requests++;
182132451Sroberto	return 0;
183132451Sroberto}
184132451Sroberto
185132451Sroberto
186132451Sroberto/*
187132451Sroberto * ar_remrequest
188132451Sroberto *
189132451Sroberto * Remove a request from the list. This must also free any memory that has
190132451Sroberto * been allocated for temporary storage of DNS results.
191132451Sroberto *
192132451Sroberto * Returns -1 if there are anyy problems removing the requested structure
193132451Sroberto * or 0 if the remove is successful.
194132451Sroberto */
195132451Srobertostatic	int	ar_remrequest(old)
196132451Srobertostruct	reslist *old;
197132451Sroberto{
198132451Sroberto	register struct	reslist	*rptr, *r2ptr;
199132451Sroberto	register char	**s;
200132451Sroberto
201132451Sroberto	if (!old)
202132451Sroberto		return -1;
203132451Sroberto	for (rptr = ar_first, r2ptr = NULL; rptr; rptr = rptr->re_next)
204132451Sroberto	    {
205132451Sroberto		if (rptr == old)
206132451Sroberto			break;
207132451Sroberto		r2ptr = rptr;
208132451Sroberto	    }
209132451Sroberto
210132451Sroberto	if (!rptr)
211132451Sroberto		return -1;
212132451Sroberto	if (rptr == ar_first)
213132451Sroberto		ar_first = ar_first->re_next;
214132451Sroberto	else if (rptr == ar_last)
215132451Sroberto	    {
216132451Sroberto		if (ar_last = r2ptr)
217132451Sroberto			ar_last->re_next = NULL;
218132451Sroberto	    }
219132451Sroberto	else
220132451Sroberto		r2ptr->re_next = rptr->re_next;
221132451Sroberto
222132451Sroberto	if (!ar_first)
223132451Sroberto		ar_last = ar_first;
224132451Sroberto
225132451Sroberto#ifdef	ARLIB_DEBUG
226132451Sroberto	ar_dump_hostent("ar_remrequest:", rptr->re_he);
227132451Sroberto#endif
228132451Sroberto
229132451Sroberto	if (rptr->re_he.h_name)
230132451Sroberto		(void)free(rptr->re_he.h_name);
231132451Sroberto	if (s = rptr->re_he.h_aliases)
232132451Sroberto		for (; *s; s++)
233132451Sroberto			(void)free(*s);
234132451Sroberto	if (rptr->re_rinfo.ri_ptr)
235132451Sroberto		(void)free(rptr->re_rinfo.ri_ptr);
236132451Sroberto	(void)free(rptr);
237132451Sroberto
238132451Sroberto	return 0;
239132451Sroberto}
240132451Sroberto
241132451Sroberto
242132451Sroberto/*
243132451Sroberto * ar_make_request
244132451Sroberto *
245132451Sroberto * Create a DNS query recorded for the request being made and place it on the
246132451Sroberto * current list awaiting replies.  Initialization of the record with set
247132451Sroberto * values should also be done.
248132451Sroberto */
249132451Srobertostatic	struct	reslist	*ar_make_request(resi)
250132451Srobertoregister struct	resinfo	*resi;
251132451Sroberto{
252132451Sroberto	register struct	reslist	*rptr;
253132451Sroberto	register struct resinfo *rp;
254132451Sroberto
255132451Sroberto	rptr = (struct reslist *)calloc(1, sizeof(struct reslist));
256132451Sroberto	rp = &rptr->re_rinfo;
257132451Sroberto
258132451Sroberto	rptr->re_next    = NULL; /* where NULL is non-zero ;) */
259132451Sroberto	rptr->re_sentat  = time(NULL);
260132451Sroberto	rptr->re_retries = _res.retry;
261132451Sroberto	rptr->re_sends = 1;
262132451Sroberto	rptr->re_resend  = 1;
263132451Sroberto	rptr->re_timeout = rptr->re_sentat + _res.retrans;
264132451Sroberto	rptr->re_he.h_name = NULL;
265132451Sroberto	rptr->re_he.h_addrtype   = AF_INET;
266132451Sroberto	rptr->re_he.h_aliases[0] = NULL;
267132451Sroberto	rp->ri_ptr = resi->ri_ptr;
268132451Sroberto	rp->ri_size = resi->ri_size;
269132451Sroberto
270132451Sroberto	(void)ar_add_request(rptr);
271132451Sroberto
272132451Sroberto	return rptr;
273132451Sroberto}
274132451Sroberto
275132451Sroberto
276132451Sroberto/*
277132451Sroberto * ar_timeout
278132451Sroberto *
279132451Sroberto * Remove queries from the list which have been there too long without
280132451Sroberto * being resolved.
281132451Sroberto */
282132451Srobertolong	ar_timeout(now, info, size)
283132451Srobertotime_t	now;
284132451Srobertochar	*info;
285132451Srobertoint	size;
286132451Sroberto{
287132451Sroberto	register struct	reslist	*rptr, *r2ptr;
288132451Sroberto	register long	next = 0;
289132451Sroberto
290132451Sroberto	for (rptr = ar_first, r2ptr = NULL; rptr; rptr = r2ptr)
291132451Sroberto	    {
292132451Sroberto		r2ptr = rptr->re_next;
293132451Sroberto		if (now >= rptr->re_timeout)
294132451Sroberto		    {
295132451Sroberto			/*
296132451Sroberto			 * If the timeout for the query has been exceeded,
297132451Sroberto			 * then resend the query if we still have some
298132451Sroberto			 * 'retry credit' and reset the timeout. If we have
299132451Sroberto			 * used it all up, then remove the request.
300132451Sroberto			 */
301132451Sroberto			if (--rptr->re_retries <= 0)
302132451Sroberto			    {
303132451Sroberto				ar_reinfo.re_timeouts++;
304132451Sroberto				if (info && rptr->re_rinfo.ri_ptr)
305132451Sroberto					bcopy(rptr->re_rinfo.ri_ptr, info,
306132451Sroberto						MIN(rptr->re_rinfo.ri_size,
307132451Sroberto						    size));
308132451Sroberto				(void)ar_remrequest(rptr);
309132451Sroberto				return now;
310132451Sroberto			    }
311132451Sroberto			else
312132451Sroberto			    {
313132451Sroberto				rptr->re_sends++;
314132451Sroberto				rptr->re_sentat = now;
315132451Sroberto				rptr->re_timeout = now + _res.retrans;
316132451Sroberto				(void)ar_resend_query(rptr);
317132451Sroberto			    }
318132451Sroberto		    }
319132451Sroberto		if (!next || rptr->re_timeout < next)
320132451Sroberto			next = rptr->re_timeout;
321132451Sroberto	    }
322132451Sroberto	return next;
323132451Sroberto}
324132451Sroberto
325132451Sroberto
326132451Sroberto/*
327132451Sroberto * ar_send_res_msg
328132451Sroberto *
329132451Sroberto * When sending queries to nameservers listed in the resolv.conf file,
330132451Sroberto * don't send a query to every one, but increase the number sent linearly
331132451Sroberto * to match the number of resends. This increase only occurs if there are
332132451Sroberto * multiple nameserver entries in the resolv.conf file.
333132451Sroberto * The return value is the number of messages successfully sent to
334132451Sroberto * nameservers or -1 if no successful sends.
335132451Sroberto */
336132451Srobertostatic	int	ar_send_res_msg(msg, len, rcount)
337132451Srobertochar	*msg;
338132451Srobertoint	len, rcount;
339132451Sroberto{
340132451Sroberto	register int	i;
341132451Sroberto	int	sent = 0;
342132451Sroberto
343132451Sroberto	if (!msg)
344132451Sroberto		return -1;
345132451Sroberto
346132451Sroberto	rcount = (_res.nscount > rcount) ? rcount : _res.nscount;
347132451Sroberto	if (_res.options & RES_PRIMARY)
348132451Sroberto		rcount = 1;
349132451Sroberto
350132451Sroberto	if (ar_vc)
351132451Sroberto	    {
352132451Sroberto		ar_reinfo.re_sent++;
353132451Sroberto		sent++;
354132451Sroberto		if (write(ar_resfd, msg, len) == -1)
355132451Sroberto		    {
356132451Sroberto			int errtmp = errno;
357132451Sroberto			(void)close(ar_resfd);
358132451Sroberto			errno = errtmp;
359132451Sroberto			ar_resfd = -1;
360132451Sroberto		    }
361132451Sroberto	    }
362132451Sroberto	else
363132451Sroberto		for (i = 0; i < rcount; i++)
364132451Sroberto		    {
365132451Sroberto			if (sendto(ar_resfd, msg, len, 0,
366132451Sroberto				   (struct sockaddr *)&(_res.NS_ADDR_LIST[i]),
367132451Sroberto				sizeof(struct sockaddr_in)) == len)
368132451Sroberto			    {
369132451Sroberto				ar_reinfo.re_sent++;
370132451Sroberto				sent++;
371132451Sroberto			    }
372132451Sroberto		    }
373132451Sroberto	return (sent) ? sent : -1;
374132451Sroberto}
375132451Sroberto
376132451Sroberto
377132451Sroberto/*
378132451Sroberto * ar_find_id
379132451Sroberto *
380132451Sroberto * find a dns query record by the id (id is determined by dn_mkquery)
381132451Sroberto */
382132451Srobertostatic	struct	reslist	*ar_find_id(id)
383132451Srobertoint	id;
384132451Sroberto{
385132451Sroberto	register struct	reslist	*rptr;
386132451Sroberto
387132451Sroberto	for (rptr = ar_first; rptr; rptr = rptr->re_next)
388132451Sroberto		if (rptr->re_id == id)
389132451Sroberto			return rptr;
390132451Sroberto	return NULL;
391132451Sroberto}
392132451Sroberto
393132451Sroberto
394132451Sroberto/*
395132451Sroberto * ar_delete
396132451Sroberto *
397132451Sroberto * Delete a request from the waiting list if it has a data pointer which
398132451Sroberto * matches the one passed.
399132451Sroberto */
400132451Srobertoint	ar_delete(ptr, size)
401132451Srobertochar	*ptr;
402132451Srobertoint	size;
403132451Sroberto{
404132451Sroberto	register struct	reslist	*rptr;
405132451Sroberto	register struct	reslist	*r2ptr;
406132451Sroberto	int	removed = 0;
407132451Sroberto
408132451Sroberto	for (rptr = ar_first; rptr; rptr = r2ptr)
409132451Sroberto	    {
410132451Sroberto		r2ptr = rptr->re_next;
411132451Sroberto		if (rptr->re_rinfo.ri_ptr && ptr && size &&
412132451Sroberto		    bcmp(rptr->re_rinfo.ri_ptr, ptr, size) == 0)
413132451Sroberto		    {
414132451Sroberto			(void)ar_remrequest(rptr);
415132451Sroberto			removed++;
416132451Sroberto		    }
417132451Sroberto	    }
418132451Sroberto	return removed;
419132451Sroberto}
420132451Sroberto
421132451Sroberto
422132451Sroberto/*
423132451Sroberto * ar_query_name
424132451Sroberto *
425132451Sroberto * generate a query based on class, type and name.
426132451Sroberto */
427132451Srobertostatic	int	ar_query_name(name, class, type, rptr)
428132451Srobertochar	*name;
429132451Srobertoint	class, type;
430132451Srobertostruct	reslist	*rptr;
431132451Sroberto{
432132451Sroberto	static	char buf[MAXPACKET];
433132451Sroberto	int	r,s,a;
434132451Sroberto	HEADER	*hptr;
435132451Sroberto
436132451Sroberto	bzero(buf, sizeof(buf));
437132451Sroberto	r = res_mkquery(QUERY, name, class, type, NULL, 0, NULL,
438132451Sroberto			buf, sizeof(buf));
439132451Sroberto	if (r <= 0)
440132451Sroberto	    {
441132451Sroberto		h_errno = NO_RECOVERY;
442132451Sroberto		return r;
443132451Sroberto	    }
444132451Sroberto	hptr = (HEADER *)buf;
445132451Sroberto	rptr->re_id = ntohs(hptr->id);
446132451Sroberto
447132451Sroberto	s = ar_send_res_msg(buf, r, rptr->re_sends);
448132451Sroberto
449132451Sroberto	if (s == -1)
450132451Sroberto	    {
451132451Sroberto		h_errno = TRY_AGAIN;
452132451Sroberto		return -1;
453132451Sroberto	    }
454132451Sroberto	else
455132451Sroberto		rptr->re_sent += s;
456132451Sroberto	return 0;
457132451Sroberto}
458132451Sroberto
459132451Sroberto
460132451Sroberto/*
461132451Sroberto * ar_gethostbyname
462132451Sroberto *
463132451Sroberto * Replacement library function call to gethostbyname().  This one, however,
464132451Sroberto * doesn't return the record being looked up but just places the query in the
465132451Sroberto * queue to await answers.
466132451Sroberto */
467132451Srobertoint	ar_gethostbyname(name, info, size)
468132451Srobertochar	*name;
469132451Srobertochar	*info;
470132451Srobertoint	size;
471132451Sroberto{
472132451Sroberto	char	host[65];
473132451Sroberto	struct	resinfo	resi;
474132451Sroberto	register struct resinfo *rp = &resi;
475132451Sroberto
476132451Sroberto	if (size && info)
477132451Sroberto	    {
478132451Sroberto		rp->ri_ptr = (char *)malloc(size);
479132451Sroberto		bcopy(info, rp->ri_ptr, size);
480132451Sroberto		rp->ri_size = size;
481132451Sroberto	    }
482132451Sroberto	else
483132451Sroberto		bzero((char *)rp, sizeof(resi));
484132451Sroberto	ar_reinfo.re_na_look++;
485132451Sroberto	(void)strncpy(host, name, 64);
486132451Sroberto	host[64] = '\0';
487132451Sroberto
488132451Sroberto	return (do_query_name(rp, host, NULL));
489132451Sroberto}
490132451Sroberto
491132451Sroberto
492132451Srobertostatic	int	do_query_name(resi, name, rptr)
493132451Srobertostruct	resinfo	*resi;
494132451Srobertochar	*name;
495132451Srobertoregister struct	reslist	*rptr;
496132451Sroberto{
497132451Sroberto	char	hname[65];
498132451Sroberto	int	len;
499132451Sroberto
500132451Sroberto	len = strlen((char *)strncpy(hname, name, sizeof(hname)-1));
501132451Sroberto
502132451Sroberto	if (rptr && (hname[len-1] != '.'))
503132451Sroberto	    {
504132451Sroberto		(void)strncat(hname, ar_dot, sizeof(hname)-len-1);
505132451Sroberto		/*
506132451Sroberto		 * NOTE: The logical relationship between DNSRCH and DEFNAMES
507132451Sroberto		 * is implies. ie no DEFNAES, no DNSRCH.
508132451Sroberto		 */
509132451Sroberto		if (_res.options & (RES_DEFNAMES|RES_DNSRCH) ==
510132451Sroberto		    (RES_DEFNAMES|RES_DNSRCH))
511132451Sroberto		    {
512132451Sroberto			if (_res.dnsrch[rptr->re_srch])
513132451Sroberto				(void)strncat(hname, _res.dnsrch[rptr->re_srch],
514132451Sroberto					sizeof(hname) - ++len -1);
515132451Sroberto		    }
516132451Sroberto		else if (_res.options & RES_DEFNAMES)
517132451Sroberto			(void)strncat(hname, ar_domainname, sizeof(hname) - len -1);
518132451Sroberto	    }
519132451Sroberto
520132451Sroberto	/*
521132451Sroberto	 * Store the name passed as the one to lookup and generate other host
522132451Sroberto	 * names to pass onto the nameserver(s) for lookups.
523132451Sroberto	 */
524132451Sroberto	if (!rptr)
525132451Sroberto	    {
526132451Sroberto		rptr = ar_make_request(resi);
527132451Sroberto		rptr->re_type = T_A;
528132451Sroberto		(void)strncpy(rptr->re_name, name, sizeof(rptr->re_name)-1);
529132451Sroberto	    }
530132451Sroberto	return (ar_query_name(hname, C_IN, T_A, rptr));
531132451Sroberto}
532132451Sroberto
533132451Sroberto
534132451Sroberto/*
535132451Sroberto * ar_gethostbyaddr
536132451Sroberto *
537132451Sroberto * Generates a query for a given IP address.
538132451Sroberto */
539132451Srobertoint	ar_gethostbyaddr(addr, info, size)
540132451Srobertochar	*addr;
541132451Srobertochar	*info;
542132451Srobertoint	size;
543132451Sroberto{
544132451Sroberto	struct	resinfo	resi;
545132451Sroberto	register struct resinfo *rp = &resi;
546132451Sroberto
547132451Sroberto	if (size && info)
548132451Sroberto	    {
549132451Sroberto		rp->ri_ptr = (char *)malloc(size);
550132451Sroberto		bcopy(info, rp->ri_ptr, size);
551132451Sroberto		rp->ri_size = size;
552132451Sroberto	    }
553132451Sroberto	else
554132451Sroberto		bzero((char *)rp, sizeof(resi));
555132451Sroberto	ar_reinfo.re_nu_look++;
556132451Sroberto	return (do_query_number(rp, addr, NULL));
557132451Sroberto}
558132451Sroberto
559132451Sroberto
560132451Sroberto/*
561132451Sroberto * do_query_number
562132451Sroberto *
563132451Sroberto * Use this to do reverse IP# lookups.
564132451Sroberto */
565132451Srobertostatic	int	do_query_number(resi, numb, rptr)
566132451Srobertostruct	resinfo	*resi;
567132451Srobertochar	*numb;
568132451Srobertoregister struct	reslist	*rptr;
569132451Sroberto{
570132451Sroberto	register unsigned char	*cp;
571132451Sroberto	static	char	ipbuf[32];
572132451Sroberto
573132451Sroberto	/*
574132451Sroberto	 * Generate name in the "in-addr.arpa" domain.  No addings bits to this
575132451Sroberto	 * name to get more names to query!.
576132451Sroberto	 */
577132451Sroberto	cp = (unsigned char *)numb;
578132451Sroberto	(void)sprintf(ipbuf,"%u.%u.%u.%u.in-addr.arpa.",
579132451Sroberto			(unsigned int)(cp[3]), (unsigned int)(cp[2]),
580132451Sroberto			(unsigned int)(cp[1]), (unsigned int)(cp[0]));
581132451Sroberto
582132451Sroberto	if (!rptr)
583132451Sroberto	    {
584132451Sroberto		rptr = ar_make_request(resi);
585132451Sroberto		rptr->re_type = T_PTR;
586132451Sroberto		rptr->re_he.h_length = sizeof(struct in_addr);
587132451Sroberto		bcopy(numb, (char *)&rptr->re_addr, rptr->re_he.h_length);
588132451Sroberto		bcopy(numb, (char *)&rptr->re_he.h_addr_list[0].s_addr,
589132451Sroberto			rptr->re_he.h_length);
590132451Sroberto	    }
591132451Sroberto	return (ar_query_name(ipbuf, C_IN, T_PTR, rptr));
592132451Sroberto}
593132451Sroberto
594132451Sroberto
595132451Sroberto/*
596132451Sroberto * ar_resent_query
597132451Sroberto *
598132451Sroberto * resends a query.
599132451Sroberto */
600132451Srobertostatic	int	ar_resend_query(rptr)
601132451Srobertostruct	reslist	*rptr;
602132451Sroberto{
603132451Sroberto	if (!rptr->re_resend)
604132451Sroberto		return -1;
605132451Sroberto
606132451Sroberto	switch(rptr->re_type)
607132451Sroberto	{
608132451Sroberto	case T_PTR:
609132451Sroberto		ar_reinfo.re_resends++;
610132451Sroberto		return do_query_number(NULL, &rptr->re_addr, rptr);
611132451Sroberto	case T_A:
612132451Sroberto		ar_reinfo.re_resends++;
613132451Sroberto		return do_query_name(NULL, rptr->re_name, rptr);
614132451Sroberto	default:
615132451Sroberto		break;
616132451Sroberto	}
617132451Sroberto
618132451Sroberto	return -1;
619132451Sroberto}
620132451Sroberto
621132451Sroberto
622132451Sroberto/*
623132451Sroberto * ar_procanswer
624132451Sroberto *
625132451Sroberto * process an answer received from a nameserver.
626132451Sroberto */
627132451Srobertostatic	int	ar_procanswer(rptr, hptr, buf, eob)
628132451Srobertostruct	reslist	*rptr;
629132451Srobertochar	*buf, *eob;
630132451SrobertoHEADER	*hptr;
631132451Sroberto{
632132451Sroberto	char	*cp, **alias, *s;
633132451Sroberto	int	class, type, dlen, len, ans = 0, n, i;
634132451Sroberto	u_int32_t ttl, dr, *adr;
635132451Sroberto	struct	hent	*hp;
636132451Sroberto
637132451Sroberto	cp = buf + sizeof(HEADER);
638132451Sroberto	adr = (u_int32_t *)rptr->re_he.h_addr_list;
639132451Sroberto
640132451Sroberto	while (*adr)
641132451Sroberto		adr++;
642132451Sroberto
643132451Sroberto	alias = rptr->re_he.h_aliases;
644132451Sroberto	while (*alias)
645132451Sroberto		alias++;
646132451Sroberto
647132451Sroberto	hp = &rptr->re_he;
648132451Sroberto
649132451Sroberto
650132451Sroberto	/*
651132451Sroberto	 * Skip over the original question.
652132451Sroberto	 */
653132451Sroberto	while (hptr->qdcount-- > 0)
654132451Sroberto		cp += dn_skipname(cp, eob) + QFIXEDSZ;
655132451Sroberto	/*
656132451Sroberto	 * proccess each answer sent to us. blech.
657132451Sroberto	 */
658132451Sroberto	while (hptr->ancount-- > 0 && cp < eob) {
659132451Sroberto		n = dn_expand(buf, eob, cp, ar_hostbuf, sizeof(ar_hostbuf));
660132451Sroberto		cp += n;
661132451Sroberto		if (n <= 0)
662132451Sroberto			return ans;
663132451Sroberto
664132451Sroberto		ans++;
665132451Sroberto		/*
666132451Sroberto		 * 'skip' past the general dns crap (ttl, class, etc) to get
667132451Sroberto		 * the pointer to the right spot.  Some of thse are actually
668132451Sroberto		 * useful so its not a good idea to skip past in one big jump.
669132451Sroberto		 */
670132451Sroberto		type = (int)_getshort(cp);
671132451Sroberto		cp += sizeof(short);
672132451Sroberto		class = (int)_getshort(cp);
673132451Sroberto		cp += sizeof(short);
674132451Sroberto		ttl = (u_int32_t)_getlong(cp);
675132451Sroberto		cp += sizeof(u_int32_t);
676132451Sroberto		dlen =  (int)_getshort(cp);
677132451Sroberto		cp += sizeof(short);
678132451Sroberto		rptr->re_type = type;
679132451Sroberto
680132451Sroberto		switch(type)
681132451Sroberto		{
682132451Sroberto		case T_A :
683132451Sroberto			rptr->re_he.h_length = dlen;
684132451Sroberto			if (ans == 1)
685132451Sroberto				rptr->re_he.h_addrtype=(class == C_IN) ?
686132451Sroberto							AF_INET : AF_UNSPEC;
687132451Sroberto			if (dlen != sizeof(dr))
688132451Sroberto			    {
689132451Sroberto				h_errno = TRY_AGAIN;
690132451Sroberto				continue;
691132451Sroberto			    }
692132451Sroberto			bcopy(cp, &dr, dlen);
693132451Sroberto			*adr++ = dr;
694132451Sroberto			*adr = 0;
695132451Sroberto			cp += dlen;
696132451Sroberto			len = strlen(ar_hostbuf);
697132451Sroberto			if (!rptr->re_he.h_name)
698132451Sroberto			    {
699132451Sroberto				rptr->re_he.h_name = (char *)malloc(len+1);
700132451Sroberto				if (!rptr->re_he.h_name)
701132451Sroberto					break;
702132451Sroberto				(void)strcpy(rptr->re_he.h_name, ar_hostbuf);
703132451Sroberto			    }
704132451Sroberto 			break;
705132451Sroberto		case T_PTR :
706132451Sroberto			if ((n = dn_expand(buf, eob, cp, ar_hostbuf,
707132451Sroberto					   sizeof(ar_hostbuf) )) < 0)
708132451Sroberto			    {
709132451Sroberto				cp += n;
710132451Sroberto				continue;
711132451Sroberto			    }
712132451Sroberto			cp += n;
713132451Sroberto			len = strlen(ar_hostbuf)+1;
714132451Sroberto			/*
715132451Sroberto			 * copy the returned hostname into the host name
716132451Sroberto			 * or alias field if there is a known hostname
717132451Sroberto			 * already.
718132451Sroberto			 */
719132451Sroberto			if (!rptr->re_he.h_name)
720132451Sroberto			    {
721132451Sroberto				rptr->re_he.h_name = (char *)malloc(len);
722132451Sroberto				if (!rptr->re_he.h_name)
723132451Sroberto					break;
724132451Sroberto				(void)strcpy(rptr->re_he.h_name, ar_hostbuf);
725132451Sroberto			    }
726132451Sroberto			else
727132451Sroberto			    {
728132451Sroberto				*alias = (char *)malloc(len);
729132451Sroberto				if (!*alias)
730132451Sroberto					return -1;
731132451Sroberto				(void)strcpy(*alias++, ar_hostbuf);
732132451Sroberto				*alias = NULL;
733132451Sroberto			    }
734132451Sroberto			break;
735132451Sroberto		case T_CNAME :
736132451Sroberto			cp += dlen;
737132451Sroberto			if (alias >= &(rptr->re_he.h_aliases[MAXALIASES-1]))
738132451Sroberto				continue;
739132451Sroberto			n = strlen(ar_hostbuf)+1;
740132451Sroberto			*alias = (char *)malloc(n);
741132451Sroberto			if (!*alias)
742132451Sroberto				return -1;
743132451Sroberto			(void)strcpy(*alias++, ar_hostbuf);
744132451Sroberto			*alias = NULL;
745132451Sroberto			break;
746132451Sroberto		default :
747132451Sroberto			break;
748132451Sroberto		}
749132451Sroberto	}
750132451Sroberto
751132451Sroberto	return ans;
752132451Sroberto}
753132451Sroberto
754132451Sroberto
755132451Sroberto/*
756132451Sroberto * ar_answer
757132451Sroberto *
758132451Sroberto * Get an answer from a DNS server and process it.  If a query is found to
759132451Sroberto * which no answer has been given to yet, copy its 'info' structure back
760132451Sroberto * to where "reip" points and return a pointer to the hostent structure.
761132451Sroberto */
762132451Srobertostruct	hostent	*ar_answer(reip, size)
763132451Srobertochar	*reip;
764132451Srobertoint	size;
765132451Sroberto{
766132451Sroberto	static	char	ar_rcvbuf[sizeof(HEADER) + MAXPACKET];
767132451Sroberto	static	struct	hostent	ar_host;
768132451Sroberto
769132451Sroberto	register HEADER	*hptr;
770132451Sroberto	register struct	reslist	*rptr = NULL;
771132451Sroberto	register struct hostent *hp;
772132451Sroberto	register char **s;
773132451Sroberto	unsigned long	*adr;
774132451Sroberto	int	rc, i, n, a;
775132451Sroberto
776132451Sroberto	rc = recv(ar_resfd, ar_rcvbuf, sizeof(ar_rcvbuf), 0);
777132451Sroberto	if (rc <= 0)
778132451Sroberto		goto getres_err;
779132451Sroberto
780132451Sroberto	ar_reinfo.re_replies++;
781132451Sroberto	hptr = (HEADER *)ar_rcvbuf;
782132451Sroberto	/*
783132451Sroberto	 * convert things to be in the right order.
784132451Sroberto	 */
785132451Sroberto	hptr->id = ntohs(hptr->id);
786132451Sroberto	hptr->ancount = ntohs(hptr->ancount);
787132451Sroberto	hptr->arcount = ntohs(hptr->arcount);
788132451Sroberto	hptr->nscount = ntohs(hptr->nscount);
789132451Sroberto	hptr->qdcount = ntohs(hptr->qdcount);
790132451Sroberto	/*
791132451Sroberto	 * response for an id which we have already received an answer for
792132451Sroberto	 * just ignore this response.
793132451Sroberto	 */
794132451Sroberto	rptr = ar_find_id(hptr->id);
795132451Sroberto	if (!rptr)
796132451Sroberto		goto getres_err;
797132451Sroberto
798132451Sroberto	if ((hptr->rcode != NOERROR) || (hptr->ancount == 0))
799132451Sroberto	    {
800132451Sroberto		switch (hptr->rcode)
801132451Sroberto		{
802132451Sroberto		case NXDOMAIN:
803132451Sroberto			h_errno = HOST_NOT_FOUND;
804132451Sroberto			break;
805132451Sroberto		case SERVFAIL:
806132451Sroberto			h_errno = TRY_AGAIN;
807132451Sroberto			break;
808132451Sroberto		case NOERROR:
809132451Sroberto			h_errno = NO_DATA;
810132451Sroberto			break;
811132451Sroberto		case FORMERR:
812132451Sroberto		case NOTIMP:
813132451Sroberto		case REFUSED:
814132451Sroberto		default:
815132451Sroberto			h_errno = NO_RECOVERY;
816132451Sroberto			break;
817132451Sroberto		}
818132451Sroberto		ar_reinfo.re_errors++;
819132451Sroberto		/*
820132451Sroberto		** If a bad error was returned, we stop here and dont send
821132451Sroberto		** send any more (no retries granted).
822132451Sroberto		*/
823132451Sroberto		if (h_errno != TRY_AGAIN)
824132451Sroberto		    {
825132451Sroberto			rptr->re_resend = 0;
826132451Sroberto			rptr->re_retries = 0;
827132451Sroberto		    }
828132451Sroberto		goto getres_err;
829132451Sroberto	    }
830132451Sroberto
831132451Sroberto	a = ar_procanswer(rptr, hptr, ar_rcvbuf, ar_rcvbuf+rc);
832132451Sroberto
833132451Sroberto	if ((rptr->re_type == T_PTR) && (_res.options & RES_CHECKPTR))
834132451Sroberto	    {
835132451Sroberto		/*
836132451Sroberto		 * For reverse lookups on IP#'s, lookup the name that is given
837132451Sroberto		 * for the ip# and return with that as the official result.
838132451Sroberto		 * -avalon
839132451Sroberto		 */
840132451Sroberto		rptr->re_type = T_A;
841132451Sroberto		/*
842132451Sroberto		 * Clean out the list of addresses already set, even though
843132451Sroberto		 * there should only be one :)
844132451Sroberto		 */
845132451Sroberto		adr = (unsigned long *)rptr->re_he.h_addr_list;
846132451Sroberto		while (*adr)
847132451Sroberto			*adr++ = 0L;
848132451Sroberto		/*
849132451Sroberto		 * Lookup the name that we were given for the ip#
850132451Sroberto		 */
851132451Sroberto		ar_reinfo.re_na_look++;
852132451Sroberto		(void)strncpy(rptr->re_name, rptr->re_he.h_name,
853132451Sroberto			sizeof(rptr->re_name)-1);
854132451Sroberto		rptr->re_he.h_name = NULL;
855132451Sroberto		rptr->re_retries = _res.retry;
856132451Sroberto		rptr->re_sends = 1;
857132451Sroberto		rptr->re_resend = 1;
858132451Sroberto		rptr->re_he.h_name = NULL;
859132451Sroberto		ar_reinfo.re_na_look++;
860132451Sroberto		(void)ar_query_name(rptr->re_name, C_IN, T_A, rptr);
861132451Sroberto		return NULL;
862132451Sroberto	    }
863132451Sroberto
864132451Sroberto	if (reip && rptr->re_rinfo.ri_ptr && size)
865132451Sroberto		bcopy(rptr->re_rinfo.ri_ptr, reip,
866132451Sroberto			MIN(rptr->re_rinfo.ri_size, size));
867132451Sroberto	/*
868132451Sroberto	 * Clean up structure from previous usage.
869132451Sroberto	 */
870132451Sroberto	hp = &ar_host;
871132451Sroberto#ifdef	ARLIB_DEBUG
872132451Sroberto	ar_dump_hostent("ar_answer: previous usage", hp);
873132451Sroberto#endif
874132451Sroberto
875132451Sroberto	if (hp->h_name)
876132451Sroberto		(void)free(hp->h_name);
877132451Sroberto	if (s = hp->h_aliases)
878132451Sroberto	    {
879132451Sroberto		while (*s)
880132451Sroberto			(void)free(*s++);
881132451Sroberto		(void)free(hp->h_aliases);
882132451Sroberto	    }
883132451Sroberto	if (s = hp->h_addr_list)
884132451Sroberto	    {
885132451Sroberto		/*
886132451Sroberto		 * Only free once since we allocated space for
887132451Sroberto		 * address in one big chunk.
888132451Sroberto		 */
889132451Sroberto		(void)free(*s);
890132451Sroberto		(void)free(hp->h_addr_list);
891132451Sroberto	    }
892132451Sroberto	bzero((char *)hp, sizeof(*hp));
893132451Sroberto
894132451Sroberto	/*
895132451Sroberto	 * Setup and copy details for the structure we return a pointer to.
896132451Sroberto	 */
897132451Sroberto	hp->h_addrtype = AF_INET;
898132451Sroberto	hp->h_length = sizeof(struct in_addr);
899132451Sroberto	if(rptr->re_he.h_name)
900132451Sroberto	    {
901132451Sroberto		hp->h_name = (char *)malloc(strlen(rptr->re_he.h_name)+1);
902132451Sroberto		if(!hp->h_name)
903132451Sroberto		    {
904132451Sroberto#ifdef	ARLIB_DEBUG
905132451Sroberto			fprintf(stderr, "no memory for hostname\n");
906132451Sroberto#endif
907132451Sroberto			h_errno = TRY_AGAIN;
908132451Sroberto			goto getres_err;
909132451Sroberto		    }
910132451Sroberto		(void)strcpy(hp->h_name, rptr->re_he.h_name);
911132451Sroberto	    }
912132451Sroberto#ifdef	ARLIB_DEBUG
913132451Sroberto	ar_dump_hostent("ar_answer: (snap) store name", hp);
914132451Sroberto#endif
915132451Sroberto
916132451Sroberto	/*
917132451Sroberto	 * Count IP#'s.
918132451Sroberto	 */
919132451Sroberto	for (i = 0, n = 0; i < MAXADDRS; i++, n++)
920132451Sroberto		if (!rptr->re_he.h_addr_list[i].s_addr)
921132451Sroberto			break;
922132451Sroberto	s = hp->h_addr_list = (char **)malloc((n + 1) * sizeof(char *));
923132451Sroberto	if (n)
924132451Sroberto	    {
925132451Sroberto		*s = (char *)malloc(n * sizeof(struct in_addr));
926132451Sroberto		if(!*s)
927132451Sroberto		    {
928132451Sroberto#ifdef	ARLIB_DEBUG
929132451Sroberto			fprintf(stderr, "no memory for IP#'s (%d)\n", n);
930132451Sroberto#endif
931132451Sroberto			h_errno = TRY_AGAIN;
932132451Sroberto			goto getres_err;
933132451Sroberto		    }
934132451Sroberto		bcopy((char *)&rptr->re_he.h_addr_list[0].s_addr, *s,
935132451Sroberto			sizeof(struct in_addr));
936132451Sroberto		s++;
937132451Sroberto		for (i = 1; i < n; i++, s++)
938132451Sroberto		    {
939132451Sroberto			*s = hp->h_addr + i * sizeof(struct in_addr);
940132451Sroberto			bcopy((char *)&rptr->re_he.h_addr_list[i].s_addr, *s,
941132451Sroberto				sizeof(struct in_addr));
942132451Sroberto		    }
943132451Sroberto	    }
944132451Sroberto	*s = NULL;
945132451Sroberto#ifdef	ARLIB_DEBUG
946132451Sroberto	ar_dump_hostent("ar_answer: (snap) store IP#'s", hp);
947132451Sroberto#endif
948132451Sroberto
949132451Sroberto	/*
950132451Sroberto	 * Count CNAMEs
951132451Sroberto	 */
952132451Sroberto	for (i = 0, n = 0; i < MAXADDRS; i++, n++)
953132451Sroberto		if (!rptr->re_he.h_aliases[i])
954132451Sroberto			break;
955132451Sroberto	s = hp->h_aliases = (char **)malloc((n + 1) * sizeof(char *));
956132451Sroberto	if (!s)
957132451Sroberto	    {
958132451Sroberto#ifdef	ARLIB_DEBUG
959132451Sroberto		fprintf(stderr, "no memory for aliases (%d)\n", n);
960132451Sroberto#endif
961132451Sroberto		h_errno = TRY_AGAIN;
962132451Sroberto		goto getres_err;
963132451Sroberto	    }
964132451Sroberto	for (i = 0; i < n; i++)
965132451Sroberto	    {
966132451Sroberto		*s++ = rptr->re_he.h_aliases[i];
967132451Sroberto		rptr->re_he.h_aliases[i] = NULL;
968132451Sroberto	    }
969132451Sroberto	*s = NULL;
970132451Sroberto#ifdef	ARLIB_DEBUG
971132451Sroberto	ar_dump_hostent("ar_answer: (snap) store CNAMEs", hp);
972132451Sroberto	ar_dump_hostent("ar_answer: new one", hp);
973132451Sroberto#endif
974132451Sroberto
975132451Sroberto	if (a > 0)
976132451Sroberto		(void)ar_remrequest(rptr);
977132451Sroberto	else
978132451Sroberto		if (!rptr->re_sent)
979132451Sroberto			(void)ar_remrequest(rptr);
980132451Sroberto	return hp;
981132451Sroberto
982132451Srobertogetres_err:
983132451Sroberto	if (rptr)
984132451Sroberto	    {
985132451Sroberto		if (reip && rptr->re_rinfo.ri_ptr && size)
986132451Sroberto			bcopy(rptr->re_rinfo.ri_ptr, reip,
987132451Sroberto				MIN(rptr->re_rinfo.ri_size, size));
988132451Sroberto		if ((h_errno != TRY_AGAIN) &&
989132451Sroberto		    (_res.options & (RES_DNSRCH|RES_DEFNAMES) ==
990132451Sroberto		     (RES_DNSRCH|RES_DEFNAMES) ))
991132451Sroberto			if (_res.dnsrch[rptr->re_srch])
992132451Sroberto			    {
993132451Sroberto				rptr->re_retries = _res.retry;
994132451Sroberto				rptr->re_sends = 1;
995132451Sroberto				rptr->re_resend = 1;
996132451Sroberto				(void)ar_resend_query(rptr);
997132451Sroberto				rptr->re_srch++;
998132451Sroberto			    }
999132451Sroberto		return NULL;
1000132451Sroberto	    }
1001132451Sroberto	return NULL;
1002132451Sroberto}
1003132451Sroberto
1004132451Sroberto
1005132451Sroberto#ifdef	ARLIB_DEBUG
1006132451Srobertovoid ar_dump_hostent(prefix, hp)
1007132451Srobertochar *prefix;
1008132451Srobertostruct hostent *hp;
1009132451Sroberto{
1010132451Sroberto	register char **s;
1011132451Sroberto
1012132451Sroberto	fflush(stdout);
1013132451Sroberto
1014132451Sroberto	fprintf(stderr, "%s\n", prefix);
1015132451Sroberto	fprintf(stderr, "  hp %p\n", hp);
1016132451Sroberto	fprintf(stderr, "    h_name %p '%s'\n",
1017132451Sroberto	hp->h_name, hp->h_name);
1018132451Sroberto	if (s = hp->h_aliases)
1019132451Sroberto	    {
1020132451Sroberto		fprintf(stderr, "    h_aliases %p\n",
1021132451Sroberto		hp->h_aliases);
1022132451Sroberto		while (*s)
1023132451Sroberto		    {
1024132451Sroberto			fprintf(stderr, "      element %p\n", *s);
1025132451Sroberto			s++;
1026132451Sroberto		    }
1027132451Sroberto	    }
1028132451Sroberto	if (s = hp->h_addr_list)
1029132451Sroberto	    {
1030132451Sroberto		fprintf(stderr, "    h_addr_list %p\n",
1031132451Sroberto		hp->h_addr_list);
1032132451Sroberto		while (*s)
1033132451Sroberto		    {
1034132451Sroberto			fprintf(stderr, "      element %p\n", *s);
1035132451Sroberto			s++;
1036132451Sroberto		    }
1037132451Sroberto	    }
1038132451Sroberto
1039132451Sroberto	fflush(stderr);
1040132451Sroberto}
1041132451Sroberto
1042132451Sroberto
1043132451Srobertovoid ar_dump_reslist(FILE* fp)
1044132451Sroberto{
1045132451Sroberto	register struct reslist *rptr;
1046132451Sroberto	int c;
1047132451Sroberto
1048132451Sroberto	c = 0;
1049132451Sroberto	for (rptr = ar_first; rptr; rptr = rptr->re_next)
1050132451Sroberto	    {
1051132451Sroberto		fprintf(fp, "%4d [%p] %4d [%p]: %s\n", rptr->re_id, rptr,
1052132451Sroberto			*(rptr->re_rinfo.ri_ptr), rptr->re_rinfo.ri_ptr,
1053132451Sroberto			rptr->re_name);
1054132451Sroberto	    }
1055132451Sroberto}
1056132451Sroberto#endif
1057