1/* 2 * Copyright (C) 2004-2008, 2011, 2012 Internet Systems Consortium, Inc. ("ISC") 3 * Copyright (C) 2000-2003 Internet Software Consortium. 4 * 5 * Permission to use, copy, modify, and/or distribute this software for any 6 * purpose with or without fee is hereby granted, provided that the above 7 * copyright notice and this permission notice appear in all copies. 8 * 9 * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH 10 * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 11 * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, 12 * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 13 * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE 14 * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 15 * PERFORMANCE OF THIS SOFTWARE. 16 */ 17 18/* $Id$ */ 19 20/*! \file */ 21 22/** 23 * Module for parsing resolv.conf files. 24 * 25 * lwres_conf_init() creates an empty lwres_conf_t structure for 26 * lightweight resolver context ctx. 27 * 28 * lwres_conf_clear() frees up all the internal memory used by that 29 * lwres_conf_t structure in resolver context ctx. 30 * 31 * lwres_conf_parse() opens the file filename and parses it to initialise 32 * the resolver context ctx's lwres_conf_t structure. 33 * 34 * lwres_conf_print() prints the lwres_conf_t structure for resolver 35 * context ctx to the FILE fp. 36 * 37 * \section lwconfig_return Return Values 38 * 39 * lwres_conf_parse() returns #LWRES_R_SUCCESS if it successfully read and 40 * parsed filename. It returns #LWRES_R_FAILURE if filename could not be 41 * opened or contained incorrect resolver statements. 42 * 43 * lwres_conf_print() returns #LWRES_R_SUCCESS unless an error occurred 44 * when converting the network addresses to a numeric host address 45 * string. If this happens, the function returns #LWRES_R_FAILURE. 46 * 47 * \section lwconfig_see See Also 48 * 49 * stdio(3), \link resolver resolver \endlink 50 * 51 * \section files Files 52 * 53 * /etc/resolv.conf 54 */ 55 56#include <config.h> 57 58#include <assert.h> 59#include <ctype.h> 60#include <errno.h> 61#include <stdlib.h> 62#include <stdio.h> 63#include <string.h> 64#include <unistd.h> 65 66#include <lwres/lwbuffer.h> 67#include <lwres/lwres.h> 68#include <lwres/net.h> 69#include <lwres/result.h> 70 71#include "assert_p.h" 72#include "context_p.h" 73 74 75#if ! defined(NS_INADDRSZ) 76#define NS_INADDRSZ 4 77#endif 78 79#if ! defined(NS_IN6ADDRSZ) 80#define NS_IN6ADDRSZ 16 81#endif 82 83static lwres_result_t 84lwres_conf_parsenameserver(lwres_context_t *ctx, FILE *fp); 85 86static lwres_result_t 87lwres_conf_parselwserver(lwres_context_t *ctx, FILE *fp); 88 89static lwres_result_t 90lwres_conf_parsedomain(lwres_context_t *ctx, FILE *fp); 91 92static lwres_result_t 93lwres_conf_parsesearch(lwres_context_t *ctx, FILE *fp); 94 95static lwres_result_t 96lwres_conf_parsesortlist(lwres_context_t *ctx, FILE *fp); 97 98static lwres_result_t 99lwres_conf_parseoption(lwres_context_t *ctx, FILE *fp); 100 101static void 102lwres_resetaddr(lwres_addr_t *addr); 103 104static lwres_result_t 105lwres_create_addr(const char *buff, lwres_addr_t *addr, int convert_zero); 106 107static int lwresaddr2af(int lwresaddrtype); 108 109 110static int 111lwresaddr2af(int lwresaddrtype) 112{ 113 int af = 0; 114 115 switch (lwresaddrtype) { 116 case LWRES_ADDRTYPE_V4: 117 af = AF_INET; 118 break; 119 120 case LWRES_ADDRTYPE_V6: 121 af = AF_INET6; 122 break; 123 } 124 125 return (af); 126} 127 128 129/*! 130 * Eat characters from FP until EOL or EOF. Returns EOF or '\n' 131 */ 132static int 133eatline(FILE *fp) { 134 int ch; 135 136 ch = fgetc(fp); 137 while (ch != '\n' && ch != EOF) 138 ch = fgetc(fp); 139 140 return (ch); 141} 142 143 144/*! 145 * Eats white space up to next newline or non-whitespace character (of 146 * EOF). Returns the last character read. Comments are considered white 147 * space. 148 */ 149static int 150eatwhite(FILE *fp) { 151 int ch; 152 153 ch = fgetc(fp); 154 while (ch != '\n' && ch != EOF && isspace((unsigned char)ch)) 155 ch = fgetc(fp); 156 157 if (ch == ';' || ch == '#') 158 ch = eatline(fp); 159 160 return (ch); 161} 162 163 164/*! 165 * Skip over any leading whitespace and then read in the next sequence of 166 * non-whitespace characters. In this context newline is not considered 167 * whitespace. Returns EOF on end-of-file, or the character 168 * that caused the reading to stop. 169 */ 170static int 171getword(FILE *fp, char *buffer, size_t size) { 172 int ch; 173 char *p = buffer; 174 175 REQUIRE(buffer != NULL); 176 REQUIRE(size > 0U); 177 178 *p = '\0'; 179 180 ch = eatwhite(fp); 181 182 if (ch == EOF) 183 return (EOF); 184 185 do { 186 *p = '\0'; 187 188 if (ch == EOF || isspace((unsigned char)ch)) 189 break; 190 else if ((size_t) (p - buffer) == size - 1) 191 return (EOF); /* Not enough space. */ 192 193 *p++ = (char)ch; 194 ch = fgetc(fp); 195 } while (1); 196 197 return (ch); 198} 199 200static void 201lwres_resetaddr(lwres_addr_t *addr) { 202 REQUIRE(addr != NULL); 203 204 memset(addr->address, 0, LWRES_ADDR_MAXLEN); 205 addr->family = 0; 206 addr->length = 0; 207} 208 209static char * 210lwres_strdup(lwres_context_t *ctx, const char *str) { 211 char *p; 212 213 REQUIRE(str != NULL); 214 REQUIRE(strlen(str) > 0U); 215 216 p = CTXMALLOC(strlen(str) + 1); 217 if (p != NULL) 218 strcpy(p, str); 219 220 return (p); 221} 222 223/*% intializes data structure for subsequent config parsing. */ 224void 225lwres_conf_init(lwres_context_t *ctx) { 226 int i; 227 lwres_conf_t *confdata; 228 229 REQUIRE(ctx != NULL); 230 confdata = &ctx->confdata; 231 232 confdata->nsnext = 0; 233 confdata->lwnext = 0; 234 confdata->domainname = NULL; 235 confdata->searchnxt = 0; 236 confdata->sortlistnxt = 0; 237 confdata->resdebug = 0; 238 confdata->ndots = 1; 239 confdata->no_tld_query = 0; 240 241 for (i = 0; i < LWRES_CONFMAXNAMESERVERS; i++) 242 lwres_resetaddr(&confdata->nameservers[i]); 243 244 for (i = 0; i < LWRES_CONFMAXSEARCH; i++) 245 confdata->search[i] = NULL; 246 247 for (i = 0; i < LWRES_CONFMAXSORTLIST; i++) { 248 lwres_resetaddr(&confdata->sortlist[i].addr); 249 lwres_resetaddr(&confdata->sortlist[i].mask); 250 } 251} 252 253/*% Frees up all the internal memory used by the config data structure, returning it to the lwres_context_t. */ 254void 255lwres_conf_clear(lwres_context_t *ctx) { 256 int i; 257 lwres_conf_t *confdata; 258 259 REQUIRE(ctx != NULL); 260 confdata = &ctx->confdata; 261 262 for (i = 0; i < confdata->nsnext; i++) 263 lwres_resetaddr(&confdata->nameservers[i]); 264 265 if (confdata->domainname != NULL) { 266 CTXFREE(confdata->domainname, 267 strlen(confdata->domainname) + 1); 268 confdata->domainname = NULL; 269 } 270 271 for (i = 0; i < confdata->searchnxt; i++) { 272 if (confdata->search[i] != NULL) { 273 CTXFREE(confdata->search[i], 274 strlen(confdata->search[i]) + 1); 275 confdata->search[i] = NULL; 276 } 277 } 278 279 for (i = 0; i < LWRES_CONFMAXSORTLIST; i++) { 280 lwres_resetaddr(&confdata->sortlist[i].addr); 281 lwres_resetaddr(&confdata->sortlist[i].mask); 282 } 283 284 confdata->nsnext = 0; 285 confdata->lwnext = 0; 286 confdata->domainname = NULL; 287 confdata->searchnxt = 0; 288 confdata->sortlistnxt = 0; 289 confdata->resdebug = 0; 290 confdata->ndots = 1; 291 confdata->no_tld_query = 0; 292} 293 294static lwres_result_t 295lwres_conf_parsenameserver(lwres_context_t *ctx, FILE *fp) { 296 char word[LWRES_CONFMAXLINELEN]; 297 int res; 298 lwres_conf_t *confdata; 299 lwres_addr_t address; 300 301 confdata = &ctx->confdata; 302 303 if (confdata->nsnext == LWRES_CONFMAXNAMESERVERS) 304 return (LWRES_R_SUCCESS); 305 306 res = getword(fp, word, sizeof(word)); 307 if (strlen(word) == 0U) 308 return (LWRES_R_FAILURE); /* Nothing on line. */ 309 else if (res == ' ' || res == '\t') 310 res = eatwhite(fp); 311 312 if (res != EOF && res != '\n') 313 return (LWRES_R_FAILURE); /* Extra junk on line. */ 314 315 res = lwres_create_addr(word, &address, 1); 316 if (res == LWRES_R_SUCCESS && 317 ((address.family == LWRES_ADDRTYPE_V4 && ctx->use_ipv4 == 1) || 318 (address.family == LWRES_ADDRTYPE_V6 && ctx->use_ipv6 == 1))) { 319 confdata->nameservers[confdata->nsnext++] = address; 320 } 321 322 return (LWRES_R_SUCCESS); 323} 324 325static lwres_result_t 326lwres_conf_parselwserver(lwres_context_t *ctx, FILE *fp) { 327 char word[LWRES_CONFMAXLINELEN]; 328 int res; 329 lwres_conf_t *confdata; 330 331 confdata = &ctx->confdata; 332 333 if (confdata->lwnext == LWRES_CONFMAXLWSERVERS) 334 return (LWRES_R_SUCCESS); 335 336 res = getword(fp, word, sizeof(word)); 337 if (strlen(word) == 0U) 338 return (LWRES_R_FAILURE); /* Nothing on line. */ 339 else if (res == ' ' || res == '\t') 340 res = eatwhite(fp); 341 342 if (res != EOF && res != '\n') 343 return (LWRES_R_FAILURE); /* Extra junk on line. */ 344 345 res = lwres_create_addr(word, 346 &confdata->lwservers[confdata->lwnext++], 1); 347 if (res != LWRES_R_SUCCESS) 348 return (res); 349 350 return (LWRES_R_SUCCESS); 351} 352 353static lwres_result_t 354lwres_conf_parsedomain(lwres_context_t *ctx, FILE *fp) { 355 char word[LWRES_CONFMAXLINELEN]; 356 int res, i; 357 lwres_conf_t *confdata; 358 359 confdata = &ctx->confdata; 360 361 res = getword(fp, word, sizeof(word)); 362 if (strlen(word) == 0U) 363 return (LWRES_R_FAILURE); /* Nothing else on line. */ 364 else if (res == ' ' || res == '\t') 365 res = eatwhite(fp); 366 367 if (res != EOF && res != '\n') 368 return (LWRES_R_FAILURE); /* Extra junk on line. */ 369 370 if (confdata->domainname != NULL) 371 CTXFREE(confdata->domainname, 372 strlen(confdata->domainname) + 1); /* */ 373 374 /* 375 * Search and domain are mutually exclusive. 376 */ 377 for (i = 0; i < LWRES_CONFMAXSEARCH; i++) { 378 if (confdata->search[i] != NULL) { 379 CTXFREE(confdata->search[i], 380 strlen(confdata->search[i])+1); 381 confdata->search[i] = NULL; 382 } 383 } 384 confdata->searchnxt = 0; 385 386 confdata->domainname = lwres_strdup(ctx, word); 387 388 if (confdata->domainname == NULL) 389 return (LWRES_R_FAILURE); 390 391 return (LWRES_R_SUCCESS); 392} 393 394static lwres_result_t 395lwres_conf_parsesearch(lwres_context_t *ctx, FILE *fp) { 396 int idx, delim; 397 char word[LWRES_CONFMAXLINELEN]; 398 lwres_conf_t *confdata; 399 400 confdata = &ctx->confdata; 401 402 if (confdata->domainname != NULL) { 403 /* 404 * Search and domain are mutually exclusive. 405 */ 406 CTXFREE(confdata->domainname, 407 strlen(confdata->domainname) + 1); 408 confdata->domainname = NULL; 409 } 410 411 /* 412 * Remove any previous search definitions. 413 */ 414 for (idx = 0; idx < LWRES_CONFMAXSEARCH; idx++) { 415 if (confdata->search[idx] != NULL) { 416 CTXFREE(confdata->search[idx], 417 strlen(confdata->search[idx])+1); 418 confdata->search[idx] = NULL; 419 } 420 } 421 confdata->searchnxt = 0; 422 423 delim = getword(fp, word, sizeof(word)); 424 if (strlen(word) == 0U) 425 return (LWRES_R_FAILURE); /* Nothing else on line. */ 426 427 idx = 0; 428 while (strlen(word) > 0U) { 429 if (confdata->searchnxt == LWRES_CONFMAXSEARCH) 430 goto ignore; /* Too many domains. */ 431 432 confdata->search[idx] = lwres_strdup(ctx, word); 433 if (confdata->search[idx] == NULL) 434 return (LWRES_R_FAILURE); 435 idx++; 436 confdata->searchnxt++; 437 438 ignore: 439 if (delim == EOF || delim == '\n') 440 break; 441 else 442 delim = getword(fp, word, sizeof(word)); 443 } 444 445 return (LWRES_R_SUCCESS); 446} 447 448static lwres_result_t 449lwres_create_addr(const char *buffer, lwres_addr_t *addr, int convert_zero) { 450 struct in_addr v4; 451 struct in6_addr v6; 452 453 if (lwres_net_aton(buffer, &v4) == 1) { 454 if (convert_zero) { 455 unsigned char zeroaddress[] = {0, 0, 0, 0}; 456 unsigned char loopaddress[] = {127, 0, 0, 1}; 457 if (memcmp(&v4, zeroaddress, 4) == 0) 458 memcpy(&v4, loopaddress, 4); 459 } 460 addr->family = LWRES_ADDRTYPE_V4; 461 addr->length = NS_INADDRSZ; 462 memcpy((void *)addr->address, &v4, NS_INADDRSZ); 463 464 } else if (lwres_net_pton(AF_INET6, buffer, &v6) == 1) { 465 addr->family = LWRES_ADDRTYPE_V6; 466 addr->length = NS_IN6ADDRSZ; 467 memcpy((void *)addr->address, &v6, NS_IN6ADDRSZ); 468 } else { 469 return (LWRES_R_FAILURE); /* Unrecognised format. */ 470 } 471 472 return (LWRES_R_SUCCESS); 473} 474 475static lwres_result_t 476lwres_conf_parsesortlist(lwres_context_t *ctx, FILE *fp) { 477 int delim, res, idx; 478 char word[LWRES_CONFMAXLINELEN]; 479 char *p; 480 lwres_conf_t *confdata; 481 482 confdata = &ctx->confdata; 483 484 delim = getword(fp, word, sizeof(word)); 485 if (strlen(word) == 0U) 486 return (LWRES_R_FAILURE); /* Empty line after keyword. */ 487 488 while (strlen(word) > 0U) { 489 if (confdata->sortlistnxt == LWRES_CONFMAXSORTLIST) 490 return (LWRES_R_FAILURE); /* Too many values. */ 491 492 p = strchr(word, '/'); 493 if (p != NULL) 494 *p++ = '\0'; 495 496 idx = confdata->sortlistnxt; 497 res = lwres_create_addr(word, &confdata->sortlist[idx].addr, 1); 498 if (res != LWRES_R_SUCCESS) 499 return (res); 500 501 if (p != NULL) { 502 res = lwres_create_addr(p, 503 &confdata->sortlist[idx].mask, 504 0); 505 if (res != LWRES_R_SUCCESS) 506 return (res); 507 } else { 508 /* 509 * Make up a mask. 510 */ 511 confdata->sortlist[idx].mask = 512 confdata->sortlist[idx].addr; 513 514 memset(&confdata->sortlist[idx].mask.address, 0xff, 515 confdata->sortlist[idx].addr.length); 516 } 517 518 confdata->sortlistnxt++; 519 520 if (delim == EOF || delim == '\n') 521 break; 522 else 523 delim = getword(fp, word, sizeof(word)); 524 } 525 526 return (LWRES_R_SUCCESS); 527} 528 529static lwres_result_t 530lwres_conf_parseoption(lwres_context_t *ctx, FILE *fp) { 531 int delim; 532 long ndots; 533 char *p; 534 char word[LWRES_CONFMAXLINELEN]; 535 lwres_conf_t *confdata; 536 537 REQUIRE(ctx != NULL); 538 confdata = &ctx->confdata; 539 540 delim = getword(fp, word, sizeof(word)); 541 if (strlen(word) == 0U) 542 return (LWRES_R_FAILURE); /* Empty line after keyword. */ 543 544 while (strlen(word) > 0U) { 545 if (strcmp("debug", word) == 0) { 546 confdata->resdebug = 1; 547 } else if (strcmp("no_tld_query", word) == 0) { 548 confdata->no_tld_query = 1; 549 } else if (strncmp("ndots:", word, 6) == 0) { 550 ndots = strtol(word + 6, &p, 10); 551 if (*p != '\0') /* Bad string. */ 552 return (LWRES_R_FAILURE); 553 if (ndots < 0 || ndots > 0xff) /* Out of range. */ 554 return (LWRES_R_FAILURE); 555 confdata->ndots = (lwres_uint8_t)ndots; 556 } 557 558 if (delim == EOF || delim == '\n') 559 break; 560 else 561 delim = getword(fp, word, sizeof(word)); 562 } 563 564 return (LWRES_R_SUCCESS); 565} 566 567/*% parses a file and fills in the data structure. */ 568lwres_result_t 569lwres_conf_parse(lwres_context_t *ctx, const char *filename) { 570 FILE *fp = NULL; 571 char word[256]; 572 lwres_result_t rval, ret; 573 lwres_conf_t *confdata; 574 int stopchar; 575 576 REQUIRE(ctx != NULL); 577 confdata = &ctx->confdata; 578 579 REQUIRE(filename != NULL); 580 REQUIRE(strlen(filename) > 0U); 581 REQUIRE(confdata != NULL); 582 583 errno = 0; 584 if ((fp = fopen(filename, "r")) == NULL) 585 return (LWRES_R_NOTFOUND); 586 587 ret = LWRES_R_SUCCESS; 588 do { 589 stopchar = getword(fp, word, sizeof(word)); 590 if (stopchar == EOF) { 591 rval = LWRES_R_SUCCESS; 592 POST(rval); 593 break; 594 } 595 596 if (strlen(word) == 0U) 597 rval = LWRES_R_SUCCESS; 598 else if (strcmp(word, "nameserver") == 0) 599 rval = lwres_conf_parsenameserver(ctx, fp); 600 else if (strcmp(word, "lwserver") == 0) 601 rval = lwres_conf_parselwserver(ctx, fp); 602 else if (strcmp(word, "domain") == 0) 603 rval = lwres_conf_parsedomain(ctx, fp); 604 else if (strcmp(word, "search") == 0) 605 rval = lwres_conf_parsesearch(ctx, fp); 606 else if (strcmp(word, "sortlist") == 0) 607 rval = lwres_conf_parsesortlist(ctx, fp); 608 else if (strcmp(word, "options") == 0) 609 rval = lwres_conf_parseoption(ctx, fp); 610 else { 611 /* unrecognised word. Ignore entire line */ 612 rval = LWRES_R_SUCCESS; 613 stopchar = eatline(fp); 614 if (stopchar == EOF) { 615 break; 616 } 617 } 618 if (ret == LWRES_R_SUCCESS && rval != LWRES_R_SUCCESS) 619 ret = rval; 620 } while (1); 621 622 fclose(fp); 623 624 return (ret); 625} 626 627/*% Prints the config data structure to the FILE. */ 628lwres_result_t 629lwres_conf_print(lwres_context_t *ctx, FILE *fp) { 630 int i; 631 int af; 632 char tmp[sizeof("ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255")]; 633 const char *p; 634 lwres_conf_t *confdata; 635 lwres_addr_t tmpaddr; 636 637 REQUIRE(ctx != NULL); 638 confdata = &ctx->confdata; 639 640 REQUIRE(confdata->nsnext <= LWRES_CONFMAXNAMESERVERS); 641 642 for (i = 0; i < confdata->nsnext; i++) { 643 af = lwresaddr2af(confdata->nameservers[i].family); 644 645 p = lwres_net_ntop(af, confdata->nameservers[i].address, 646 tmp, sizeof(tmp)); 647 if (p != tmp) 648 return (LWRES_R_FAILURE); 649 650 fprintf(fp, "nameserver %s\n", tmp); 651 } 652 653 for (i = 0; i < confdata->lwnext; i++) { 654 af = lwresaddr2af(confdata->lwservers[i].family); 655 656 p = lwres_net_ntop(af, confdata->lwservers[i].address, 657 tmp, sizeof(tmp)); 658 if (p != tmp) 659 return (LWRES_R_FAILURE); 660 661 fprintf(fp, "lwserver %s\n", tmp); 662 } 663 664 if (confdata->domainname != NULL) { 665 fprintf(fp, "domain %s\n", confdata->domainname); 666 } else if (confdata->searchnxt > 0) { 667 REQUIRE(confdata->searchnxt <= LWRES_CONFMAXSEARCH); 668 669 fprintf(fp, "search"); 670 for (i = 0; i < confdata->searchnxt; i++) 671 fprintf(fp, " %s", confdata->search[i]); 672 fputc('\n', fp); 673 } 674 675 REQUIRE(confdata->sortlistnxt <= LWRES_CONFMAXSORTLIST); 676 677 if (confdata->sortlistnxt > 0) { 678 fputs("sortlist", fp); 679 for (i = 0; i < confdata->sortlistnxt; i++) { 680 af = lwresaddr2af(confdata->sortlist[i].addr.family); 681 682 p = lwres_net_ntop(af, 683 confdata->sortlist[i].addr.address, 684 tmp, sizeof(tmp)); 685 if (p != tmp) 686 return (LWRES_R_FAILURE); 687 688 fprintf(fp, " %s", tmp); 689 690 tmpaddr = confdata->sortlist[i].mask; 691 memset(&tmpaddr.address, 0xff, tmpaddr.length); 692 693 if (memcmp(&tmpaddr.address, 694 confdata->sortlist[i].mask.address, 695 confdata->sortlist[i].mask.length) != 0) { 696 af = lwresaddr2af( 697 confdata->sortlist[i].mask.family); 698 p = lwres_net_ntop 699 (af, 700 confdata->sortlist[i].mask.address, 701 tmp, sizeof(tmp)); 702 if (p != tmp) 703 return (LWRES_R_FAILURE); 704 705 fprintf(fp, "/%s", tmp); 706 } 707 } 708 fputc('\n', fp); 709 } 710 711 if (confdata->resdebug) 712 fprintf(fp, "options debug\n"); 713 714 if (confdata->ndots > 0) 715 fprintf(fp, "options ndots:%d\n", confdata->ndots); 716 717 if (confdata->no_tld_query) 718 fprintf(fp, "options no_tld_query\n"); 719 720 return (LWRES_R_SUCCESS); 721} 722 723/*% Returns a pointer to the current config structure. */ 724lwres_conf_t * 725lwres_conf_get(lwres_context_t *ctx) { 726 REQUIRE(ctx != NULL); 727 728 return (&ctx->confdata); 729} 730