xref: /openbsd-src/lib/libc/asr/getnetnamadr_async.c (revision 0b7734b3d77bb9b21afec6f4621cae6c805dbd45)
1 /*	$OpenBSD: getnetnamadr_async.c,v 1.23 2015/09/20 14:19:21 eric Exp $	*/
2 /*
3  * Copyright (c) 2012 Eric Faurot <eric@openbsd.org>
4  *
5  * Permission to use, copy, modify, and distribute this software for any
6  * purpose with or without fee is hereby granted, provided that the above
7  * copyright notice and this permission notice appear in all copies.
8  *
9  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16  */
17 
18 #include <sys/types.h>
19 #include <sys/socket.h>
20 #include <netinet/in.h>
21 #include <arpa/inet.h>
22 #include <arpa/nameser.h>
23 #include <netdb.h>
24 
25 #include <asr.h>
26 #include <errno.h>
27 #include <resolv.h> /* for res_hnok */
28 #include <stdlib.h>
29 #include <string.h>
30 #include <unistd.h>
31 
32 #include "asr_private.h"
33 
34 #define MAXALIASES	16
35 
36 struct netent_ext {
37 	struct netent	 n;
38 	char		*aliases[MAXALIASES + 1];
39 	char		*end;
40 	char		*pos;
41 };
42 
43 static int getnetnamadr_async_run(struct asr_query *, struct asr_result *);
44 static struct netent_ext *netent_alloc(int);
45 static int netent_set_cname(struct netent_ext *, const char *, int);
46 static int netent_add_alias(struct netent_ext *, const char *, int);
47 static struct netent_ext *netent_file_match(FILE *, int, const char *);
48 static struct netent_ext *netent_from_packet(int, char *, size_t);
49 
50 struct asr_query *
51 getnetbyname_async(const char *name, void *asr)
52 {
53 	struct asr_ctx	 *ac;
54 	struct asr_query *as;
55 
56 	/* The current resolver segfaults. */
57 	if (name == NULL) {
58 		errno = EINVAL;
59 		return (NULL);
60 	}
61 
62 	ac = _asr_use_resolver(asr);
63 	if ((as = _asr_async_new(ac, ASR_GETNETBYNAME)) == NULL)
64 		goto abort; /* errno set */
65 	as->as_run = getnetnamadr_async_run;
66 
67 	as->as.netnamadr.family = AF_INET;
68 	as->as.netnamadr.name = strdup(name);
69 	if (as->as.netnamadr.name == NULL)
70 		goto abort; /* errno set */
71 
72 	_asr_ctx_unref(ac);
73 	return (as);
74 
75     abort:
76 	if (as)
77 		_asr_async_free(as);
78 	_asr_ctx_unref(ac);
79 	return (NULL);
80 }
81 DEF_WEAK(getnetbyname_async);
82 
83 struct asr_query *
84 getnetbyaddr_async(in_addr_t net, int family, void *asr)
85 {
86 	struct asr_ctx	 *ac;
87 	struct asr_query *as;
88 
89 	ac = _asr_use_resolver(asr);
90 	if ((as = _asr_async_new(ac, ASR_GETNETBYADDR)) == NULL)
91 		goto abort; /* errno set */
92 	as->as_run = getnetnamadr_async_run;
93 
94 	as->as.netnamadr.family = family;
95 	as->as.netnamadr.addr = net;
96 
97 	_asr_ctx_unref(ac);
98 	return (as);
99 
100     abort:
101 	if (as)
102 		_asr_async_free(as);
103 	_asr_ctx_unref(ac);
104 	return (NULL);
105 }
106 DEF_WEAK(getnetbyaddr_async);
107 
108 static int
109 getnetnamadr_async_run(struct asr_query *as, struct asr_result *ar)
110 {
111 	struct netent_ext	*n;
112 	int			 r, type, saved_errno;
113 	FILE			*f;
114 	char			 dname[MAXDNAME], *name, *data;
115 	in_addr_t		 in;
116 
117     next:
118 	switch (as->as_state) {
119 
120 	case ASR_STATE_INIT:
121 
122 		if (as->as.netnamadr.family != AF_INET) {
123 			ar->ar_h_errno = NETDB_INTERNAL;
124 			ar->ar_errno = EAFNOSUPPORT;
125 			async_set_state(as, ASR_STATE_HALT);
126 			break;
127 		}
128 
129 		if (as->as_type == ASR_GETNETBYNAME &&
130 		    as->as.netnamadr.name[0] == '\0') {
131 			ar->ar_h_errno = NO_DATA;
132 			async_set_state(as, ASR_STATE_HALT);
133 			break;
134 		}
135 
136 		async_set_state(as, ASR_STATE_NEXT_DB);
137 		break;
138 
139 	case ASR_STATE_NEXT_DB:
140 
141 		if (_asr_iter_db(as) == -1) {
142 			async_set_state(as, ASR_STATE_NOT_FOUND);
143 			break;
144 		}
145 
146 		switch (AS_DB(as)) {
147 		case ASR_DB_DNS:
148 
149 			if (as->as_type == ASR_GETNETBYNAME) {
150 				type = T_A;
151 				/*
152 				 * I think we want to do the former, but our
153 				 * resolver is doing the following, so let's
154 				 * preserve bugward-compatibility there.
155 				 */
156 				type = T_PTR;
157 				name = as->as.netnamadr.name;
158 				as->as.netnamadr.subq = _res_search_async_ctx(
159 				    name, C_IN, type, as->as_ctx);
160 			} else {
161 				type = T_PTR;
162 				name = dname;
163 
164 				in = htonl(as->as.netnamadr.addr);
165 				_asr_addr_as_fqdn((char *)&in,
166 				    as->as.netnamadr.family,
167 				    dname, sizeof(dname));
168 				as->as.netnamadr.subq = _res_query_async_ctx(
169 				    name, C_IN, type, as->as_ctx);
170 			}
171 
172 			if (as->as.netnamadr.subq == NULL) {
173 				ar->ar_errno = errno;
174 				ar->ar_h_errno = NETDB_INTERNAL;
175 				async_set_state(as, ASR_STATE_HALT);
176 			}
177 			async_set_state(as, ASR_STATE_SUBQUERY);
178 			break;
179 
180 		case ASR_DB_FILE:
181 
182 			if ((f = fopen(_PATH_NETWORKS, "re")) == NULL)
183 				break;
184 
185 			if (as->as_type == ASR_GETNETBYNAME)
186 				data = as->as.netnamadr.name;
187 			else
188 				data = (void *)&as->as.netnamadr.addr;
189 
190 			n = netent_file_match(f, as->as_type, data);
191 			saved_errno = errno;
192 			fclose(f);
193 			errno = saved_errno;
194 			if (n == NULL) {
195 				if (errno) {
196 					ar->ar_errno = errno;
197 					ar->ar_h_errno = NETDB_INTERNAL;
198 					async_set_state(as, ASR_STATE_HALT);
199 				}
200 				/* otherwise not found */
201 				break;
202 			}
203 
204 			ar->ar_netent = &n->n;
205 			ar->ar_h_errno = NETDB_SUCCESS;
206 			async_set_state(as, ASR_STATE_HALT);
207 			break;
208 		}
209 		break;
210 
211 	case ASR_STATE_SUBQUERY:
212 
213 		if ((r = asr_run(as->as.netnamadr.subq, ar)) == ASYNC_COND)
214 			return (ASYNC_COND);
215 		as->as.netnamadr.subq = NULL;
216 
217 		if (ar->ar_datalen == -1) {
218 			async_set_state(as, ASR_STATE_NEXT_DB);
219 			break;
220 		}
221 
222 		/* Got packet, but no answer */
223 		if (ar->ar_count == 0) {
224 			free(ar->ar_data);
225 			async_set_state(as, ASR_STATE_NEXT_DB);
226 			break;
227 		}
228 
229 		n = netent_from_packet(as->as_type, ar->ar_data,
230 		    ar->ar_datalen);
231 		free(ar->ar_data);
232 		if (n == NULL) {
233 			ar->ar_errno = errno;
234 			ar->ar_h_errno = NETDB_INTERNAL;
235 			async_set_state(as, ASR_STATE_HALT);
236 			break;
237 		}
238 
239 		if (as->as_type == ASR_GETNETBYADDR)
240 			n->n.n_net = as->as.netnamadr.addr;
241 
242 		/*
243 		 * No valid hostname or address found in the dns packet.
244 		 * Ignore it.
245 		 */
246 		if ((as->as_type == ASR_GETNETBYNAME && n->n.n_net == 0) ||
247 		    n->n.n_name == NULL) {
248 			free(n);
249 			async_set_state(as, ASR_STATE_NEXT_DB);
250 			break;
251 		}
252 
253 		ar->ar_netent = &n->n;
254 		ar->ar_h_errno = NETDB_SUCCESS;
255 		async_set_state(as, ASR_STATE_HALT);
256 		break;
257 
258 	case ASR_STATE_NOT_FOUND:
259 
260 		ar->ar_errno = 0;
261 		ar->ar_h_errno = HOST_NOT_FOUND;
262 		async_set_state(as, ASR_STATE_HALT);
263 		break;
264 
265 	case ASR_STATE_HALT:
266 
267 		if (ar->ar_h_errno)
268 			ar->ar_netent = NULL;
269 		else
270 			ar->ar_errno = 0;
271 		return (ASYNC_DONE);
272 
273 	default:
274 		ar->ar_errno = EOPNOTSUPP;
275 		ar->ar_h_errno = NETDB_INTERNAL;
276 		ar->ar_gai_errno = EAI_SYSTEM;
277 		async_set_state(as, ASR_STATE_HALT);
278 		break;
279 	}
280 	goto next;
281 }
282 
283 static struct netent_ext *
284 netent_file_match(FILE *f, int reqtype, const char *data)
285 {
286 	struct netent_ext	*e;
287 	char			*tokens[MAXTOKEN], buf[BUFSIZ + 1];
288 	int			 n, i;
289 	in_addr_t		 net;
290 
291 	for (;;) {
292 		n = _asr_parse_namedb_line(f, tokens, MAXTOKEN, buf, sizeof(buf));
293 		if (n == -1) {
294 			errno = 0; /* ignore errors reading the file */
295 			return (NULL);
296 		}
297 
298 		/* there must be an address and at least one name */
299 		if (n < 2)
300 			continue;
301 
302 		if (reqtype == ASR_GETNETBYADDR) {
303 			net = inet_network(tokens[1]);
304 			if (memcmp(&net, data, sizeof net) == 0)
305 				goto found;
306 		} else {
307 			for (i = 0; i < n; i++) {
308 				if (i == 1)
309 					continue;
310 				if (strcasecmp(data, tokens[i]))
311 					continue;
312 				goto found;
313 			}
314 		}
315 	}
316 
317 found:
318 	if ((e = netent_alloc(AF_INET)) == NULL)
319 		return (NULL);
320 	if (netent_set_cname(e, tokens[0], 0) == -1)
321 		goto fail;
322 	for (i = 2; i < n; i ++)
323 		if (netent_add_alias(e, tokens[i], 0) == -1)
324 			goto fail;
325 	e->n.n_net = inet_network(tokens[1]);
326 	return (e);
327 fail:
328 	free(e);
329 	return (NULL);
330 }
331 
332 static struct netent_ext *
333 netent_from_packet(int reqtype, char *pkt, size_t pktlen)
334 {
335 	struct netent_ext	*n;
336 	struct asr_unpack	 p;
337 	struct asr_dns_header	 hdr;
338 	struct asr_dns_query	 q;
339 	struct asr_dns_rr	 rr;
340 
341 	if ((n = netent_alloc(AF_INET)) == NULL)
342 		return (NULL);
343 
344 	_asr_unpack_init(&p, pkt, pktlen);
345 	_asr_unpack_header(&p, &hdr);
346 	for (; hdr.qdcount; hdr.qdcount--)
347 		_asr_unpack_query(&p, &q);
348 	for (; hdr.ancount; hdr.ancount--) {
349 		_asr_unpack_rr(&p, &rr);
350 		if (rr.rr_class != C_IN)
351 			continue;
352 		switch (rr.rr_type) {
353 		case T_CNAME:
354 			if (reqtype == ASR_GETNETBYNAME) {
355 				if (netent_add_alias(n, rr.rr_dname, 1) == -1)
356 					goto fail;
357 			} else {
358 				if (netent_set_cname(n, rr.rr_dname, 1) == -1)
359 					goto fail;
360 			}
361 			break;
362 
363 		case T_PTR:
364 			if (reqtype != ASR_GETNETBYADDR)
365 				continue;
366 			if (netent_set_cname(n, rr.rr.ptr.ptrname, 1) == -1)
367 				goto fail;
368 			/* XXX See if we need to have MULTI_PTRS_ARE_ALIASES */
369 			break;
370 
371 		case T_A:
372 			if (n->n.n_addrtype != AF_INET)
373 				break;
374 			if (netent_set_cname(n, rr.rr_dname, 1) ==  -1)
375 				goto fail;
376 			n->n.n_net = ntohl(rr.rr.in_a.addr.s_addr);
377 			break;
378 		}
379 	}
380 
381 	return (n);
382 fail:
383 	free(n);
384 	return (NULL);
385 }
386 
387 static struct netent_ext *
388 netent_alloc(int family)
389 {
390 	struct netent_ext	*n;
391 	size_t			 alloc;
392 
393 	alloc = sizeof(*n) + 1024;
394 	if ((n = calloc(1, alloc)) == NULL)
395 		return (NULL);
396 
397 	n->n.n_addrtype = family;
398 	n->n.n_aliases = n->aliases;
399 	n->pos = (char *)(n) + sizeof(*n);
400 	n->end = n->pos + 1024;
401 
402 	return (n);
403 }
404 
405 static int
406 netent_set_cname(struct netent_ext *n, const char *name, int isdname)
407 {
408 	char	buf[MAXDNAME];
409 	size_t	l;
410 
411 	if (n->n.n_name)
412 		return (-1);
413 
414 	if (isdname) {
415 		_asr_strdname(name, buf, sizeof buf);
416 		buf[strlen(buf) - 1] = '\0';
417 		if (!res_hnok(buf))
418 			return (-1);
419 		name = buf;
420 	}
421 
422 	l = strlen(name) + 1;
423 	if (n->pos + l >= n->end)
424 		return (-1);
425 
426 	n->n.n_name = n->pos;
427 	memmove(n->pos, name, l);
428 	n->pos += l;
429 
430 	return (0);
431 }
432 
433 static int
434 netent_add_alias(struct netent_ext *n, const char *name, int isdname)
435 {
436 	char	buf[MAXDNAME];
437 	size_t	i, l;
438 
439 	for (i = 0; i < MAXALIASES; i++)
440 		if (n->aliases[i] == NULL)
441 			break;
442 	if (i == MAXALIASES)
443 		return (-1);
444 
445 	if (isdname) {
446 		_asr_strdname(name, buf, sizeof buf);
447 		buf[strlen(buf)-1] = '\0';
448 		if (!res_hnok(buf))
449 			return (-1);
450 		name = buf;
451 	}
452 
453 	l = strlen(name) + 1;
454 	if (n->pos + l >= n->end)
455 		return (-1);
456 
457 	n->aliases[i] = n->pos;
458 	memmove(n->pos, name, l);
459 	n->pos += l;
460 	return (0);
461 }
462