xref: /openbsd-src/lib/libc/asr/getnetnamadr_async.c (revision 0d7b84a898f082aed9d1f2ec3ae7c476483a8f40)
1 /*	$OpenBSD: getnetnamadr_async.c,v 1.19 2014/11/02 13:59:16 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 <err.h>
27 #include <errno.h>
28 #include <resolv.h> /* for res_hnok */
29 #include <stdlib.h>
30 #include <string.h>
31 #include <unistd.h>
32 
33 #include "asr_private.h"
34 
35 #define MAXALIASES	16
36 
37 struct netent_ext {
38 	struct netent	 n;
39 	char		*aliases[MAXALIASES + 1];
40 	char		*end;
41 	char		*pos;
42 };
43 
44 static int getnetnamadr_async_run(struct asr_query *, struct asr_result *);
45 static struct netent_ext *netent_alloc(int);
46 static int netent_set_cname(struct netent_ext *, const char *, int);
47 static int netent_add_alias(struct netent_ext *, const char *, int);
48 static struct netent_ext *netent_file_match(FILE *, int, const char *);
49 static struct netent_ext *netent_from_packet(int, char *, size_t);
50 
51 struct asr_query *
52 getnetbyname_async(const char *name, void *asr)
53 {
54 	struct asr_ctx	 *ac;
55 	struct asr_query *as;
56 
57 	/* The current resolver segfaults. */
58 	if (name == NULL) {
59 		errno = EINVAL;
60 		return (NULL);
61 	}
62 
63 	ac = asr_use_resolver(asr);
64 	if ((as = asr_async_new(ac, ASR_GETNETBYNAME)) == NULL)
65 		goto abort; /* errno set */
66 	as->as_run = getnetnamadr_async_run;
67 
68 	as->as.netnamadr.family = AF_INET;
69 	as->as.netnamadr.name = strdup(name);
70 	if (as->as.netnamadr.name == NULL)
71 		goto abort; /* errno set */
72 
73 	asr_ctx_unref(ac);
74 	return (as);
75 
76     abort:
77 	if (as)
78 		asr_async_free(as);
79 	asr_ctx_unref(ac);
80 	return (NULL);
81 }
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 
107 static int
108 getnetnamadr_async_run(struct asr_query *as, struct asr_result *ar)
109 {
110 	struct netent_ext	*n;
111 	int			 r, type, saved_errno;
112 	FILE			*f;
113 	char			 dname[MAXDNAME], *name, *data;
114 	in_addr_t		 in;
115 
116     next:
117 	switch (as->as_state) {
118 
119 	case ASR_STATE_INIT:
120 
121 		if (as->as.netnamadr.family != AF_INET) {
122 			ar->ar_h_errno = NETDB_INTERNAL;
123 			ar->ar_errno = EAFNOSUPPORT;
124 			async_set_state(as, ASR_STATE_HALT);
125 			break;
126 		}
127 
128 		if (as->as_type == ASR_GETNETBYNAME &&
129 		    as->as.netnamadr.name[0] == '\0') {
130 			ar->ar_h_errno = NO_DATA;
131 			async_set_state(as, ASR_STATE_HALT);
132 			break;
133 		}
134 
135 		async_set_state(as, ASR_STATE_NEXT_DB);
136 		break;
137 
138 	case ASR_STATE_NEXT_DB:
139 
140 		if (asr_iter_db(as) == -1) {
141 			async_set_state(as, ASR_STATE_NOT_FOUND);
142 			break;
143 		}
144 
145 		switch (AS_DB(as)) {
146 		case ASR_DB_DNS:
147 
148 			if (as->as_type == ASR_GETNETBYNAME) {
149 				type = T_A;
150 				/*
151 				 * I think we want to do the former, but our
152 				 * resolver is doing the following, so let's
153 				 * preserve bugward-compatibility there.
154 				 */
155 				type = T_PTR;
156 				name = as->as.netnamadr.name;
157 				as->as.netnamadr.subq = res_search_async_ctx(
158 				    name, C_IN, type, as->as_ctx);
159 			} else {
160 				type = T_PTR;
161 				name = dname;
162 
163 				in = htonl(as->as.netnamadr.addr);
164 				asr_addr_as_fqdn((char *)&in,
165 				    as->as.netnamadr.family,
166 				    dname, sizeof(dname));
167 				as->as.netnamadr.subq = res_query_async_ctx(
168 				    name, C_IN, type, as->as_ctx);
169 			}
170 
171 			if (as->as.netnamadr.subq == NULL) {
172 				ar->ar_errno = errno;
173 				ar->ar_h_errno = NETDB_INTERNAL;
174 				async_set_state(as, ASR_STATE_HALT);
175 			}
176 			async_set_state(as, ASR_STATE_SUBQUERY);
177 			break;
178 
179 		case ASR_DB_FILE:
180 
181 			if ((f = fopen(_PATH_NETWORKS, "re")) == NULL)
182 				break;
183 
184 			if (as->as_type == ASR_GETNETBYNAME)
185 				data = as->as.netnamadr.name;
186 			else
187 				data = (void *)&as->as.netnamadr.addr;
188 
189 			n = netent_file_match(f, as->as_type, data);
190 			saved_errno = errno;
191 			fclose(f);
192 			errno = saved_errno;
193 			if (n == NULL) {
194 				if (errno) {
195 					ar->ar_errno = errno;
196 					ar->ar_h_errno = NETDB_INTERNAL;
197 					async_set_state(as, ASR_STATE_HALT);
198 				}
199 				/* otherwise not found */
200 				break;
201 			}
202 
203 			ar->ar_netent = &n->n;
204 			ar->ar_h_errno = NETDB_SUCCESS;
205 			async_set_state(as, ASR_STATE_HALT);
206 			break;
207 		}
208 		break;
209 
210 	case ASR_STATE_SUBQUERY:
211 
212 		if ((r = asr_run(as->as.netnamadr.subq, ar)) == ASYNC_COND)
213 			return (ASYNC_COND);
214 		as->as.netnamadr.subq = NULL;
215 
216 		if (ar->ar_datalen == -1) {
217 			async_set_state(as, ASR_STATE_NEXT_DB);
218 			break;
219 		}
220 
221 		/* Got packet, but no answer */
222 		if (ar->ar_count == 0) {
223 			free(ar->ar_data);
224 			async_set_state(as, ASR_STATE_NEXT_DB);
225 			break;
226 		}
227 
228 		n = netent_from_packet(as->as_type, ar->ar_data,
229 		    ar->ar_datalen);
230 		free(ar->ar_data);
231 		if (n == NULL) {
232 			ar->ar_errno = errno;
233 			ar->ar_h_errno = NETDB_INTERNAL;
234 			async_set_state(as, ASR_STATE_HALT);
235 			break;
236 		}
237 
238 		if (as->as_type == ASR_GETNETBYADDR)
239 			n->n.n_net = as->as.netnamadr.addr;
240 
241 		/*
242 		 * No valid hostname or address found in the dns packet.
243 		 * Ignore it.
244 		 */
245 		if ((as->as_type == ASR_GETNETBYNAME && n->n.n_net == 0) ||
246 		    n->n.n_name == NULL) {
247 			free(n);
248 			async_set_state(as, ASR_STATE_NEXT_DB);
249 			break;
250 		}
251 
252 		ar->ar_netent = &n->n;
253 		ar->ar_h_errno = NETDB_SUCCESS;
254 		async_set_state(as, ASR_STATE_HALT);
255 		break;
256 
257 	case ASR_STATE_NOT_FOUND:
258 
259 		ar->ar_errno = 0;
260 		ar->ar_h_errno = HOST_NOT_FOUND;
261 		async_set_state(as, ASR_STATE_HALT);
262 		break;
263 
264 	case ASR_STATE_HALT:
265 
266 		if (ar->ar_h_errno)
267 			ar->ar_netent = NULL;
268 		else
269 			ar->ar_errno = 0;
270 		return (ASYNC_DONE);
271 
272 	default:
273 		ar->ar_errno = EOPNOTSUPP;
274 		ar->ar_h_errno = NETDB_INTERNAL;
275 		ar->ar_gai_errno = EAI_SYSTEM;
276 		async_set_state(as, ASR_STATE_HALT);
277 		break;
278 	}
279 	goto next;
280 }
281 
282 static struct netent_ext *
283 netent_file_match(FILE *f, int reqtype, const char *data)
284 {
285 	struct netent_ext	*e;
286 	char			*tokens[MAXTOKEN];
287 	int			 n, i;
288 	in_addr_t		 net;
289 
290 	for (;;) {
291 		n = asr_parse_namedb_line(f, tokens, MAXTOKEN);
292 		if (n == -1) {
293 			errno = 0; /* ignore errors reading the file */
294 			return (NULL);
295 		}
296 
297 		/* there must be an address and at least one name */
298 		if (n < 2)
299 			continue;
300 
301 		if (reqtype == ASR_GETNETBYADDR) {
302 			net = inet_network(tokens[1]);
303 			if (memcmp(&net, data, sizeof net) == 0)
304 				goto found;
305 		} else {
306 			for (i = 0; i < n; i++) {
307 				if (i == 1)
308 					continue;
309 				if (strcasecmp(data, tokens[i]))
310 					continue;
311 				goto found;
312 			}
313 		}
314 	}
315 
316 found:
317 	if ((e = netent_alloc(AF_INET)) == NULL)
318 		return (NULL);
319 	if (netent_set_cname(e, tokens[0], 0) == -1)
320 		goto fail;
321 	for (i = 2; i < n; i ++)
322 		if (netent_add_alias(e, tokens[i], 0) == -1)
323 			goto fail;
324 	e->n.n_net = inet_network(tokens[1]);
325 	return (e);
326 fail:
327 	free(e);
328 	return (NULL);
329 }
330 
331 static struct netent_ext *
332 netent_from_packet(int reqtype, char *pkt, size_t pktlen)
333 {
334 	struct netent_ext	*n;
335 	struct asr_unpack	 p;
336 	struct asr_dns_header	 hdr;
337 	struct asr_dns_query	 q;
338 	struct asr_dns_rr	 rr;
339 
340 	if ((n = netent_alloc(AF_INET)) == NULL)
341 		return (NULL);
342 
343 	asr_unpack_init(&p, pkt, pktlen);
344 	asr_unpack_header(&p, &hdr);
345 	for (; hdr.qdcount; hdr.qdcount--)
346 		asr_unpack_query(&p, &q);
347 	for (; hdr.ancount; hdr.ancount--) {
348 		asr_unpack_rr(&p, &rr);
349 		if (rr.rr_class != C_IN)
350 			continue;
351 		switch (rr.rr_type) {
352 		case T_CNAME:
353 			if (reqtype == ASR_GETNETBYNAME) {
354 				if (netent_add_alias(n, rr.rr_dname, 1) == -1)
355 					goto fail;
356 			} else {
357 				if (netent_set_cname(n, rr.rr_dname, 1) == -1)
358 					goto fail;
359 			}
360 			break;
361 
362 		case T_PTR:
363 			if (reqtype != ASR_GETNETBYADDR)
364 				continue;
365 			if (netent_set_cname(n, rr.rr.ptr.ptrname, 1) == -1)
366 				goto fail;
367 			/* XXX See if we need to have MULTI_PTRS_ARE_ALIASES */
368 			break;
369 
370 		case T_A:
371 			if (n->n.n_addrtype != AF_INET)
372 				break;
373 			if (netent_set_cname(n, rr.rr_dname, 1) ==  -1)
374 				goto fail;
375 			n->n.n_net = ntohl(rr.rr.in_a.addr.s_addr);
376 			break;
377 		}
378 	}
379 
380 	return (n);
381 fail:
382 	free(n);
383 	return (NULL);
384 }
385 
386 static struct netent_ext *
387 netent_alloc(int family)
388 {
389 	struct netent_ext	*n;
390 	size_t			 alloc;
391 
392 	alloc = sizeof(*n) + 1024;
393 	if ((n = calloc(1, alloc)) == NULL)
394 		return (NULL);
395 
396 	n->n.n_addrtype = family;
397 	n->n.n_aliases = n->aliases;
398 	n->pos = (char *)(n) + sizeof(*n);
399 	n->end = n->pos + 1024;
400 
401 	return (n);
402 }
403 
404 static int
405 netent_set_cname(struct netent_ext *n, const char *name, int isdname)
406 {
407 	char	buf[MAXDNAME];
408 	size_t	l;
409 
410 	if (n->n.n_name)
411 		return (-1);
412 
413 	if (isdname) {
414 		asr_strdname(name, buf, sizeof buf);
415 		buf[strlen(buf) - 1] = '\0';
416 		if (!res_hnok(buf))
417 			return (-1);
418 		name = buf;
419 	}
420 
421 	l = strlen(name) + 1;
422 	if (n->pos + l >= n->end)
423 		return (-1);
424 
425 	n->n.n_name = n->pos;
426 	memmove(n->pos, name, l);
427 	n->pos += l;
428 
429 	return (0);
430 }
431 
432 static int
433 netent_add_alias(struct netent_ext *n, const char *name, int isdname)
434 {
435 	char	buf[MAXDNAME];
436 	size_t	i, l;
437 
438 	for (i = 0; i < MAXALIASES; i++)
439 		if (n->aliases[i] == NULL)
440 			break;
441 	if (i == MAXALIASES)
442 		return (-1);
443 
444 	if (isdname) {
445 		asr_strdname(name, buf, sizeof buf);
446 		buf[strlen(buf)-1] = '\0';
447 		if (!res_hnok(buf))
448 			return (-1);
449 		name = buf;
450 	}
451 
452 	l = strlen(name) + 1;
453 	if (n->pos + l >= n->end)
454 		return (-1);
455 
456 	n->aliases[i] = n->pos;
457 	memmove(n->pos, name, l);
458 	n->pos += l;
459 	return (0);
460 }
461