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