xref: /freebsd/lib/libc/net/getnetbydns.c (revision fed1ca4b719c56c930f2259d80663cd34be812bb)
1 /*-
2  * Copyright (c) 1985, 1988, 1993
3  *	The Regents of the University of California.  All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  * 4. Neither the name of the University nor the names of its contributors
14  *    may be used to endorse or promote products derived from this software
15  *    without specific prior written permission.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
18  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
21  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27  * SUCH DAMAGE.
28  * -
29  * Portions Copyright (c) 1993 by Digital Equipment Corporation.
30  *
31  * Permission to use, copy, modify, and distribute this software for any
32  * purpose with or without fee is hereby granted, provided that the above
33  * copyright notice and this permission notice appear in all copies, and that
34  * the name of Digital Equipment Corporation not be used in advertising or
35  * publicity pertaining to distribution of the document or software without
36  * specific, written prior permission.
37  *
38  * THE SOFTWARE IS PROVIDED "AS IS" AND DIGITAL EQUIPMENT CORP. DISCLAIMS ALL
39  * WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES
40  * OF MERCHANTABILITY AND FITNESS.   IN NO EVENT SHALL DIGITAL EQUIPMENT
41  * CORPORATION BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
42  * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
43  * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
44  * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
45  * SOFTWARE.
46  * -
47  * --Copyright--
48  */
49 /* Portions Copyright (c) 1993 Carlos Leandro and Rui Salgueiro
50  *	Dep. Matematica Universidade de Coimbra, Portugal, Europe
51  *
52  * Permission to use, copy, modify, and distribute this software for any
53  * purpose with or without fee is hereby granted, provided that the above
54  * copyright notice and this permission notice appear in all copies.
55  */
56 
57 #if defined(LIBC_SCCS) && !defined(lint)
58 static char sccsid[] = "@(#)gethostnamadr.c	8.1 (Berkeley) 6/4/93";
59 #endif /* LIBC_SCCS and not lint */
60 #include <sys/cdefs.h>
61 __FBSDID("$FreeBSD$");
62 
63 #include <sys/param.h>
64 #include <sys/socket.h>
65 #include <netinet/in.h>
66 #include <arpa/inet.h>
67 #include <arpa/nameser.h>
68 
69 #include <errno.h>
70 #include <stdio.h>
71 #include <stdlib.h>
72 #include <netdb.h>
73 #include <resolv.h>
74 #include <ctype.h>
75 #include <string.h>
76 #include <unistd.h>
77 #include <syslog.h>
78 #include <stdarg.h>
79 #include <nsswitch.h>
80 
81 #include "netdb_private.h"
82 #include "res_config.h"
83 
84 #define BYADDR 0
85 #define BYNAME 1
86 
87 #define MAXPACKET	(64*1024)
88 
89 typedef union {
90 	HEADER	hdr;
91 	u_char	buf[MAXPACKET];
92 } querybuf;
93 
94 typedef union {
95 	long	al;
96 	char	ac;
97 } align;
98 
99 /*
100  * Reverse the order of first four dotted entries of in.
101  * Out must contain space for at least strlen(in) characters.
102  * The result does not include any leading 0s of in.
103  */
104 static void
105 ipreverse(char *in, char *out)
106 {
107 	char *pos[4];
108 	int len[4];
109 	char *p, *start;
110 	int i = 0;
111 	int leading = 1;
112 
113 	/* Fill-in element positions and lengths: pos[], len[]. */
114 	start = p = in;
115 	for (;;) {
116 		if (*p == '.' || *p == '\0') {
117 			/* Leading 0? */
118 			if (leading && p - start == 1 && *start == '0')
119 				len[i] = 0;
120 			else {
121 				len[i] = p - start;
122 				leading = 0;
123 			}
124 			pos[i] = start;
125 			start = p + 1;
126 			i++;
127 		}
128 		if (i == 4)
129 			break;
130 		if (*p == 0) {
131 			for (; i < 4; i++) {
132 				pos[i] = p;
133 				len[i] = 0;
134 			}
135 			break;
136 		}
137 		p++;
138 	}
139 
140 	/* Copy the entries in reverse order */
141 	p = out;
142 	leading = 1;
143 	for (i = 3; i >= 0; i--) {
144 		memcpy(p, pos[i], len[i]);
145 		if (len[i])
146 			leading = 0;
147 		p += len[i];
148 		/* Need a . separator? */
149 		if (!leading && i > 0 && len[i - 1])
150 			*p++ = '.';
151 	}
152 	*p = '\0';
153 }
154 
155 static int
156 getnetanswer(querybuf *answer, int anslen, int net_i, struct netent *ne,
157     struct netent_data *ned, res_state statp)
158 {
159 
160 	HEADER *hp;
161 	u_char *cp;
162 	int n;
163 	u_char *eom;
164 	int type, class, ancount, qdcount, haveanswer;
165 	char aux[MAXHOSTNAMELEN];
166 	char ans[MAXHOSTNAMELEN];
167 	char *in, *bp, *ep, **ap;
168 
169 	/*
170 	 * find first satisfactory answer
171 	 *
172 	 *      answer --> +------------+  ( MESSAGE )
173 	 *		   |   Header   |
174 	 *		   +------------+
175 	 *		   |  Question  | the question for the name server
176 	 *		   +------------+
177 	 *		   |   Answer   | RRs answering the question
178 	 *		   +------------+
179 	 *		   | Authority  | RRs pointing toward an authority
180 	 *		   | Additional | RRs holding additional information
181 	 *		   +------------+
182 	 */
183 	eom = answer->buf + anslen;
184 	hp = &answer->hdr;
185 	ancount = ntohs(hp->ancount); /* #/records in the answer section */
186 	qdcount = ntohs(hp->qdcount); /* #/entries in the question section */
187 	bp = ned->netbuf;
188 	ep = ned->netbuf + sizeof(ned->netbuf);
189 	cp = answer->buf + HFIXEDSZ;
190 	if (!qdcount) {
191 		if (hp->aa)
192 			RES_SET_H_ERRNO(statp, HOST_NOT_FOUND);
193 		else
194 			RES_SET_H_ERRNO(statp, TRY_AGAIN);
195 		return (-1);
196 	}
197 	while (qdcount-- > 0)
198 		cp += __dn_skipname(cp, eom) + QFIXEDSZ;
199 	ap = ned->net_aliases;
200 	*ap = NULL;
201 	ne->n_aliases = ned->net_aliases;
202 	haveanswer = 0;
203 	while (--ancount >= 0 && cp < eom) {
204 		n = dn_expand(answer->buf, eom, cp, bp, ep - bp);
205 		if ((n < 0) || !res_dnok(bp))
206 			break;
207 		cp += n;
208 		ans[0] = '\0';
209 		(void)strncpy(&ans[0], bp, sizeof(ans) - 1);
210 		ans[sizeof(ans) - 1] = '\0';
211 		GETSHORT(type, cp);
212 		GETSHORT(class, cp);
213 		cp += INT32SZ;		/* TTL */
214 		GETSHORT(n, cp);
215 		if (class == C_IN && type == T_PTR) {
216 			n = dn_expand(answer->buf, eom, cp, bp, ep - bp);
217 			if ((n < 0) || !res_hnok(bp)) {
218 				cp += n;
219 				return (-1);
220 			}
221 			cp += n;
222 			*ap++ = bp;
223 			n = strlen(bp) + 1;
224 			bp += n;
225 			ne->n_addrtype = (class == C_IN) ? AF_INET : AF_UNSPEC;
226 			haveanswer++;
227 		}
228 	}
229 	if (haveanswer) {
230 		*ap = NULL;
231 		switch (net_i) {
232 		case BYADDR:
233 			ne->n_name = *ne->n_aliases;
234 			ne->n_net = 0L;
235 			break;
236 		case BYNAME:
237 			in = *ne->n_aliases;
238 			n = strlen(ans) + 1;
239 			if (ep - bp < n) {
240 				RES_SET_H_ERRNO(statp, NETDB_INTERNAL);
241 				errno = ENOBUFS;
242 				return (-1);
243 			}
244 			strlcpy(bp, ans, ep - bp);
245 			ne->n_name = bp;
246 			if (strlen(in) + 1 > sizeof(aux)) {
247 				RES_SET_H_ERRNO(statp, NETDB_INTERNAL);
248 				errno = ENOBUFS;
249 				return (-1);
250 			}
251 			ipreverse(in, aux);
252 			ne->n_net = inet_network(aux);
253 			break;
254 		}
255 		ne->n_aliases++;
256 		return (0);
257 	}
258 	RES_SET_H_ERRNO(statp, TRY_AGAIN);
259 	return (-1);
260 }
261 
262 int
263 _dns_getnetbyaddr(void *rval, void *cb_data, va_list ap)
264 {
265 	uint32_t net;
266 	int net_type;
267 	char *buffer;
268 	size_t buflen;
269 	int *errnop, *h_errnop;
270 	struct netent *nptr, ne;
271 	struct netent_data *ned;
272 	unsigned int netbr[4];
273 	int nn, anslen, error;
274 	querybuf *buf;
275 	char qbuf[MAXDNAME];
276 	uint32_t net2;
277 	res_state statp;
278 
279 	net = va_arg(ap, uint32_t);
280 	net_type = va_arg(ap, int);
281 	nptr = va_arg(ap, struct netent *);
282 	buffer = va_arg(ap, char *);
283 	buflen = va_arg(ap, size_t);
284 	errnop = va_arg(ap, int *);
285 	h_errnop = va_arg(ap, int *);
286 
287 	statp = __res_state();
288 	if ((statp->options & RES_INIT) == 0 && res_ninit(statp) == -1) {
289 		RES_SET_H_ERRNO(statp, NETDB_INTERNAL);
290 		*h_errnop = statp->res_h_errno;
291 		return (NS_UNAVAIL);
292 	}
293 
294 	if ((ned = __netent_data_init()) == NULL) {
295 		RES_SET_H_ERRNO(statp, NETDB_INTERNAL);
296 		*h_errnop = statp->res_h_errno;
297 		return (NS_UNAVAIL);
298 	}
299 
300 	*((struct netent **)rval) = NULL;
301 
302 	if (net_type != AF_INET) {
303 		RES_SET_H_ERRNO(statp, NETDB_INTERNAL);
304 		*h_errnop = statp->res_h_errno;
305 		return (NS_UNAVAIL);
306 	}
307 
308 	for (nn = 4, net2 = net; net2; net2 >>= 8)
309 		netbr[--nn] = net2 & 0xff;
310 	switch (nn) {
311 	case 3: 	/* Class A */
312 		sprintf(qbuf, "0.0.0.%u.in-addr.arpa", netbr[3]);
313 		break;
314 	case 2: 	/* Class B */
315 		sprintf(qbuf, "0.0.%u.%u.in-addr.arpa", netbr[3], netbr[2]);
316 		break;
317 	case 1: 	/* Class C */
318 		sprintf(qbuf, "0.%u.%u.%u.in-addr.arpa", netbr[3], netbr[2],
319 		    netbr[1]);
320 		break;
321 	case 0: 	/* Class D - E */
322 		sprintf(qbuf, "%u.%u.%u.%u.in-addr.arpa", netbr[3], netbr[2],
323 		    netbr[1], netbr[0]);
324 		break;
325 	}
326 	if ((buf = malloc(sizeof(*buf))) == NULL) {
327 		RES_SET_H_ERRNO(statp, NETDB_INTERNAL);
328 		*h_errnop = statp->res_h_errno;
329 		return (NS_NOTFOUND);
330 	}
331 	anslen = res_nquery(statp, qbuf, C_IN, T_PTR, (u_char *)buf,
332 	    sizeof(*buf));
333 	if (anslen < 0) {
334 		free(buf);
335 #ifdef DEBUG
336 		if (statp->options & RES_DEBUG)
337 			printf("res_nsearch failed\n");
338 #endif
339 		*h_errnop = statp->res_h_errno;
340 		return (NS_UNAVAIL);
341 	} else if (anslen > sizeof(*buf)) {
342 		free(buf);
343 #ifdef DEBUG
344 		if (statp->options & RES_DEBUG)
345 			printf("res_nsearch static buffer too small\n");
346 #endif
347 		*h_errnop = statp->res_h_errno;
348 		return (NS_UNAVAIL);
349 	}
350 	error = getnetanswer(buf, anslen, BYADDR, &ne, ned, statp);
351 	free(buf);
352 	if (error == 0) {
353 		/* Strip trailing zeros */
354 		while ((net & 0xff) == 0 && net != 0)
355 			net >>= 8;
356 		ne.n_net = net;
357 		if (__copy_netent(&ne, nptr, buffer, buflen) != 0) {
358 			*errnop = errno;
359 			RES_SET_H_ERRNO(statp, NETDB_INTERNAL);
360 			*h_errnop = statp->res_h_errno;
361 			return (NS_RETURN);
362 		}
363 		*((struct netent **)rval) = nptr;
364 		return (NS_SUCCESS);
365 	}
366 	*h_errnop = statp->res_h_errno;
367 	return (NS_NOTFOUND);
368 }
369 
370 int
371 _dns_getnetbyname(void *rval, void *cb_data, va_list ap)
372 {
373 	const char *net;
374 	char *buffer;
375 	size_t buflen;
376 	int *errnop, *h_errnop;
377 	struct netent *nptr, ne;
378 	struct netent_data *ned;
379 	int anslen, error;
380 	querybuf *buf;
381 	char qbuf[MAXDNAME];
382 	res_state statp;
383 
384 	net = va_arg(ap, const char *);
385 	nptr = va_arg(ap, struct netent *);
386 	buffer = va_arg(ap, char *);
387 	buflen = va_arg(ap, size_t);
388 	errnop = va_arg(ap, int *);
389 	h_errnop = va_arg(ap, int *);
390 
391 	statp = __res_state();
392 	if ((statp->options & RES_INIT) == 0 && res_ninit(statp) == -1) {
393 		RES_SET_H_ERRNO(statp, NETDB_INTERNAL);
394 		*h_errnop = statp->res_h_errno;
395 		return (NS_UNAVAIL);
396 	}
397 	if ((ned = __netent_data_init()) == NULL) {
398 		RES_SET_H_ERRNO(statp, NETDB_INTERNAL);
399 		*h_errnop = statp->res_h_errno;
400 		return (NS_UNAVAIL);
401 	}
402 	if ((buf = malloc(sizeof(*buf))) == NULL) {
403 		RES_SET_H_ERRNO(statp, NETDB_INTERNAL);
404 		*h_errnop = statp->res_h_errno;
405 		return (NS_NOTFOUND);
406 	}
407 
408 	*((struct netent **)rval) = NULL;
409 
410 	strncpy(qbuf, net, sizeof(qbuf) - 1);
411 	qbuf[sizeof(qbuf) - 1] = '\0';
412 	anslen = res_nsearch(statp, qbuf, C_IN, T_PTR, (u_char *)buf,
413 	    sizeof(*buf));
414 	if (anslen < 0) {
415 		free(buf);
416 #ifdef DEBUG
417 		if (statp->options & RES_DEBUG)
418 			printf("res_nsearch failed\n");
419 #endif
420 		return (NS_UNAVAIL);
421 	} else if (anslen > sizeof(*buf)) {
422 		free(buf);
423 #ifdef DEBUG
424 		if (statp->options & RES_DEBUG)
425 			printf("res_search static buffer too small\n");
426 #endif
427 		return (NS_UNAVAIL);
428 	}
429 	error = getnetanswer(buf, anslen, BYNAME, &ne, ned, statp);
430 	free(buf);
431 	if (error != 0) {
432 		*h_errnop = statp->res_h_errno;
433 		return (NS_NOTFOUND);
434 	}
435 	if (__copy_netent(&ne, nptr, buffer, buflen) != 0) {
436 		*errnop = errno;
437 		RES_SET_H_ERRNO(statp, NETDB_INTERNAL);
438 		*h_errnop = statp->res_h_errno;
439 		return (NS_RETURN);
440 	}
441 	*((struct netent **)rval) = nptr;
442 	return (NS_SUCCESS);
443 }
444 
445 void
446 _setnetdnsent(int stayopen)
447 {
448 	res_state statp;
449 
450 	statp = __res_state();
451 	if ((statp->options & RES_INIT) == 0 && res_ninit(statp) == -1)
452 		return;
453 	if (stayopen)
454 		statp->options |= RES_STAYOPEN | RES_USEVC;
455 }
456 
457 void
458 _endnetdnsent(void)
459 {
460 	res_state statp;
461 
462 	statp = __res_state();
463 	statp->options &= ~(RES_STAYOPEN | RES_USEVC);
464 	res_nclose(statp);
465 }
466