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