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