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