2 * Copyright (c) 2009 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@
36 #include "dnsinfo_private.h"
37 #include "dnsinfo_create.h"
53 * The supported configuration token strings and enumerated values.
60 { "nameserver", TOKEN_NAMESERVER
, MAXNS
},
61 { "port", TOKEN_PORT
, 1 },
62 { "domain", TOKEN_DOMAIN
, 1 },
63 { "search", TOKEN_SEARCH
, 1 },
64 { "search_order", TOKEN_SEARCH_ORDER
, 1 },
65 { "sortlist", TOKEN_SORTLIST
, 1 },
66 { "timeout", TOKEN_TIMEOUT
, 1 },
67 { "options", TOKEN_OPTIONS
, 1 },
72 * _dnsinfo_parse_address
76 static struct sockaddr
*
77 _dnsinfo_parse_address(char *nameserver
)
80 struct addrinfo hints
;
82 struct sockaddr
*sa
= NULL
;
84 memset(&hints
, 0, sizeof(hints
));
85 hints
.ai_flags
= AI_NUMERICHOST
;
87 res
= getaddrinfo(nameserver
, NULL
, &hints
, &ai
);
89 if ((ai
->ai_family
== AF_INET
) || (ai
->ai_family
== AF_INET6
)) {
90 sa
= malloc(ai
->ai_addrlen
);
91 memcpy(sa
, ai
->ai_addr
, ai
->ai_addrlen
);
101 * _dnsinfo_parse_nameserver
103 * Parse arguments to the nameserver token. This is essentially a getaddrinfo(3)
104 * with AI_NUMERICHOST. However, if the conversion fails, check if the address
105 * contains an optional trailing '.' followed by a numeric port number. If found,
106 * remove the port number and retry the conversion (e.g. 127.0.0.1.55 or ::1.55).
108 static struct sockaddr
*
109 _dnsinfo_parse_nameserver(char *token
)
115 sa
= _dnsinfo_parse_address(token
);
120 // if we could not parse address, attempt to remove
121 // an optional trailing port number
122 dot
= strrchr(token
, '.');
127 number
= strtol(dot
+ 1, NULL
, 10);
128 if ((number
< 0) || (number
> UINT16_MAX
)) {
133 sa
= _dnsinfo_parse_address(token
);
135 in_port_t port
= htons(number
);
137 switch (sa
->sa_family
) {
139 ((struct sockaddr_in
*)sa
)->sin_port
= port
;
142 ((struct sockaddr_in6
*)sa
)->sin6_port
= port
;
152 * _dnsinfo_parse_sortaddr
154 * Parse arguments to the sortlist token.
156 static dns_sortaddr_t
*
157 _dnsinfo_parse_sortaddr(char *token
)
163 dns_sortaddr_t
*sortaddr
= NULL
;
165 slash
= strchr(token
, '/');
170 sa
= _dnsinfo_parse_address(token
);
172 // if we could not parse the address
174 } else if (sa
->sa_family
!= AF_INET
) {
178 addr
= ((struct sockaddr_in
*)sa
)->sin_addr
;
184 sa
= _dnsinfo_parse_address(slash
+ 1);
186 // if we could not parse the provided mask
188 } else if (sa
->sa_family
!= AF_INET
) {
189 // if mask not AF_INET
192 mask
= ((struct sockaddr_in
*)sa
)->sin_addr
;
200 a
= ntohl(addr
.s_addr
);
203 } else if (IN_CLASSB(a
)) {
205 } else if (IN_CLASSC(a
)) {
211 mask
.s_addr
= htonl(m
);
214 sortaddr
= malloc(sizeof(*sortaddr
));
215 sortaddr
->address
= addr
;
216 sortaddr
->mask
= mask
;
220 if (sa
!= NULL
) free(sa
);
226 * _dnsinfo_flatfile_create_resolver
228 * Create a new dns resolver configuration from the configuration file at the
229 * specified path. (e.g. /etc/resolv.conf or /etc/resolver/apple.com)
231 static dns_create_resolver_t
232 _dnsinfo_flatfile_create_resolver(const char *dir
, const char *path
)
236 char filename
[FILENAME_MAX
];
239 dns_create_resolver_t res
= NULL
;
240 const char *sep
= " \t";
241 int token_count
[TOKEN_MAX
] = { 0 };
245 strlcpy(filename
, dir
, sizeof(filename
));
246 strlcat(filename
, "/", sizeof(filename
));
248 strlcat(filename
, path
, sizeof(filename
));
250 f
= fopen(filename
, "r");
251 if (f
== NULL
) return NULL
;
253 while ((buf
= fgetln(f
, &len
)) != NULL
) {
260 if (len
== 0) continue;
261 if (buf
[len
-1] == '\n') buf
[len
-1] = '\0';
263 line
= reallocf(line
, len
+1);
264 if (line
== NULL
) continue;
266 strncpy(line
, buf
, len
);
269 // parse the first word of the line (the config token)
271 word
= strsep(&lineptr
, sep
);
276 if (word
[0] == ';' || word
[0] == '#') {
281 // translate config token to enumerated value
283 for (i
= 0; i
< sizeof(tokens
) / sizeof(tokens
[0]); i
++) {
284 if (strcasecmp(word
, tokens
[i
].name
) == 0) {
285 token
= tokens
[i
].token
;
286 max_count
= tokens
[i
].max_count
;
291 // if not a recognized token
295 // parse the next word of the line (the config option)
296 word
= strsep(&lineptr
, sep
);
301 if (++token_count
[token
] > max_count
) {
302 // if too many options
308 res
= _dns_resolver_create();
310 // if we could not create a resolver
317 _dns_resolver_set_domain(&res
, word
);
320 case TOKEN_NAMESERVER
: {
323 sa
= _dnsinfo_parse_nameserver(word
);
325 _dns_resolver_add_nameserver(&res
, sa
);
334 number
= strtol(word
, NULL
, 0);
335 if (number
< 0 || number
> UINT16_MAX
) break;
336 _dns_resolver_set_port(&res
, number
);
343 // multiple search domains are supported
344 while ((word
!= NULL
) && (n
++ < MAXDNSRCH
)) {
345 _dns_resolver_add_search(&res
, word
);
346 word
= strsep(&lineptr
, sep
);
351 case TOKEN_SORTLIST
: {
354 while ((word
!= NULL
) && (n
++ < MAXRESOLVSORT
)) {
355 dns_sortaddr_t
*sortaddr
;
357 sortaddr
= _dnsinfo_parse_sortaddr(word
);
358 if (sortaddr
== NULL
) break;
359 _dns_resolver_add_sortaddr(&res
, sortaddr
);
361 word
= strsep(&lineptr
, sep
);
367 _dns_resolver_set_options(&res
, lineptr
);
370 case TOKEN_TIMEOUT
: {
373 number
= strtol(word
, NULL
, 0);
374 if (number
< 0 || number
> UINT32_MAX
) break;
375 _dns_resolver_set_timeout(&res
, number
);
379 case TOKEN_SEARCH_ORDER
: {
382 number
= strtol(word
, NULL
, 0);
383 if (number
< 0 || number
> UINT32_MAX
) break;
384 _dns_resolver_set_order(&res
, number
);
389 if (line
!= NULL
) free(line
);
391 // set the domain to the basename of the path if not specified
392 if ((res
!= NULL
) && (token_count
[TOKEN_DOMAIN
] == 0)) {
395 domain
= strrchr(path
, '/');
396 if (domain
== NULL
) {
401 _dns_resolver_set_domain(&res
, domain
);
412 * _dnsinfo_flatfile_add_resolvers
414 * Parse the files in the resolver config directory (/etc/resolver) and add each
415 * resolver to the dns config.
418 _dnsinfo_flatfile_add_resolvers(dns_create_config_t
*config
)
422 dns_create_resolver_t res
;
424 dp
= opendir(_PATH_RESOLVER_DIR
);
429 while ((de
= readdir(dp
)) != NULL
) {
430 if (strcmp(de
->d_name
, ".") == 0 ||
431 strcmp(de
->d_name
, "..") == 0) continue;
433 res
= _dnsinfo_flatfile_create_resolver(_PATH_RESOLVER_DIR
, de
->d_name
);
435 _dns_configuration_add_resolver(config
, res
);
436 _dns_resolver_free(&res
);
447 #include "dnsinfo_copy.c"
450 main(int argc
, char **argv
)
453 dns_config_t
*config
;
454 dns_create_config_t create_config
;
455 _dns_config_buf_t
*config_buf
;
458 dns_create_resolver_t resolver
;
460 resolver
= _dnsinfo_flatfile_create_resolver(NULL
, _PATH_RESCONF
);
462 create_config
= _dns_configuration_create();
463 _dnsinfo_flatfile_add_resolvers(&create_config
);
465 config_buf
= (_dns_config_buf_t
*)create_config
;
466 n_config
= sizeof(_dns_config_buf_t
) + ntohl(config_buf
->n_attribute
);
467 n_padding
= ntohl(config_buf
->n_padding
);
468 buf
= malloc(n_config
+ n_padding
);
469 bcopy((void *)config_buf
, buf
, n_config
);
470 bzero(&buf
[n_config
], n_padding
);
471 config
= expand_config((_dns_config_buf_t
*)buf
);