]> git.saurik.com Git - apple/configd.git/blob - dnsinfo/dnsinfo_flatfile.c
configd-289.tar.gz
[apple/configd.git] / dnsinfo / dnsinfo_flatfile.c
1 /*
2 * Copyright (c) 2009 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 <sys/dir.h>
33 #include <unistd.h>
34
35 #include "dnsinfo.h"
36 #include "dnsinfo_private.h"
37 #include "dnsinfo_create.h"
38
39 enum {
40 TOKEN_NAMESERVER,
41 TOKEN_PORT,
42 TOKEN_DOMAIN,
43 TOKEN_SEARCH,
44 TOKEN_SEARCH_ORDER,
45 TOKEN_SORTLIST,
46 TOKEN_TIMEOUT,
47 TOKEN_OPTIONS,
48 TOKEN_MAX
49 };
50
51 /*
52 * tokens
53 * The supported configuration token strings and enumerated values.
54 */
55 static const struct {
56 const char *name;
57 int token;
58 int max_count;
59 } tokens [] = {
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 },
68 };
69
70
71 /*
72 * _dnsinfo_parse_address
73 *
74 * Parse IP address
75 */
76 static struct sockaddr *
77 _dnsinfo_parse_address(char *nameserver)
78 {
79 struct addrinfo *ai;
80 struct addrinfo hints;
81 int res;
82 struct sockaddr *sa = NULL;
83
84 memset(&hints, 0, sizeof(hints));
85 hints.ai_flags = AI_NUMERICHOST;
86
87 res = getaddrinfo(nameserver, NULL, &hints, &ai);
88 if (res == 0) {
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);
92 }
93 freeaddrinfo(ai);
94 }
95
96 return sa;
97 }
98
99
100 /*
101 * _dnsinfo_parse_nameserver
102 *
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).
107 */
108 static struct sockaddr *
109 _dnsinfo_parse_nameserver(char *token)
110 {
111 char *dot;
112 long number;
113 struct sockaddr *sa;
114
115 sa = _dnsinfo_parse_address(token);
116 if (sa != NULL) {
117 return sa;
118 }
119
120 // if we could not parse address, attempt to remove
121 // an optional trailing port number
122 dot = strrchr(token, '.');
123 if (dot == NULL) {
124 return NULL;
125 }
126
127 number = strtol(dot + 1, NULL, 10);
128 if ((number < 0) || (number > UINT16_MAX)) {
129 return NULL;
130 }
131
132 *dot = '\0';
133 sa = _dnsinfo_parse_address(token);
134 if (sa != NULL) {
135 in_port_t port = htons(number);
136
137 switch (sa->sa_family) {
138 case AF_INET :
139 ((struct sockaddr_in *)sa)->sin_port = port;
140 break;
141 case AF_INET6 :
142 ((struct sockaddr_in6 *)sa)->sin6_port = port;
143 break;
144 }
145 }
146
147 return sa;
148 }
149
150
151 /*
152 * _dnsinfo_parse_sortaddr
153 *
154 * Parse arguments to the sortlist token.
155 */
156 static dns_sortaddr_t *
157 _dnsinfo_parse_sortaddr(char *token)
158 {
159 struct in_addr addr;
160 struct in_addr mask;
161 struct sockaddr *sa;
162 char *slash;
163 dns_sortaddr_t *sortaddr = NULL;
164
165 slash = strchr(token, '/');
166 if (slash != NULL) {
167 *slash = '\0';
168 }
169
170 sa = _dnsinfo_parse_address(token);
171 if (sa == NULL) {
172 // if we could not parse the address
173 goto done;
174 } else if (sa->sa_family != AF_INET) {
175 // if not AF_INET
176 goto done;
177 } else {
178 addr = ((struct sockaddr_in *)sa)->sin_addr;
179 free(sa);
180 sa = NULL;
181 }
182
183 if (slash != NULL) {
184 sa = _dnsinfo_parse_address(slash + 1);
185 if (sa == NULL) {
186 // if we could not parse the provided mask
187 goto done;
188 } else if (sa->sa_family != AF_INET) {
189 // if mask not AF_INET
190 goto done;
191 } else {
192 mask = ((struct sockaddr_in *)sa)->sin_addr;
193 free(sa);
194 sa = NULL;
195 }
196 } else {
197 in_addr_t a;
198 in_addr_t m;
199
200 a = ntohl(addr.s_addr);
201 if (IN_CLASSA(a)) {
202 m = IN_CLASSA_NET;
203 } else if (IN_CLASSB(a)) {
204 m = IN_CLASSB_NET;
205 } else if (IN_CLASSC(a)) {
206 m = IN_CLASSC_NET;
207 } else {
208 goto done;
209 }
210
211 mask.s_addr = htonl(m);
212 }
213
214 sortaddr = malloc(sizeof(*sortaddr));
215 sortaddr->address = addr;
216 sortaddr->mask = mask;
217
218 done :
219
220 if (sa != NULL) free(sa);
221 return sortaddr;
222 }
223
224
225 /*
226 * _dnsinfo_flatfile_create_resolver
227 *
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)
230 */
231 static dns_create_resolver_t
232 _dnsinfo_flatfile_create_resolver(const char *dir, const char *path)
233 {
234 char *buf;
235 FILE *f;
236 char filename[FILENAME_MAX];
237 size_t len = 0;
238 char *line = NULL;
239 dns_create_resolver_t res = NULL;
240 const char *sep = " \t";
241 int token_count[TOKEN_MAX] = { 0 };
242
243 filename[0] = 0;
244 if (dir != NULL) {
245 strlcpy(filename, dir, sizeof(filename));
246 strlcat(filename, "/", sizeof(filename));
247 }
248 strlcat(filename, path, sizeof(filename));
249
250 f = fopen(filename, "r");
251 if (f == NULL) return NULL;
252
253 while ((buf = fgetln(f, &len)) != NULL) {
254 int i;
255 char *lineptr;
256 int max_count;
257 int token;
258 char *word;
259
260 if (len == 0) continue;
261 if (buf[len-1] == '\n') buf[len-1] = '\0';
262
263 line = reallocf(line, len+1);
264 if (line == NULL) continue;
265
266 strncpy(line, buf, len);
267 line[len] = '\0';
268
269 // parse the first word of the line (the config token)
270 lineptr = line;
271 word = strsep(&lineptr, sep);
272 if (word == NULL) {
273 // if empty line
274 continue;
275 }
276 if (word[0] == ';' || word[0] == '#') {
277 // if comment
278 continue;
279 }
280
281 // translate config token to enumerated value
282 token = -1;
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;
287 break;
288 }
289 }
290 if (token == -1) {
291 // if not a recognized token
292 continue;
293 }
294
295 // parse the next word of the line (the config option)
296 word = strsep(&lineptr, sep);
297 if (word == NULL) {
298 // if no option
299 continue;
300 }
301 if (++token_count[token] > max_count) {
302 // if too many options
303 continue;
304 }
305
306 // create resolver
307 if (res == NULL) {
308 res = _dns_resolver_create();
309 if (res == NULL) {
310 // if we could not create a resolver
311 goto done;
312 }
313 }
314
315 switch (token) {
316 case TOKEN_DOMAIN:
317 _dns_resolver_set_domain(&res, word);
318 break;
319
320 case TOKEN_NAMESERVER: {
321 struct sockaddr *sa;
322
323 sa = _dnsinfo_parse_nameserver(word);
324 if (sa != NULL) {
325 _dns_resolver_add_nameserver(&res, sa);
326 free(sa);
327 }
328 break;
329 }
330
331 case TOKEN_PORT: {
332 long number = -1;
333
334 number = strtol(word, NULL, 0);
335 if (number < 0 || number > UINT16_MAX) break;
336 _dns_resolver_set_port(&res, number);
337 break;
338 }
339
340 case TOKEN_SEARCH: {
341 int n = 0;
342
343 // multiple search domains are supported
344 while ((word != NULL) && (n++ < MAXDNSRCH)) {
345 _dns_resolver_add_search(&res, word);
346 word = strsep(&lineptr, sep);
347 }
348 break;
349 }
350
351 case TOKEN_SORTLIST: {
352 int n = 0;
353
354 while ((word != NULL) && (n++ < MAXRESOLVSORT)) {
355 dns_sortaddr_t *sortaddr;
356
357 sortaddr = _dnsinfo_parse_sortaddr(word);
358 if (sortaddr == NULL) break;
359 _dns_resolver_add_sortaddr(&res, sortaddr);
360 free(sortaddr);
361 word = strsep(&lineptr, sep);
362 }
363 break;
364 }
365
366 case TOKEN_OPTIONS:
367 _dns_resolver_set_options(&res, lineptr);
368 break;
369
370 case TOKEN_TIMEOUT: {
371 long number = -1;
372
373 number = strtol(word, NULL, 0);
374 if (number < 0 || number > UINT32_MAX) break;
375 _dns_resolver_set_timeout(&res, number);
376 break;
377 }
378
379 case TOKEN_SEARCH_ORDER: {
380 long number = -1;
381
382 number = strtol(word, NULL, 0);
383 if (number < 0 || number > UINT32_MAX) break;
384 _dns_resolver_set_order(&res, number);
385 break;
386 }
387 }
388 }
389 if (line != NULL) free(line);
390
391 // set the domain to the basename of the path if not specified
392 if ((res != NULL) && (token_count[TOKEN_DOMAIN] == 0)) {
393 const char *domain;
394
395 domain = strrchr(path, '/');
396 if (domain == NULL) {
397 domain = path;
398 } else {
399 domain = domain + 1;
400 }
401 _dns_resolver_set_domain(&res, domain);
402 }
403
404 done :
405
406 fclose(f);
407 return res;
408 }
409
410
411 /*
412 * _dnsinfo_flatfile_add_resolvers
413 *
414 * Parse the files in the resolver config directory (/etc/resolver) and add each
415 * resolver to the dns config.
416 */
417 void
418 _dnsinfo_flatfile_add_resolvers(dns_create_config_t *config)
419 {
420 struct dirent *de;
421 DIR *dp;
422 dns_create_resolver_t res;
423
424 dp = opendir(_PATH_RESOLVER_DIR);
425 if (dp == NULL) {
426 return;
427 }
428
429 while ((de = readdir(dp)) != NULL) {
430 if (strcmp(de->d_name, ".") == 0 ||
431 strcmp(de->d_name, "..") == 0) continue;
432
433 res = _dnsinfo_flatfile_create_resolver(_PATH_RESOLVER_DIR, de->d_name);
434 if (res != NULL) {
435 _dns_configuration_add_resolver(config, res);
436 _dns_resolver_free(&res);
437 }
438 }
439
440 closedir(dp);
441 return;
442 }
443
444
445 #ifdef MAIN
446
447 #include "dnsinfo_copy.c"
448
449 int
450 main(int argc, char **argv)
451 {
452 uint8_t *buf;
453 dns_config_t *config;
454 dns_create_config_t create_config;
455 _dns_config_buf_t *config_buf;
456 uint32_t n_config;
457 uint32_t n_padding;
458 dns_create_resolver_t resolver;
459
460 resolver = _dnsinfo_flatfile_create_resolver(NULL, _PATH_RESCONF);
461
462 create_config = _dns_configuration_create();
463 _dnsinfo_flatfile_add_resolvers(&create_config);
464
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);
472
473 return 0;
474 }
475 #endif