1/*
2 * Copyright (c) 1988, 1993
3 *    The Regents of the University of California.  All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 * 3. All advertising materials mentioning features or use of this software
14 *    must display the following acknowledgement:
15 * 	This product includes software developed by the University of
16 * 	California, Berkeley and its contributors.
17 * 4. Neither the name of the University nor the names of its contributors
18 *    may be used to endorse or promote products derived from this software
19 *    without specific prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31 * SUCH DAMAGE.
32 */
33
34/*
35 * Portions Copyright (c) 1993 by Digital Equipment Corporation.
36 *
37 * Permission to use, copy, modify, and distribute this software for any
38 * purpose with or without fee is hereby granted, provided that the above
39 * copyright notice and this permission notice appear in all copies, and that
40 * the name of Digital Equipment Corporation not be used in advertising or
41 * publicity pertaining to distribution of the document or software without
42 * specific, written prior permission.
43 *
44 * THE SOFTWARE IS PROVIDED "AS IS" AND DIGITAL EQUIPMENT CORP. DISCLAIMS ALL
45 * WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES
46 * OF MERCHANTABILITY AND FITNESS.   IN NO EVENT SHALL DIGITAL EQUIPMENT
47 * CORPORATION BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
48 * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
49 * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
50 * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
51 * SOFTWARE.
52 */
53
54/*
55 * Portions Copyright (c) 1996-1999 by Internet Software Consortium.
56 *
57 * Permission to use, copy, modify, and distribute this software for any
58 * purpose with or without fee is hereby granted, provided that the above
59 * copyright notice and this permission notice appear in all copies.
60 *
61 * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS
62 * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
63 * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE
64 * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
65 * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
66 * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
67 * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
68 * SOFTWARE.
69 */
70
71#if defined(LIBC_SCCS) && !defined(lint)
72static const char sccsid[] = "@(#)res_query.c	8.1 (Berkeley) 6/4/93";
73static const char rcsid[] = "$Id: res_query.c,v 1.1 2006/03/01 19:01:38 majka Exp $";
74#endif /* LIBC_SCCS and not lint */
75
76#ifndef __APPLE__
77#include "port_before.h"
78#endif
79#include <sys/types.h>
80#include <sys/param.h>
81#include <netinet/in.h>
82#include <arpa/inet.h>
83#include <arpa/nameser.h>
84#include <ctype.h>
85#include <errno.h>
86#include <netdb.h>
87#include <resolv.h>
88#include <stdio.h>
89#include <stdlib.h>
90#include <string.h>
91#include "res_private.h"
92#include <dns_sd.h>
93#include <sys/event.h>
94#include <sys/time.h>
95#include <time.h>
96#include <unistd.h>
97#include <notify.h>
98#include <pthread.h>
99#ifndef __APPLE__
100#include "port_after.h"
101#endif
102
103/* interrupt mechanism is implemented in res_send.c */
104__private_extern__ int interrupt_pipe_enabled;
105__private_extern__ pthread_key_t interrupt_pipe_key;
106
107/* Options.  Leave them on. */
108#define DEBUG
109
110#if PACKETSZ > 1024
111#define MAXPACKET	PACKETSZ
112#else
113#define MAXPACKET	1024
114#endif
115
116#define BILLION 1000000000
117
118/* length of a reverse DNS IPv6 address query name, e.g. "9.4.a.f.c.e.e.f.e.e.1.5.4.1.4.0.0.0.0.0.0.0.0.0.0.0.0.0.0.8.e.f.ip6.arpa" */
119#define IPv6_REVERSE_LEN 72
120
121/* index of the trailing char that must be "8", "9", "A", "a", "b", or "B" */
122#define IPv6_REVERSE_LINK_LOCAL_TRAILING_CHAR 58
123
124/* index of low-order nibble of embedded scope id */
125#define IPv6_REVERSE_LINK_LOCAL_SCOPE_ID_LOW 48
126
127const static uint8_t hexval[128] =
128{
129	0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,		/*  0 - 15 */
130	0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,		/* 16 - 31 */
131	0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,		/* 32 - 47 */
132	0,  1,  2,  3,  4,  5,  6,  7,  8,  9,  0,  0,  0,  0,  0,  0,		/* 48 - 63 */
133	0, 10, 11, 12, 13, 14, 15,  0,  0,  0,  0,  0,  0,  0,  0,  0,		/* 64 - 79 */
134	0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,		/* 80 - 95 */
135	0, 10, 11, 12, 13, 14, 15,  0,  0,  0,  0,  0,  0,  0,  0,  0,		/* 96 - 111 */
136	0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0		/* 112 - 127 */
137};
138
139struct res_query_context
140{
141	u_char *answer;
142	size_t anslen;
143	size_t ansmaxlen;
144	uint16_t lastanstype;
145	uint32_t ifnum;
146	uint32_t res_flags;
147	DNSServiceFlags flags;
148	DNSServiceErrorType error;
149};
150
151static void
152res_query_callback(DNSServiceRef sdRef, DNSServiceFlags flags, uint32_t ifIndex, DNSServiceErrorType errorCode, const char *fullname, uint16_t rrtype, uint16_t rrclass, uint16_t rdlen, const void *rdata, uint32_t ttl, void *ctx)
153{
154	struct res_query_context *context;
155	int n;
156	size_t buflen;
157	u_char *dnlist[2], *cp;
158	HEADER *ans;
159	struct in6_addr a6;
160
161	context = (struct res_query_context *)ctx;
162
163	context->flags = flags;
164	context->error = errorCode;
165
166	if (errorCode != kDNSServiceErr_NoError)
167	{
168		if (context->res_flags & RES_DEBUG) printf(";; res_query_mDNSResponder callback [%s %hu %hu]: error %u\n", fullname, rrtype, rrclass, errorCode);
169		return;
170	}
171
172	buflen = context->ansmaxlen - context->anslen;
173	if (buflen < NS_HFIXEDSZ)
174	{
175		if (context->res_flags & RES_DEBUG) printf(";; res_query_mDNSResponder callback [%s %hu %hu]: malformed reply\n", fullname, rrtype, rrclass);
176		return;
177	}
178
179	dnlist[0] = context->answer + NS_HFIXEDSZ;
180	dnlist[1] = NULL;
181
182	cp = context->answer + context->anslen;
183
184	n = dn_comp((char *)fullname, cp, buflen, dnlist, &dnlist[1]);
185	if (n < 0)
186	{
187		if (context->res_flags & RES_DEBUG) printf(";; res_query_mDNSResponder callback [%s %hu %hu]: name mismatch\n", fullname, rrtype, rrclass);
188		return;
189	}
190
191	/*
192	 * Check that there is enough space in the buffer for the resource name (n),
193	 * the resource record data (rdlen) and the resource record header (10).
194	 */
195	if (buflen < n + rdlen + 10)
196	{
197		if (context->res_flags & RES_DEBUG) printf(";; res_query_mDNSResponder callback [%s %hu %hu]: insufficient buffer space for reply\n", fullname, rrtype, rrclass);
198		return;
199	}
200
201	if (context->res_flags & RES_DEBUG) printf(";; res_query_mDNSResponder callback for %s %hu %hu\n", fullname, rrtype, rrclass);
202
203	cp += n;
204	buflen -= n;
205
206	putshort(rrtype, cp);
207	cp += sizeof(uint16_t);
208
209	putshort(rrclass, cp);
210	cp += sizeof(uint16_t);
211
212	putlong(ttl, cp);
213	cp += sizeof(uint32_t);
214
215	putshort(rdlen, cp);
216	cp += sizeof(uint16_t);
217
218	memcpy(cp, rdata, rdlen);
219	cp += rdlen;
220
221	ans = (HEADER *)context->answer;
222	ans->ancount = htons(ntohs(ans->ancount) + 1);
223
224	context->anslen = (size_t)(cp - context->answer);
225
226	context->lastanstype = rrtype;
227
228	/*
229	 * Save the interface number for the first AAAA record for link-local addresses.
230	 * It's used by getaddrinfo to set the scope id.
231	 */
232	if ((context->ifnum == 0) && (rrtype == ns_t_aaaa))
233	{
234		memset(&a6, 0, sizeof(struct in6_addr));
235		memcpy(&a6, rdata, sizeof(struct in6_addr));
236		if (IN6_IS_ADDR_LINKLOCAL(&a6)) context->ifnum = ifIndex;
237	}
238}
239
240static void
241h_errno_for_dnssd_err(DNSServiceErrorType dnssd_err, int *h_errno_err)
242{
243	switch (dnssd_err)
244	{
245		case kDNSServiceErr_NoError:
246			*h_errno_err = NETDB_SUCCESS;
247			break;
248		case kDNSServiceErr_Unknown:
249			*h_errno_err = NO_RECOVERY;
250			break;
251		case kDNSServiceErr_NoSuchRecord:
252			*h_errno_err = NO_DATA;
253			break;
254		case kDNSServiceErr_NoSuchName:
255			*h_errno_err = HOST_NOT_FOUND;
256			break;
257		case kDNSServiceErr_NoMemory:
258		default:
259			*h_errno_err = NETDB_INTERNAL;
260			break;
261	}
262}
263
264static int
265_is_rev_link_local(const char *name)
266{
267	int len, i;
268
269	if (name == NULL) return 0;
270
271	len = strlen(name);
272	if (len == 0) return 0;
273
274	/* check for trailing '.' */
275	if (name[len - 1] == '.') len--;
276
277	if (len != IPv6_REVERSE_LEN) return 0;
278
279	i = IPv6_REVERSE_LINK_LOCAL_TRAILING_CHAR;
280	if ((name[i] != '8') && (name[i] != '9') && (name[i] != 'A') && (name[i] != 'a') && (name[i] != 'B') && (name[i] != 'b')) return 0;
281
282	i = IPv6_REVERSE_LINK_LOCAL_TRAILING_CHAR + 1;
283	if (strncasecmp(name + i, ".e.f.ip6.arpa", 13)) return 0;
284
285	for (i = 0; i < IPv6_REVERSE_LINK_LOCAL_TRAILING_CHAR; i += 2)
286	{
287		if (name[i] < '0') return 0;
288		if ((name[i] > '9') && (name[i] < 'A')) return 0;
289		if ((name[i] > 'F') && (name[i] < 'a')) return 0;
290		if (name[i] > 'f') return 0;
291		if (name[i + 1] != '.') return 0;
292	}
293
294	return 1;
295}
296
297int
298res_query_mDNSResponder(res_state statp, const char *name, int class, int type, u_char *answer, int anslen, struct sockaddr *from, uint32_t *fromlen)
299{
300	DNSServiceRef sdRef;
301	DNSServiceErrorType result;
302	struct res_query_context context;
303	int i, kq, n, wait, cancelled, notify_token, status;
304	struct kevent mevent, ievent, event;
305	struct timeval ctv;
306	struct timespec now, finish, timeout;
307	HEADER *ans;
308	uint32_t iface;
309	uint16_t nibble;
310	char *qname, *notify_name;
311	int *interrupt_pipe;
312	uint64_t exit_requested;
313
314	interrupt_pipe = NULL;
315	result = 0;
316	kq = -1;
317	ans = (HEADER *)answer;
318	cancelled = 0;
319	ans->rcode = 0;
320
321	memset(&context, 0, sizeof(struct res_query_context));
322
323	/* Build a dummy DNS header with question for the answer */
324	context.res_flags = statp->options;
325	context.answer = answer;
326	context.ansmaxlen = anslen;
327	context.anslen = res_nmkquery(statp, ns_o_query, name, class, type, NULL, 0, NULL, answer, anslen);
328	if (context.anslen <= 0) return 0;
329
330	/* Mark DNS packet as a response */
331	ans->qr = 1;
332	ans->qr = htons(ans->qr);
333
334	/* Pull out Scope ID in link-local reverse queries */
335	qname = (char *)name;
336	iface = 0;
337	if (_is_rev_link_local(name))
338	{
339		/* _is_rev_link_local rejects chars > 127 so it's safe to index into hexval */
340		i = IPv6_REVERSE_LINK_LOCAL_SCOPE_ID_LOW;
341		nibble = hexval[(uint32_t)name[i]];
342		iface = nibble;
343
344		i += 2;
345		nibble = hexval[(uint32_t)name[i]];
346		iface += (nibble << 4);
347
348		i += 2;
349		nibble = hexval[(uint32_t)name[i]];
350		iface += (nibble << 8);
351
352		i += 2;
353		nibble = hexval[(uint32_t)name[i]];
354		iface += (nibble << 12);
355
356		if (iface != 0)
357		{
358			qname = strdup(name);
359			if (qname == NULL)
360			{
361				h_errno = NO_RECOVERY;
362				errno = ENOMEM;
363				return -1;
364			}
365
366			i = IPv6_REVERSE_LINK_LOCAL_SCOPE_ID_LOW;
367			qname[i] = '0';
368			qname[i + 2] = '0';
369			qname[i + 4] = '0';
370			qname[i + 6] = '0';
371		}
372	}
373
374	if (statp->options & RES_DEBUG) printf(";; res_query_mDNSResponder\n");
375
376	result = DNSServiceQueryRecord(&sdRef, kDNSServiceFlagsReturnIntermediates, iface, qname, type, class, res_query_callback, &context);
377	if (iface != 0) free(qname);
378
379	if (result != 0) return 0;
380
381	/* Use a kqueue to wait for a response from mDNSResponder */
382	kq = kqueue();
383
384	/* determine the maximum time to wait for a result */
385	gettimeofday(&ctv, NULL);
386
387	/* N.B. statp->retrans is actually the total timeount in seconds */
388	timeout.tv_sec = statp->retrans;
389	timeout.tv_nsec = 0;
390
391	finish.tv_sec = ctv.tv_sec + statp->retrans;
392	finish.tv_nsec = ctv.tv_usec * 1000;
393
394	/* add mdns reply FD to kqueue */
395	EV_SET(&mevent, DNSServiceRefSockFD(sdRef), EVFILT_READ, EV_ADD, 0, 0, 0);
396	n = kevent(kq, &mevent, 1, NULL, 0, NULL);
397	if (n != 0) return 0;
398
399	/* add interrupt pipe to kqueue if interrupt is enabled */
400	if (interrupt_pipe_enabled != 0)
401	{
402		interrupt_pipe = pthread_getspecific(interrupt_pipe_key);
403		if (interrupt_pipe != NULL)
404		{
405			if (interrupt_pipe[0] >= 0)
406			{
407				EV_SET(&ievent, interrupt_pipe[0], EVFILT_READ, EV_ADD, 0, 0, (void *)name);
408				/* allow this to fail silently (should never happen, but it would only break interrupts */
409				n = kevent(kq, &ievent, 1, NULL, 0, NULL);
410			}
411		}
412	}
413
414	/*
415	 * Get notification token
416	 * we use a self-notification token to allow a caller
417	 * to signal the thread doing this DNS query to quit.
418	 */
419	notify_name = NULL;
420	notify_token = -1;
421
422	asprintf(&notify_name, "self.thread.%lu", (unsigned long)pthread_self());
423	if (notify_name != NULL)
424	{
425		status = notify_register_plain(notify_name, &notify_token);
426		free(notify_name);
427	}
428
429	wait = 1;
430	while (wait == 1)
431	{
432		memset(&event, 0, sizeof(struct kevent));
433		n = kevent(kq, NULL, 0, &event, 1, &timeout);
434
435		if (notify_token != -1)
436		{
437			exit_requested = 0;
438			status = notify_get_state(notify_token, &exit_requested);
439			if (exit_requested == ThreadStateExitRequested)
440			{
441				/* interrupted */
442				if (statp->options & RES_DEBUG) printf(";; cancelled\n");
443				cancelled = 1;
444				break;
445			}
446		}
447
448		if (n < 0)
449		{
450			if (errno == EINTR) goto keep_waiting;
451			h_errno = NO_RECOVERY;
452			wait = 0;
453		}
454		else if ((n == 0) && (ans->ancount == 0))
455		{
456			h_errno = TRY_AGAIN;
457			wait = 0;
458		}
459		else if (event.udata == (void *)name)
460		{
461			/* interrupted */
462			if (statp->options & RES_DEBUG) printf(";; cancelled\n");
463			cancelled = 1;
464			break;
465		}
466		else
467		{
468			result = DNSServiceProcessResult(sdRef);
469			if ((result != 0) || (context.error != 0))
470			{
471				if (result == 0) result = context.error;
472				h_errno_for_dnssd_err(result, &h_errno);
473				wait = 0;
474			}
475
476			if ((ans->ancount > 0) && ((context.flags & kDNSServiceFlagsMoreComing) == 0) && ((context.lastanstype != ns_t_cname) || (type == ns_t_cname))) wait = 0;
477		}
478
479	keep_waiting:
480
481		if (wait == 1)
482		{
483			/* calculate remaining timeout */
484			gettimeofday(&ctv, NULL);
485
486			now.tv_sec = ctv.tv_sec;
487			now.tv_nsec = ctv.tv_usec * 1000;
488
489			timeout.tv_sec = finish.tv_sec - now.tv_sec;
490			if (finish.tv_nsec >= now.tv_nsec)
491			{
492				timeout.tv_nsec = finish.tv_nsec - now.tv_nsec;
493			}
494			else
495			{
496				timeout.tv_nsec = BILLION - now.tv_nsec + finish.tv_nsec;
497				timeout.tv_sec--;
498			}
499		}
500	}
501
502	if (notify_token != -1) notify_cancel(notify_token);
503	DNSServiceRefDeallocate(sdRef);
504	close(kq);
505
506	if ((ans->ancount == 0) || (cancelled == 1)) context.anslen = -1;
507
508	if ((from != NULL) && (fromlen != NULL) && (context.ifnum != 0))
509	{
510		((struct sockaddr_in6 *)from)->sin6_len = sizeof(struct sockaddr_in6);
511		((struct sockaddr_in6 *)from)->sin6_family = AF_INET6;
512		((struct sockaddr_in6 *)from)->sin6_addr.__u6_addr.__u6_addr8[15] = 1;
513		((struct sockaddr_in6 *)from)->sin6_scope_id = context.ifnum;
514		*fromlen = sizeof(struct sockaddr_in6);
515	}
516
517	return context.anslen;
518}
519
520static int
521res_soa_minimum(const u_char *msg, int len)
522{
523	ns_msg handle;
524	const u_char *eom;
525	uint32_t i;
526	int32_t b;
527	int min, soa_min;
528
529	eom = msg + len;
530
531	handle._msg = msg;
532	handle._eom = eom;
533
534	if (msg + NS_INT16SZ > eom) return -1;
535	NS_GET16(handle._id, msg);
536
537	if (msg + NS_INT16SZ > eom) return -1;
538	NS_GET16(handle._flags, msg);
539
540	for (i = 0; i < ns_s_max; i++)
541	{
542		if (msg + NS_INT16SZ > eom) return -1;
543		NS_GET16(handle._counts[i], msg);
544	}
545
546	if (handle._counts[ns_s_ns] == 0) return -1;
547
548	/* Skip forward to nameserver section */
549	for (i = 0; i < ns_s_ns; i++)
550	{
551		if (handle._counts[i] == 0) handle._sections[i] = NULL;
552		else
553		{
554			b = ns_skiprr(msg, eom, (ns_sect)i, handle._counts[i]);
555			if (b < 0) return -1;
556
557			handle._sections[i] = msg;
558			msg += b;
559		}
560	}
561
562	min = -1;
563	for (i = 0; i < handle._counts[ns_s_ns]; i++)
564	{
565		b = ns_skiprr(msg, eom, ns_s_ns, 1);
566		if (b < 0) return -1;
567
568		memcpy(&soa_min, msg + b - sizeof(int32_t), sizeof(int32_t));
569		soa_min = ntohl(soa_min);
570		if ((i == 0) || (soa_min < min)) min = soa_min;
571		msg += b;
572	}
573
574	return min;
575}
576
577/*
578 * Formulate a normal query, send, and await answer.
579 * Returned answer is placed in supplied buffer "answer".
580 * Perform preliminary check of answer, returning success only
581 * if no error is indicated and the answer count is nonzero.
582 * Return the size of the response on success, -1 on error.
583 * Error number is left in H_ERRNO.
584 *
585 * Caller must parse answer and determine whether it answers the question.
586 */
587__private_extern__ int
588res_nquery_soa_min(res_state statp, const char *name, int class, int type, u_char *answer, int anslen, struct sockaddr *from, int *fromlen, int *min)
589{
590	u_char buf[MAXPACKET];
591	HEADER *hp = (HEADER *) answer;
592	int n;
593	u_int oflags;
594
595	if (min != NULL) *min = -1;
596
597	oflags = statp->_flags;
598
599again:
600	__h_errno_set(statp, 0);
601	hp->rcode = ns_r_noerror;	/* default */
602
603#ifdef DEBUG
604	if (statp->options & RES_DEBUG) printf(";; res_query(%s, %d, %d)\n", name, class, type);
605#endif
606
607	n = res_nmkquery(statp, ns_o_query, name, class, type, NULL, 0, NULL, buf, sizeof(buf));
608#ifdef RES_USE_EDNS0
609	if (n > 0 && (statp->_flags & RES_F_EDNS0ERR) == 0 && (statp->options & (RES_USE_EDNS0|RES_USE_DNSSEC)) != 0)
610		n = res_nopt(statp, n, buf, sizeof(buf), anslen);
611#endif
612	if (n <= 0)
613	{
614#ifdef DEBUG
615		if (statp->options & RES_DEBUG) printf(";; res_query: mkquery failed\n");
616#endif
617		__h_errno_set(statp, NO_RECOVERY);
618		return (n);
619	}
620
621	n = res_nsend_2(statp, buf, n, answer, anslen, from, fromlen);
622	if (n < 0)
623	{
624#ifdef RES_USE_EDNS0
625		/* if the query choked with EDNS0, retry without EDNS0 */
626		if ((statp->options & (RES_USE_EDNS0|RES_USE_DNSSEC)) != 0 && ((oflags ^ statp->_flags) & RES_F_EDNS0ERR) != 0)
627		{
628			statp->_flags |= RES_F_EDNS0ERR;
629			if (statp->options & RES_DEBUG) printf(";; res_nquery: retry without EDNS0\n");
630			goto again;
631		}
632#endif
633#ifdef DEBUG
634		if (statp->options & RES_DEBUG) printf(";; res_query: send error\n");
635#endif
636		__h_errno_set(statp, TRY_AGAIN);
637		return (n);
638	}
639
640	if ((hp->rcode == ns_r_nxdomain) || ((hp->rcode == ns_r_noerror) && (ntohs(hp->ancount) == 0)))
641	{
642		if (min != NULL)
643		{
644			*min = res_soa_minimum(answer, anslen);
645			if (statp->options & RES_DEBUG) printf(";; res_nquery: SOA minimum TTL = %d\n", *min);
646		}
647	}
648
649	if (hp->rcode != ns_r_noerror || ntohs(hp->ancount) == 0)
650	{
651#ifdef DEBUG
652		if (statp->options & RES_DEBUG) printf(";; rcode = %d, ancount=%d\n", hp->rcode, ntohs(hp->ancount));
653#endif
654		switch (hp->rcode)
655		{
656			case ns_r_nxdomain:
657				__h_errno_set(statp, HOST_NOT_FOUND);
658				break;
659			case ns_r_servfail:
660				__h_errno_set(statp, TRY_AGAIN);
661				break;
662			case ns_r_noerror:
663				__h_errno_set(statp, NO_DATA);
664				break;
665			case ns_r_formerr:
666			case ns_r_notimpl:
667			case ns_r_refused:
668			default:
669				__h_errno_set(statp, NO_RECOVERY);
670				break;
671		}
672
673		return (-1);
674	}
675
676	return (n);
677}
678
679int
680res_nquery_2(res_state statp, const char *name, int class, int type, u_char *answer, int anslen, struct sockaddr *from, int *fromlen)
681{
682	int unused = 0;
683
684	return res_nquery_soa_min(statp, name, class, type, answer, anslen, from, fromlen, &unused);
685}
686
687int
688res_nquery(res_state statp, const char *name, int class, int type, u_char *answer, int anslen)
689{
690	struct sockaddr_storage f;
691	int l;
692
693	l = sizeof(struct sockaddr_storage);
694
695	return res_nquery_2(statp, name, class, type, answer, anslen, (struct sockaddr *)&f, &l);
696}
697
698/*
699 * Perform a call on res_query on the concatenation of name and domain,
700 * removing a trailing dot from name if domain is NULL.
701 */
702int
703res_nquerydomain_2(res_state statp, const char *name, const char *domain, int class, int type, u_char *answer, int anslen, struct sockaddr *from, int *fromlen)
704{
705	char nbuf[NS_MAXDNAME];
706	const char *longname = nbuf;
707	int n, d;
708
709#ifdef DEBUG
710	if (statp->options & RES_DEBUG) printf(";; res_nquerydomain(%s, %s, %d, %d)\n", name, domain?domain:"<Nil>", class, type);
711#endif
712	if (domain == NULL)
713	{
714		/*
715		 * Check for trailing '.';
716		 * copy without '.' if present.
717		 */
718		n = strlen(name);
719		if (n >= NS_MAXDNAME)
720		{
721			__h_errno_set(statp, NO_RECOVERY);
722			return (-1);
723		}
724
725		n--;
726		if (n >= 0 && name[n] == '.')
727		{
728			strncpy(nbuf, name, n);
729			nbuf[n] = '\0';
730		}
731		else
732		{
733			longname = name;
734		}
735	}
736	else
737	{
738		n = strlen(name);
739		d = strlen(domain);
740		if (n + d + 1 >= NS_MAXDNAME)
741		{
742			__h_errno_set(statp, NO_RECOVERY);
743			return (-1);
744		}
745
746		sprintf(nbuf, "%s.%s", name, domain);
747	}
748
749	return (res_nquery_2(statp, longname, class, type, answer, anslen, from, fromlen));
750}
751
752int
753res_nquerydomain(res_state statp, const char *name, const char *domain, int class, int type, u_char *answer, int anslen)
754{
755	struct sockaddr_storage f;
756	int l;
757
758	l = sizeof(struct sockaddr_storage);
759
760	return res_nquerydomain_2(statp, name, domain, class, type, answer, anslen, (struct sockaddr *)&f, &l);
761}
762
763/*
764 * Formulate a normal query, send, and retrieve answer in supplied buffer.
765 * Return the size of the response on success, -1 on error.
766 * If enabled, implement search rules until answer or unrecoverable failure
767 * is detected.  Error code, if any, is left in H_ERRNO.
768 */
769int
770res_nsearch_2(res_state statp, const char *name, int class, int type, u_char *answer, int anslen, struct sockaddr *from, int *fromlen)
771{
772	const char *cp, * const *domain;
773	HEADER *hp = (HEADER *) answer;
774	char tmp[NS_MAXDNAME];
775	u_int dots;
776	int trailing_dot, ret, saved_herrno;
777	int got_nodata = 0, got_servfail = 0, root_on_list = 0;
778	int tried_as_is = 0;
779	int searched = 0;
780
781	errno = 0;
782	__h_errno_set(statp, HOST_NOT_FOUND);  /* True if we never query. */
783
784	dots = 0;
785	for (cp = name; *cp != '\0'; cp++) dots += (*cp == '.');
786
787	trailing_dot = 0;
788	if (cp > name && *--cp == '.') trailing_dot++;
789
790	/* If there aren't any dots, it could be a user-level alias. */
791	if (!dots && (cp = res_hostalias(statp, name, tmp, sizeof tmp))!= NULL)
792		return (res_nquery(statp, cp, class, type, answer, anslen));
793
794	/*
795	 * If there are enough dots in the name, let's just give it a
796	 * try 'as is'. The threshold can be set with the "ndots" option.
797	 * Also, query 'as is', if there is a trailing dot in the name.
798	 */
799	saved_herrno = -1;
800	if (dots >= statp->ndots || trailing_dot)
801	{
802		ret = res_nquerydomain_2(statp, name, NULL, class, type, answer, anslen, from, fromlen);
803		if (ret > 0 || trailing_dot) return (ret);
804		saved_herrno = h_errno;
805		tried_as_is++;
806	}
807
808	/*
809	 * We do at least one level of search if
810	 *	- there is no dot and RES_DEFNAME is set, or
811	 *	- there is at least one dot, there is no trailing dot,
812	 *	  and RES_DNSRCH is set.
813	 */
814	if ((!dots && (statp->options & RES_DEFNAMES) != 0) || (dots && !trailing_dot && (statp->options & RES_DNSRCH) != 0))
815	{
816		int done = 0;
817
818		for (domain = (const char * const *)statp->dnsrch; *domain && !done; domain++)
819		{
820			searched = 1;
821
822			if (domain[0][0] == '\0' || (domain[0][0] == '.' && domain[0][1] == '\0')) root_on_list++;
823
824			ret = res_nquerydomain_2(statp, name, *domain, class, type, answer, anslen, from, fromlen);
825			if (ret > 0) return (ret);
826
827			/*
828			 * If no server present, give up.
829			 * If name isn't found in this domain,
830			 * keep trying higher domains in the search list
831			 * (if that's enabled).
832			 * On a NO_DATA error, keep trying, otherwise
833			 * a wildcard entry of another type could keep us
834			 * from finding this entry higher in the domain.
835			 * If we get some other error (negative answer or
836			 * server failure), then stop searching up,
837			 * but try the input name below in case it's
838			 * fully-qualified.
839			 */
840			if (errno == ECONNREFUSED)
841			{
842				__h_errno_set(statp, TRY_AGAIN);
843				return (-1);
844			}
845
846			switch (statp->res_h_errno)
847			{
848				case NO_DATA:
849					got_nodata++;
850					/* FALLTHROUGH */
851				case HOST_NOT_FOUND:
852					/* keep trying */
853					break;
854				case TRY_AGAIN:
855					if (hp->rcode == ns_r_refused)
856					{
857						/* try next search element, if any */
858						got_servfail++;
859						break;
860					}
861					/* FALLTHROUGH */
862				default:
863					/* anything else implies that we're done */
864					done++;
865			}
866
867			/* if we got here for some reason other than DNSRCH,
868			 * we only wanted one iteration of the loop, so stop.
869			 */
870			if ((statp->options & RES_DNSRCH) == 0) done++;
871		}
872	}
873
874	/*
875	 * If the query has not already been tried as is then try it
876	 * unless RES_NOTLDQUERY is set and there were no dots.
877	 */
878	if ((dots || !searched || (statp->options & RES_NOTLDQUERY) == 0) && !(tried_as_is || root_on_list))
879	{
880		ret = res_nquerydomain_2(statp, name, NULL, class, type, answer, anslen, from, fromlen);
881		if (ret > 0) return (ret);
882	}
883
884	/* if we got here, we didn't satisfy the search.
885	 * if we did an initial full query, return that query's H_ERRNO
886	 * (note that we wouldn't be here if that query had succeeded).
887	 * else if we ever got a nodata, send that back as the reason.
888	 * else send back meaningless H_ERRNO, that being the one from
889	 * the last DNSRCH we did.
890	 */
891	if (saved_herrno != -1)
892		__h_errno_set(statp, saved_herrno);
893	else if (got_nodata)
894		__h_errno_set(statp, NO_DATA);
895	else if (got_servfail)
896		__h_errno_set(statp, TRY_AGAIN);
897	return (-1);
898}
899
900int
901__res_nsearch_list_2(res_state statp, const char *name,	int class, int type, u_char *answer, int anslen, struct sockaddr *from, int *fromlen, int nsearch, char **search)
902{
903	const char *cp, *domain;
904	HEADER *hp = (HEADER *) answer;
905	char tmp[NS_MAXDNAME];
906	u_int dots;
907	int trailing_dot, ret, saved_herrno, i;
908	int got_nodata = 0, got_servfail = 0, root_on_list = 0;
909	int tried_as_is = 0;
910	int searched = 0;
911
912	errno = 0;
913	__h_errno_set(statp, HOST_NOT_FOUND);  /* True if we never query. */
914
915	dots = 0;
916	for (cp = name; *cp != '\0'; cp++) dots += (*cp == '.');
917
918	trailing_dot = 0;
919	if (cp > name && *--cp == '.') trailing_dot++;
920
921	/* If there aren't any dots, it could be a user-level alias. */
922	if (!dots && (cp = res_hostalias(statp, name, tmp, sizeof tmp)) != NULL)
923		return (res_nquery(statp, cp, class, type, answer, anslen));
924
925	/*
926	 * If there are enough dots in the name, let's just give it a
927	 * try 'as is'. The threshold can be set with the "ndots" option.
928	 * Also, query 'as is', if there is a trailing dot in the name.
929	 */
930	saved_herrno = -1;
931	if (dots >= statp->ndots || trailing_dot)
932	{
933		ret = res_nquerydomain(statp, name, NULL, class, type, answer, anslen);
934		if (ret > 0 || trailing_dot) return ret;
935		saved_herrno = h_errno;
936		tried_as_is++;
937	}
938
939	/*
940	 * We do at least one level of search if
941	 *	- there is no dot and RES_DEFNAME is set, or
942	 *	- there is at least one dot, there is no trailing dot,
943	 *	  and RES_DNSRCH is set.
944	 */
945	if ((!dots && (statp->options & RES_DEFNAMES) != 0) || (dots && !trailing_dot && (statp->options & RES_DNSRCH) != 0))
946	{
947		int done = 0;
948
949		for (i = 0; i < nsearch; i++)
950		{
951			domain = search[i];
952			searched = 1;
953
954			if (domain[0] == '\0' || (domain[0] == '.' && domain[1] == '\0')) root_on_list++;
955
956			ret = res_nquerydomain_2(statp, name, domain, class, type, answer, anslen, from, fromlen);
957			if (ret > 0) return ret;
958
959			/*
960			 * If no server present, give up.
961			 * If name isn't found in this domain,
962			 * keep trying higher domains in the search list
963			 * (if that's enabled).
964			 * On a NO_DATA error, keep trying, otherwise
965			 * a wildcard entry of another type could keep us
966			 * from finding this entry higher in the domain.
967			 * If we get some other error (negative answer or
968										   * server failure), then stop searching up,
969			 * but try the input name below in case it's
970			 * fully-qualified.
971			 */
972			if (errno == ECONNREFUSED)
973			{
974				__h_errno_set(statp, TRY_AGAIN);
975				return -1;
976			}
977
978			switch (statp->res_h_errno)
979			{
980				case NO_DATA:
981					got_nodata++;
982					/* FALLTHROUGH */
983				case HOST_NOT_FOUND:
984					/* keep trying */
985					break;
986				case TRY_AGAIN:
987					if (hp->rcode == ns_r_refused)
988					{
989						/* try next search element, if any */
990						got_servfail++;
991						break;
992					}
993					/* FALLTHROUGH */
994				default:
995					/* anything else implies that we're done */
996					done++;
997			}
998
999			/*
1000			 * if we got here for some reason other than DNSRCH,
1001			 * we only wanted one iteration of the loop, so stop.
1002			 */
1003			if ((statp->options & RES_DNSRCH) == 0) done++;
1004		}
1005	}
1006
1007	/*
1008	 * If the query has not already been tried as is then try it
1009	 * unless RES_NOTLDQUERY is set and there were no dots.
1010	 */
1011	if ((dots || !searched || (statp->options & RES_NOTLDQUERY) == 0) && !(tried_as_is || root_on_list))
1012	{
1013		ret = res_nquerydomain_2(statp, name, NULL, class, type, answer, anslen, from, fromlen);
1014		if (ret > 0) return ret;
1015	}
1016
1017	/*
1018	 * we got here, we didn't satisfy the search.
1019	 * if we did an initial full query, return that query's H_ERRNO
1020	 * (note that we wouldn't be here if that query had succeeded).
1021	 * else if we ever got a nodata, send that back as the reason.
1022	 * else send back meaningless H_ERRNO, that being the one from
1023	 * the last DNSRCH we did.
1024	 */
1025	if (saved_herrno != -1) __h_errno_set(statp, saved_herrno);
1026	else if (got_nodata) __h_errno_set(statp, NO_DATA);
1027	else if (got_servfail) __h_errno_set(statp, TRY_AGAIN);
1028	return -1;
1029}
1030
1031int
1032res_nsearch(res_state statp, const char *name, int class, int type, u_char *answer, int anslen)
1033{
1034	struct sockaddr_storage f;
1035	int l;
1036
1037	l = sizeof(struct sockaddr_storage);
1038
1039	return res_nsearch_2(statp, name, class, type, answer, anslen, (struct sockaddr *)&f, &l);
1040}
1041
1042const char *
1043res_hostalias(const res_state statp, const char *name, char *dst, size_t siz)
1044{
1045	char *file, *cp1, *cp2;
1046	char buf[BUFSIZ];
1047	FILE *fp;
1048
1049	if (statp->options & RES_NOALIASES) return (NULL);
1050
1051	file = getenv("HOSTALIASES");
1052	if (file == NULL || (fp = fopen(file, "r")) == NULL) return (NULL);
1053
1054	setbuf(fp, NULL);
1055	buf[sizeof(buf) - 1] = '\0';
1056	while (fgets(buf, sizeof(buf), fp))
1057	{
1058		for (cp1 = buf; *cp1 && !isspace((unsigned char)*cp1); ++cp1) ;
1059
1060		if (!*cp1) break;
1061		*cp1 = '\0';
1062
1063		if (ns_samename(buf, name) == 1)
1064		{
1065			while (isspace((unsigned char)*++cp1)) ;
1066
1067			if (!*cp1) break;
1068
1069			for (cp2 = cp1 + 1; *cp2 && !isspace((unsigned char)*cp2); ++cp2) ;
1070
1071			*cp2 = '\0';
1072			strncpy(dst, cp1, siz - 1);
1073			dst[siz - 1] = '\0';
1074			fclose(fp);
1075			return (dst);
1076		}
1077	}
1078
1079	fclose(fp);
1080	return (NULL);
1081}
1082