Libinfo-173.tar.gz
[apple/libinfo.git] / dns.subproj / res_query.c
1 /*
2 * Copyright (c) 1999 Apple Computer, Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * Copyright (c) 1999-2003 Apple Computer, Inc. All Rights Reserved.
7 *
8 * This file contains Original Code and/or Modifications of Original Code
9 * as defined in and that are subject to the Apple Public Source License
10 * Version 2.0 (the 'License'). You may not use this file except in
11 * compliance with the License. Please obtain a copy of the License at
12 * http://www.opensource.apple.com/apsl/ and read it before using this
13 * file.
14 *
15 * The Original Code and all software distributed under the License are
16 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
17 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
18 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
19 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
20 * Please see the License for the specific language governing rights and
21 * limitations under the License.
22 *
23 * @APPLE_LICENSE_HEADER_END@
24 */
25 /*
26 * ++Copyright++ 1988, 1993
27 * -
28 * Copyright (c) 1988, 1993
29 * The Regents of the University of California. All rights reserved.
30 *
31 * Redistribution and use in source and binary forms, with or without
32 * modification, are permitted provided that the following conditions
33 * are met:
34 * 1. Redistributions of source code must retain the above copyright
35 * notice, this list of conditions and the following disclaimer.
36 * 2. Redistributions in binary form must reproduce the above copyright
37 * notice, this list of conditions and the following disclaimer in the
38 * documentation and/or other materials provided with the distribution.
39 * 3. All advertising materials mentioning features or use of this software
40 * must display the following acknowledgement:
41 * This product includes software developed by the University of
42 * California, Berkeley and its contributors.
43 * 4. Neither the name of the University nor the names of its contributors
44 * may be used to endorse or promote products derived from this software
45 * without specific prior written permission.
46 *
47 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
48 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
49 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
50 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
51 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
52 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
53 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
54 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
55 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
56 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
57 * SUCH DAMAGE.
58 * -
59 * Portions Copyright (c) 1993 by Digital Equipment Corporation.
60 *
61 * Permission to use, copy, modify, and distribute this software for any
62 * purpose with or without fee is hereby granted, provided that the above
63 * copyright notice and this permission notice appear in all copies, and that
64 * the name of Digital Equipment Corporation not be used in advertising or
65 * publicity pertaining to distribution of the document or software without
66 * specific, written prior permission.
67 *
68 * THE SOFTWARE IS PROVIDED "AS IS" AND DIGITAL EQUIPMENT CORP. DISCLAIMS ALL
69 * WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES
70 * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL DIGITAL EQUIPMENT
71 * CORPORATION BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
72 * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
73 * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
74 * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
75 * SOFTWARE.
76 * -
77 * --Copyright--
78 */
79
80 #if defined(LIBC_SCCS) && !defined(lint)
81 static char sccsid[] = "@(#)res_query.c 8.1 (Berkeley) 6/4/93";
82 static char rcsid[] = "$Id: res_query.c,v 1.3 2003/02/18 17:29:24 majka Exp $";
83 #endif /* LIBC_SCCS and not lint */
84
85 #include <sys/param.h>
86 #include <netinet/in.h>
87 #include <arpa/inet.h>
88 #include <arpa/nameser8_compat.h>
89
90 #include <stdio.h>
91 #include <netdb.h>
92 #include <resolv8_compat.h>
93 #include <ctype.h>
94 #include <errno.h>
95 #if defined(BSD) && (BSD >= 199306)
96 # include <stdlib.h>
97 # include <string.h>
98 #else
99 # include "portability.h"
100 #endif
101
102 #if defined(USE_OPTIONS_H)
103 # include "options.h"
104 #endif
105
106 #if PACKETSZ > 1024
107 #define MAXPACKET PACKETSZ
108 #else
109 #define MAXPACKET 1024
110 #endif
111
112 char *__hostalias __P((const char *));
113 #if defined(__APPLE__)
114 extern
115 #endif
116 int h_errno;
117
118 /*
119 * Formulate a normal query, send, and await answer.
120 * Returned answer is placed in supplied buffer "answer".
121 * Perform preliminary check of answer, returning success only
122 * if no error is indicated and the answer count is nonzero.
123 * Return the size of the response on success, -1 on error.
124 * Error number is left in h_errno.
125 *
126 * Caller must parse answer and determine whether it answers the question.
127 */
128 int
129 res_query(name, class, type, answer, anslen)
130 const char *name; /* domain name */
131 int class, type; /* class and type of query */
132 u_char *answer; /* buffer to put answer */
133 int anslen; /* size of answer buffer */
134 {
135 u_char buf[MAXPACKET];
136 register HEADER *hp = (HEADER *) answer;
137 int n;
138
139 hp->rcode = NOERROR; /* default */
140
141 if ((_res.options & RES_INIT) == 0 && res_init() == -1) {
142 h_errno = NETDB_INTERNAL;
143 return (-1);
144 }
145 #ifdef DEBUG
146 if (_res.options & RES_DEBUG)
147 printf(";; res_query(%s, %d, %d)\n", name, class, type);
148 #endif
149
150 n = res_mkquery(QUERY, name, class, type, NULL, 0, NULL,
151 buf, sizeof(buf));
152 if (n <= 0) {
153 #ifdef DEBUG
154 if (_res.options & RES_DEBUG)
155 printf(";; res_query: mkquery failed\n");
156 #endif
157 h_errno = NO_RECOVERY;
158 return (n);
159 }
160 n = res_send(buf, n, answer, anslen);
161 if (n < 0) {
162 #ifdef DEBUG
163 if (_res.options & RES_DEBUG)
164 printf(";; res_query: send error\n");
165 #endif
166 h_errno = TRY_AGAIN;
167 return (n);
168 }
169
170 if (hp->rcode != NOERROR || ntohs(hp->ancount) == 0) {
171 #ifdef DEBUG
172 if (_res.options & RES_DEBUG)
173 printf(";; rcode = %d, ancount=%d\n", hp->rcode,
174 ntohs(hp->ancount));
175 #endif
176 switch (hp->rcode) {
177 case NXDOMAIN:
178 h_errno = HOST_NOT_FOUND;
179 break;
180 case SERVFAIL:
181 h_errno = TRY_AGAIN;
182 break;
183 case NOERROR:
184 h_errno = NO_DATA;
185 break;
186 case FORMERR:
187 case NOTIMP:
188 case REFUSED:
189 default:
190 h_errno = NO_RECOVERY;
191 break;
192 }
193 return (-1);
194 }
195 return (n);
196 }
197
198 /*
199 * Formulate a normal query, send, and retrieve answer in supplied buffer.
200 * Return the size of the response on success, -1 on error.
201 * If enabled, implement search rules until answer or unrecoverable failure
202 * is detected. Error code, if any, is left in h_errno.
203 */
204 int
205 res_search(name, class, type, answer, anslen)
206 const char *name; /* domain name */
207 int class, type; /* class and type of query */
208 u_char *answer; /* buffer to put answer */
209 int anslen; /* size of answer */
210 {
211 register const char *cp, * const *domain;
212 HEADER *hp = (HEADER *) answer;
213 u_int dots;
214 int trailing_dot, ret, saved_herrno;
215 int got_nodata = 0, got_servfail = 0, tried_as_is = 0;
216
217 if ((_res.options & RES_INIT) == 0 && res_init() == -1) {
218 h_errno = NETDB_INTERNAL;
219 return (-1);
220 }
221 errno = 0;
222 h_errno = HOST_NOT_FOUND; /* default, if we never query */
223 dots = 0;
224 for (cp = name; *cp; cp++)
225 dots += (*cp == '.');
226 trailing_dot = 0;
227 if (cp > name && *--cp == '.')
228 trailing_dot++;
229
230 /*
231 * if there aren't any dots, it could be a user-level alias
232 */
233 if (!dots && (cp = __hostalias(name)) != NULL)
234 return (res_query(cp, class, type, answer, anslen));
235
236 /*
237 * If there are dots in the name already, let's just give it a try
238 * 'as is'. The threshold can be set with the "ndots" option.
239 */
240 saved_herrno = -1;
241 if (dots >= _res.ndots) {
242 ret = res_querydomain(name, NULL, class, type, answer, anslen);
243 if (ret > 0)
244 return (ret);
245 saved_herrno = h_errno;
246 tried_as_is++;
247 }
248
249 /*
250 * We do at least one level of search if
251 * - there is no dot and RES_DEFNAME is set, or
252 * - there is at least one dot, there is no trailing dot,
253 * and RES_DNSRCH is set.
254 */
255 if ((!dots && (_res.options & RES_DEFNAMES)) ||
256 (dots && !trailing_dot && (_res.options & RES_DNSRCH))) {
257 int done = 0;
258
259 for (domain = (const char * const *)_res.dnsrch;
260 *domain && !done;
261 domain++) {
262
263 ret = res_querydomain(name, *domain, class, type,
264 answer, anslen);
265 if (ret > 0)
266 return (ret);
267
268 /*
269 * If no server present, give up.
270 * If name isn't found in this domain,
271 * keep trying higher domains in the search list
272 * (if that's enabled).
273 * On a NO_DATA error, keep trying, otherwise
274 * a wildcard entry of another type could keep us
275 * from finding this entry higher in the domain.
276 * If we get some other error (negative answer or
277 * server failure), then stop searching up,
278 * but try the input name below in case it's
279 * fully-qualified.
280 */
281 if (errno == ECONNREFUSED) {
282 h_errno = TRY_AGAIN;
283 return (-1);
284 }
285
286 switch (h_errno) {
287 case NO_DATA:
288 got_nodata++;
289 /* FALLTHROUGH */
290 case HOST_NOT_FOUND:
291 /* keep trying */
292 break;
293 case TRY_AGAIN:
294 if (hp->rcode == SERVFAIL) {
295 /* try next search element, if any */
296 got_servfail++;
297 break;
298 }
299 /* FALLTHROUGH */
300 default:
301 /* anything else implies that we're done */
302 done++;
303 }
304
305 /* if we got here for some reason other than DNSRCH,
306 * we only wanted one iteration of the loop, so stop.
307 */
308 if (!(_res.options & RES_DNSRCH))
309 done++;
310 }
311 }
312
313 /* if we have not already tried the name "as is", do that now.
314 * note that we do this regardless of how many dots were in the
315 * name or whether it ends with a dot.
316 */
317 if (!tried_as_is) {
318 ret = res_querydomain(name, NULL, class, type, answer, anslen);
319 if (ret > 0)
320 return (ret);
321 }
322
323 /* if we got here, we didn't satisfy the search.
324 * if we did an initial full query, return that query's h_errno
325 * (note that we wouldn't be here if that query had succeeded).
326 * else if we ever got a nodata, send that back as the reason.
327 * else send back meaningless h_errno, that being the one from
328 * the last DNSRCH we did.
329 */
330 if (saved_herrno != -1)
331 h_errno = saved_herrno;
332 else if (got_nodata)
333 h_errno = NO_DATA;
334 else if (got_servfail)
335 h_errno = TRY_AGAIN;
336 return (-1);
337 }
338
339 /*
340 * Perform a call on res_query on the concatenation of name and domain,
341 * removing a trailing dot from name if domain is NULL.
342 */
343 int
344 res_querydomain(name, domain, class, type, answer, anslen)
345 const char *name, *domain;
346 int class, type; /* class and type of query */
347 u_char *answer; /* buffer to put answer */
348 int anslen; /* size of answer */
349 {
350 char nbuf[2*MAXDNAME+2];
351 const char *longname = nbuf;
352 int n;
353
354 if ((_res.options & RES_INIT) == 0 && res_init() == -1) {
355 h_errno = NETDB_INTERNAL;
356 return (-1);
357 }
358 #ifdef DEBUG
359 if (_res.options & RES_DEBUG)
360 printf(";; res_querydomain(%s, %s, %d, %d)\n",
361 name, domain?domain:"<Nil>", class, type);
362 #endif
363 if (domain == NULL) {
364 /*
365 * Check for trailing '.';
366 * copy without '.' if present.
367 */
368 n = strlen(name) - 1;
369 if (n != (0 - 1) && name[n] == '.' && n < sizeof(nbuf) - 1) {
370 bcopy(name, nbuf, n);
371 nbuf[n] = '\0';
372 } else
373 longname = name;
374 } else
375 sprintf(nbuf, "%.*s.%.*s", MAXDNAME, name, MAXDNAME, domain);
376
377 return (res_query(longname, class, type, answer, anslen));
378 }
379
380 char *
381 __hostalias(name)
382 register const char *name;
383 {
384 register char *cp1, *cp2;
385 FILE *fp;
386 char *file;
387 char buf[BUFSIZ];
388 static char abuf[MAXDNAME];
389
390 if (_res.options & RES_NOALIASES)
391 return (NULL);
392 file = getenv("HOSTALIASES");
393 if (file == NULL || (fp = fopen(file, "r")) == NULL)
394 return (NULL);
395 setbuf(fp, NULL);
396 buf[sizeof(buf) - 1] = '\0';
397 while (fgets(buf, sizeof(buf), fp)) {
398 for (cp1 = buf; *cp1 && !isspace(*cp1); ++cp1)
399 ;
400 if (!*cp1)
401 break;
402 *cp1 = '\0';
403 if (!strcasecmp(buf, name)) {
404 while (isspace(*++cp1))
405 ;
406 if (!*cp1)
407 break;
408 for (cp2 = cp1 + 1; *cp2 && !isspace(*cp2); ++cp2)
409 ;
410 abuf[sizeof(abuf) - 1] = *cp2 = '\0';
411 strncpy(abuf, cp1, sizeof(abuf) - 1);
412 fclose(fp);
413 return (abuf);
414 }
415 }
416 fclose(fp);
417 return (NULL);
418 }