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