1 /* 2 * Copyright (C) Internet Systems Consortium, Inc. ("ISC") 3 * 4 * Permission to use, copy, modify, and/or distribute this software for any 5 * purpose with or without fee is hereby granted, provided that the above 6 * copyright notice and this permission notice appear in all copies. 7 * 8 * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH 9 * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 10 * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, 11 * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 12 * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE 13 * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 14 * PERFORMANCE OF THIS SOFTWARE. 15 */ 16 17 /*! \file */ 18 19 /** 20 * Module for parsing resolv.conf files. 21 * 22 * lwres_conf_init() creates an empty lwres_conf_t structure for 23 * lightweight resolver context ctx. 24 * 25 * lwres_conf_clear() frees up all the internal memory used by that 26 * lwres_conf_t structure in resolver context ctx. 27 * 28 * lwres_conf_parse() opens the file filename and parses it to initialise 29 * the resolver context ctx's lwres_conf_t structure. 30 * 31 * \section lwconfig_return Return Values 32 * 33 * lwres_conf_parse() returns #LWRES_R_SUCCESS if it successfully read and 34 * parsed filename. It returns #LWRES_R_FAILURE if filename could not be 35 * opened or contained incorrect resolver statements. 36 * 37 * \section lwconfig_see See Also 38 * 39 * stdio(3), \link resolver resolver \endlink 40 * 41 * \section files Files 42 * 43 * /etc/resolv.conf 44 */ 45 46 #include <assert.h> 47 #include <ctype.h> 48 #include <errno.h> 49 #include <stdlib.h> 50 #include <stdio.h> 51 #include <string.h> 52 53 #include <lwres/lwres.h> 54 #include <lwres/result.h> 55 56 static lwres_result_t 57 lwres_conf_parsenameserver(lwres_conf_t *confdata, FILE *fp); 58 59 static lwres_result_t 60 lwres_conf_parselwserver(lwres_conf_t *confdata, FILE *fp); 61 62 static lwres_result_t 63 lwres_conf_parsedomain(lwres_conf_t *confdata, FILE *fp); 64 65 static lwres_result_t 66 lwres_conf_parsesearch(lwres_conf_t *confdata, FILE *fp); 67 68 static lwres_result_t 69 lwres_conf_parsesortlist(lwres_conf_t *confdata, FILE *fp); 70 71 static lwres_result_t 72 lwres_conf_parseoption(lwres_conf_t *confdata, FILE *fp); 73 74 static void 75 lwres_resetaddr(lwres_addr_t *addr); 76 77 static lwres_result_t 78 lwres_create_addr(const char *buff, lwres_addr_t *addr, int convert_zero); 79 80 /*! 81 * Eat characters from FP until EOL or EOF. Returns EOF or '\n' 82 */ 83 static int 84 eatline(FILE *fp) { 85 int ch; 86 87 ch = fgetc(fp); 88 while (ch != '\n' && ch != EOF) 89 ch = fgetc(fp); 90 91 return (ch); 92 } 93 94 /*! 95 * Eats white space up to next newline or non-whitespace character (of 96 * EOF). Returns the last character read. Comments are considered white 97 * space. 98 */ 99 static int 100 eatwhite(FILE *fp) { 101 int ch; 102 103 ch = fgetc(fp); 104 while (ch != '\n' && ch != EOF && isspace((unsigned char)ch)) 105 ch = fgetc(fp); 106 107 if (ch == ';' || ch == '#') 108 ch = eatline(fp); 109 110 return (ch); 111 } 112 113 /*! 114 * Skip over any leading whitespace and then read in the next sequence of 115 * non-whitespace characters. In this context newline is not considered 116 * whitespace. Returns EOF on end-of-file, or the character 117 * that caused the reading to stop. 118 */ 119 static int 120 getword(FILE *fp, char *buffer, size_t size) { 121 int ch; 122 char *p = buffer; 123 124 assert(buffer != NULL); 125 assert(size > 0U); 126 127 *p = '\0'; 128 129 ch = eatwhite(fp); 130 131 if (ch == EOF) 132 return (EOF); 133 134 do { 135 *p = '\0'; 136 137 if (ch == EOF || isspace((unsigned char)ch)) 138 break; 139 else if ((size_t) (p - buffer) == size - 1) 140 return (EOF); /* Not enough space. */ 141 142 *p++ = (char)ch; 143 ch = fgetc(fp); 144 } while (1); 145 146 return (ch); 147 } 148 149 static void 150 lwres_resetaddr(lwres_addr_t *addr) { 151 assert(addr != NULL); 152 153 memset(addr, 0, sizeof(*addr)); 154 } 155 156 /*% intializes data structure for subsequent config parsing. */ 157 void 158 lwres_conf_init(lwres_conf_t *confdata, int lwresflags) { 159 int i; 160 161 confdata->nsnext = 0; 162 confdata->lwnext = 0; 163 confdata->domainname = NULL; 164 confdata->searchnxt = 0; 165 confdata->sortlistnxt = 0; 166 confdata->resdebug = 0; 167 confdata->ndots = 1; 168 confdata->no_tld_query = 0; 169 confdata->flags = lwresflags; 170 171 for (i = 0; i < LWRES_CONFMAXNAMESERVERS; i++) 172 lwres_resetaddr(&confdata->nameservers[i]); 173 174 for (i = 0; i < LWRES_CONFMAXSEARCH; i++) 175 confdata->search[i] = NULL; 176 177 for (i = 0; i < LWRES_CONFMAXSORTLIST; i++) { 178 lwres_resetaddr(&confdata->sortlist[i].addr); 179 lwres_resetaddr(&confdata->sortlist[i].mask); 180 } 181 } 182 183 /*% Frees up all the internal memory used by the config data structure, returning it to the lwres_context_t. */ 184 void 185 lwres_conf_clear(lwres_conf_t *confdata) { 186 int i; 187 188 for (i = 0; i < confdata->nsnext; i++) 189 lwres_resetaddr(&confdata->nameservers[i]); 190 191 free(confdata->domainname); 192 confdata->domainname = NULL; 193 194 for (i = 0; i < confdata->searchnxt; i++) { 195 free(confdata->search[i]); 196 confdata->search[i] = NULL; 197 } 198 199 for (i = 0; i < LWRES_CONFMAXSORTLIST; i++) { 200 lwres_resetaddr(&confdata->sortlist[i].addr); 201 lwres_resetaddr(&confdata->sortlist[i].mask); 202 } 203 204 confdata->nsnext = 0; 205 confdata->lwnext = 0; 206 confdata->domainname = NULL; 207 confdata->searchnxt = 0; 208 confdata->sortlistnxt = 0; 209 confdata->resdebug = 0; 210 confdata->ndots = 1; 211 confdata->no_tld_query = 0; 212 } 213 214 static lwres_result_t 215 lwres_conf_parsenameserver(lwres_conf_t *confdata, FILE *fp) { 216 char word[LWRES_CONFMAXLINELEN]; 217 int res, use_ipv4, use_ipv6; 218 lwres_addr_t address; 219 220 if (confdata->nsnext == LWRES_CONFMAXNAMESERVERS) 221 return (LWRES_R_SUCCESS); 222 223 res = getword(fp, word, sizeof(word)); 224 if (strlen(word) == 0U) 225 return (LWRES_R_FAILURE); /* Nothing on line. */ 226 else if (res == ' ' || res == '\t') 227 res = eatwhite(fp); 228 229 if (res != EOF && res != '\n') 230 return (LWRES_R_FAILURE); /* Extra junk on line. */ 231 232 res = lwres_create_addr(word, &address, 1); 233 use_ipv4 = confdata->flags & LWRES_USEIPV4; 234 use_ipv6 = confdata->flags & LWRES_USEIPV6; 235 if (res == LWRES_R_SUCCESS && 236 ((address.family == LWRES_ADDRTYPE_V4 && use_ipv4) || 237 (address.family == LWRES_ADDRTYPE_V6 && use_ipv6))) { 238 confdata->nameservers[confdata->nsnext++] = address; 239 } 240 241 return (LWRES_R_SUCCESS); 242 } 243 244 static lwres_result_t 245 lwres_conf_parselwserver(lwres_conf_t *confdata, FILE *fp) { 246 char word[LWRES_CONFMAXLINELEN]; 247 int res; 248 249 if (confdata->lwnext == LWRES_CONFMAXLWSERVERS) 250 return (LWRES_R_SUCCESS); 251 252 res = getword(fp, word, sizeof(word)); 253 if (strlen(word) == 0U) 254 return (LWRES_R_FAILURE); /* Nothing on line. */ 255 else if (res == ' ' || res == '\t') 256 res = eatwhite(fp); 257 258 if (res != EOF && res != '\n') 259 return (LWRES_R_FAILURE); /* Extra junk on line. */ 260 261 res = lwres_create_addr(word, 262 &confdata->lwservers[confdata->lwnext++], 1); 263 if (res != LWRES_R_SUCCESS) 264 return (res); 265 266 return (LWRES_R_SUCCESS); 267 } 268 269 static lwres_result_t 270 lwres_conf_parsedomain(lwres_conf_t *confdata, FILE *fp) { 271 char word[LWRES_CONFMAXLINELEN]; 272 int res, i; 273 274 res = getword(fp, word, sizeof(word)); 275 if (strlen(word) == 0U) 276 return (LWRES_R_FAILURE); /* Nothing else on line. */ 277 else if (res == ' ' || res == '\t') 278 res = eatwhite(fp); 279 280 if (res != EOF && res != '\n') 281 return (LWRES_R_FAILURE); /* Extra junk on line. */ 282 283 free(confdata->domainname); 284 285 /* 286 * Search and domain are mutually exclusive. 287 */ 288 for (i = 0; i < LWRES_CONFMAXSEARCH; i++) { 289 free(confdata->search[i]); 290 confdata->search[i] = NULL; 291 } 292 confdata->searchnxt = 0; 293 294 confdata->domainname = strdup(word); 295 296 if (confdata->domainname == NULL) 297 return (LWRES_R_FAILURE); 298 299 return (LWRES_R_SUCCESS); 300 } 301 302 static lwres_result_t 303 lwres_conf_parsesearch(lwres_conf_t *confdata, FILE *fp) { 304 int idx, delim; 305 char word[LWRES_CONFMAXLINELEN]; 306 307 free(confdata->domainname); 308 confdata->domainname = NULL; 309 310 /* 311 * Remove any previous search definitions. 312 */ 313 for (idx = 0; idx < LWRES_CONFMAXSEARCH; idx++) { 314 free(confdata->search[idx]); 315 confdata->search[idx] = NULL; 316 } 317 confdata->searchnxt = 0; 318 319 delim = getword(fp, word, sizeof(word)); 320 if (strlen(word) == 0U) 321 return (LWRES_R_FAILURE); /* Nothing else on line. */ 322 323 idx = 0; 324 while (strlen(word) > 0U) { 325 if (confdata->searchnxt == LWRES_CONFMAXSEARCH) 326 goto ignore; /* Too many domains. */ 327 328 confdata->search[idx] = strdup(word); 329 if (confdata->search[idx] == NULL) 330 return (LWRES_R_FAILURE); 331 idx++; 332 confdata->searchnxt++; 333 334 ignore: 335 if (delim == EOF || delim == '\n') 336 break; 337 else 338 delim = getword(fp, word, sizeof(word)); 339 } 340 341 return (LWRES_R_SUCCESS); 342 } 343 344 static lwres_result_t 345 lwres_create_addr(const char *buffer, lwres_addr_t *addr, int convert_zero) { 346 struct in_addr v4; 347 struct in6_addr v6; 348 char buf[sizeof("ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255") + 349 sizeof("%4294967295")]; 350 char *percent; 351 size_t n; 352 353 n = strlcpy(buf, buffer, sizeof(buf)); 354 if (n >= sizeof(buf)) 355 return (LWRES_R_FAILURE); 356 357 percent = strchr(buf, '%'); 358 if (percent != NULL) 359 *percent = 0; 360 361 if (inet_aton(buffer, &v4) == 1) { 362 if (convert_zero) { 363 unsigned char zeroaddress[] = {0, 0, 0, 0}; 364 unsigned char loopaddress[] = {127, 0, 0, 1}; 365 if (memcmp(&v4, zeroaddress, 4) == 0) 366 memmove(&v4, loopaddress, 4); 367 } 368 addr->family = LWRES_ADDRTYPE_V4; 369 addr->length = sizeof(v4); 370 addr->zone = 0; 371 memcpy(addr->address, &v4, sizeof(v4)); 372 373 } else if (inet_pton(AF_INET6, buf, &v6) == 1) { 374 addr->family = LWRES_ADDRTYPE_V6; 375 addr->length = sizeof(v6); 376 memcpy(addr->address, &v6, sizeof(v6)); 377 if (percent != NULL) { 378 unsigned long zone; 379 char *ep; 380 381 percent++; 382 383 zone = if_nametoindex(percent); 384 if (zone != 0U) { 385 addr->zone = zone; 386 return (LWRES_R_SUCCESS); 387 } 388 zone = strtoul(percent, &ep, 10); 389 if (ep != percent && *ep == 0) 390 addr->zone = zone; 391 else 392 return (LWRES_R_FAILURE); 393 } else 394 addr->zone = 0; 395 } else 396 return (LWRES_R_FAILURE); /* Unrecognised format. */ 397 398 return (LWRES_R_SUCCESS); 399 } 400 401 static lwres_result_t 402 lwres_conf_parsesortlist(lwres_conf_t *confdata, FILE *fp) { 403 int delim, res, idx; 404 char word[LWRES_CONFMAXLINELEN]; 405 char *p; 406 407 delim = getword(fp, word, sizeof(word)); 408 if (strlen(word) == 0U) 409 return (LWRES_R_FAILURE); /* Empty line after keyword. */ 410 411 while (strlen(word) > 0U) { 412 if (confdata->sortlistnxt == LWRES_CONFMAXSORTLIST) 413 return (LWRES_R_FAILURE); /* Too many values. */ 414 415 p = strchr(word, '/'); 416 if (p != NULL) 417 *p++ = '\0'; 418 419 idx = confdata->sortlistnxt; 420 res = lwres_create_addr(word, &confdata->sortlist[idx].addr, 1); 421 if (res != LWRES_R_SUCCESS) 422 return (res); 423 424 if (p != NULL) { 425 res = lwres_create_addr(p, 426 &confdata->sortlist[idx].mask, 427 0); 428 if (res != LWRES_R_SUCCESS) 429 return (res); 430 } else { 431 /* 432 * Make up a mask. 433 */ 434 confdata->sortlist[idx].mask = 435 confdata->sortlist[idx].addr; 436 437 memset(&confdata->sortlist[idx].mask.address, 0xff, 438 confdata->sortlist[idx].addr.length); 439 } 440 441 confdata->sortlistnxt++; 442 443 if (delim == EOF || delim == '\n') 444 break; 445 else 446 delim = getword(fp, word, sizeof(word)); 447 } 448 449 return (LWRES_R_SUCCESS); 450 } 451 452 static lwres_result_t 453 lwres_conf_parseoption(lwres_conf_t *confdata, FILE *fp) { 454 int delim; 455 long ndots; 456 char *p; 457 char word[LWRES_CONFMAXLINELEN]; 458 459 delim = getword(fp, word, sizeof(word)); 460 if (strlen(word) == 0U) 461 return (LWRES_R_FAILURE); /* Empty line after keyword. */ 462 463 while (strlen(word) > 0U) { 464 if (strcmp("debug", word) == 0) { 465 confdata->resdebug = 1; 466 } else if (strcmp("no_tld_query", word) == 0) { 467 confdata->no_tld_query = 1; 468 } else if (strncmp("ndots:", word, 6) == 0) { 469 ndots = strtol(word + 6, &p, 10); 470 if (*p != '\0') /* Bad string. */ 471 return (LWRES_R_FAILURE); 472 if (ndots < 0 || ndots > 0xff) /* Out of range. */ 473 return (LWRES_R_FAILURE); 474 confdata->ndots = (uint8_t)ndots; 475 } 476 477 if (delim == EOF || delim == '\n') 478 break; 479 else 480 delim = getword(fp, word, sizeof(word)); 481 } 482 483 return (LWRES_R_SUCCESS); 484 } 485 486 /*% parses a file and fills in the data structure. */ 487 lwres_result_t 488 lwres_conf_parse(lwres_conf_t *confdata, const char *filename) { 489 FILE *fp = NULL; 490 char word[256]; 491 lwres_result_t rval, ret; 492 int stopchar; 493 494 assert(filename != NULL); 495 assert(strlen(filename) > 0U); 496 assert(confdata != NULL); 497 498 errno = 0; 499 if ((fp = fopen(filename, "r")) == NULL) 500 return (LWRES_R_NOTFOUND); 501 502 ret = LWRES_R_SUCCESS; 503 do { 504 stopchar = getword(fp, word, sizeof(word)); 505 if (stopchar == EOF) 506 break; 507 508 if (strlen(word) == 0U) 509 rval = LWRES_R_SUCCESS; 510 else if (strcmp(word, "nameserver") == 0) 511 rval = lwres_conf_parsenameserver(confdata, fp); 512 else if (strcmp(word, "lwserver") == 0) 513 rval = lwres_conf_parselwserver(confdata, fp); 514 else if (strcmp(word, "domain") == 0) 515 rval = lwres_conf_parsedomain(confdata, fp); 516 else if (strcmp(word, "search") == 0) 517 rval = lwres_conf_parsesearch(confdata, fp); 518 else if (strcmp(word, "sortlist") == 0) 519 rval = lwres_conf_parsesortlist(confdata, fp); 520 else if (strcmp(word, "options") == 0) 521 rval = lwres_conf_parseoption(confdata, fp); 522 else { 523 /* unrecognised word. Ignore entire line */ 524 rval = LWRES_R_SUCCESS; 525 stopchar = eatline(fp); 526 if (stopchar == EOF) { 527 break; 528 } 529 } 530 if (ret == LWRES_R_SUCCESS && rval != LWRES_R_SUCCESS) 531 ret = rval; 532 } while (1); 533 534 fclose(fp); 535 536 return (ret); 537 } 538