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