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