move regex from libc to libcompat
[unix-history] / usr / src / lib / libc / net / res_query.c
CommitLineData
345feb69
KB
1/*
2 * Copyright (c) 1988 Regents of the University of California.
3 * All rights reserved.
4 *
269a7923 5 * %sccs.include.redist.c%
345feb69
KB
6 */
7
8#if defined(LIBC_SCCS) && !defined(lint)
dcdb79c9 9static char sccsid[] = "@(#)res_query.c 5.10 (Berkeley) %G%";
345feb69
KB
10#endif /* LIBC_SCCS and not lint */
11
12#include <sys/param.h>
345feb69 13#include <netinet/in.h>
24fac7d8
KB
14#include <arpa/inet.h>
15#include <arpa/nameser.h>
345feb69 16#include <netdb.h>
24fac7d8 17#include <resolv.h>
345feb69 18#include <stdio.h>
dcdb79c9 19#include <ctype.h>
345feb69 20#include <errno.h>
24fac7d8 21#include <stdlib.h>
6ebcb998 22#include <string.h>
345feb69
KB
23
24#if PACKETSZ > 1024
25#define MAXPACKET PACKETSZ
26#else
27#define MAXPACKET 1024
28#endif
29
345feb69
KB
30int 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.
a5e08740 39 * Caller must parse answer and determine whether it answers the question.
345feb69
KB
40 */
41res_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 }
24fac7d8 68 n = res_send(buf, n, (char *)answer, anslen);
345feb69
KB
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:
a5e08740 87 h_errno = HOST_NOT_FOUND;
345feb69
KB
88 break;
89 case SERVFAIL:
90 h_errno = TRY_AGAIN;
91 break;
92 case NOERROR:
d7ec9b41 93 h_errno = NO_DATA;
345feb69
KB
94 break;
95 case FORMERR:
96 case NOTIMP:
97 case REFUSED:
a5e08740 98 default:
345feb69 99 h_errno = NO_RECOVERY;
a5e08740 100 break;
345feb69
KB
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 */
115res_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;
d7ec9b41 122 int n, ret, got_nodata = 0;
371d3c9b 123 static char *hostalias();
345feb69
KB
124
125 if ((_res.options & RES_INIT) == 0 && res_init() == -1)
126 return (-1);
127
128 errno = 0;
d7ec9b41 129 h_errno = HOST_NOT_FOUND; /* default, if we never query */
345feb69
KB
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
3ec8dabc
MK
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++) {
345feb69
KB
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).
d7ec9b41 154 * On a NO_DATA error, keep trying, otherwise
345feb69
KB
155 * a wildcard entry of another type could keep us
156 * from finding this entry higher in the domain.
3ec8dabc
MK
157 * If we get some other error (negative answer or
158 * server failure), then stop searching up,
345feb69
KB
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 }
d7ec9b41
MK
165 if (h_errno == NO_DATA)
166 got_nodata++;
167 if ((h_errno != HOST_NOT_FOUND && h_errno != NO_DATA) ||
345feb69
KB
168 (_res.options & RES_DNSRCH) == 0)
169 break;
345feb69
KB
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).
3ec8dabc
MK
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.
345feb69 177 */
3ec8dabc
MK
178 if (n && (ret = res_querydomain(name, (char *)NULL, class, type,
179 answer, anslen)) > 0)
180 return (ret);
d7ec9b41
MK
181 if (got_nodata)
182 h_errno = NO_DATA;
345feb69
KB
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 */
190res_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
d7ec9b41
MK
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
345feb69
KB
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
371d3c9b 223static char *
345feb69
KB
224hostalias(name)
225 register 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}