xref: /csrg-svn/lib/libc/net/res_query.c (revision 46496)
1 /*
2  * Copyright (c) 1988 Regents of the University of California.
3  * All rights reserved.
4  *
5  * %sccs.include.redist.c%
6  */
7 
8 #if defined(LIBC_SCCS) && !defined(lint)
9 static char sccsid[] = "@(#)res_query.c	5.8 (Berkeley) 02/21/91";
10 #endif /* LIBC_SCCS and not lint */
11 
12 #include <sys/param.h>
13 #include <sys/socket.h>
14 #include <netinet/in.h>
15 #include <ctype.h>
16 #include <netdb.h>
17 #include <stdio.h>
18 #include <errno.h>
19 #include <string.h>
20 #include <arpa/inet.h>
21 #include <arpa/nameser.h>
22 #include <resolv.h>
23 
24 #if PACKETSZ > 1024
25 #define MAXPACKET	PACKETSZ
26 #else
27 #define MAXPACKET	1024
28 #endif
29 
30 extern int errno;
31 int h_errno;
32 
33 /*
34  * Formulate a normal query, send, and await answer.
35  * Returned answer is placed in supplied buffer "answer".
36  * Perform preliminary check of answer, returning success only
37  * if no error is indicated and the answer count is nonzero.
38  * Return the size of the response on success, -1 on error.
39  * Error number is left in h_errno.
40  * Caller must parse answer and determine whether it answers the question.
41  */
42 res_query(name, class, type, answer, anslen)
43 	char *name;		/* domain name */
44 	int class, type;	/* class and type of query */
45 	u_char *answer;		/* buffer to put answer */
46 	int anslen;		/* size of answer buffer */
47 {
48 	char buf[MAXPACKET];
49 	HEADER *hp;
50 	int n;
51 
52 	if ((_res.options & RES_INIT) == 0 && res_init() == -1)
53 		return (-1);
54 #ifdef DEBUG
55 	if (_res.options & RES_DEBUG)
56 		printf("res_query(%s, %d, %d)\n", name, class, type);
57 #endif
58 	n = res_mkquery(QUERY, name, class, type, (char *)NULL, 0, NULL,
59 	    buf, sizeof(buf));
60 
61 	if (n <= 0) {
62 #ifdef DEBUG
63 		if (_res.options & RES_DEBUG)
64 			printf("res_query: mkquery failed\n");
65 #endif
66 		h_errno = NO_RECOVERY;
67 		return (n);
68 	}
69 	n = res_send(buf, n, answer, anslen);
70 	if (n < 0) {
71 #ifdef DEBUG
72 		if (_res.options & RES_DEBUG)
73 			printf("res_query: send error\n");
74 #endif
75 		h_errno = TRY_AGAIN;
76 		return(n);
77 	}
78 
79 	hp = (HEADER *) answer;
80 	if (hp->rcode != NOERROR || ntohs(hp->ancount) == 0) {
81 #ifdef DEBUG
82 		if (_res.options & RES_DEBUG)
83 			printf("rcode = %d, ancount=%d\n", hp->rcode,
84 			    ntohs(hp->ancount));
85 #endif
86 		switch (hp->rcode) {
87 			case NXDOMAIN:
88 				h_errno = HOST_NOT_FOUND;
89 				break;
90 			case SERVFAIL:
91 				h_errno = TRY_AGAIN;
92 				break;
93 			case NOERROR:
94 				h_errno = NO_DATA;
95 				break;
96 			case FORMERR:
97 			case NOTIMP:
98 			case REFUSED:
99 			default:
100 				h_errno = NO_RECOVERY;
101 				break;
102 		}
103 		return (-1);
104 	}
105 	return(n);
106 }
107 
108 /*
109  * Formulate a normal query, send, and retrieve answer in supplied buffer.
110  * Return the size of the response on success, -1 on error.
111  * If enabled, implement search rules until answer or unrecoverable failure
112  * is detected.  Error number is left in h_errno.
113  * Only useful for queries in the same name hierarchy as the local host
114  * (not, for example, for host address-to-name lookups in domain in-addr.arpa).
115  */
116 res_search(name, class, type, answer, anslen)
117 	char *name;		/* domain name */
118 	int class, type;	/* class and type of query */
119 	u_char *answer;		/* buffer to put answer */
120 	int anslen;		/* size of answer */
121 {
122 	register char *cp, **domain;
123 	int n, ret, got_nodata = 0;
124 	static char *hostalias();
125 
126 	if ((_res.options & RES_INIT) == 0 && res_init() == -1)
127 		return (-1);
128 
129 	errno = 0;
130 	h_errno = HOST_NOT_FOUND;		/* default, if we never query */
131 	for (cp = name, n = 0; *cp; cp++)
132 		if (*cp == '.')
133 			n++;
134 	if (n == 0 && (cp = hostalias(name)))
135 		return (res_query(cp, class, type, answer, anslen));
136 
137 	/*
138 	 * We do at least one level of search if
139 	 *	- there is no dot and RES_DEFNAME is set, or
140 	 *	- there is at least one dot, there is no trailing dot,
141 	 *	  and RES_DNSRCH is set.
142 	 */
143 	if ((n == 0 && _res.options & RES_DEFNAMES) ||
144 	   (n != 0 && *--cp != '.' && _res.options & RES_DNSRCH))
145 	     for (domain = _res.dnsrch; *domain; domain++) {
146 		ret = res_querydomain(name, *domain, class, type,
147 		    answer, anslen);
148 		if (ret > 0)
149 			return (ret);
150 		/*
151 		 * If no server present, give up.
152 		 * If name isn't found in this domain,
153 		 * keep trying higher domains in the search list
154 		 * (if that's enabled).
155 		 * On a NO_DATA error, keep trying, otherwise
156 		 * a wildcard entry of another type could keep us
157 		 * from finding this entry higher in the domain.
158 		 * If we get some other error (negative answer or
159 		 * server failure), then stop searching up,
160 		 * but try the input name below in case it's fully-qualified.
161 		 */
162 		if (errno == ECONNREFUSED) {
163 			h_errno = TRY_AGAIN;
164 			return (-1);
165 		}
166 		if (h_errno == NO_DATA)
167 			got_nodata++;
168 		if ((h_errno != HOST_NOT_FOUND && h_errno != NO_DATA) ||
169 		    (_res.options & RES_DNSRCH) == 0)
170 			break;
171 	}
172 	/*
173 	 * If the search/default failed, try the name as fully-qualified,
174 	 * but only if it contained at least one dot (even trailing).
175 	 * This is purely a heuristic; we assume that any reasonable query
176 	 * about a top-level domain (for servers, SOA, etc) will not use
177 	 * res_search.
178 	 */
179 	if (n && (ret = res_querydomain(name, (char *)NULL, class, type,
180 	    answer, anslen)) > 0)
181 		return (ret);
182 	if (got_nodata)
183 		h_errno = NO_DATA;
184 	return (-1);
185 }
186 
187 /*
188  * Perform a call on res_query on the concatenation of name and domain,
189  * removing a trailing dot from name if domain is NULL.
190  */
191 res_querydomain(name, domain, class, type, answer, anslen)
192 	char *name, *domain;
193 	int class, type;	/* class and type of query */
194 	u_char *answer;		/* buffer to put answer */
195 	int anslen;		/* size of answer */
196 {
197 	char nbuf[2*MAXDNAME+2];
198 	char *longname = nbuf;
199 	int n;
200 
201 #ifdef DEBUG
202 	if (_res.options & RES_DEBUG)
203 		printf("res_querydomain(%s, %s, %d, %d)\n",
204 		    name, domain, class, type);
205 #endif
206 	if (domain == NULL) {
207 		/*
208 		 * Check for trailing '.';
209 		 * copy without '.' if present.
210 		 */
211 		n = strlen(name) - 1;
212 		if (name[n] == '.' && n < sizeof(nbuf) - 1) {
213 			bcopy(name, nbuf, n);
214 			nbuf[n] = '\0';
215 		} else
216 			longname = name;
217 	} else
218 		(void)sprintf(nbuf, "%.*s.%.*s",
219 		    MAXDNAME, name, MAXDNAME, domain);
220 
221 	return (res_query(longname, class, type, answer, anslen));
222 }
223 
224 static char *
225 hostalias(name)
226 	register char *name;
227 {
228 	register char *C1, *C2;
229 	FILE *fp;
230 	char *file, *getenv(), *strcpy(), *strncpy();
231 	char buf[BUFSIZ];
232 	static char abuf[MAXDNAME];
233 
234 	file = getenv("HOSTALIASES");
235 	if (file == NULL || (fp = fopen(file, "r")) == NULL)
236 		return (NULL);
237 	buf[sizeof(buf) - 1] = '\0';
238 	while (fgets(buf, sizeof(buf), fp)) {
239 		for (C1 = buf; *C1 && !isspace(*C1); ++C1);
240 		if (!*C1)
241 			break;
242 		*C1 = '\0';
243 		if (!strcasecmp(buf, name)) {
244 			while (isspace(*++C1));
245 			if (!*C1)
246 				break;
247 			for (C2 = C1 + 1; *C2 && !isspace(*C2); ++C2);
248 			abuf[sizeof(abuf) - 1] = *C2 = '\0';
249 			(void)strncpy(abuf, C1, sizeof(abuf) - 1);
250 			fclose(fp);
251 			return (abuf);
252 		}
253 	}
254 	fclose(fp);
255 	return (NULL);
256 }
257