diff options
Diffstat (limited to 'lib/libc/net/res_query.c')
-rw-r--r-- | lib/libc/net/res_query.c | 288 |
1 files changed, 188 insertions, 100 deletions
diff --git a/lib/libc/net/res_query.c b/lib/libc/net/res_query.c index 4980af1..8229fbc 100644 --- a/lib/libc/net/res_query.c +++ b/lib/libc/net/res_query.c @@ -1,7 +1,9 @@ -/*- +/* + * ++Copyright++ 1988, 1993 + * - * Copyright (c) 1988, 1993 - * The Regents of the University of California. All rights reserved. - * + * The Regents of the University of California. All rights reserved. + * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: @@ -12,12 +14,12 @@ * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: - * This product includes software developed by the University of - * California, Berkeley and its contributors. + * This product includes software developed by the University of + * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. - * + * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE @@ -53,20 +55,29 @@ #if defined(LIBC_SCCS) && !defined(lint) static char sccsid[] = "@(#)res_query.c 8.1 (Berkeley) 6/4/93"; -static char rcsid[] = "$Id: res_query.c,v 1.1 1993/06/01 09:42:14 vixie Exp vixie $"; +static char rcsid[] = "$Id: res_query.c,v 8.6 1995/06/29 09:26:28 vixie Exp $"; #endif /* LIBC_SCCS and not lint */ #include <sys/param.h> #include <netinet/in.h> #include <arpa/inet.h> #include <arpa/nameser.h> + +#include <stdio.h> #include <netdb.h> #include <resolv.h> -#include <stdio.h> #include <ctype.h> #include <errno.h> -#include <stdlib.h> -#include <string.h> +#if defined(BSD) && (BSD >= 199306) +# include <stdlib.h> +# include <string.h> +#else +# include "../conf/portability.h" +#endif + +#if defined(USE_OPTIONS_H) +# include <../conf/options.h> +#endif #if PACKETSZ > 1024 #define MAXPACKET PACKETSZ @@ -74,6 +85,7 @@ static char rcsid[] = "$Id: res_query.c,v 1.1 1993/06/01 09:42:14 vixie Exp vixi #define MAXPACKET 1024 #endif +char *__hostalias __P((const char *)); int h_errno; /* @@ -83,27 +95,33 @@ int h_errno; * if no error is indicated and the answer count is nonzero. * Return the size of the response on success, -1 on error. * Error number is left in h_errno. + * * Caller must parse answer and determine whether it answers the question. */ +int res_query(name, class, type, answer, anslen) - char *name; /* domain name */ + const char *name; /* domain name */ int class, type; /* class and type of query */ u_char *answer; /* buffer to put answer */ int anslen; /* size of answer buffer */ { - char buf[MAXPACKET]; - HEADER *hp; + u_char buf[MAXPACKET]; + register HEADER *hp = (HEADER *) answer; int n; - if ((_res.options & RES_INIT) == 0 && res_init() == -1) + hp->rcode = NOERROR; /* default */ + + if ((_res.options & RES_INIT) == 0 && res_init() == -1) { + h_errno = NETDB_INTERNAL; return (-1); + } #ifdef DEBUG if (_res.options & RES_DEBUG) printf(";; res_query(%s, %d, %d)\n", name, class, type); #endif - n = res_mkquery(QUERY, name, class, type, (char *)NULL, 0, NULL, - buf, sizeof(buf)); + n = res_mkquery(QUERY, name, class, type, NULL, 0, NULL, + buf, sizeof(buf)); if (n <= 0) { #ifdef DEBUG if (_res.options & RES_DEBUG) @@ -112,17 +130,16 @@ res_query(name, class, type, answer, anslen) h_errno = NO_RECOVERY; return (n); } - n = res_send(buf, n, (char *)answer, anslen); + n = res_send(buf, n, answer, anslen); if (n < 0) { #ifdef DEBUG if (_res.options & RES_DEBUG) printf(";; res_query: send error\n"); #endif h_errno = TRY_AGAIN; - return(n); + return (n); } - hp = (HEADER *) answer; if (hp->rcode != NOERROR || ntohs(hp->ancount) == 0) { #ifdef DEBUG if (_res.options & RES_DEBUG) @@ -130,104 +147,165 @@ res_query(name, class, type, answer, anslen) ntohs(hp->ancount)); #endif switch (hp->rcode) { - case NXDOMAIN: - h_errno = HOST_NOT_FOUND; - break; - case SERVFAIL: - h_errno = TRY_AGAIN; - break; - case NOERROR: - h_errno = NO_DATA; - break; - case FORMERR: - case NOTIMP: - case REFUSED: - default: - h_errno = NO_RECOVERY; - break; + case NXDOMAIN: + h_errno = HOST_NOT_FOUND; + break; + case SERVFAIL: + h_errno = TRY_AGAIN; + break; + case NOERROR: + h_errno = NO_DATA; + break; + case FORMERR: + case NOTIMP: + case REFUSED: + default: + h_errno = NO_RECOVERY; + break; } return (-1); } - return(n); + return (n); } /* * Formulate a normal query, send, and retrieve answer in supplied buffer. * Return the size of the response on success, -1 on error. * If enabled, implement search rules until answer or unrecoverable failure - * is detected. Error number is left in h_errno. - * Only useful for queries in the same name hierarchy as the local host - * (not, for example, for host address-to-name lookups in domain in-addr.arpa). + * is detected. Error code, if any, is left in h_errno. */ int res_search(name, class, type, answer, anslen) - char *name; /* domain name */ + const char *name; /* domain name */ int class, type; /* class and type of query */ u_char *answer; /* buffer to put answer */ int anslen; /* size of answer */ { - register char *cp, **domain; - int n, ret, got_nodata = 0; - char *__hostalias(); + register const char *cp, * const *domain; + HEADER *hp = (HEADER *) answer; + u_int dots; + int trailing_dot, ret, saved_herrno; + int got_nodata = 0, got_servfail = 0, tried_as_is = 0; - if ((_res.options & RES_INIT) == 0 && res_init() == -1) + if ((_res.options & RES_INIT) == 0 && res_init() == -1) { + h_errno = NETDB_INTERNAL; return (-1); - + } errno = 0; h_errno = HOST_NOT_FOUND; /* default, if we never query */ - for (cp = name, n = 0; *cp; cp++) - if (*cp == '.') - n++; - if (n == 0 && (cp = __hostalias(name))) + dots = 0; + for (cp = name; *cp; cp++) + dots += (*cp == '.'); + trailing_dot = 0; + if (cp > name && *--cp == '.') + trailing_dot++; + + /* + * if there aren't any dots, it could be a user-level alias + */ + if (!dots && (cp = __hostalias(name)) != NULL) return (res_query(cp, class, type, answer, anslen)); /* + * If there are dots in the name already, let's just give it a try + * 'as is'. The threshold can be set with the "ndots" option. + */ + saved_herrno = -1; + if (dots >= _res.ndots) { + ret = res_querydomain(name, NULL, class, type, answer, anslen); + if (ret > 0) + return (ret); + saved_herrno = h_errno; + tried_as_is++; + } + + /* * We do at least one level of search if * - there is no dot and RES_DEFNAME is set, or * - there is at least one dot, there is no trailing dot, * and RES_DNSRCH is set. */ - if ((n == 0 && _res.options & RES_DEFNAMES) || - (n != 0 && *--cp != '.' && _res.options & RES_DNSRCH)) - for (domain = _res.dnsrch; *domain; domain++) { - ret = res_querydomain(name, *domain, class, type, - answer, anslen); + if ((!dots && (_res.options & RES_DEFNAMES)) || + (dots && !trailing_dot && (_res.options & RES_DNSRCH))) { + int done = 0; + + for (domain = (const char * const *)_res.dnsrch; + *domain && !done; + domain++) { + + ret = res_querydomain(name, *domain, class, type, + answer, anslen); + if (ret > 0) + return (ret); + + /* + * If no server present, give up. + * If name isn't found in this domain, + * keep trying higher domains in the search list + * (if that's enabled). + * On a NO_DATA error, keep trying, otherwise + * a wildcard entry of another type could keep us + * from finding this entry higher in the domain. + * If we get some other error (negative answer or + * server failure), then stop searching up, + * but try the input name below in case it's + * fully-qualified. + */ + if (errno == ECONNREFUSED) { + h_errno = TRY_AGAIN; + return (-1); + } + + switch (h_errno) { + case NO_DATA: + got_nodata++; + /* FALLTHROUGH */ + case HOST_NOT_FOUND: + /* keep trying */ + break; + case TRY_AGAIN: + if (hp->rcode == SERVFAIL) { + /* try next search element, if any */ + got_servfail++; + break; + } + /* FALLTHROUGH */ + default: + /* anything else implies that we're done */ + done++; + } + + /* if we got here for some reason other than DNSRCH, + * we only wanted one iteration of the loop, so stop. + */ + if (!(_res.options & RES_DNSRCH)) + done++; + } + } + + /* if we have not already tried the name "as is", do that now. + * note that we do this regardless of how many dots were in the + * name or whether it ends with a dot. + */ + if (!tried_as_is) { + ret = res_querydomain(name, NULL, class, type, answer, anslen); if (ret > 0) return (ret); - /* - * If no server present, give up. - * If name isn't found in this domain, - * keep trying higher domains in the search list - * (if that's enabled). - * On a NO_DATA error, keep trying, otherwise - * a wildcard entry of another type could keep us - * from finding this entry higher in the domain. - * If we get some other error (negative answer or - * server failure), then stop searching up, - * but try the input name below in case it's fully-qualified. - */ - if (errno == ECONNREFUSED) { - h_errno = TRY_AGAIN; - return (-1); - } - if (h_errno == NO_DATA) - got_nodata++; - if ((h_errno != HOST_NOT_FOUND && h_errno != NO_DATA) || - (_res.options & RES_DNSRCH) == 0) - break; } - /* - * If the search/default failed, try the name as fully-qualified, - * but only if it contained at least one dot (even trailing). - * This is purely a heuristic; we assume that any reasonable query - * about a top-level domain (for servers, SOA, etc) will not use - * res_search. + + /* if we got here, we didn't satisfy the search. + * if we did an initial full query, return that query's h_errno + * (note that we wouldn't be here if that query had succeeded). + * else if we ever got a nodata, send that back as the reason. + * else send back meaningless h_errno, that being the one from + * the last DNSRCH we did. */ - if (n && (ret = res_querydomain(name, (char *)NULL, class, type, - answer, anslen)) > 0) - return (ret); - if (got_nodata) + if (saved_herrno != -1) + h_errno = saved_herrno; + else if (got_nodata) h_errno = NO_DATA; + else if (got_servfail) + h_errno = TRY_AGAIN; return (-1); } @@ -235,20 +313,25 @@ res_search(name, class, type, answer, anslen) * Perform a call on res_query on the concatenation of name and domain, * removing a trailing dot from name if domain is NULL. */ +int res_querydomain(name, domain, class, type, answer, anslen) - char *name, *domain; + const char *name, *domain; int class, type; /* class and type of query */ u_char *answer; /* buffer to put answer */ int anslen; /* size of answer */ { char nbuf[2*MAXDNAME+2]; - char *longname = nbuf; + const char *longname = nbuf; int n; + if ((_res.options & RES_INIT) == 0 && res_init() == -1) { + h_errno = NETDB_INTERNAL; + return (-1); + } #ifdef DEBUG if (_res.options & RES_DEBUG) printf(";; res_querydomain(%s, %s, %d, %d)\n", - name, domain, class, type); + name, domain?domain:"<Nil>", class, type); #endif if (domain == NULL) { /* @@ -256,14 +339,13 @@ res_querydomain(name, domain, class, type, answer, anslen) * copy without '.' if present. */ n = strlen(name) - 1; - if (name[n] == '.' && n < sizeof(nbuf) - 1) { + if (n != (0 - 1) && name[n] == '.' && n < sizeof(nbuf) - 1) { bcopy(name, nbuf, n); nbuf[n] = '\0'; } else longname = name; } else - (void)sprintf(nbuf, "%.*s.%.*s", - MAXDNAME, name, MAXDNAME, domain); + sprintf(nbuf, "%.*s.%.*s", MAXDNAME, name, MAXDNAME, domain); return (res_query(longname, class, type, answer, anslen)); } @@ -272,28 +354,34 @@ char * __hostalias(name) register const char *name; { - register char *C1, *C2; + register char *cp1, *cp2; FILE *fp; - char *file, *getenv(), *strcpy(), *strncpy(); + char *file; char buf[BUFSIZ]; static char abuf[MAXDNAME]; + if (_res.options & RES_NOALIASES) + return (NULL); file = getenv("HOSTALIASES"); if (file == NULL || (fp = fopen(file, "r")) == NULL) return (NULL); + setbuf(fp, NULL); buf[sizeof(buf) - 1] = '\0'; while (fgets(buf, sizeof(buf), fp)) { - for (C1 = buf; *C1 && !isspace(*C1); ++C1); - if (!*C1) + for (cp1 = buf; *cp1 && !isspace(*cp1); ++cp1) + ; + if (!*cp1) break; - *C1 = '\0'; + *cp1 = '\0'; if (!strcasecmp(buf, name)) { - while (isspace(*++C1)); - if (!*C1) + while (isspace(*++cp1)) + ; + if (!*cp1) break; - for (C2 = C1 + 1; *C2 && !isspace(*C2); ++C2); - abuf[sizeof(abuf) - 1] = *C2 = '\0'; - (void)strncpy(abuf, C1, sizeof(abuf) - 1); + for (cp2 = cp1 + 1; *cp2 && !isspace(*cp2); ++cp2) + ; + abuf[sizeof(abuf) - 1] = *cp2 = '\0'; + strncpy(abuf, cp1, sizeof(abuf) - 1); fclose(fp); return (abuf); } |