1/*
2 * Copyright (c) 2009, 2011, 2012, 2014 Apple Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
11 * file.
12 *
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23
24#include <dirent.h>
25#include <fcntl.h>
26#include <libgen.h>
27#include <netdb.h>
28#include <resolv.h>
29#include <stdio.h>
30#include <stdlib.h>
31#include <string.h>
32#include <net/if.h>
33#include <sys/dir.h>
34#include <sys/types.h>
35#include <sys/socket.h>
36#include <unistd.h>
37
38#include "dnsinfo.h"
39#include "dnsinfo_private.h"
40#include "dnsinfo_create.h"
41
42static uint32_t _dnsinfo_flatfile_flags;
43
44enum {
45	TOKEN_DOMAIN,
46	TOKEN_FLAGS,
47	TOKEN_INTERFACE,
48	TOKEN_NAMESERVER,
49	TOKEN_OPTIONS,
50	TOKEN_PORT,
51	TOKEN_SEARCH,
52	TOKEN_SEARCH_ORDER,
53	TOKEN_SORTLIST,
54	TOKEN_TIMEOUT,
55	TOKEN_MAX
56};
57
58/*
59 * tokens
60 * The supported configuration token strings and enumerated values.
61 */
62static const struct {
63	const char	*name;
64	int		token;
65	int		max_count;
66} tokens [] = {
67	{ "domain",		TOKEN_DOMAIN,		1	},
68	{ "flags",		TOKEN_FLAGS,		1	},
69	{ "interface",		TOKEN_INTERFACE,	1	},
70	{ "nameserver",		TOKEN_NAMESERVER,	MAXNS	},
71	{ "options",		TOKEN_OPTIONS,		1	},
72	{ "port",		TOKEN_PORT,		1	},
73	{ "search",		TOKEN_SEARCH,		1	},
74	{ "search_order",	TOKEN_SEARCH_ORDER,	1	},
75	{ "sortlist",		TOKEN_SORTLIST,		1	},
76	{ "timeout",		TOKEN_TIMEOUT,		1	},
77};
78
79
80/*
81 * _dnsinfo_parse_address
82 *
83 * Parse IP address
84 */
85static struct sockaddr *
86_dnsinfo_parse_address(char *nameserver)
87{
88	struct addrinfo	*ai;
89	struct addrinfo	hints;
90	int		res;
91	struct sockaddr	*sa	= NULL;
92
93	memset(&hints, 0, sizeof(hints));
94	hints.ai_flags = AI_NUMERICHOST;
95
96	res = getaddrinfo(nameserver, NULL, &hints, &ai);
97	if (res == 0) {
98		if ((ai->ai_family == AF_INET) || (ai->ai_family == AF_INET6)) {
99			sa = malloc(ai->ai_addrlen);
100			memcpy(sa, ai->ai_addr, ai->ai_addrlen);
101		}
102		freeaddrinfo(ai);
103	}
104
105	return sa;
106}
107
108
109/*
110 * _dnsinfo_parse_nameserver
111 *
112 * Parse arguments to the nameserver token. This is essentially a getaddrinfo(3)
113 * with AI_NUMERICHOST. However, if the conversion fails, check if the address
114 * contains an optional trailing '.' followed by a numeric port number.  If found,
115 * remove the port number and retry the conversion (e.g. 127.0.0.1.55 or ::1.55).
116 */
117static struct sockaddr *
118_dnsinfo_parse_nameserver(char *token)
119{
120	char		*dot;
121	long		number;
122	struct sockaddr	*sa;
123
124	sa = _dnsinfo_parse_address(token);
125	if (sa != NULL) {
126		return sa;
127	}
128
129	// if we could not parse address, attempt to remove
130	// an optional trailing port number
131	dot = strrchr(token, '.');
132	if (dot == NULL) {
133		return NULL;
134	}
135
136	number = strtol(dot + 1, NULL, 10);
137	if ((number < 0) || (number > UINT16_MAX)) {
138		return NULL;
139	}
140
141	*dot = '\0';
142	sa = _dnsinfo_parse_address(token);
143	if (sa != NULL) {
144		in_port_t	port	= htons(number);
145
146		switch (sa->sa_family) {
147			case AF_INET :
148				/* ALIGN: cast ok, sockaddr was malloc'd */
149				((struct sockaddr_in *)(void *)sa)->sin_port = port;
150				break;
151			case AF_INET6 :
152				/* ALIGN: cast ok, sockaddr was malloc'd */
153				((struct sockaddr_in6 *)(void *)sa)->sin6_port = port;
154				break;
155		}
156	}
157
158	return sa;
159}
160
161
162/*
163 * _dnsinfo_parse_sortaddr
164 *
165 * Parse arguments to the sortlist token.
166 */
167static dns_sortaddr_t *
168_dnsinfo_parse_sortaddr(char *token)
169{
170	struct in_addr		addr;
171	struct in_addr		mask;
172	struct sockaddr		*sa;
173	char			*slash;
174	dns_sortaddr_t		*sortaddr	= NULL;
175
176	slash = strchr(token, '/');
177	if (slash != NULL) {
178		*slash = '\0';
179	}
180
181	sa = _dnsinfo_parse_address(token);
182	if (sa == NULL) {
183		// if we could not parse the address
184		goto done;
185	} else if (sa->sa_family != AF_INET) {
186		// if not AF_INET
187		goto done;
188	} else {
189		/* ALIGN: cast ok, sockaddr was malloc'd */
190		addr = ((struct sockaddr_in *)(void *)sa)->sin_addr;
191		free(sa);
192		sa = NULL;
193	}
194
195	if (slash != NULL) {
196		sa = _dnsinfo_parse_address(slash + 1);
197		if (sa == NULL) {
198			// if we could not parse the provided mask
199			goto done;
200		} else if (sa->sa_family != AF_INET) {
201			// if mask not AF_INET
202			goto done;
203		} else {
204			/* ALIGN: cast ok, sockaddr was malloc'd */
205			mask = ((struct sockaddr_in *)(void *)sa)->sin_addr;
206			free(sa);
207			sa = NULL;
208		}
209	} else {
210		in_addr_t	a;
211		in_addr_t	m;
212
213		a = ntohl(addr.s_addr);
214		if (IN_CLASSA(a)) {
215			m = IN_CLASSA_NET;
216		} else if (IN_CLASSB(a)) {
217			m = IN_CLASSB_NET;
218		} else if (IN_CLASSC(a)) {
219			m = IN_CLASSC_NET;
220		} else {
221			goto done;
222		}
223
224		mask.s_addr = htonl(m);
225	}
226
227	sortaddr = malloc(sizeof(*sortaddr));
228	sortaddr->address = addr;
229	sortaddr->mask    = mask;
230
231    done :
232
233	if (sa != NULL) free(sa);
234	return sortaddr;
235}
236
237
238/*
239 * _dnsinfo_flatfile_set_flags
240 *
241 * Set the default resolver flags.
242 */
243__private_extern__
244void
245_dnsinfo_flatfile_set_flags(uint32_t flags)
246{
247	_dnsinfo_flatfile_flags = flags;
248	return;
249}
250
251
252static void
253_dnsinfo_flatfile_update_flags(dns_create_resolver_t *_resolver)
254{
255	uint32_t		new_flags;
256	uint32_t		old_flags;
257	_dns_resolver_buf_t	*resolver	= (_dns_resolver_buf_t *)*_resolver;
258
259	old_flags = ntohl(resolver->resolver.flags);
260	new_flags = old_flags | _dnsinfo_flatfile_flags;
261	_dns_resolver_set_flags(_resolver, new_flags);
262	return;
263}
264
265
266/*
267 * _dnsinfo_flatfile_create_resolver
268 *
269 * Create a new dns resolver configuration from the configuration file at the
270 * specified path. (e.g. /etc/resolv.conf or /etc/resolver/apple.com)
271 */
272static dns_create_resolver_t
273_dnsinfo_flatfile_create_resolver(const char *dir, const char *path)
274{
275	char			*buf;
276	uint32_t		config_flags		= 0;
277	FILE			*f;
278	char			filename[FILENAME_MAX];
279	size_t			len			= 0;
280	char			*line			= NULL;
281	dns_create_resolver_t	res			= NULL;
282	const char		*sep			= " \t";
283	int			token_count[TOKEN_MAX]	= { 0 };
284
285	filename[0] = 0;
286	if (dir != NULL) {
287		strlcpy(filename, dir, sizeof(filename));
288		strlcat(filename, "/", sizeof(filename));
289	}
290	strlcat(filename, path, sizeof(filename));
291
292	f = fopen(filename, "r");
293	if (f == NULL) return NULL;
294
295	while ((buf = fgetln(f, &len)) != NULL) {
296		int	i;
297		char	*lineptr;
298		int	max_count;
299		int	token;
300		char	*word;
301
302		if (len == 0) continue;
303		if (buf[len-1] == '\n') buf[len-1] = '\0';
304
305		line = reallocf(line, len+1);
306		if (line == NULL) continue;
307
308		strncpy(line, buf, len);
309		line[len] = '\0';
310
311		// parse the first word of the line (the config token)
312		lineptr = line;
313		word = strsep(&lineptr, sep);
314		if (word == NULL) {
315			// if empty line
316			continue;
317		}
318		if (word[0] == ';' || word[0] == '#') {
319			// if comment
320			continue;
321		}
322
323		// translate config token to enumerated value
324		token = -1;
325		for (i = 0; i < sizeof(tokens) / sizeof(tokens[0]); i++) {
326			if (strcasecmp(word, tokens[i].name) == 0) {
327				token     = tokens[i].token;
328				max_count = tokens[i].max_count;
329				break;
330			}
331		}
332		if (token == -1) {
333			// if not a recognized token
334			continue;
335		}
336
337		// parse the next word of the line (the config option)
338		word = strsep(&lineptr, sep);
339		if (word == NULL) {
340			// if no option
341			continue;
342		}
343		if (++token_count[token] > max_count) {
344			// if too many options
345			continue;
346		}
347
348		// create resolver
349		if (res == NULL) {
350			res = _dns_resolver_create();
351			if (res == NULL) {
352				// if we could not create a resolver
353				goto done;
354			}
355		}
356
357		switch (token) {
358			case TOKEN_DOMAIN: {
359				size_t	len;
360
361				len = strlen(word);
362				while ((len > 0) && (word[len - 1] == '.')) {
363					// trim trailing '.'
364					word[--len] = '\0';
365				}
366				if (len > 0) {
367					_dns_resolver_set_domain(&res, word);
368				}
369				break;
370			}
371
372			case TOKEN_FLAGS: {
373				while (word != NULL) {
374					if (word[0] != '\0') {
375						if (strcasecmp(word, "scoped") == 0) {
376							config_flags |= DNS_RESOLVER_FLAGS_SCOPED;
377						} else if (strcasecmp(word, "a") == 0) {
378							config_flags |= DNS_RESOLVER_FLAGS_REQUEST_A_RECORDS;
379						} else if (strcasecmp(word, "aaaa") == 0) {
380							config_flags |= DNS_RESOLVER_FLAGS_REQUEST_AAAA_RECORDS;
381						}
382					}
383					word = strsep(&lineptr, sep);
384				}
385				break;
386			}
387
388			case TOKEN_INTERFACE: {
389				unsigned int	if_index;
390
391				if_index = if_nametoindex(word);
392				if (if_index > 0) {
393					_dns_resolver_set_if_index(&res, if_index);
394				}
395				break;
396			}
397
398			case TOKEN_NAMESERVER: {
399				struct sockaddr	*sa;
400
401				sa = _dnsinfo_parse_nameserver(word);
402				if (sa != NULL) {
403					_dns_resolver_add_nameserver(&res, sa);
404					free(sa);
405				}
406				break;
407			}
408
409			case TOKEN_OPTIONS: {
410				char	*options	= NULL;
411
412				while (word != NULL) {
413					if (word[0] != '\0') {
414						if (options == NULL) {
415							options = malloc(len+1);
416							if (options == NULL) break;
417
418							strlcpy(options, word, len+1);
419						} else {
420							strlcat(options, " ", len+1);
421							strlcat(options, word, len+1);
422						}
423					}
424					word = strsep(&lineptr, sep);
425				}
426
427				if (options != NULL) {
428					_dns_resolver_set_options(&res, options);
429					free(options);
430				}
431				break;
432			}
433
434			case TOKEN_PORT: {
435				long	number	= -1;
436
437				number = strtol(word, NULL, 0);
438				if (number < 0 || number > UINT16_MAX) break;
439				_dns_resolver_set_port(&res, number);
440				break;
441			}
442
443			case TOKEN_SEARCH: {
444				int	n	= 0;
445
446				// multiple search domains are supported
447				while ((word != NULL) && (n++ < MAXDNSRCH)) {
448					size_t	len;
449
450					len = strlen(word);
451					while ((len > 0) && (word[len - 1] == '.')) {
452						// trim trailing '.'
453						word[--len] = '\0';
454					}
455					if (len > 0) {
456						_dns_resolver_add_search(&res, word);
457					}
458					word = strsep(&lineptr, sep);
459				}
460				break;
461			}
462
463			case TOKEN_SEARCH_ORDER: {
464				long	number	= -1;
465
466				number = strtol(word, NULL, 0);
467				if (number < 0 || number > UINT32_MAX) break;
468				_dns_resolver_set_order(&res, (uint32_t)number);
469				break;
470			}
471
472			case TOKEN_SORTLIST: {
473				int	n	= 0;
474
475				while ((word != NULL) && (n++ < MAXRESOLVSORT)) {
476					dns_sortaddr_t	*sortaddr;
477
478					sortaddr = _dnsinfo_parse_sortaddr(word);
479					if (sortaddr == NULL) break;
480					_dns_resolver_add_sortaddr(&res, sortaddr);
481					free(sortaddr);
482					word = strsep(&lineptr, sep);
483				}
484				break;
485			}
486
487			case TOKEN_TIMEOUT: {
488				long	number	= -1;
489
490				number = strtol(word, NULL, 0);
491				if (number < 0 || number > UINT32_MAX) break;
492				_dns_resolver_set_timeout(&res, (uint32_t)number);
493				break;
494			}
495		}
496	}
497	if (line != NULL) free(line);
498
499	// set the domain to the basename of the path if not specified
500	if ((res != NULL) && (token_count[TOKEN_DOMAIN] == 0)) {
501		const char	*domain;
502
503		domain = strrchr(path, '/');
504		if (domain == NULL) {
505			domain = path;
506		} else {
507			domain = domain + 1;
508		}
509		_dns_resolver_set_domain(&res, domain);
510	}
511
512	if (res != NULL) {
513		// config flags should overwrite any default flags
514		if (config_flags != 0) {
515			_dns_resolver_set_flags(&res, config_flags);
516		} else {
517			_dnsinfo_flatfile_update_flags(&res);
518		}
519	}
520
521    done :
522
523	fclose(f);
524	return res;
525}
526
527
528/*
529 * _dnsinfo_flatfile_add_resolvers
530 *
531 * Parse the files in the resolver config directory (/etc/resolver) and add each
532 * resolver to the dns config.
533 */
534__private_extern__
535void
536_dnsinfo_flatfile_add_resolvers(dns_create_config_t *config)
537{
538	struct dirent		*de;
539	DIR			*dp;
540	dns_create_resolver_t	res;
541
542	dp = opendir(_PATH_RESOLVER_DIR);
543	if (dp == NULL) {
544		return;
545	}
546
547	while ((de = readdir(dp)) != NULL) {
548		if (strcmp(de->d_name, ".") == 0 ||
549		    strcmp(de->d_name, "..") == 0) continue;
550
551		res = _dnsinfo_flatfile_create_resolver(_PATH_RESOLVER_DIR, de->d_name);
552		if (res != NULL) {
553			_dns_configuration_add_resolver(config, res);
554			_dns_resolver_free(&res);
555		}
556	}
557
558	closedir(dp);
559	return;
560}
561
562
563#ifdef	MAIN
564#undef	MAIN
565
566#include "dnsinfo_copy.c"
567
568int
569main(int argc, char **argv)
570{
571	uint8_t			*buf;
572	dns_config_t		*config;
573	dns_create_config_t	create_config;
574	_dns_config_buf_t	*config_buf;
575	uint32_t		n_config;
576	uint32_t		n_padding;
577	dns_create_resolver_t	resolver;
578
579	resolver = _dnsinfo_flatfile_create_resolver(NULL, _PATH_RESCONF);
580
581	create_config = _dns_configuration_create();
582	_dnsinfo_flatfile_add_resolvers(&create_config);
583
584	config_buf = (_dns_config_buf_t *)create_config;
585	n_config  = sizeof(_dns_config_buf_t) + ntohl(config_buf->n_attribute);
586	n_padding = ntohl(config_buf->n_padding);
587	buf = malloc(n_config + n_padding);
588	bcopy((void *)config_buf, buf, n_config);
589	bzero(&buf[n_config], n_padding);
590	config = expand_config((_dns_config_buf_t *)buf);
591
592	return 0;
593}
594
595#endif
596