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