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