2 * Copyright (c) 2009, 2011, 2012, 2014, 2015, 2017-2020 Apple Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
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
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.
21 * @APPLE_LICENSE_HEADER_END@
34 #include <sys/types.h>
35 #include <sys/socket.h>
37 #include <SystemConfiguration/SCPrivate.h> // for SC_log
41 os_log_t
SC_LOG_HANDLE(void);
42 #endif //SC_LOG_HANDLE
45 #include "dnsinfo_private.h"
46 #include "dnsinfo_create.h"
48 static uint32_t _dnsinfo_flatfile_flags
;
66 * The supported configuration token strings and enumerated values.
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 },
87 * _dnsinfo_parse_address
91 static struct sockaddr
*
92 _dnsinfo_parse_address(char *nameserver
)
95 struct addrinfo hints
;
97 struct sockaddr
*sa
= NULL
;
99 memset(&hints
, 0, sizeof(hints
));
100 hints
.ai_flags
= AI_NUMERICHOST
;
102 res
= getaddrinfo(nameserver
, NULL
, &hints
, &ai
);
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
);
116 * _dnsinfo_parse_nameserver
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).
123 static struct sockaddr
*
124 _dnsinfo_parse_nameserver(char *token
)
130 sa
= _dnsinfo_parse_address(token
);
135 // if we could not parse address, attempt to remove
136 // an optional trailing port number
137 dot
= strrchr(token
, '.');
142 number
= strtol(dot
+ 1, NULL
, 10);
143 if ((number
< 0) || (number
> UINT16_MAX
)) {
148 sa
= _dnsinfo_parse_address(token
);
150 in_port_t port
= htons(number
);
152 switch (sa
->sa_family
) {
154 /* ALIGN: cast ok, sockaddr was malloc'd */
155 ((struct sockaddr_in
*)(void *)sa
)->sin_port
= port
;
158 /* ALIGN: cast ok, sockaddr was malloc'd */
159 ((struct sockaddr_in6
*)(void *)sa
)->sin6_port
= port
;
169 * _dnsinfo_parse_sortaddr
171 * Parse arguments to the sortlist token.
173 static dns_sortaddr_t
*
174 _dnsinfo_parse_sortaddr(char *token
)
180 dns_sortaddr_t
*sortaddr
= NULL
;
182 slash
= strchr(token
, '/');
187 sa
= _dnsinfo_parse_address(token
);
189 // if we could not parse the address
191 } else if (sa
->sa_family
!= AF_INET
) {
195 /* ALIGN: cast ok, sockaddr was malloc'd */
196 addr
= ((struct sockaddr_in
*)(void *)sa
)->sin_addr
;
202 sa
= _dnsinfo_parse_address(slash
+ 1);
204 // if we could not parse the provided mask
206 } else if (sa
->sa_family
!= AF_INET
) {
207 // if mask not AF_INET
210 /* ALIGN: cast ok, sockaddr was malloc'd */
211 mask
= ((struct sockaddr_in
*)(void *)sa
)->sin_addr
;
219 a
= ntohl(addr
.s_addr
);
222 } else if (IN_CLASSB(a
)) {
224 } else if (IN_CLASSC(a
)) {
230 mask
.s_addr
= htonl(m
);
233 sortaddr
= malloc(sizeof(*sortaddr
));
234 sortaddr
->address
= addr
;
235 sortaddr
->mask
= mask
;
239 if (sa
!= NULL
) free(sa
);
245 * _dnsinfo_flatfile_set_flags
247 * Set the default resolver flags.
251 _dnsinfo_flatfile_set_flags(uint32_t flags
)
253 _dnsinfo_flatfile_flags
= flags
;
259 _dnsinfo_flatfile_update_flags(dns_create_resolver_t
*_resolver
)
263 _dns_resolver_buf_t
*resolver
= (_dns_resolver_buf_t
*)*_resolver
;
265 old_flags
= ntohl(resolver
->resolver
.flags
);
266 new_flags
= old_flags
| _dnsinfo_flatfile_flags
;
267 _dns_resolver_set_flags(_resolver
, new_flags
);
273 * _dnsinfo_flatfile_create_resolver
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)
278 static dns_create_resolver_t
279 _dnsinfo_flatfile_create_resolver(const char *dir
, const char *path
)
282 uint32_t config_flags
= 0;
284 char filename
[FILENAME_MAX
];
287 dns_create_resolver_t res
= NULL
;
288 const char *sep
= " \t";
289 int token_count
[TOKEN_MAX
] = { 0 };
293 strlcpy(filename
, dir
, sizeof(filename
));
294 strlcat(filename
, "/", sizeof(filename
));
296 strlcat(filename
, path
, sizeof(filename
));
298 f
= fopen(filename
, "r");
299 if (f
== NULL
) return NULL
;
301 while ((buf
= fgetln(f
, &len
)) != NULL
) {
307 if (len
== 0) continue;
308 if (buf
[len
-1] == '\n') buf
[len
-1] = '\0';
310 line
= reallocf(line
, len
+1);
311 if (line
== NULL
) continue;
313 strlcpy(line
, buf
, len
+1);
315 // parse the first word of the line (the config token)
317 word
= strsep(&lineptr
, sep
);
322 if (word
[0] == ';' || word
[0] == '#') {
327 // translate config token to enumerated value
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
;
337 // if not a recognized token
339 "Unrecognized token (%s) found in: %s",
345 // parse the next word of the line (the config option)
346 word
= strsep(&lineptr
, sep
);
351 if (++token_count
[token
] > max_count
) {
352 // if too many options
358 res
= _dns_resolver_create();
360 // if we could not create a resolver
370 while ((len
> 0) && (word
[len
- 1] == '.')) {
375 _dns_resolver_set_domain(&res
, word
);
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
;
391 word
= strsep(&lineptr
, sep
);
396 case TOKEN_INTERFACE
: {
397 unsigned int if_index
;
399 if_index
= if_nametoindex(word
);
401 _dns_resolver_set_if_index(&res
, if_index
, word
);
406 case TOKEN_NAMESERVER
: {
409 sa
= _dnsinfo_parse_nameserver(word
);
411 _dns_resolver_add_nameserver(&res
, sa
);
417 case TOKEN_OPTIONS
: {
418 char *options
= NULL
;
420 while (word
!= NULL
) {
421 if (word
[0] != '\0') {
422 if (options
== NULL
) {
423 options
= malloc(len
+1);
424 if (options
== NULL
) break;
426 strlcpy(options
, word
, len
+1);
428 strlcat(options
, " ", len
+1);
429 strlcat(options
, word
, len
+1);
432 word
= strsep(&lineptr
, sep
);
435 if (options
!= NULL
) {
436 _dns_resolver_set_options(&res
, options
);
445 number
= strtol(word
, NULL
, 0);
446 if (number
< 0 || number
> UINT16_MAX
) break;
447 _dns_resolver_set_port(&res
, number
);
454 // multiple search domains are supported
455 while ((word
!= NULL
) && (n
++ < MAXDNSRCH
)) {
459 while ((len
> 0) && (word
[len
- 1] == '.')) {
464 _dns_resolver_add_search(&res
, word
);
466 word
= strsep(&lineptr
, sep
);
471 case TOKEN_SEARCH_ORDER
: {
474 number
= strtol(word
, NULL
, 0);
475 if (number
< 0 || number
> (long)UINT32_MAX
) break;
476 _dns_resolver_set_order(&res
, (uint32_t)number
);
480 case TOKEN_SORTLIST
: {
483 while ((word
!= NULL
) && (n
++ < MAXRESOLVSORT
)) {
484 dns_sortaddr_t
*sortaddr
;
486 sortaddr
= _dnsinfo_parse_sortaddr(word
);
487 if (sortaddr
== NULL
) break;
488 _dns_resolver_add_sortaddr(&res
, sortaddr
);
490 word
= strsep(&lineptr
, sep
);
495 case TOKEN_TIMEOUT
: {
498 number
= strtol(word
, NULL
, 0);
499 if (number
< 0 || number
> (long)UINT32_MAX
) break;
500 _dns_resolver_set_timeout(&res
, (uint32_t)number
);
506 // set the domain to the basename of the path if not specified
507 if ((res
!= NULL
) && (token_count
[TOKEN_DOMAIN
] == 0)) {
510 domain
= strrchr(path
, '/');
511 if (domain
== NULL
) {
516 _dns_resolver_set_domain(&res
, domain
);
520 // config flags should overwrite any default flags
521 if (config_flags
!= 0) {
522 _dns_resolver_set_flags(&res
, config_flags
);
524 _dnsinfo_flatfile_update_flags(&res
);
530 if (line
!= NULL
) free(line
);
537 * _dnsinfo_flatfile_add_resolvers
539 * Parse the files in the resolver config directory (/etc/resolver) and add each
540 * resolver to the dns config.
544 _dnsinfo_flatfile_add_resolvers(dns_create_config_t
*config
)
548 dns_create_resolver_t res
;
550 dp
= opendir(_PATH_RESOLVER_DIR
);
555 while ((de
= readdir(dp
)) != NULL
) {
556 if (strcmp(de
->d_name
, ".") == 0 ||
557 strcmp(de
->d_name
, "..") == 0) continue;
559 res
= _dnsinfo_flatfile_create_resolver(_PATH_RESOLVER_DIR
, de
->d_name
);
561 _dns_configuration_add_resolver(config
, res
);
562 _dns_resolver_free(&res
);
574 #include "dnsinfo_logging.h"
575 #include "dnsinfo_copy.c"
578 main(int argc
, char **argv
)
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
;
585 dns_create_resolver
= _dnsinfo_flatfile_create_resolver(NULL
, _PATH_RESCONF
);
586 _dns_resolver_free(&dns_create_resolver
);
588 dns_create_config
= _dns_configuration_create();
589 if (dns_create_config
!= NULL
) {
592 _dnsinfo_flatfile_add_resolvers(&dns_create_config
);
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
);
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
);
608 if (dns_config
!= NULL
) {
609 _dns_configuration_log(dns_config
, TRUE
, NULL
);