]> git.saurik.com Git - apple/configd.git/blame - dnsinfo/dnsinfo_flatfile.c
configd-699.1.5.tar.gz
[apple/configd.git] / dnsinfo / dnsinfo_flatfile.c
CommitLineData
a40a14f8 1/*
78403150 2 * Copyright (c) 2009, 2011, 2012, 2014 Apple Inc. All rights reserved.
a40a14f8
A
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>
6bb65964 32#include <net/if.h>
a40a14f8 33#include <sys/dir.h>
6bb65964
A
34#include <sys/types.h>
35#include <sys/socket.h>
a40a14f8
A
36#include <unistd.h>
37
38#include "dnsinfo.h"
39#include "dnsinfo_private.h"
40#include "dnsinfo_create.h"
41
5e9ce69e
A
42static uint32_t _dnsinfo_flatfile_flags;
43
a40a14f8 44enum {
6bb65964
A
45 TOKEN_DOMAIN,
46 TOKEN_FLAGS,
47 TOKEN_INTERFACE,
a40a14f8 48 TOKEN_NAMESERVER,
6bb65964 49 TOKEN_OPTIONS,
a40a14f8 50 TOKEN_PORT,
a40a14f8
A
51 TOKEN_SEARCH,
52 TOKEN_SEARCH_ORDER,
53 TOKEN_SORTLIST,
54 TOKEN_TIMEOUT,
a40a14f8
A
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 [] = {
6bb65964
A
67 { "domain", TOKEN_DOMAIN, 1 },
68 { "flags", TOKEN_FLAGS, 1 },
69 { "interface", TOKEN_INTERFACE, 1 },
a40a14f8 70 { "nameserver", TOKEN_NAMESERVER, MAXNS },
6bb65964 71 { "options", TOKEN_OPTIONS, 1 },
a40a14f8 72 { "port", TOKEN_PORT, 1 },
a40a14f8
A
73 { "search", TOKEN_SEARCH, 1 },
74 { "search_order", TOKEN_SEARCH_ORDER, 1 },
75 { "sortlist", TOKEN_SORTLIST, 1 },
76 { "timeout", TOKEN_TIMEOUT, 1 },
a40a14f8
A
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 :
17d3ee29
A
148 /* ALIGN: cast ok, sockaddr was malloc'd */
149 ((struct sockaddr_in *)(void *)sa)->sin_port = port;
a40a14f8
A
150 break;
151 case AF_INET6 :
17d3ee29
A
152 /* ALIGN: cast ok, sockaddr was malloc'd */
153 ((struct sockaddr_in6 *)(void *)sa)->sin6_port = port;
a40a14f8
A
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 {
17d3ee29
A
189 /* ALIGN: cast ok, sockaddr was malloc'd */
190 addr = ((struct sockaddr_in *)(void *)sa)->sin_addr;
a40a14f8
A
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 {
17d3ee29
A
204 /* ALIGN: cast ok, sockaddr was malloc'd */
205 mask = ((struct sockaddr_in *)(void *)sa)->sin_addr;
a40a14f8
A
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
5e9ce69e
A
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
a40a14f8
A
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;
5e9ce69e 276 uint32_t config_flags = 0;
a40a14f8
A
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) {
6bb65964
A
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 }
a40a14f8 369 break;
6bb65964
A
370 }
371
372 case TOKEN_FLAGS: {
6bb65964
A
373 while (word != NULL) {
374 if (word[0] != '\0') {
375 if (strcasecmp(word, "scoped") == 0) {
5e9ce69e
A
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;
6bb65964
A
381 }
382 }
383 word = strsep(&lineptr, sep);
384 }
6bb65964
A
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 }
a40a14f8
A
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
6bb65964
A
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
a40a14f8
A
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)) {
6bb65964
A
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 }
a40a14f8
A
458 word = strsep(&lineptr, sep);
459 }
460 break;
461 }
462
6bb65964
A
463 case TOKEN_SEARCH_ORDER: {
464 long number = -1;
465
466 number = strtol(word, NULL, 0);
467 if (number < 0 || number > UINT32_MAX) break;
78403150 468 _dns_resolver_set_order(&res, (uint32_t)number);
6bb65964
A
469 break;
470 }
471
a40a14f8
A
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
a40a14f8
A
487 case TOKEN_TIMEOUT: {
488 long number = -1;
489
490 number = strtol(word, NULL, 0);
491 if (number < 0 || number > UINT32_MAX) break;
78403150 492 _dns_resolver_set_timeout(&res, (uint32_t)number);
a40a14f8
A
493 break;
494 }
a40a14f8
A
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
5e9ce69e
A
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
a40a14f8
A
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 */
5e9ce69e 534__private_extern__
a40a14f8
A
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
6bb65964
A
563#ifdef MAIN
564#undef MAIN
a40a14f8
A
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}
6bb65964 594
a40a14f8 595#endif