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