1/* $NetBSD: nslint.c,v 1.2.6.1 2012/06/05 21:15:32 bouyer Exp $ */ 2 3/* 4 * Copyright (c) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001 5 * The Regents of the University of California. All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that: (1) source code distributions 9 * retain the above copyright notice and this paragraph in its entirety, (2) 10 * distributions including binary code include the above copyright notice and 11 * this paragraph in its entirety in the documentation or other materials 12 * provided with the distribution, and (3) all advertising materials mentioning 13 * features or use of this software display the following acknowledgement: 14 * ``This product includes software developed by the University of California, 15 * Lawrence Berkeley Laboratory and its contributors.'' Neither the name of 16 * the University nor the names of its contributors may be used to endorse 17 * or promote products derived from this software without specific prior 18 * written permission. 19 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED 20 * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF 21 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. 22 */ 23#ifndef lint 24static const char copyright[] = 25 "@(#) Copyright (c) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001\n\ 26The Regents of the University of California. All rights reserved.\n"; 27static const char rcsid[] = 28 "@(#) Id: nslint.c,v 1.2 2011/11/30 00:48:51 marka Exp (LBL)"; 29#endif 30/* 31 * nslint - perform consistency checks on dns files 32 */ 33 34#include <sys/types.h> 35#include <sys/stat.h> 36 37#include <netinet/in.h> 38 39#include <arpa/inet.h> 40 41#include <ctype.h> 42#include <errno.h> 43#ifdef HAVE_FCNTL_H 44#include <fcntl.h> 45#endif 46#ifdef HAVE_MALLOC_H 47#include <malloc.h> 48#endif 49#ifdef HAVE_MEMORY_H 50#include <memory.h> 51#endif 52#include <netdb.h> 53#include <stdio.h> 54#include <stdlib.h> 55#include <string.h> 56#include <time.h> 57#include <unistd.h> 58 59#include "savestr.h" 60 61#include "gnuc.h" 62#ifdef HAVE_OS_PROTO_H 63#include "os-proto.h" 64#endif 65 66#define NSLINTBOOT "nslint.boot" /* default nslint.boot file */ 67#define NSLINTCONF "nslint.conf" /* default nslint.conf file */ 68 69/* item struct */ 70struct item { 71 char *host; /* pointer to hostname */ 72 u_int32_t addr; /* ip address */ 73 u_int ttl; /* ttl of A records */ 74 int records; /* resource records seen */ 75 int flags; /* flags word */ 76}; 77 78/* Resource records seen */ 79#define REC_A 0x0001 80#define REC_PTR 0x0002 81#define REC_WKS 0x0004 82#define REC_HINFO 0x0008 83#define REC_MX 0x0010 84#define REC_CNAME 0x0020 85#define REC_NS 0x0040 86#define REC_SOA 0x0080 87#define REC_RP 0x0100 88#define REC_TXT 0x0200 89#define REC_SRV 0x0400 90 91/* These aren't real records */ 92#define REC_OTHER 0x0800 93#define REC_REF 0x1000 94#define REC_UNKNOWN 0x2000 95 96/* Test for records we want to map to REC_OTHER */ 97#define MASK_TEST_REC (REC_WKS | REC_HINFO | \ 98 REC_MX | REC_SOA | REC_RP | REC_TXT | REC_SRV | REC_UNKNOWN) 99 100/* Mask away records we don't care about in the final processing to REC_OTHER */ 101#define MASK_CHECK_REC \ 102 (REC_A | REC_PTR | REC_CNAME | REC_REF | REC_OTHER) 103 104/* Test for records we want to check for duplicate name detection */ 105#define MASK_TEST_DUP \ 106 (REC_A | REC_HINFO) 107 108/* Flags */ 109#define FLG_SELFMX 0x001 /* mx record refers to self */ 110#define FLG_MXREF 0x002 /* this record referred to by a mx record */ 111#define FLG_SMTPWKS 0x004 /* saw wks with smtp/tcp */ 112#define FLG_ALLOWDUPA 0x008 /* allow duplicate a records */ 113 114/* Test for smtp problems */ 115#define MASK_TEST_SMTP \ 116 (FLG_SELFMX | FLG_SMTPWKS) 117 118 119#define ITEMSIZE (1 << 17) /* power of two */ 120#define ITEMHASH(str, h, p) \ 121 for (p = str, h = 0; *p != '.' && *p != '\0';) h = (h << 5) - h + *p++ 122 123struct item items[ITEMSIZE]; 124int itemcnt; /* count of items */ 125 126/* Hostname string storage */ 127#define STRSIZE 8192; /* size to malloc when more space is needed */ 128char *strptr; /* pointer to string pool */ 129int strsize; /* size of space left in pool */ 130 131int debug; 132int errors; 133char *bootfile = "/etc/named.boot"; 134char *conffile = "/etc/named.conf"; 135char *nslintboot; 136char *nslintconf; 137char *prog; 138char *cwd = "."; 139 140char **protoserv; /* valid protocol/service names */ 141int protoserv_init; 142int protoserv_last; 143int protoserv_len; 144 145static char inaddr[] = ".in-addr.arpa."; 146 147/* SOA record */ 148#define SOA_SERIAL 0 149#define SOA_REFRESH 1 150#define SOA_RETRY 2 151#define SOA_EXPIRE 3 152#define SOA_MINIMUM 4 153 154static u_int soaval[5]; 155static int nsoaval; 156#define NSOAVAL (sizeof(soaval) / sizeof(soaval[0])) 157 158/* Forwards */ 159static inline void add_domain(char *, const char *); 160int checkdots(const char *); 161void checkdups(struct item *, int); 162int checkserv(const char *, char **p); 163int checkwks(FILE *, char *, int *, char **); 164int cmpaddr(const void *, const void *); 165int cmphost(const void *, const void *); 166int doboot(const char *, int); 167int doconf(const char *, int); 168void initprotoserv(void); 169char *intoa(u_int32_t); 170int main(int, char **); 171int nslint(void); 172int parseinaddr(const char *, u_int32_t *, u_int32_t *); 173int parsenetwork(const char *, char **); 174u_int32_t parseptr(const char *, u_int32_t, u_int32_t, char **); 175char *parsequoted(char *); 176int parsesoa(const char *, char **); 177void process(const char *, const char *, const char *); 178int rfc1034host(const char *, int); 179int updateitem(const char *, u_int32_t, int, u_int, int); 180__dead void usage(void) __attribute__((volatile)); 181 182extern char *optarg; 183extern int optind, opterr; 184 185/* add domain if necessary */ 186static inline void 187add_domain(register char *name, register const char *domain) 188{ 189 register char *cp; 190 191 /* Kill trailing white space and convert to lowercase */ 192 for (cp = name; *cp != '\0' && !isspace(*cp); ++cp) 193 if (isupper(*cp)) 194 *cp = tolower(*cp); 195 *cp-- = '\0'; 196 /* If necessary, append domain */ 197 if (cp >= name && *cp++ != '.') { 198 if (*domain != '.') 199 *cp++ = '.'; 200 (void)strcpy(cp, domain); 201 } 202 /* XXX should we insure a trailing dot? */ 203} 204 205int 206main(int argc, char **argv) 207{ 208 register char *cp; 209 register int op, status, i, donamedboot, donamedconf; 210 211 if ((cp = strrchr(argv[0], '/')) != NULL) 212 prog = cp + 1; 213 else 214 prog = argv[0]; 215 216 donamedboot = 0; 217 donamedconf = 0; 218 while ((op = getopt(argc, argv, "b:c:B:C:d")) != -1) 219 switch (op) { 220 221 case 'b': 222 bootfile = optarg; 223 ++donamedboot; 224 break; 225 226 case 'c': 227 conffile = optarg; 228 ++donamedconf; 229 break; 230 231 case 'B': 232 nslintboot = optarg; 233 ++donamedboot; 234 break; 235 236 case 'C': 237 nslintconf = optarg; 238 ++donamedconf; 239 break; 240 241 case 'd': 242 ++debug; 243 break; 244 245 default: 246 usage(); 247 } 248 if (optind != argc || (donamedboot && donamedconf)) 249 usage(); 250 251 if (donamedboot) 252 status = doboot(bootfile, 1); 253 else if (donamedconf) 254 status = doconf(conffile, 1); 255 else { 256 status = doconf(conffile, 0); 257 if (status < 0) { 258 status = doboot(bootfile, 1); 259 ++donamedboot; 260 } else 261 ++donamedconf; 262 } 263 264 if (donamedboot) { 265 if (nslintboot != NULL) 266 status |= doboot(nslintboot, 1); 267 else if ((i = doboot(NSLINTBOOT, 0)) > 0) 268 status |= i; 269 } else { 270 if (nslintconf != NULL) 271 status |= doconf(nslintconf, 1); 272 else if ((i = doconf(NSLINTCONF, 0)) > 0) 273 status |= i; 274 } 275 status |= nslint(); 276 exit (status); 277} 278 279struct netlist { 280 u_int32_t net; 281 u_int32_t mask; 282}; 283 284static struct netlist *netlist; 285static u_int netlistsize; /* size of array */ 286static u_int netlistcnt; /* next free element */ 287 288static u_int32_t 289findmask(u_int32_t addr) 290{ 291 register int i; 292 293 for (i = 0; i < netlistcnt; ++i) 294 if ((addr & netlist[i].mask) == netlist[i].net) 295 return (netlist[i].mask); 296 return (0); 297} 298 299int 300parsenetwork(register const char *cp, register char **errstrp) 301{ 302 register int i, w; 303 register u_int32_t net, mask; 304 register u_int32_t o; 305 register int shift; 306 static char errstr[132]; 307 308 while (isspace(*cp)) 309 ++cp; 310 net = 0; 311 mask = 0; 312 shift = 24; 313 while (isdigit(*cp) && shift >= 0) { 314 o = 0; 315 do { 316 o = o * 10 + (*cp++ - '0'); 317 } while (isdigit(*cp)); 318 net |= o << shift; 319 shift -= 8; 320 if (*cp != '.') 321 break; 322 ++cp; 323 } 324 325 326 if (isspace(*cp)) { 327 ++cp; 328 while (isspace(*cp)) 329 ++cp; 330 mask = htonl(inet_addr(cp)); 331 if ((int)mask == -1) { 332 *errstrp = errstr; 333 (void)sprintf(errstr, "bad mask \"%s\"", cp); 334 return (0); 335 } 336 i = 0; 337 while (isdigit(*cp)) 338 ++cp; 339 for (i = 0; i < 3 && *cp == '.'; ++i) { 340 ++cp; 341 while (isdigit(*cp)) 342 ++cp; 343 } 344 if (i != 3) { 345 *errstrp = "wrong number of dots in mask"; 346 return (0); 347 } 348 } else if (*cp == '/') { 349 ++cp; 350 w = atoi(cp); 351 do { 352 ++cp; 353 } while (isdigit(*cp)); 354 if (w < 1 || w > 32) { 355 *errstrp = "bad mask width"; 356 return (0); 357 } 358 mask = 0xffffffff << (32 - w); 359 } else { 360 *errstrp = "garbage after net"; 361 return (0); 362 } 363 364 while (isspace(*cp)) 365 ++cp; 366 367 if (*cp != '\0') { 368 *errstrp = "trailing garbage"; 369 return (0); 370 } 371 372 /* Finaly sanity checks */ 373 if ((net & ~ mask) != 0) { 374 *errstrp = errstr; 375 (void)sprintf(errstr, "host bits set in net \"%s\"", 376 intoa(net)); 377 return (0); 378 } 379 380 /* Make sure there's room */ 381 if (netlistsize <= netlistcnt) { 382 if (netlistsize == 0) { 383 netlistsize = 32; 384 netlist = (struct netlist *) 385 malloc(netlistsize * sizeof(*netlist)); 386 } else { 387 netlistsize <<= 1; 388 netlist = (struct netlist *) 389 realloc(netlist, netlistsize * sizeof(*netlist)); 390 } 391 if (netlist == NULL) { 392 fprintf(stderr, "%s: nslint: malloc/realloc: %s\n", 393 prog, strerror(errno)); 394 exit(1); 395 } 396 } 397 398 /* Add to list */ 399 netlist[netlistcnt].net = net; 400 netlist[netlistcnt].mask = mask; 401 ++netlistcnt; 402 403 return (1); 404} 405 406int 407doboot(register const char *file, register int mustexist) 408{ 409 register int n; 410 register char *cp, *cp2; 411 register FILE *f; 412 char *errstr; 413 char buf[1024], name[128]; 414 415 errno = 0; 416 f = fopen(file, "r"); 417 if (f == NULL) { 418 /* Not an error if it doesn't exist */ 419 if (!mustexist && errno == ENOENT) { 420 if (debug > 1) 421 printf( 422 "%s: doit: %s doesn't exist (ignoring)\n", 423 prog, file); 424 return (-1); 425 } 426 fprintf(stderr, "%s: %s: %s\n", prog, file, strerror(errno)); 427 exit(1); 428 } 429 if (debug > 1) 430 printf("%s: doit: opened %s\n", prog, file); 431 432 n = 0; 433 while (fgets(buf, sizeof(buf), f) != NULL) { 434 ++n; 435 436 /* Skip comments */ 437 if (buf[0] == ';') 438 continue; 439 cp = strchr(buf, ';'); 440 if (cp) 441 *cp = '\0'; 442 cp = buf + strlen(buf) - 1; 443 if (cp >= buf && *cp == '\n') 444 *cp = '\0'; 445 cp = buf; 446 447 /* Eat leading whitespace */ 448 while (isspace(*cp)) 449 ++cp; 450 451 /* Skip blank lines */ 452 if (*cp == '\n' || *cp == '\0') 453 continue; 454 455 /* Get name */ 456 cp2 = cp; 457 while (!isspace(*cp) && *cp != '\0') 458 ++cp; 459 *cp++ = '\0'; 460 461 /* Find next keyword */ 462 while (isspace(*cp)) 463 ++cp; 464 if (strcasecmp(cp2, "directory") == 0) { 465 /* Terminate directory */ 466 cp2 = cp; 467 while (!isspace(*cp) && *cp != '\0') 468 ++cp; 469 *cp = '\0'; 470 if (chdir(cp2) < 0) { 471 ++errors; 472 fprintf(stderr, "%s: can't chdir %s: %s\n", 473 prog, cp2, strerror(errno)); 474 exit(1); 475 } 476 cwd = savestr(cp2); 477 continue; 478 } 479 if (strcasecmp(cp2, "primary") == 0) { 480 /* Extract domain, converting to lowercase */ 481 for (cp2 = name; !isspace(*cp) && *cp != '\0'; ++cp) 482 if (isupper(*cp)) 483 *cp2++ = tolower(*cp); 484 else 485 *cp2++ = *cp; 486 /* Insure trailing dot */ 487 if (cp2 > name && cp2[-1] != '.') 488 *cp2++ = '.'; 489 *cp2 = '\0'; 490 491 /* Find file */ 492 while (isspace(*cp)) 493 ++cp; 494 495 /* Terminate directory */ 496 cp2 = cp; 497 while (!isspace(*cp) && *cp != '\0') 498 ++cp; 499 *cp = '\0'; 500 501 /* Process it! (zone is the same as the domain) */ 502 nsoaval = -1; 503 memset(soaval, 0, sizeof(soaval)); 504 process(cp2, name, name); 505 continue; 506 } 507 if (strcasecmp(cp2, "network") == 0) { 508 if (!parsenetwork(cp, &errstr)) { 509 ++errors; 510 fprintf(stderr, 511 "%s: %s:%d: bad network: %s\n", 512 prog, file, n, errstr); 513 } 514 continue; 515 } 516 if (strcasecmp(cp2, "include") == 0) { 517 /* Terminate include file */ 518 cp2 = cp; 519 while (!isspace(*cp) && *cp != '\0') 520 ++cp; 521 *cp = '\0'; 522 errors += doboot(cp2, 1); 523 continue; 524 } 525 /* Eat any other options */ 526 } 527 (void)fclose(f); 528 529 return (errors != 0); 530} 531 532int 533doconf(register const char *file, register int mustexist) 534{ 535 register int n, fd, cc, i, depth; 536 register char *cp, *cp2, *buf; 537 register char *name, *zonename, *filename, *typename; 538 register int namelen, zonenamelen, filenamelen, typenamelen; 539 char *errstr; 540 struct stat sbuf; 541 char zone[128], includefile[256]; 542 543 errno = 0; 544 fd = open(file, O_RDONLY, 0); 545 if (fd < 0) { 546 /* Not an error if it doesn't exist */ 547 if (!mustexist && errno == ENOENT) { 548 if (debug > 1) 549 printf( 550 "%s: doconf: %s doesn't exist (ignoring)\n", 551 prog, file); 552 return (-1); 553 } 554 fprintf(stderr, "%s: %s: %s\n", prog, file, strerror(errno)); 555 exit(1); 556 } 557 if (debug > 1) 558 printf("%s: doconf: opened %s\n", prog, file); 559 560 if (fstat(fd, &sbuf) < 0) { 561 fprintf(stderr, "%s: fstat(%s) %s\n", 562 prog, file, strerror(errno)); 563 exit(1); 564 } 565 buf = (char *)malloc(sbuf.st_size + 1); 566 if (buf == NULL) { 567 fprintf(stderr, "%s: malloc: %s\n", prog, strerror(errno)); 568 exit(1); 569 } 570 571 /* Slurp entire config file */ 572 n = sbuf.st_size; 573 cp = buf; 574 do { 575 cc = read(fd, cp, n); 576 if (cc < 0) { 577 fprintf(stderr, "%s: read(%s) %s\n", 578 prog, file, strerror(errno)); 579 exit(1); 580 } 581 cp += cc; 582 n -= cc; 583 } while (cc != 0 && cc < n); 584 buf[cc] = '\0'; 585 586#define EATWHITESPACE \ 587 while (isspace(*cp)) { \ 588 if (*cp == '\n') \ 589 ++n; \ 590 ++cp; \ 591 } 592 593/* Handle both to-end-of-line and C style comments */ 594#define EATCOMMENTS \ 595 { \ 596 int sawcomment; \ 597 do { \ 598 EATWHITESPACE \ 599 sawcomment = 0; \ 600 if (*cp == '#') { \ 601 sawcomment = 1; \ 602 ++cp; \ 603 while (*cp != '\n' && *cp != '\0') \ 604 ++cp; \ 605 } \ 606 else if (strncmp(cp, "//", 2) == 0) { \ 607 sawcomment = 1; \ 608 cp += 2; \ 609 while (*cp != '\n' && *cp != '\0') \ 610 ++cp; \ 611 } \ 612 else if (strncmp(cp, "/*", 2) == 0) { \ 613 sawcomment = 1; \ 614 for (cp += 2; *cp != '\0'; ++cp) { \ 615 if (*cp == '\n') \ 616 ++n; \ 617 else if (strncmp(cp, "*/", 2) == 0) { \ 618 cp += 2; \ 619 break; \ 620 } \ 621 } \ 622 } \ 623 } while (sawcomment); \ 624 } 625 626#define GETNAME(name, len) \ 627 { \ 628 (name) = cp; \ 629 (len) = 0; \ 630 while (!isspace(*cp) && *cp != ';' && *cp != '\0') { \ 631 ++(len); \ 632 ++cp; \ 633 } \ 634 } 635 636#define GETQUOTEDNAME(name, len) \ 637 { \ 638 if (*cp != '"') { \ 639 ++errors; \ 640 fprintf(stderr, "%s: %s:%d missing left quote\n", \ 641 prog, file, n); \ 642 } else \ 643 ++cp; \ 644 (name) = cp; \ 645 (len) = 0; \ 646 while (*cp != '"' && *cp != '\n' && *cp != '\0') { \ 647 ++(len); \ 648 ++cp; \ 649 } \ 650 if (*cp != '"') { \ 651 ++errors; \ 652 fprintf(stderr, "%s: %s:%d missing right quote\n", \ 653 prog, file, n); \ 654 } else \ 655 ++cp; \ 656 } 657 658/* Eat everything to the next semicolon, perhaps eating matching qbraces */ 659#define EATSEMICOLON \ 660 { \ 661 register int depth = 0; \ 662 while (*cp != '\0') { \ 663 EATCOMMENTS \ 664 if (*cp == ';') { \ 665 ++cp; \ 666 if (depth == 0) \ 667 break; \ 668 continue; \ 669 } \ 670 if (*cp == '{') { \ 671 ++depth; \ 672 ++cp; \ 673 continue; \ 674 } \ 675 if (*cp == '}') { \ 676 --depth; \ 677 ++cp; \ 678 continue; \ 679 } \ 680 ++cp; \ 681 } \ 682 } 683 684 n = 1; 685 zone[0] = '\0'; 686 cp = buf; 687 while (*cp != '\0') { 688 EATCOMMENTS 689 if (*cp == '\0') 690 break; 691 GETNAME(name, namelen) 692 if (namelen == 0) { 693 ++errors; 694 fprintf(stderr, "%s: %s:%d garbage char '%c' (1)\n", 695 prog, file, n, *cp); 696 ++cp; 697 continue; 698 } 699 EATCOMMENTS 700 if (strncasecmp(name, "options", namelen) == 0) { 701 EATCOMMENTS 702 if (*cp != '{') { 703 ++errors; 704 fprintf(stderr, 705 "%s: %s:%d missing left qbrace in options\n", 706 prog, file, n); 707 } else 708 ++cp; 709 EATCOMMENTS 710 while (*cp != '}' && *cp != '\0') { 711 EATCOMMENTS 712 GETNAME(name, namelen) 713 if (namelen == 0) { 714 ++errors; 715 fprintf(stderr, 716 "%s: %s:%d garbage char '%c' (2)\n", 717 prog, file, n, *cp); 718 ++cp; 719 break; 720 } 721 722 /* If not the "directory" option, just eat it */ 723 if (strncasecmp(name, "directory", 724 namelen) == 0) { 725 EATCOMMENTS 726 GETQUOTEDNAME(cp2, i) 727 cp2[i] = '\0'; 728 if (chdir(cp2) < 0) { 729 ++errors; 730 fprintf(stderr, 731 "%s: %s:.%d can't chdir %s: %s\n", 732 prog, file, n, cp2, 733 strerror(errno)); 734 exit(1); 735 } 736 cwd = savestr(cp2); 737 } 738 EATSEMICOLON 739 EATCOMMENTS 740 } 741 ++cp; 742 EATCOMMENTS 743 if (*cp != ';') { 744 ++errors; 745 fprintf(stderr, 746 "%s: %s:%d missing options semi\n", 747 prog, file, n); 748 } else 749 ++cp; 750 continue; 751 } 752 if (strncasecmp(name, "zone", namelen) == 0) { 753 EATCOMMENTS 754 GETQUOTEDNAME(zonename, zonenamelen) 755 typename = NULL; 756 filename = NULL; 757 typenamelen = 0; 758 filenamelen = 0; 759 EATCOMMENTS 760 if (strncasecmp(cp, "in", 2) == 0) { 761 cp += 2; 762 EATWHITESPACE 763 } else if (strncasecmp(cp, "chaos", 5) == 0) { 764 cp += 5; 765 EATWHITESPACE 766 } 767 if (*cp != '{') { /* } */ 768 ++errors; 769 fprintf(stderr, 770 "%s: %s:%d missing left qbrace in zone\n", 771 prog, file, n); 772 continue; 773 } 774 depth = 0; 775 EATCOMMENTS 776 while (*cp != '\0') { 777 if (*cp == '{') { 778 ++cp; 779 ++depth; 780 } else if (*cp == '}') { 781 if (--depth <= 1) 782 break; 783 ++cp; 784 } 785 EATCOMMENTS 786 GETNAME(name, namelen) 787 if (namelen == 0) { 788 ++errors; 789 fprintf(stderr, 790 "%s: %s:%d garbage char '%c' (3)\n", 791 prog, file, n, *cp); 792 ++cp; 793 break; 794 } 795 if (strncasecmp(name, "type", 796 namelen) == 0) { 797 EATCOMMENTS 798 GETNAME(typename, typenamelen) 799 if (namelen == 0) { 800 ++errors; 801 fprintf(stderr, 802 "%s: %s:%d garbage char '%c' (4)\n", 803 prog, file, n, *cp); 804 ++cp; 805 break; 806 } 807 } else if (strncasecmp(name, "file", 808 namelen) == 0) { 809 EATCOMMENTS 810 GETQUOTEDNAME(filename, filenamelen) 811 } 812 /* Just ignore keywords we don't understand */ 813 EATSEMICOLON 814 EATCOMMENTS 815 } 816 /* { */ 817 if (*cp != '}') { 818 ++errors; 819 fprintf(stderr, 820 "%s: %s:%d missing zone right qbrace\n", 821 prog, file, n); 822 } else 823 ++cp; 824 if (*cp != ';') { 825 ++errors; 826 fprintf(stderr, 827 "%s: %s:%d missing zone semi\n", 828 prog, file, n); 829 } else 830 ++cp; 831 EATCOMMENTS 832 /* If we got something interesting, process it */ 833 if (typenamelen == 0) { 834 ++errors; 835 fprintf(stderr, "%s: missing zone type!\n", 836 prog); 837 continue; 838 } 839 if (strncasecmp(typename, "master", typenamelen) == 0) { 840 if (filenamelen == 0) { 841 ++errors; 842 fprintf(stderr, 843 "%s: missing zone filename!\n", 844 prog); 845 continue; 846 } 847 strncpy(zone, zonename, zonenamelen); 848 zone[zonenamelen] = '\0'; 849 for (cp2 = zone; *cp2 != '\0'; ++cp2) 850 if (isupper(*cp2)) 851 *cp2 = tolower(*cp2); 852 /* Insure trailing dot */ 853 if (cp2 > zone && cp2[-1] != '.') { 854 *cp2++ = '.'; 855 *cp2 = '\0'; 856 } 857 filename[filenamelen] = '\0'; 858 nsoaval = -1; 859 memset(soaval, 0, sizeof(soaval)); 860 process(filename, zone, zone); 861 } 862 continue; 863 } 864 if (strncasecmp(name, "nslint", namelen) == 0) { 865 EATCOMMENTS 866 if (*cp != '{') { 867 ++errors; 868 fprintf(stderr, 869 "%s: %s:%d missing left qbrace in nslint\n", 870 prog, file, n); 871 } else 872 ++cp; 873 ++cp; 874 EATCOMMENTS 875 while (*cp != '}' && *cp != '\0') { 876 EATCOMMENTS 877 GETNAME(name, namelen) 878 if (strncasecmp(name, "network", 879 namelen) == 0) { 880 EATCOMMENTS 881 GETQUOTEDNAME(cp2, i) 882 883 884 cp2[i] = '\0'; 885 if (!parsenetwork(cp2, &errstr)) { 886 ++errors; 887 fprintf(stderr, 888 "%s: %s:%d: bad network: %s\n", 889 prog, file, n, errstr); 890 } 891 } else { 892 ++errors; 893 fprintf(stderr, 894 "%s: unknown nslint \"%.*s\"\n", 895 prog, namelen, name); 896 } 897 EATSEMICOLON 898 EATCOMMENTS 899 } 900 ++cp; 901 EATCOMMENTS 902 if (*cp != ';') { 903 ++errors; 904 fprintf(stderr, "missing options semi\n"); 905 } else 906 ++cp; 907 continue; 908 } 909 if (strncasecmp(name, "include", namelen) == 0) { 910 EATCOMMENTS 911 GETQUOTEDNAME(filename, filenamelen) 912 strncpy(includefile, filename, filenamelen); 913 includefile[filenamelen] = '\0'; 914 errors += doconf(includefile, 1); 915 EATSEMICOLON 916 continue; 917 } 918 919 /* Skip over statements we don't understand */ 920 EATSEMICOLON 921 } 922 923 free(buf); 924 close(fd); 925 return (errors != 0); 926} 927 928/* Return true when done */ 929int 930parsesoa(register const char *cp, register char **errstrp) 931{ 932 register char ch, *garbage; 933 static char errstr[132]; 934 935 /* Eat leading whitespace */ 936 while (isspace(*cp)) 937 ++cp; 938 939 /* Find opening paren */ 940 if (nsoaval < 0) { 941 cp = strchr(cp, '('); 942 if (cp == NULL) 943 return (0); 944 ++cp; 945 while (isspace(*cp)) 946 ++cp; 947 nsoaval = 0; 948 } 949 950 /* Grab any numbers we find */ 951 garbage = "leading garbage"; 952 while (isdigit(*cp) && nsoaval < NSOAVAL) { 953 soaval[nsoaval] = atoi(cp); 954 do { 955 ++cp; 956 } while (isdigit(*cp)); 957 if (nsoaval == SOA_SERIAL && *cp == '.' && isdigit(cp[1])) { 958 do { 959 ++cp; 960 } while (isdigit(*cp)); 961 } else { 962 ch = *cp; 963 if (isupper(ch)) 964 ch = tolower(ch); 965 switch (ch) { 966 967 case 'w': 968 soaval[nsoaval] *= 7; 969 /* fall through */ 970 971 case 'd': 972 soaval[nsoaval] *= 24; 973 /* fall through */ 974 975 case 'h': 976 soaval[nsoaval] *= 60; 977 /* fall through */ 978 979 case 'm': 980 soaval[nsoaval] *= 60; 981 /* fall through */ 982 983 case 's': 984 ++cp; 985 break; 986 987 default: 988 ; /* none */ 989 } 990 } 991 while (isspace(*cp)) 992 ++cp; 993 garbage = "trailing garbage"; 994 ++nsoaval; 995 } 996 997 /* If we're done, do some sanity checks */ 998 if (nsoaval >= NSOAVAL && *cp == ')') { 999 ++cp; 1000 if (*cp != '\0') 1001 *errstrp = garbage; 1002 else if (soaval[SOA_EXPIRE] < 1003 soaval[SOA_REFRESH] + 10 * soaval[SOA_RETRY]) { 1004 (void)sprintf(errstr, 1005 "expire less than refresh + 10 * retry (%u < %u + 10 * %u)", 1006 soaval[SOA_EXPIRE], 1007 soaval[SOA_REFRESH], 1008 soaval[SOA_RETRY]); 1009 *errstrp = errstr; 1010 } else if (soaval[SOA_REFRESH] < 2 * soaval[SOA_RETRY]) { 1011 (void)sprintf(errstr, 1012 "refresh less than 2 * retry (%u < 2 * %u)", 1013 soaval[SOA_REFRESH], 1014 soaval[SOA_RETRY]); 1015 *errstrp = errstr; 1016 } 1017 return (1); 1018 } 1019 1020 if (*cp != '\0') { 1021 *errstrp = garbage; 1022 return (1); 1023 } 1024 1025 return (0); 1026} 1027 1028void 1029process(register const char *file, register const char *domain, 1030 register const char *zone) 1031{ 1032 register FILE *f; 1033 register char ch, *cp, *cp2, *cp3, *rtype; 1034 register const char *ccp; 1035 register int n, sawsoa, flags, i; 1036 register u_int ttl; 1037 register u_int32_t addr; 1038 u_int32_t net, mask; 1039 int smtp; 1040 char buf[1024], name[128], lastname[128], odomain[128]; 1041 char *errstr; 1042 char *dotfmt = "%s: %s/%s:%d \"%s\" target missing trailing dot: %s\n"; 1043 1044 f = fopen(file, "r"); 1045 if (f == NULL) { 1046 fprintf(stderr, "%s: %s/%s: %s\n", 1047 prog, cwd, file, strerror(errno)); 1048 ++errors; 1049 return; 1050 } 1051 if (debug > 1) 1052 printf("%s: process: opened %s/%s\n", prog, cwd, file); 1053 1054 /* Are we doing an in-addr.arpa domain? */ 1055 n = 0; 1056 net = 0; 1057 mask = 0; 1058 ccp = domain + strlen(domain) - sizeof(inaddr) + 1; 1059 if (ccp >= domain && strcasecmp(ccp, inaddr) == 0 && 1060 !parseinaddr(domain, &net, &mask)) { 1061 ++errors; 1062 fprintf(stderr, "%s: %s/%s:%d bad in-addr.arpa domain\n", 1063 prog, cwd, file, n); 1064 fclose(f); 1065 return; 1066 } 1067 1068 lastname[0] = '\0'; 1069 sawsoa = 0; 1070 while (fgets(buf, sizeof(buf), f) != NULL) { 1071 ++n; 1072 cp = buf; 1073 while (*cp != '\0') { 1074 /* Handle quoted strings (but don't report errors) */ 1075 if (*cp == '"') { 1076 ++cp; 1077 while (*cp != '"' && *cp != '\n' && *cp != '\0') 1078 ++cp; 1079 continue; 1080 } 1081 if (*cp == '\n' || *cp == ';') 1082 break; 1083 ++cp; 1084 } 1085 *cp-- = '\0'; 1086 1087 /* Nuke trailing white space */ 1088 while (cp >= buf && isspace(*cp)) 1089 *cp-- = '\0'; 1090 1091 cp = buf; 1092 if (*cp == '\0') 1093 continue; 1094 1095 /* Handle multi-line soa records */ 1096 if (sawsoa) { 1097 errstr = NULL; 1098 if (parsesoa(cp, &errstr)) 1099 sawsoa = 0; 1100 if (errstr != NULL) { 1101 ++errors; 1102 fprintf(stderr, 1103 "%s: %s/%s:%d bad \"soa\" record (%s)\n", 1104 prog, cwd, file, n, errstr); 1105 } 1106 continue; 1107 } 1108 if (debug > 3) 1109 printf(">%s<\n", cp); 1110 1111 /* Look for name */ 1112 if (isspace(*cp)) { 1113 /* Same name as last record */ 1114 if (lastname[0] == '\0') { 1115 ++errors; 1116 fprintf(stderr, 1117 "%s: %s/%s:%d no default name\n", 1118 prog, cwd, file, n); 1119 continue; 1120 } 1121 (void)strcpy(name, lastname); 1122 } else { 1123 /* Extract name, converting to lowercase */ 1124 for (cp2 = name; !isspace(*cp) && *cp != '\0'; ++cp) 1125 if (isupper(*cp)) 1126 *cp2++ = tolower(*cp); 1127 else 1128 *cp2++ = *cp; 1129 *cp2 = '\0'; 1130 1131 /* Check for domain shorthand */ 1132 if (name[0] == '@' && name[1] == '\0') 1133 (void)strcpy(name, domain); 1134 } 1135 1136 /* Find next token */ 1137 while (isspace(*cp)) 1138 ++cp; 1139 1140 /* Handle includes (gag) */ 1141 if (name[0] == '$' && strcasecmp(name, "$include") == 0) { 1142 /* Extract filename */ 1143 cp2 = name; 1144 while (!isspace(*cp) && *cp != '\0') 1145 *cp2++ = *cp++; 1146 *cp2 = '\0'; 1147 1148 /* Look for optional domain */ 1149 while (isspace(*cp)) 1150 ++cp; 1151 if (*cp == '\0') 1152 process(name, domain, zone); 1153 else { 1154 cp2 = cp; 1155 /* Convert optional domain to lowercase */ 1156 for (; !isspace(*cp) && *cp != '\0'; ++cp) 1157 if (isupper(*cp)) 1158 *cp = tolower(*cp); 1159 *cp = '\0'; 1160 process(name, cp2, cp2); 1161 } 1162 continue; 1163 } 1164 1165 /* Handle $origin */ 1166 if (name[0] == '$' && strcasecmp(name, "$origin") == 0) { 1167 /* Extract domain, converting to lowercase */ 1168 for (cp2 = odomain; !isspace(*cp) && *cp != '\0'; ++cp) 1169 if (isupper(*cp)) 1170 *cp2++ = tolower(*cp); 1171 else 1172 *cp2++ = *cp; 1173 *cp2 = '\0'; 1174 domain = odomain; 1175 lastname[0] = '\0'; 1176 1177 /* Are we doing an in-addr.arpa domain? */ 1178 net = 0; 1179 mask = 0; 1180 ccp = domain + strlen(domain) - (sizeof(inaddr) - 1); 1181 if (ccp >= domain && strcasecmp(ccp, inaddr) == 0 && 1182 !parseinaddr(domain, &net, &mask)) { 1183 ++errors; 1184 fprintf(stderr, 1185 "%s: %s/%s:%d bad in-addr.arpa domain\n", 1186 prog, cwd, file, n); 1187 return; 1188 } 1189 continue; 1190 } 1191 1192 /* Handle ttl */ 1193 if (name[0] == '$' && strcasecmp(name, "$ttl") == 0) { 1194 cp2 = cp; 1195 while (isdigit(*cp)) 1196 ++cp; 1197 ch = *cp; 1198 if (isupper(ch)) 1199 ch = tolower(ch); 1200 if (strchr("wdhms", ch) != NULL) 1201 ++cp; 1202 while (isspace(*cp)) 1203 ++cp; 1204 if (*cp != '\0') { 1205 ++errors; 1206 fprintf(stderr, 1207 "%s: %s/%s:%d bad $ttl \"%s\"\n", 1208 prog, cwd, file, n, cp2); 1209 } 1210 (void)strcpy(name, lastname); 1211 continue; 1212 } 1213 1214 /* Parse ttl or use default */ 1215 if (isdigit(*cp)) { 1216 ttl = atoi(cp); 1217 do { 1218 ++cp; 1219 } while (isdigit(*cp)); 1220 1221 ch = *cp; 1222 if (isupper(ch)) 1223 ch = tolower(ch); 1224 switch (ch) { 1225 1226 case 'w': 1227 ttl *= 7; 1228 /* fall through */ 1229 1230 case 'd': 1231 ttl *= 24; 1232 /* fall through */ 1233 1234 case 'h': 1235 ttl *= 60; 1236 /* fall through */ 1237 1238 case 'm': 1239 ttl *= 60; 1240 /* fall through */ 1241 1242 case 's': 1243 ++cp; 1244 break; 1245 1246 default: 1247 ; /* none */ 1248 } 1249 1250 1251 if (!isspace(*cp)) { 1252 ++errors; 1253 fprintf(stderr, "%s: %s/%s:%d bad ttl\n", 1254 prog, cwd, file, n); 1255 continue; 1256 } 1257 1258 /* Find next token */ 1259 ++cp; 1260 while (isspace(*cp)) 1261 ++cp; 1262 } else 1263 ttl = soaval[SOA_MINIMUM]; 1264 1265 /* Eat optional "in" */ 1266 if ((cp[0] == 'i' || cp[0] == 'I') && 1267 (cp[1] == 'n' || cp[1] == 'N') && isspace(cp[2])) { 1268 /* Find next token */ 1269 cp += 3; 1270 while (isspace(*cp)) 1271 ++cp; 1272 } else if ((cp[0] == 'c' || cp[0] == 'C') && 1273 isspace(cp[5]) && strncasecmp(cp, "chaos", 5) == 0) { 1274 /* Find next token */ 1275 cp += 5; 1276 while (isspace(*cp)) 1277 ++cp; 1278 } 1279 1280 /* Find end of record type, converting to lowercase */ 1281 rtype = cp; 1282 for (rtype = cp; !isspace(*cp) && *cp != '\0'; ++cp) 1283 if (isupper(*cp)) 1284 *cp = tolower(*cp); 1285 *cp++ = '\0'; 1286 1287 /* Find "the rest" */ 1288 while (isspace(*cp)) 1289 ++cp; 1290 1291 /* Check for non-ptr names with dots but no trailing dot */ 1292 if (!isdigit(*name) && 1293 checkdots(name) && strcmp(domain, ".") != 0) { 1294 ++errors; 1295 fprintf(stderr, 1296 "%s: %s/%s:%d \"%s\" name missing trailing dot: %s\n", 1297 prog, cwd, file, n, rtype, name); 1298 } 1299 1300 /* Check for FQDNs outside the zone */ 1301 cp2 = name + strlen(name) - 1; 1302 if (cp2 >= name && *cp2 == '.' && strchr(name, '.') != NULL) { 1303 cp2 = name + strlen(name) - strlen(zone); 1304 if (cp2 >= name && strcasecmp(cp2, zone) != 0) { 1305 ++errors; 1306 fprintf(stderr, 1307 "%s: %s/%s:%d \"%s\" outside zone %s\n", 1308 prog, cwd, file, n, name, zone); 1309 } 1310 } 1311 1312#define CHECK4(p, a, b, c, d) \ 1313 (p[0] == (a) && p[1] == (b) && p[2] == (c) && p[3] == (d) && p[4] == '\0') 1314#define CHECK3(p, a, b, c) \ 1315 (p[0] == (a) && p[1] == (b) && p[2] == (c) && p[3] == '\0') 1316#define CHECK2(p, a, b) \ 1317 (p[0] == (a) && p[1] == (b) && p[2] == '\0') 1318#define CHECKDOT(p) \ 1319 (p[0] == '.' && p[1] == '\0') 1320 1321 if (rtype[0] == 'a' && rtype[1] == '\0') { 1322 /* Handle "a" record */ 1323 add_domain(name, domain); 1324 addr = htonl(inet_addr(cp)); 1325 if ((int)addr == -1) { 1326 ++errors; 1327 cp2 = cp + strlen(cp) - 1; 1328 if (cp2 >= cp && *cp2 == '\n') 1329 *cp2 = '\0'; 1330 fprintf(stderr, 1331 "%s: %s/%s:%d bad \"a\" record ip addr \"%s\"\n", 1332 prog, cwd, file, n, cp); 1333 continue; 1334 } 1335 errors += updateitem(name, addr, REC_A, ttl, 0); 1336 } else if (CHECK4(rtype, 'a', 'a', 'a', 'a')) { 1337 /* Just eat for now */ 1338 continue; 1339 } else if (CHECK3(rtype, 'p', 't', 'r')) { 1340 /* Handle "ptr" record */ 1341 add_domain(name, domain); 1342 if (strcmp(cp, "@") == 0) 1343 (void)strcpy(cp, zone); 1344 if (checkdots(cp)) { 1345 ++errors; 1346 fprintf(stderr, dotfmt, 1347 prog, cwd, file, n, rtype, cp); 1348 } 1349 add_domain(cp, domain); 1350 errstr = NULL; 1351 addr = parseptr(name, net, mask, &errstr); 1352 if (errstr != NULL) { 1353 ++errors; 1354 fprintf(stderr, 1355 "%s: %s/%s:%d bad \"ptr\" record (%s) ip addr \"%s\"\n", 1356 prog, cwd, file, n, errstr, name); 1357 continue; 1358 } 1359 errors += updateitem(cp, addr, REC_PTR, 0, 0); 1360 } else if (CHECK3(rtype, 's', 'o', 'a')) { 1361 /* Handle "soa" record */ 1362 if (!CHECKDOT(name)) { 1363 add_domain(name, domain); 1364 errors += updateitem(name, 0, REC_SOA, 0, 0); 1365 } 1366 errstr = NULL; 1367 if (!parsesoa(cp, &errstr)) 1368 ++sawsoa; 1369 if (errstr != NULL) { 1370 ++errors; 1371 fprintf(stderr, 1372 "%s: %s/%s:%d bad \"soa\" record (%s)\n", 1373 prog, cwd, file, n, errstr); 1374 continue; 1375 } 1376 } else if (CHECK3(rtype, 'w', 'k', 's')) { 1377 /* Handle "wks" record */ 1378 addr = htonl(inet_addr(cp)); 1379 if ((int)addr == -1) { 1380 ++errors; 1381 cp2 = cp; 1382 while (!isspace(*cp2) && *cp2 != '\0') 1383 ++cp2; 1384 *cp2 = '\0'; 1385 fprintf(stderr, 1386 "%s: %s/%s:%d bad \"wks\" record ip addr \"%s\"\n", 1387 prog, cwd, file, n, cp); 1388 continue; 1389 } 1390 /* Step over ip address */ 1391 while (*cp == '.' || isdigit(*cp)) 1392 ++cp; 1393 while (isspace(*cp)) 1394 *cp++ = '\0'; 1395 /* Make sure services are legit */ 1396 errstr = NULL; 1397 n += checkwks(f, cp, &smtp, &errstr); 1398 if (errstr != NULL) { 1399 ++errors; 1400 fprintf(stderr, 1401 "%s: %s/%s:%d bad \"wks\" record (%s)\n", 1402 prog, cwd, file, n, errstr); 1403 continue; 1404 } 1405 add_domain(name, domain); 1406 errors += updateitem(name, addr, REC_WKS, 1407 0, smtp ? FLG_SMTPWKS : 0); 1408 /* XXX check to see if ip address records exists? */ 1409 } else if (rtype[0] == 'h' && strcmp(rtype, "hinfo") == 0) { 1410 /* Handle "hinfo" record */ 1411 add_domain(name, domain); 1412 errors += updateitem(name, 0, REC_HINFO, 0, 0); 1413 cp2 = cp; 1414 cp = parsequoted(cp); 1415 if (cp == NULL) { 1416 ++errors; 1417 fprintf(stderr, 1418 "%s: %s/%s:%d \"hinfo\" missing quote: %s\n", 1419 prog, cwd, file, n, cp2); 1420 continue; 1421 } 1422 if (!isspace(*cp)) { 1423 ++errors; 1424 fprintf(stderr, 1425 "%s: %s/%s:%d \"hinfo\" missing white space: %s\n", 1426 prog, cwd, file, n, cp2); 1427 continue; 1428 } 1429 ++cp; 1430 while (isspace(*cp)) 1431 ++cp; 1432 if (*cp == '\0') { 1433 ++errors; 1434 fprintf(stderr, 1435 "%s: %s/%s:%d \"hinfo\" missing keyword: %s\n", 1436 prog, cwd, file, n, cp2); 1437 continue; 1438 } 1439 cp = parsequoted(cp); 1440 if (cp == NULL) { 1441 ++errors; 1442 fprintf(stderr, 1443 "%s: %s/%s:%d \"hinfo\" missing quote: %s\n", 1444 prog, cwd, file, n, cp2); 1445 continue; 1446 } 1447 if (*cp != '\0') { 1448 ++errors; 1449 fprintf(stderr, 1450 "%s: %s/%s:%d \"hinfo\" garbage after keywords: %s\n", 1451 prog, cwd, file, n, cp2); 1452 continue; 1453 } 1454 } else if (CHECK2(rtype, 'm', 'x')) { 1455 /* Handle "mx" record */ 1456 add_domain(name, domain); 1457 errors += updateitem(name, 0, REC_MX, ttl, 0); 1458 1459 /* Look for priority */ 1460 if (!isdigit(*cp)) { 1461 ++errors; 1462 fprintf(stderr, 1463 "%s: %s/%s:%d bad \"mx\" priority: %s\n", 1464 prog, cwd, file, n, cp); 1465 } 1466 1467 /* Skip over priority */ 1468 ++cp; 1469 while (isdigit(*cp)) 1470 ++cp; 1471 while (isspace(*cp)) 1472 ++cp; 1473 if (*cp == '\0') { 1474 ++errors; 1475 fprintf(stderr, 1476 "%s: %s/%s:%d missing \"mx\" hostname\n", 1477 prog, cwd, file, n); 1478 } 1479 if (strcmp(cp, "@") == 0) 1480 (void)strcpy(cp, zone); 1481 if (checkdots(cp)) { 1482 ++errors; 1483 fprintf(stderr, dotfmt, 1484 prog, cwd, file, n, rtype, cp); 1485 } 1486 1487 /* Check to see if mx host exists */ 1488 add_domain(cp, domain); 1489 flags = FLG_MXREF; 1490 if (*name == *cp && strcmp(name, cp) == 0) 1491 flags |= FLG_SELFMX; 1492 errors += updateitem(cp, 0, REC_REF, 0, flags); 1493 } else if (rtype[0] == 'c' && strcmp(rtype, "cname") == 0) { 1494 /* Handle "cname" record */ 1495 add_domain(name, domain); 1496 errors += updateitem(name, 0, REC_CNAME, 0, 0); 1497 if (checkdots(cp)) { 1498 ++errors; 1499 fprintf(stderr, dotfmt, 1500 prog, cwd, file, n, rtype, cp); 1501 } 1502 1503 /* Make sure cname points somewhere */ 1504 if (strcmp(cp, "@") == 0) 1505 (void)strcpy(cp, zone); 1506 add_domain(cp, domain); 1507 errors += updateitem(cp, 0, REC_REF, 0, 0); 1508 } else if (CHECK3(rtype, 's', 'r', 'v')) { 1509 /* Handle "srv" record */ 1510 add_domain(name, domain); 1511 errors += updateitem(name, 0, REC_SRV, 0, 0); 1512 cp2 = cp; 1513 1514 /* Skip over three values */ 1515 for (i = 0; i < 3; ++i) { 1516 if (!isdigit(*cp)) { 1517 ++errors; 1518 fprintf(stderr, "%s: %s/%s:%d" 1519 " bad \"srv\" value: %s\n", 1520 prog, cwd, file, n, cp); 1521 } 1522 1523 /* Skip over value */ 1524 ++cp; 1525 while (isdigit(*cp)) 1526 ++cp; 1527 while (isspace(*cp)) 1528 ++cp; 1529 } 1530 1531 /* Check to see if mx host exists */ 1532 add_domain(cp, domain); 1533 errors += updateitem(cp, 0, REC_REF, 0, 0); 1534 } else if (CHECK3(rtype, 't', 'x', 't')) { 1535 /* Handle "txt" record */ 1536 add_domain(name, domain); 1537 errors += updateitem(name, 0, REC_TXT, 0, 0); 1538 cp2 = cp; 1539 cp = parsequoted(cp); 1540 if (cp == NULL) { 1541 ++errors; 1542 fprintf(stderr, 1543 "%s: %s/%s:%d \"txt\" missing quote: %s\n", 1544 prog, cwd, file, n, cp2); 1545 continue; 1546 } 1547 while (isspace(*cp)) 1548 ++cp; 1549 if (*cp != '\0') { 1550 ++errors; 1551 fprintf(stderr, 1552 "%s: %s/%s:%d \"txt\" garbage after text: %s\n", 1553 prog, cwd, file, n, cp2); 1554 continue; 1555 } 1556 } else if (CHECK2(rtype, 'n', 's')) { 1557 /* Handle "ns" record */ 1558 errors += updateitem(zone, 0, REC_NS, 0, 0); 1559 if (strcmp(cp, "@") == 0) 1560 (void)strcpy(cp, zone); 1561 if (checkdots(cp)) { 1562 ++errors; 1563 fprintf(stderr, dotfmt, 1564 prog, cwd, file, n, rtype, cp); 1565 } 1566 add_domain(cp, domain); 1567 errors += updateitem(cp, 0, REC_REF, 0, 0); 1568 } else if (CHECK2(rtype, 'r', 'p')) { 1569 /* Handle "rp" record */ 1570 add_domain(name, domain); 1571 errors += updateitem(name, 0, REC_RP, 0, 0); 1572 cp2 = cp; 1573 1574 /* Step over mailbox name */ 1575 /* XXX could add_domain() and check further */ 1576 while (!isspace(*cp) && *cp != '\0') 1577 ++cp; 1578 if (*cp == '\0') { 1579 ++errors; 1580 fprintf(stderr, 1581 "%s: %s/%s:%d \"rp\" missing text name: %s\n", 1582 prog, cwd, file, n, cp2); 1583 continue; 1584 } 1585 ++cp; 1586 cp3 = cp; 1587 1588 /* Step over text name */ 1589 while (!isspace(*cp) && *cp != '\0') 1590 ++cp; 1591 1592 if (*cp != '\0') { 1593 ++errors; 1594 fprintf(stderr, 1595 "%s: %s/%s:%d \"rp\" garbage after text name: %s\n", 1596 prog, cwd, file, n, cp2); 1597 continue; 1598 } 1599 1600 /* Make sure text name points somewhere (if not ".") */ 1601 if (!CHECKDOT(cp3)) { 1602 add_domain(cp3, domain); 1603 errors += updateitem(cp3, 0, REC_REF, 0, 0); 1604 } 1605 } else if (rtype[0] == 'a' && strcmp(rtype, "allowdupa") == 0) { 1606 /* Handle "allow duplicate a" record */ 1607 add_domain(name, domain); 1608 addr = htonl(inet_addr(cp)); 1609 if ((int)addr == -1) { 1610 ++errors; 1611 cp2 = cp + strlen(cp) - 1; 1612 if (cp2 >= cp && *cp2 == '\n') 1613 *cp2 = '\0'; 1614 fprintf(stderr, 1615 "%s: %s/%s:%d bad \"allowdupa\" record ip addr \"%s\"\n", 1616 prog, cwd, file, n, cp); 1617 continue; 1618 } 1619 errors += updateitem(name, addr, 0, 0, FLG_ALLOWDUPA); 1620 } else { 1621 /* Unknown record type */ 1622 ++errors; 1623 fprintf(stderr, 1624 "%s: %s/%s:%d unknown record type \"%s\"\n", 1625 prog, cwd, file, n, rtype); 1626 add_domain(name, domain); 1627 errors += updateitem(name, 0, REC_UNKNOWN, 0, 0); 1628 } 1629 (void)strcpy(lastname, name); 1630 } 1631 (void)fclose(f); 1632 return; 1633} 1634 1635/* Records we use to detect duplicates */ 1636static struct duprec { 1637 int record; 1638 char *name; 1639} duprec[] = { 1640 { REC_A, "a" }, 1641 { REC_HINFO, "hinfo" }, 1642 { 0, NULL }, 1643}; 1644 1645void 1646checkdups(register struct item *ip, register int records) 1647{ 1648 register struct duprec *dp; 1649 1650 records &= (ip->records & MASK_TEST_DUP); 1651 if (records == 0) 1652 return; 1653 for (dp = duprec; dp->name != NULL; ++dp) 1654 if ((records & dp->record) != 0) { 1655 ++errors; 1656 fprintf(stderr, "%s: multiple \"%s\" records for %s\n", 1657 prog, dp->name, ip->host); 1658 records &= ~dp->record; 1659 } 1660 if (records != 0) 1661 fprintf(stderr, "%s: checkdups: records not zero (%d)\n", 1662 prog, records); 1663} 1664 1665int 1666updateitem(register const char *host, register u_int32_t addr, 1667 register int records, register u_int ttl, register int flags) 1668{ 1669 register const char *ccp; 1670 register int n, errs; 1671 register u_int i; 1672 register struct item *ip; 1673 int foundsome; 1674 1675 n = 0; 1676 foundsome = 0; 1677 errs = 0; 1678 ITEMHASH(host, i, ccp); 1679 ip = &items[i & (ITEMSIZE - 1)]; 1680 while (n < ITEMSIZE && ip->host) { 1681 if ((addr == 0 || addr == ip->addr || ip->addr == 0) && 1682 *host == *ip->host && strcmp(host, ip->host) == 0) { 1683 ++foundsome; 1684 if (ip->addr == 0) 1685 ip->addr = addr; 1686 if ((records & MASK_TEST_DUP) != 0) 1687 checkdups(ip, records); 1688 ip->records |= records; 1689 /* Only check differing ttl's for A and MX records */ 1690 if (ip->ttl == 0) 1691 ip->ttl = ttl; 1692 else if (ttl != 0 && ip->ttl != ttl) { 1693 fprintf(stderr, 1694 "%s: differing ttls for %s (%u != %u)\n", 1695 prog, ip->host, ttl, ip->ttl); 1696 ++errs; 1697 } 1698 ip->flags |= flags; 1699 /* Not done if we wildcard matched the name */ 1700 if (addr) 1701 return (errs); 1702 } 1703 ++n; 1704 ++ip; 1705 if (ip >= &items[ITEMSIZE]) 1706 ip = items; 1707 } 1708 1709 if (n >= ITEMSIZE) { 1710 fprintf(stderr, "%s: out of item slots (max %d)\n", 1711 prog, ITEMSIZE); 1712 exit(1); 1713 } 1714 1715 /* Done if we were wildcarding the name (and found entries for it) */ 1716 if (addr == 0 && foundsome) 1717 return (errs); 1718 1719 /* Didn't find it, make new entry */ 1720 ++itemcnt; 1721 if (ip->host) { 1722 fprintf(stderr, "%s: reusing bucket!\n", prog); 1723 exit(1); 1724 } 1725 ip->addr = addr; 1726 ip->host = savestr(host); 1727 if ((records & MASK_TEST_DUP) != 0) 1728 checkdups(ip, records); 1729 ip->records |= records; 1730 if (ttl != 0) 1731 ip->ttl = ttl; 1732 ip->flags |= flags; 1733 return (errs); 1734} 1735 1736static const char *microlist[] = { 1737 "_tcp", 1738 "_udp", 1739 "_msdcs", 1740 "_sites", 1741 NULL 1742}; 1743 1744int 1745rfc1034host(register const char *host, register int recs) 1746{ 1747 register const char *cp, **p; 1748 register int underok; 1749 1750 underok = 0; 1751 for (p = microlist; *p != NULL ;++p) 1752 if ((cp = strstr(host, *p)) != NULL && 1753 cp > host && 1754 cp[-1] == '.' && 1755 cp[strlen(*p)] == '.') { 1756 ++underok; 1757 break; 1758 } 1759 1760 cp = host; 1761 if (!(isalpha(*cp) || isdigit(*cp) || (*cp == '_' && underok))) { 1762 fprintf(stderr, 1763 "%s: illegal hostname \"%s\" (starts with non-alpha/numeric)\n", 1764 prog, host); 1765 return (1); 1766 } 1767 for (++cp; *cp != '.' && *cp != '\0'; ++cp) 1768 if (!(isalpha(*cp) || isdigit(*cp) || *cp == '-' || 1769 (*cp == '/' && (recs & REC_SOA) != 0))) { 1770 fprintf(stderr, 1771 "%s: illegal hostname \"%s\" ('%c' illegal character)\n", 1772 prog, host, *cp); 1773 return (1); 1774 } 1775 if (--cp >= host && *cp == '-') { 1776 fprintf(stderr, "%s: illegal hostname \"%s\" (ends with '-')\n", 1777 prog, host); 1778 return (1); 1779 } 1780 return (0); 1781} 1782 1783int 1784nslint(void) 1785{ 1786 register int n, records, flags; 1787 register struct item *ip, *lastaip, **ipp, **itemlist; 1788 register u_int32_t addr, lastaddr, mask; 1789 1790 itemlist = (struct item **)calloc(itemcnt, sizeof(*ipp)); 1791 if (itemlist == NULL) { 1792 fprintf(stderr, "%s: nslint: calloc: %s\n", 1793 prog, strerror(errno)); 1794 exit(1); 1795 } 1796 ipp = itemlist; 1797 for (n = 0, ip = items; n < ITEMSIZE; ++n, ++ip) { 1798 if (ip->host == NULL) 1799 continue; 1800 1801 /* Save entries with addresses for later check */ 1802 if (ip->addr != 0) 1803 *ipp++ = ip; 1804 1805 if (debug > 1) { 1806 if (debug > 2) 1807 printf("%d\t", n); 1808 printf("%s\t%s\t0x%x\t0x%x\n", 1809 ip->host, intoa(ip->addr), ip->records, ip->flags); 1810 } 1811 1812 /* Check for illegal hostnames (rfc1034) */ 1813 if (rfc1034host(ip->host, ip->records)) 1814 ++errors; 1815 1816 /* Check for missing ptr records (ok if also an ns record) */ 1817 records = ip->records & MASK_CHECK_REC; 1818 if ((ip->records & MASK_TEST_REC) != 0) 1819 records |= REC_OTHER; 1820 switch (records) { 1821 1822 case REC_A | REC_OTHER | REC_PTR | REC_REF: 1823 case REC_A | REC_OTHER | REC_PTR: 1824 case REC_A | REC_PTR | REC_REF: 1825 case REC_A | REC_PTR: 1826 case REC_CNAME: 1827 /* These are O.K. */ 1828 break; 1829 1830 case REC_CNAME | REC_REF: 1831 ++errors; 1832 fprintf(stderr, "%s: \"cname\" referenced by other" 1833 " \"cname\" or \"mx\": %s\n", prog, ip->host); 1834 break; 1835 1836 case REC_OTHER | REC_REF: 1837 case REC_OTHER: 1838 /* 1839 * This is only an error if there is an address 1840 * associated with the hostname; this means 1841 * there was a wks entry with bogus address. 1842 * Otherwise, we have an mx or hinfo. 1843 */ 1844 if (ip->addr != 0) { 1845 ++errors; 1846 fprintf(stderr, 1847 "%s: \"wks\" without \"a\" and \"ptr\": %s -> %s\n", 1848 prog, ip->host, intoa(ip->addr)); 1849 } 1850 break; 1851 1852 case REC_REF: 1853 ++errors; 1854 fprintf(stderr, 1855 "%s: name referenced without other records: %s\n", 1856 prog, ip->host); 1857 break; 1858 1859 case REC_A | REC_OTHER | REC_REF: 1860 case REC_A | REC_OTHER: 1861 case REC_A | REC_REF: 1862 case REC_A: 1863 ++errors; 1864 fprintf(stderr, "%s: missing \"ptr\": %s -> %s\n", 1865 prog, ip->host, intoa(ip->addr)); 1866 break; 1867 1868 case REC_OTHER | REC_PTR | REC_REF: 1869 case REC_OTHER | REC_PTR: 1870 case REC_PTR | REC_REF: 1871 case REC_PTR: 1872 ++errors; 1873 fprintf(stderr, "%s: missing \"a\": %s -> %s\n", 1874 prog, ip->host, intoa(ip->addr)); 1875 break; 1876 1877 case REC_A | REC_CNAME | REC_OTHER | REC_PTR | REC_REF: 1878 case REC_A | REC_CNAME | REC_OTHER | REC_PTR: 1879 case REC_A | REC_CNAME | REC_OTHER | REC_REF: 1880 case REC_A | REC_CNAME | REC_OTHER: 1881 case REC_A | REC_CNAME | REC_PTR | REC_REF: 1882 case REC_A | REC_CNAME | REC_PTR: 1883 case REC_A | REC_CNAME | REC_REF: 1884 case REC_A | REC_CNAME: 1885 case REC_CNAME | REC_OTHER | REC_PTR | REC_REF: 1886 case REC_CNAME | REC_OTHER | REC_PTR: 1887 case REC_CNAME | REC_OTHER | REC_REF: 1888 case REC_CNAME | REC_OTHER: 1889 case REC_CNAME | REC_PTR | REC_REF: 1890 case REC_CNAME | REC_PTR: 1891 ++errors; 1892 fprintf(stderr, "%s: \"cname\" %s has other records\n", 1893 prog, ip->host); 1894 break; 1895 1896 case 0: 1897 /* Second level test */ 1898 if ((ip->records & ~(REC_NS | REC_TXT)) == 0) 1899 break; 1900 /* Fall through... */ 1901 1902 default: 1903 ++errors; 1904 fprintf(stderr, 1905 "%s: records == 0x%x: can't happen (%s 0x%x)\n", 1906 prog, records, ip->host, ip->records); 1907 break; 1908 } 1909 1910 /* Check for smtp problems */ 1911 flags = ip->flags & MASK_TEST_SMTP; 1912 1913 if ((flags & FLG_SELFMX) != 0 && (ip->records & REC_A) == 0) { 1914 ++errors; 1915 fprintf(stderr, 1916 "%s: self \"mx\" for %s missing \"a\" record\n", 1917 prog, ip->host); 1918 } 1919 1920 switch (flags) { 1921 1922 case 0: 1923 case FLG_SELFMX | FLG_SMTPWKS: 1924 /* These are O.K. */ 1925 break; 1926 1927 case FLG_SELFMX: 1928 if ((ip->records & REC_WKS) != 0) { 1929 ++errors; 1930 fprintf(stderr, 1931 "%s: smtp/tcp missing from \"wks\": %s\n", 1932 prog, ip->host); 1933 } 1934 break; 1935 1936 case FLG_SMTPWKS: 1937 ++errors; 1938 fprintf(stderr, 1939 "%s: saw smtp/tcp without self \"mx\": %s\n", 1940 prog, ip->host); 1941 break; 1942 1943 default: 1944 ++errors; 1945 fprintf(stderr, 1946 "%s: flags == 0x%x: can't happen (%s)\n", 1947 prog, flags, ip->host); 1948 } 1949 1950 /* Check for chained MX records */ 1951 if ((ip->flags & (FLG_SELFMX | FLG_MXREF)) == FLG_MXREF && 1952 (ip->records & REC_MX) != 0) { 1953 ++errors; 1954 fprintf(stderr, "%s: \"mx\" referenced by other" 1955 " \"mx\" record: %s\n", prog, ip->host); 1956 } 1957 } 1958 1959 /* Check for doubly booked addresses */ 1960 n = ipp - itemlist; 1961 qsort(itemlist, n, sizeof(itemlist[0]), cmpaddr); 1962 lastaddr = 0; 1963 ip = NULL; 1964 for (ipp = itemlist; n > 0; ++ipp, --n) { 1965 addr = (*ipp)->addr; 1966 if (lastaddr == addr && 1967 ((*ipp)->flags & FLG_ALLOWDUPA) == 0 && 1968 (ip->flags & FLG_ALLOWDUPA) == 0) { 1969 ++errors; 1970 fprintf(stderr, "%s: %s in use by %s and %s\n", 1971 prog, intoa(addr), (*ipp)->host, ip->host); 1972 } 1973 lastaddr = addr; 1974 ip = *ipp; 1975 } 1976 1977 /* Check for hosts with multiple addresses on the same subnet */ 1978 n = ipp - itemlist; 1979 qsort(itemlist, n, sizeof(itemlist[0]), cmphost); 1980 if (netlistcnt > 0) { 1981 n = ipp - itemlist; 1982 lastaip = NULL; 1983 for (ipp = itemlist; n > 0; ++ipp, --n) { 1984 ip = *ipp; 1985 if ((ip->records & REC_A) == 0 || 1986 (ip->flags & FLG_ALLOWDUPA) != 0) 1987 continue; 1988 if (lastaip != NULL && 1989 strcasecmp(ip->host, lastaip->host) == 0) { 1990 mask = findmask(ip->addr); 1991 if (mask == 0) { 1992 ++errors; 1993 fprintf(stderr, 1994 "%s: can't find mask for %s (%s)\n", 1995 prog, ip->host, intoa(ip->addr)); 1996 } else if ((lastaip->addr & mask) == 1997 (ip->addr & mask) ) { 1998 ++errors; 1999 fprintf(stderr, 2000 "%s: multiple \"a\" records for %s on subnet %s", 2001 prog, ip->host, 2002 intoa(ip->addr & mask)); 2003 fprintf(stderr, "\n\t(%s", 2004 intoa(lastaip->addr)); 2005 fprintf(stderr, " and %s)\n", 2006 intoa(ip->addr)); 2007 } 2008 } 2009 lastaip = ip; 2010 } 2011 } 2012 2013 if (debug) 2014 printf("%s: %d/%d items used, %d error%s\n", prog, itemcnt, 2015 ITEMSIZE, errors, errors == 1 ? "" : "s"); 2016 return (errors != 0); 2017} 2018 2019/* Similar to inet_ntoa() */ 2020char * 2021intoa(u_int32_t addr) 2022{ 2023 register char *cp; 2024 register u_int byte; 2025 register int n; 2026 static char buf[sizeof(".xxx.xxx.xxx.xxx")]; 2027 2028 cp = &buf[sizeof buf]; 2029 *--cp = '\0'; 2030 2031 n = 4; 2032 do { 2033 byte = addr & 0xff; 2034 *--cp = byte % 10 + '0'; 2035 byte /= 10; 2036 if (byte > 0) { 2037 *--cp = byte % 10 + '0'; 2038 byte /= 10; 2039 if (byte > 0) 2040 *--cp = byte + '0'; 2041 } 2042 *--cp = '.'; 2043 addr >>= 8; 2044 } while (--n > 0); 2045 2046 return cp + 1; 2047} 2048 2049int 2050parseinaddr(register const char *cp, register u_int32_t *netp, 2051 register u_int32_t *maskp) 2052{ 2053 register int i, bits; 2054 register u_int32_t o, net, mask; 2055 2056 if (!isdigit(*cp)) 2057 return (0); 2058 net = 0; 2059 mask = 0xff000000; 2060 bits = 0; 2061 o = 0; 2062 do { 2063 o = o * 10 + (*cp++ - '0'); 2064 } while (isdigit(*cp)); 2065 net = o << 24; 2066 2067 /* Check for classless delegation mask width */ 2068 if (*cp == '/') { 2069 ++cp; 2070 o = 0; 2071 do { 2072 o = o * 10 + (*cp++ - '0'); 2073 } while (isdigit(*cp)); 2074 bits = o; 2075 if (bits <= 0 || bits > 32) 2076 return (0); 2077 } 2078 2079 if (*cp == '.' && isdigit(cp[1])) { 2080 ++cp; 2081 o = 0; 2082 do { 2083 o = o * 10 + (*cp++ - '0'); 2084 } while (isdigit(*cp)); 2085 net = (net >> 8) | (o << 24); 2086 mask = 0xffff0000; 2087 if (*cp == '.' && isdigit(cp[1])) { 2088 ++cp; 2089 o = 0; 2090 do { 2091 o = o * 10 + (*cp++ - '0'); 2092 } while (isdigit(*cp)); 2093 net = (net >> 8) | (o << 24); 2094 mask = 0xffffff00; 2095 if (*cp == '.' && isdigit(cp[1])) { 2096 ++cp; 2097 o = 0; 2098 do { 2099 o = o * 10 + (*cp++ - '0'); 2100 } while (isdigit(*cp)); 2101 net = (net >> 8) | (o << 24); 2102 mask = 0xffffffff; 2103 } 2104 } 2105 } 2106 if (strcasecmp(cp, inaddr) != 0) 2107 return (0); 2108 2109 /* Classless delegation */ 2110 /* XXX check that calculated mask isn't smaller than octet mask? */ 2111 if (bits != 0) 2112 for (mask = 0, i = 31; bits > 0; --i, --bits) 2113 mask |= (1 << i); 2114 2115 *netp = net; 2116 *maskp = mask; 2117 return (1); 2118} 2119 2120u_int32_t 2121parseptr(register const char *cp, u_int32_t net, u_int32_t mask, 2122 register char **errstrp) 2123{ 2124 register u_int32_t o, addr; 2125 register int shift; 2126 2127 addr = 0; 2128 shift = 0; 2129 while (isdigit(*cp) && shift < 32) { 2130 o = 0; 2131 do { 2132 o = o * 10 + (*cp++ - '0'); 2133 } while (isdigit(*cp)); 2134 addr |= o << shift; 2135 shift += 8; 2136 if (*cp != '.') { 2137 if (*cp == '\0') 2138 break; 2139 *errstrp = "missing dot"; 2140 return (0); 2141 } 2142 ++cp; 2143 } 2144 2145 if (shift > 32) { 2146 *errstrp = "more than 4 octets"; 2147 return (0); 2148 } 2149 2150 if (shift == 32 && strcasecmp(cp, inaddr + 1) == 0) 2151 return (addr); 2152 2153#ifdef notdef 2154 if (*cp != '\0') { 2155 *errstrp = "trailing junk"; 2156 return (0); 2157 } 2158#endif 2159#ifdef notdef 2160 if ((~mask & net) != 0) { 2161 *errstrp = "too many octets for net"; 2162 return (0); 2163 } 2164#endif 2165 return (net | addr); 2166} 2167 2168int 2169checkwks(register FILE *f, register char *proto, register int *smtpp, 2170 register char **errstrp) 2171{ 2172 register int n, sawparen; 2173 register char *cp, *serv, **p; 2174 static char errstr[132]; 2175 char buf[1024]; 2176 char psbuf[512]; 2177 2178 if (!protoserv_init) { 2179 initprotoserv(); 2180 ++protoserv_init; 2181 } 2182 2183 /* Line count */ 2184 n = 0; 2185 2186 /* Terminate protocol */ 2187 cp = proto; 2188 while (!isspace(*cp) && *cp != '\0') 2189 ++cp; 2190 if (*cp != '\0') 2191 *cp++ = '\0'; 2192 2193 /* Find services */ 2194 *smtpp = 0; 2195 sawparen = 0; 2196 if (*cp == '(') { 2197 ++sawparen; 2198 ++cp; 2199 while (isspace(*cp)) 2200 ++cp; 2201 } 2202 for (;;) { 2203 if (*cp == '\0') { 2204 if (!sawparen) 2205 break; 2206 if (fgets(buf, sizeof(buf), f) == NULL) { 2207 *errstrp = "mismatched parens"; 2208 return (n); 2209 } 2210 ++n; 2211 cp = buf; 2212 while (isspace(*cp)) 2213 ++cp; 2214 } 2215 /* Find end of service, converting to lowercase */ 2216 for (serv = cp; !isspace(*cp) && *cp != '\0'; ++cp) 2217 if (isupper(*cp)) 2218 *cp = tolower(*cp); 2219 if (*cp != '\0') 2220 *cp++ = '\0'; 2221 if (sawparen && *cp == ')') { 2222 /* XXX should check for trailing junk */ 2223 break; 2224 } 2225 2226 (void)sprintf(psbuf, "%s/%s", serv, proto); 2227 2228 if (*serv == 's' && strcmp(psbuf, "tcp/smtp") == 0) 2229 ++*smtpp; 2230 2231 for (p = protoserv; *p != NULL; ++p) 2232 if (*psbuf == **p && strcmp(psbuf, *p) == 0) { 2233 break; 2234 } 2235 if (*p == NULL) { 2236 sprintf(errstr, "%s unknown", psbuf); 2237 *errstrp = errstr; 2238 break; 2239 } 2240 } 2241 2242 return (n); 2243} 2244 2245int 2246checkserv(register const char *serv, register char **p) 2247{ 2248 for (; *p != NULL; ++p) 2249 if (*serv == **p && strcmp(serv, *p) == 0) 2250 return (1); 2251 return (0); 2252} 2253 2254void 2255initprotoserv(void) 2256{ 2257 register char *cp; 2258 register struct servent *sp; 2259 char psbuf[512]; 2260 2261 protoserv_len = 256; 2262 protoserv = (char **)malloc(protoserv_len * sizeof(*protoserv)); 2263 if (protoserv == NULL) { 2264 fprintf(stderr, "%s: nslint: malloc: %s\n", 2265 prog, strerror(errno)); 2266 exit(1); 2267 } 2268 2269 while ((sp = getservent()) != NULL) { 2270 (void)sprintf(psbuf, "%s/%s", sp->s_name, sp->s_proto); 2271 2272 /* Convert to lowercase */ 2273 for (cp = psbuf; *cp != '\0'; ++cp) 2274 if (isupper(*cp)) 2275 *cp = tolower(*cp); 2276 2277 if (protoserv_last + 1 >= protoserv_len) { 2278 protoserv_len <<= 1; 2279 protoserv = realloc(protoserv, 2280 protoserv_len * sizeof(*protoserv)); 2281 if (protoserv == NULL) { 2282 fprintf(stderr, "%s: nslint: realloc: %s\n", 2283 prog, strerror(errno)); 2284 exit(1); 2285 } 2286 } 2287 protoserv[protoserv_last] = savestr(psbuf); 2288 ++protoserv_last; 2289 } 2290 protoserv[protoserv_last] = NULL; 2291} 2292 2293/* 2294 * Returns true if name contains a dot but not a trailing dot. 2295 * Special case: allow a single dot if the second part is not one 2296 * of the 3 or 4 letter top level domains or is any 2 letter TLD 2297 */ 2298int 2299checkdots(register const char *name) 2300{ 2301 register const char *cp, *cp2; 2302 2303 if ((cp = strchr(name, '.')) == NULL) 2304 return (0); 2305 cp2 = name + strlen(name) - 1; 2306 if (cp2 >= name && *cp2 == '.') 2307 return (0); 2308 2309 /* Return true of more than one dot*/ 2310 ++cp; 2311 if (strchr(cp, '.') != NULL) 2312 return (1); 2313 2314 if (strlen(cp) == 2 || 2315 strcasecmp(cp, "gov") == 0 || 2316 strcasecmp(cp, "edu") == 0 || 2317 strcasecmp(cp, "com") == 0 || 2318 strcasecmp(cp, "net") == 0 || 2319 strcasecmp(cp, "org") == 0 || 2320 strcasecmp(cp, "mil") == 0 || 2321 strcasecmp(cp, "int") == 0 || 2322 strcasecmp(cp, "nato") == 0 || 2323 strcasecmp(cp, "arpa") == 0) 2324 return (1); 2325 return (0); 2326} 2327 2328int 2329cmpaddr(register const void *ip1, register const void *ip2) 2330{ 2331 register u_int32_t a1, a2; 2332 2333 a1 = (*(struct item **)ip1)->addr; 2334 a2 = (*(struct item **)ip2)->addr; 2335 2336 if (a1 < a2) 2337 return (-1); 2338 else if (a1 > a2) 2339 return (1); 2340 else 2341 return (0); 2342} 2343 2344int 2345cmphost(register const void *ip1, register const void *ip2) 2346{ 2347 register const char *s1, *s2; 2348 2349 s1 = (*(struct item **)ip1)->host; 2350 s2 = (*(struct item **)ip2)->host; 2351 2352 return (strcasecmp(s1, s2)); 2353} 2354 2355/* Returns a pointer after the next token or quoted string, else NULL */ 2356char * 2357parsequoted(register char *cp) 2358{ 2359 2360 if (*cp == '"') { 2361 ++cp; 2362 while (*cp != '"' && *cp != '\0') 2363 ++cp; 2364 if (*cp != '"') 2365 return (NULL); 2366 ++cp; 2367 } else { 2368 while (!isspace(*cp) && *cp != '\0') 2369 ++cp; 2370 } 2371 return (cp); 2372} 2373 2374__dead void 2375usage(void) 2376{ 2377 extern char version[]; 2378 2379 fprintf(stderr, "Version %s\n", version); 2380 fprintf(stderr, "usage: %s [-d] [-b named.boot] [-B nslint.boot]\n", 2381 prog); 2382 fprintf(stderr, " %s [-d] [-c named.conf] [-C nslint.conf]\n", 2383 prog); 2384 exit(1); 2385} 2386