xref: /openbsd-src/lib/libc/asr/getnetnamadr_async.c (revision d216d6b1290c53cb63da2778d039467b2c88efcf)
1 /*	$OpenBSD: getnetnamadr_async.c,v 1.14 2014/03/26 18:13:15 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 		async_set_state(as, ASR_STATE_NEXT_DB);
129 		break;
130 
131 	case ASR_STATE_NEXT_DB:
132 
133 		if (asr_iter_db(as) == -1) {
134 			async_set_state(as, ASR_STATE_NOT_FOUND);
135 			break;
136 		}
137 
138 		switch (AS_DB(as)) {
139 		case ASR_DB_DNS:
140 
141 			if (as->as_type == ASR_GETNETBYNAME) {
142 				type = T_A;
143 				/*
144 				 * I think we want to do the former, but our
145 				 * resolver is doing the following, so let's
146 				 * preserve bugward-compatibility there.
147 				 */
148 				type = T_PTR;
149 				name = as->as.netnamadr.name;
150 				as->as.netnamadr.subq = res_search_async_ctx(
151 				    name, C_IN, type, as->as_ctx);
152 			} else {
153 				type = T_PTR;
154 				name = dname;
155 
156 				in = htonl(as->as.netnamadr.addr);
157 				asr_addr_as_fqdn((char *)&in,
158 				    as->as.netnamadr.family,
159 				    dname, sizeof(dname));
160 				as->as.netnamadr.subq = res_query_async_ctx(
161 				    name, C_IN, type, as->as_ctx);
162 			}
163 
164 			if (as->as.netnamadr.subq == NULL) {
165 				ar->ar_errno = errno;
166 				ar->ar_h_errno = NETDB_INTERNAL;
167 				async_set_state(as, ASR_STATE_HALT);
168 			}
169 			async_set_state(as, ASR_STATE_SUBQUERY);
170 			break;
171 
172 		case ASR_DB_FILE:
173 
174 			if ((f = fopen("/etc/networks", "r")) == NULL)
175 				break;
176 
177 			if (as->as_type == ASR_GETNETBYNAME)
178 				data = as->as.netnamadr.name;
179 			else
180 				data = (void *)&as->as.netnamadr.addr;
181 
182 			n = netent_file_match(f, as->as_type, data);
183 			saved_errno = errno;
184 			fclose(f);
185 			errno = saved_errno;
186 			if (n == NULL) {
187 				if (errno) {
188 					ar->ar_errno = errno;
189 					ar->ar_h_errno = NETDB_INTERNAL;
190 					async_set_state(as, ASR_STATE_HALT);
191 				}
192 				/* otherwise not found */
193 				break;
194 			}
195 
196 			ar->ar_netent = &n->n;
197 			ar->ar_h_errno = NETDB_SUCCESS;
198 			async_set_state(as, ASR_STATE_HALT);
199 			break;
200 		}
201 		break;
202 
203 	case ASR_STATE_SUBQUERY:
204 
205 		if ((r = asr_run(as->as.netnamadr.subq, ar)) == ASYNC_COND)
206 			return (ASYNC_COND);
207 		as->as.netnamadr.subq = NULL;
208 
209 		if (ar->ar_datalen == -1) {
210 			async_set_state(as, ASR_STATE_NEXT_DB);
211 			break;
212 		}
213 
214 		/* Got packet, but no answer */
215 		if (ar->ar_count == 0) {
216 			free(ar->ar_data);
217 			async_set_state(as, ASR_STATE_NEXT_DB);
218 			break;
219 		}
220 
221 		n = netent_from_packet(as->as_type, ar->ar_data,
222 		    ar->ar_datalen);
223 		free(ar->ar_data);
224 		if (n == NULL) {
225 			ar->ar_errno = errno;
226 			ar->ar_h_errno = NETDB_INTERNAL;
227 			async_set_state(as, ASR_STATE_HALT);
228 			break;
229 		}
230 
231 		if (as->as_type == ASR_GETNETBYADDR)
232 			n->n.n_net = as->as.netnamadr.addr;
233 
234 		/*
235 		 * No address found in the dns packet. The blocking version
236 		 * reports this as an error.
237 		 */
238 		if (as->as_type == ASR_GETNETBYNAME && n->n.n_net == 0) {
239 			 /* XXX wrong */
240 			free(n);
241 			async_set_state(as, ASR_STATE_NEXT_DB);
242 			break;
243 		}
244 
245 		ar->ar_netent = &n->n;
246 		ar->ar_h_errno = NETDB_SUCCESS;
247 		async_set_state(as, ASR_STATE_HALT);
248 		break;
249 
250 	case ASR_STATE_NOT_FOUND:
251 
252 		ar->ar_errno = 0;
253 		ar->ar_h_errno = HOST_NOT_FOUND;
254 		async_set_state(as, ASR_STATE_HALT);
255 		break;
256 
257 	case ASR_STATE_HALT:
258 
259 		if (ar->ar_h_errno)
260 			ar->ar_netent = NULL;
261 		else
262 			ar->ar_errno = 0;
263 		return (ASYNC_DONE);
264 
265 	default:
266 		ar->ar_errno = EOPNOTSUPP;
267 		ar->ar_h_errno = NETDB_INTERNAL;
268 		ar->ar_gai_errno = EAI_SYSTEM;
269 		async_set_state(as, ASR_STATE_HALT);
270 		break;
271 	}
272 	goto next;
273 }
274 
275 static struct netent_ext *
276 netent_file_match(FILE *f, int reqtype, const char *data)
277 {
278 	struct netent_ext	*e;
279 	char			*tokens[MAXTOKEN];
280 	int			 n, i;
281 	in_addr_t		 net;
282 
283 	for (;;) {
284 		n = asr_parse_namedb_line(f, tokens, MAXTOKEN);
285 		if (n == -1) {
286 			errno = 0; /* ignore errors reading the file */
287 			return (NULL);
288 		}
289 
290 		if (reqtype == ASR_GETNETBYADDR) {
291 			net = inet_network(tokens[1]);
292 			if (memcmp(&net, data, sizeof net) == 0)
293 				goto found;
294 		} else {
295 			for (i = 0; i < n; i++) {
296 				if (i == 1)
297 					continue;
298 				if (strcasecmp(data, tokens[i]))
299 					continue;
300 				goto found;
301 			}
302 		}
303 	}
304 
305 found:
306 	if ((e = netent_alloc(AF_INET)) == NULL)
307 		return (NULL);
308 	if (netent_set_cname(e, tokens[0], 0) == -1)
309 		goto fail;
310 	for (i = 2; i < n; i ++)
311 		if (netent_add_alias(e, tokens[i], 0) == -1)
312 			goto fail;
313 	e->n.n_net = inet_network(tokens[1]);
314 	return (e);
315 fail:
316 	free(e);
317 	return (NULL);
318 }
319 
320 static struct netent_ext *
321 netent_from_packet(int reqtype, char *pkt, size_t pktlen)
322 {
323 	struct netent_ext	*n;
324 	struct asr_unpack	 p;
325 	struct asr_dns_header	 hdr;
326 	struct asr_dns_query	 q;
327 	struct asr_dns_rr	 rr;
328 
329 	if ((n = netent_alloc(AF_INET)) == NULL)
330 		return (NULL);
331 
332 	asr_unpack_init(&p, pkt, pktlen);
333 	asr_unpack_header(&p, &hdr);
334 	for (; hdr.qdcount; hdr.qdcount--)
335 		asr_unpack_query(&p, &q);
336 	for (; hdr.ancount; hdr.ancount--) {
337 		asr_unpack_rr(&p, &rr);
338 		if (rr.rr_class != C_IN)
339 			continue;
340 		switch (rr.rr_type) {
341 		case T_CNAME:
342 			if (reqtype == ASR_GETNETBYNAME) {
343 				if (netent_add_alias(n, rr.rr_dname, 1) == -1)
344 					goto fail;
345 			} else {
346 				if (netent_set_cname(n, rr.rr_dname, 1) == -1)
347 					goto fail;
348 			}
349 			break;
350 
351 		case T_PTR:
352 			if (reqtype != ASR_GETNETBYADDR)
353 				continue;
354 			if (netent_set_cname(n, rr.rr.ptr.ptrname, 1) == -1)
355 				goto fail;
356 			/* XXX See if we need to have MULTI_PTRS_ARE_ALIASES */
357 			break;
358 
359 		case T_A:
360 			if (n->n.n_addrtype != AF_INET)
361 				break;
362 			if (netent_set_cname(n, rr.rr_dname, 1) ==  -1)
363 				goto fail;
364 			n->n.n_net = ntohl(rr.rr.in_a.addr.s_addr);
365 			break;
366 		}
367 	}
368 
369 	return (n);
370 fail:
371 	free(n);
372 	return (NULL);
373 }
374 
375 static struct netent_ext *
376 netent_alloc(int family)
377 {
378 	struct netent_ext	*n;
379 	size_t			 alloc;
380 
381 	alloc = sizeof(*n) + 1024;
382 	if ((n = calloc(1, alloc)) == NULL)
383 		return (NULL);
384 
385 	n->n.n_addrtype = family;
386 	n->n.n_aliases = n->aliases;
387 	n->pos = (char *)(n) + sizeof(*n);
388 	n->end = n->pos + 1024;
389 
390 	return (n);
391 }
392 
393 static int
394 netent_set_cname(struct netent_ext *n, const char *name, int isdname)
395 {
396 	char	buf[MAXDNAME];
397 	size_t	l;
398 
399 	if (n->n.n_name)
400 		return (-1);
401 
402 	if (isdname) {
403 		asr_strdname(name, buf, sizeof buf);
404 		buf[strlen(buf) - 1] = '\0';
405 		if (!res_hnok(buf))
406 			return (-1);
407 		name = buf;
408 	}
409 
410 	l = strlen(name) + 1;
411 	if (n->pos + l >= n->end)
412 		return (-1);
413 
414 	n->n.n_name = n->pos;
415 	memmove(n->pos, name, l);
416 	n->pos += l;
417 
418 	return (0);
419 }
420 
421 static int
422 netent_add_alias(struct netent_ext *n, const char *name, int isdname)
423 {
424 	char	buf[MAXDNAME];
425 	size_t	i, l;
426 
427 	for (i = 0; i < MAXALIASES; i++)
428 		if (n->aliases[i] == NULL)
429 			break;
430 	if (i == MAXALIASES)
431 		return (-1);
432 
433 	if (isdname) {
434 		asr_strdname(name, buf, sizeof buf);
435 		buf[strlen(buf)-1] = '\0';
436 		if (!res_hnok(buf))
437 			return (-1);
438 		name = buf;
439 	}
440 
441 	l = strlen(name) + 1;
442 	if (n->pos + l >= n->end)
443 		return (-1);
444 
445 	n->aliases[i] = n->pos;
446 	memmove(n->pos, name, l);
447 	n->pos += l;
448 	return (0);
449 }
450