4 * Copyright (c) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001
5 * The Regents of the University of California. All rights reserved.
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
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.
24 static const char copyright
[] =
25 "@(#) Copyright (c) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001\n\
26 The Regents of the University of California. All rights reserved.\n";
27 static const char rcsid
[] =
28 "@(#) Id: nslint.c,v 1.1 2001/12/21 04:12:04 marka Exp (LBL)";
31 * nslint - perform consistency checks on dns files
34 #include <sys/types.h>
37 #include <netinet/in.h>
39 #include <arpa/inet.h>
62 #ifdef HAVE_OS_PROTO_H
66 #define NSLINTBOOT "nslint.boot" /* default nslint.boot file */
67 #define NSLINTCONF "nslint.conf" /* default nslint.conf file */
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 */
78 /* Resource records seen */
80 #define REC_PTR 0x0002
81 #define REC_WKS 0x0004
82 #define REC_HINFO 0x0008
84 #define REC_CNAME 0x0020
86 #define REC_SOA 0x0080
88 #define REC_TXT 0x0200
89 #define REC_SRV 0x0400
91 /* These aren't real records */
92 #define REC_OTHER 0x0800
93 #define REC_REF 0x1000
94 #define REC_UNKNOWN 0x2000
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)
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)
104 /* Test for records we want to check for duplicate name detection */
105 #define MASK_TEST_DUP \
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 */
114 /* Test for smtp problems */
115 #define MASK_TEST_SMTP \
116 (FLG_SELFMX | FLG_SMTPWKS)
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++
123 struct item items
[ITEMSIZE
];
124 int itemcnt
; /* count of items */
126 /* Hostname string storage */
127 #define STRSIZE 8192; /* size to malloc when more space is needed */
128 char *strptr
; /* pointer to string pool */
129 int strsize
; /* size of space left in pool */
133 char *bootfile
= "/etc/named.boot";
134 char *conffile
= "/etc/named.conf";
140 char **protoserv
; /* valid protocol/service names */
145 static char inaddr
[] = ".in-addr.arpa.";
149 #define SOA_REFRESH 1
152 #define SOA_MINIMUM 4
154 static u_int soaval
[5];
156 #define NSOAVAL (sizeof(soaval) / sizeof(soaval[0]))
159 static inline void add_domain(char *, const char *);
160 int checkdots(const char *);
161 void checkdups(struct item
*, int);
162 int checkserv(const char *, char **p
);
163 int checkwks(FILE *, char *, int *, char **);
164 int cmpaddr(const void *, const void *);
165 int cmphost(const void *, const void *);
166 int doboot(const char *, int);
167 int doconf(const char *, int);
168 void initprotoserv(void);
169 char *intoa(u_int32_t
);
170 int main(int, char **);
172 int parseinaddr(const char *, u_int32_t
*, u_int32_t
*);
173 int parsenetwork(const char *, char **);
174 u_int32_t
parseptr(const char *, u_int32_t
, u_int32_t
, char **);
175 char *parsequoted(char *);
176 int parsesoa(const char *, char **);
177 void process(const char *, const char *, const char *);
178 int rfc1034host(const char *, int);
179 int updateitem(const char *, u_int32_t
, int, u_int
, int);
180 __dead
void usage(void) __attribute__((volatile));
183 extern int optind
, opterr
;
185 /* add domain if necessary */
187 add_domain(register char *name
, register const char *domain
)
191 /* Kill trailing white space and convert to lowercase */
192 for (cp
= name
; *cp
!= '\0' && !isspace(*cp
); ++cp
)
196 /* If necessary, append domain */
197 if (cp
>= name
&& *cp
++ != '.') {
200 (void)strcpy(cp
, domain
);
202 /* XXX should we insure a trailing dot? */
206 main(int argc
, char **argv
)
209 register int op
, status
, i
, donamedboot
, donamedconf
;
211 if ((cp
= strrchr(argv
[0], '/')) != NULL
)
218 while ((op
= getopt(argc
, argv
, "b:c:B:C:d")) != -1)
248 if (optind
!= argc
|| (donamedboot
&& donamedconf
))
252 status
= doboot(bootfile
, 1);
253 else if (donamedconf
)
254 status
= doconf(conffile
, 1);
256 status
= doconf(conffile
, 0);
258 status
= doboot(bootfile
, 1);
265 if (nslintboot
!= NULL
)
266 status
|= doboot(nslintboot
, 1);
267 else if ((i
= doboot(NSLINTBOOT
, 0)) > 0)
270 if (nslintconf
!= NULL
)
271 status
|= doconf(nslintconf
, 1);
272 else if ((i
= doconf(NSLINTCONF
, 0)) > 0)
284 static struct netlist
*netlist
;
285 static u_int netlistsize
; /* size of array */
286 static u_int netlistcnt
; /* next free element */
289 findmask(u_int32_t addr
)
293 for (i
= 0; i
< netlistcnt
; ++i
)
294 if ((addr
& netlist
[i
].mask
) == netlist
[i
].net
)
295 return (netlist
[i
].mask
);
300 parsenetwork(register const char *cp
, register char **errstrp
)
303 register u_int32_t net
, mask
;
304 register u_int32_t o
;
306 static char errstr
[132];
313 while (isdigit(*cp
) && shift
>= 0) {
316 o
= o
* 10 + (*cp
++ - '0');
317 } while (isdigit(*cp
));
330 mask
= htonl(inet_addr(cp
));
331 if ((int)mask
== -1) {
333 (void)sprintf(errstr
, "bad mask \"%s\"", cp
);
339 for (i
= 0; i
< 3 && *cp
== '.'; ++i
) {
345 *errstrp
= "wrong number of dots in mask";
348 } else if (*cp
== '/') {
353 } while (isdigit(*cp
));
354 if (w
< 1 || w
> 32) {
355 *errstrp
= "bad mask width";
358 mask
= 0xffffffff << (32 - w
);
360 *errstrp
= "garbage after net";
368 *errstrp
= "trailing garbage";
372 /* Finaly sanity checks */
373 if ((net
& ~ mask
) != 0) {
375 (void)sprintf(errstr
, "host bits set in net \"%s\"",
380 /* Make sure there's room */
381 if (netlistsize
<= netlistcnt
) {
382 if (netlistsize
== 0) {
384 netlist
= (struct netlist
*)
385 malloc(netlistsize
* sizeof(*netlist
));
388 netlist
= (struct netlist
*)
389 realloc(netlist
, netlistsize
* sizeof(*netlist
));
391 if (netlist
== NULL
) {
392 fprintf(stderr
, "%s: nslint: malloc/realloc: %s\n",
393 prog
, strerror(errno
));
399 netlist
[netlistcnt
].net
= net
;
400 netlist
[netlistcnt
].mask
= mask
;
407 doboot(register const char *file
, register int mustexist
)
410 register char *cp
, *cp2
;
413 char buf
[1024], name
[128];
416 f
= fopen(file
, "r");
418 /* Not an error if it doesn't exist */
419 if (!mustexist
&& errno
== ENOENT
) {
422 "%s: doit: %s doesn't exist (ignoring)\n",
426 fprintf(stderr
, "%s: %s: %s\n", prog
, file
, strerror(errno
));
430 printf("%s: doit: opened %s\n", prog
, file
);
433 while (fgets(buf
, sizeof(buf
), f
) != NULL
) {
439 cp
= strchr(buf
, ';');
442 cp
= buf
+ strlen(buf
) - 1;
443 if (cp
>= buf
&& *cp
== '\n')
447 /* Eat leading whitespace */
451 /* Skip blank lines */
452 if (*cp
== '\n' || *cp
== '\0')
457 while (!isspace(*cp
) && *cp
!= '\0')
461 /* Find next keyword */
464 if (strcasecmp(cp2
, "directory") == 0) {
465 /* Terminate directory */
467 while (!isspace(*cp
) && *cp
!= '\0')
470 if (chdir(cp2
) < 0) {
472 fprintf(stderr
, "%s: can't chdir %s: %s\n",
473 prog
, cp2
, strerror(errno
));
479 if (strcasecmp(cp2
, "primary") == 0) {
480 /* Extract domain, converting to lowercase */
481 for (cp2
= name
; !isspace(*cp
) && *cp
!= '\0'; ++cp
)
483 *cp2
++ = tolower(*cp
);
486 /* Insure trailing dot */
487 if (cp2
> name
&& cp2
[-1] != '.')
495 /* Terminate directory */
497 while (!isspace(*cp
) && *cp
!= '\0')
501 /* Process it! (zone is the same as the domain) */
503 memset(soaval
, 0, sizeof(soaval
));
504 process(cp2
, name
, name
);
507 if (strcasecmp(cp2
, "network") == 0) {
508 if (!parsenetwork(cp
, &errstr
)) {
511 "%s: %s:%d: bad network: %s\n",
512 prog
, file
, n
, errstr
);
516 if (strcasecmp(cp2
, "include") == 0) {
517 /* Terminate include file */
519 while (!isspace(*cp
) && *cp
!= '\0')
522 errors
+= doboot(cp2
, 1);
525 /* Eat any other options */
529 return (errors
!= 0);
533 doconf(register const char *file
, register int mustexist
)
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
;
541 char zone
[128], includefile
[256];
544 fd
= open(file
, O_RDONLY
, 0);
546 /* Not an error if it doesn't exist */
547 if (!mustexist
&& errno
== ENOENT
) {
550 "%s: doconf: %s doesn't exist (ignoring)\n",
554 fprintf(stderr
, "%s: %s: %s\n", prog
, file
, strerror(errno
));
558 printf("%s: doconf: opened %s\n", prog
, file
);
560 if (fstat(fd
, &sbuf
) < 0) {
561 fprintf(stderr
, "%s: fstat(%s) %s\n",
562 prog
, file
, strerror(errno
));
565 buf
= (char *)malloc(sbuf
.st_size
+ 1);
567 fprintf(stderr
, "%s: malloc: %s\n", prog
, strerror(errno
));
571 /* Slurp entire config file */
575 cc
= read(fd
, cp
, n
);
577 fprintf(stderr
, "%s: read(%s) %s\n",
578 prog
, file
, strerror(errno
));
583 } while (cc
!= 0 && cc
< n
);
586 #define EATWHITESPACE \
587 while (isspace(*cp)) { \
593 /* Handle both to-end-of-line and C style comments */
594 #define EATCOMMENTS \
603 while (*cp != '\n' && *cp != '\0') \
606 else if (strncmp(cp, "//", 2) == 0) { \
609 while (*cp != '\n' && *cp != '\0') \
612 else if (strncmp(cp, "/*", 2) == 0) { \
614 for (cp += 2; *cp != '\0'; ++cp) { \
617 else if (strncmp(cp, "*/", 2) == 0) { \
623 } while (sawcomment); \
626 #define GETNAME(name, len) \
630 while (!isspace(*cp) && *cp != ';' && *cp != '\0') { \
636 #define GETQUOTEDNAME(name, len) \
640 fprintf(stderr, "%s: %s:%d missing left quote\n", \
646 while (*cp != '"' && *cp != '\n' && *cp != '\0') { \
652 fprintf(stderr, "%s: %s:%d missing right quote\n", \
658 /* Eat everything to the next semicolon, perhaps eating matching qbraces */
659 #define EATSEMICOLON \
661 register int depth = 0; \
662 while (*cp != '\0') { \
687 while (*cp
!= '\0') {
691 GETNAME(name
, namelen
)
694 fprintf(stderr
, "%s: %s:%d garbage char '%c' (1)\n",
700 if (strncasecmp(name
, "options", namelen
) == 0) {
705 "%s: %s:%d missing left qbrace in options\n",
710 while (*cp
!= '}' && *cp
!= '\0') {
712 GETNAME(name
, namelen
)
716 "%s: %s:%d garbage char '%c' (2)\n",
722 /* If not the "directory" option, just eat it */
723 if (strncasecmp(name
, "directory",
726 GETQUOTEDNAME(cp2
, i
)
728 if (chdir(cp2
) < 0) {
731 "%s: %s:.%d can't chdir %s: %s\n",
746 "%s: %s:%d missing options semi\n",
752 if (strncasecmp(name
, "zone", namelen
) == 0) {
754 GETQUOTEDNAME(zonename
, zonenamelen
)
760 if (strncasecmp(cp
, "in", 2) == 0) {
763 } else if (strncasecmp(cp
, "chaos", 5) == 0) {
767 if (*cp
!= '{') { /* } */
770 "%s: %s:%d missing left qbrace in zone\n",
776 while (*cp
!= '\0') {
780 } else if (*cp
== '}') {
786 GETNAME(name
, namelen
)
790 "%s: %s:%d garbage char '%c' (3)\n",
795 if (strncasecmp(name
, "type",
798 GETNAME(typename
, typenamelen
)
802 "%s: %s:%d garbage char '%c' (4)\n",
807 } else if (strncasecmp(name
, "file",
810 GETQUOTEDNAME(filename
, filenamelen
)
812 /* Just ignore keywords we don't understand */
820 "%s: %s:%d missing zone right qbrace\n",
827 "%s: %s:%d missing zone semi\n",
832 /* If we got something interesting, process it */
833 if (typenamelen
== 0) {
835 fprintf(stderr
, "%s: missing zone type!\n",
839 if (strncasecmp(typename
, "master", typenamelen
) == 0) {
840 if (filenamelen
== 0) {
843 "%s: missing zone filename!\n",
847 strncpy(zone
, zonename
, zonenamelen
);
848 zone
[zonenamelen
] = '\0';
849 for (cp2
= zone
; *cp2
!= '\0'; ++cp2
)
851 *cp2
= tolower(*cp2
);
852 /* Insure trailing dot */
853 if (cp2
> zone
&& cp2
[-1] != '.') {
857 filename
[filenamelen
] = '\0';
859 memset(soaval
, 0, sizeof(soaval
));
860 process(filename
, zone
, zone
);
864 if (strncasecmp(name
, "nslint", namelen
) == 0) {
869 "%s: %s:%d missing left qbrace in nslint\n",
875 while (*cp
!= '}' && *cp
!= '\0') {
877 GETNAME(name
, namelen
)
878 if (strncasecmp(name
, "network",
881 GETQUOTEDNAME(cp2
, i
)
885 if (!parsenetwork(cp2
, &errstr
)) {
888 "%s: %s:%d: bad network: %s\n",
889 prog
, file
, n
, errstr
);
894 "%s: unknown nslint \"%.*s\"\n",
895 prog
, namelen
, name
);
904 fprintf(stderr
, "missing options semi\n");
909 if (strncasecmp(name
, "include", namelen
) == 0) {
911 GETQUOTEDNAME(filename
, filenamelen
)
912 strncpy(includefile
, filename
, filenamelen
);
913 includefile
[filenamelen
] = '\0';
914 errors
+= doconf(includefile
, 1);
919 /* Skip over statements we don't understand */
925 return (errors
!= 0);
928 /* Return true when done */
930 parsesoa(register const char *cp
, register char **errstrp
)
932 register char ch
, *garbage
;
933 static char errstr
[132];
935 /* Eat leading whitespace */
939 /* Find opening paren */
941 cp
= strchr(cp
, '(');
950 /* Grab any numbers we find */
951 garbage
= "leading garbage";
952 while (isdigit(*cp
) && nsoaval
< NSOAVAL
) {
953 soaval
[nsoaval
] = atoi(cp
);
956 } while (isdigit(*cp
));
957 if (nsoaval
== SOA_SERIAL
&& *cp
== '.' && isdigit(cp
[1])) {
960 } while (isdigit(*cp
));
968 soaval
[nsoaval
] *= 7;
972 soaval
[nsoaval
] *= 24;
976 soaval
[nsoaval
] *= 60;
980 soaval
[nsoaval
] *= 60;
993 garbage
= "trailing garbage";
997 /* If we're done, do some sanity checks */
998 if (nsoaval
>= NSOAVAL
&& *cp
== ')') {
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)",
1007 soaval
[SOA_REFRESH
],
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
],
1029 process(register const char *file
, register const char *domain
,
1030 register const char *zone
)
1033 register char ch
, *cp
, *cp2
, *cp3
, *rtype
;
1034 register const char *ccp
;
1035 register int n
, sawsoa
, flags
, i
;
1037 register u_int32_t addr
;
1038 u_int32_t net
, mask
;
1040 char buf
[1024], name
[128], lastname
[128], odomain
[128];
1042 char *dotfmt
= "%s: %s/%s:%d \"%s\" target missing trailing dot: %s\n";
1044 f
= fopen(file
, "r");
1046 fprintf(stderr
, "%s: %s/%s: %s\n",
1047 prog
, cwd
, file
, strerror(errno
));
1052 printf("%s: process: opened %s/%s\n", prog
, cwd
, file
);
1054 /* Are we doing an in-addr.arpa domain? */
1058 ccp
= domain
+ strlen(domain
) - sizeof(inaddr
) + 1;
1059 if (ccp
>= domain
&& strcasecmp(ccp
, inaddr
) == 0 &&
1060 !parseinaddr(domain
, &net
, &mask
)) {
1062 fprintf(stderr
, "%s: %s/%s:%d bad in-addr.arpa domain\n",
1063 prog
, cwd
, file
, n
);
1069 while (fgets(buf
, sizeof(buf
), f
) != NULL
) {
1072 while (*cp
!= '\0') {
1073 /* Handle quoted strings (but don't report errors) */
1076 while (*cp
!= '"' && *cp
!= '\n' && *cp
!= '\0')
1080 if (*cp
== '\n' || *cp
== ';')
1086 /* Nuke trailing white space */
1087 while (cp
>= buf
&& isspace(*cp
))
1094 /* Handle multi-line soa records */
1097 if (parsesoa(cp
, &errstr
))
1099 if (errstr
!= NULL
) {
1102 "%s: %s/%s:%d bad \"soa\" record (%s)\n",
1103 prog
, cwd
, file
, n
, errstr
);
1108 printf(">%s<\n", cp
);
1112 /* Same name as last record */
1113 if (lastname
[0] == '\0') {
1116 "%s: %s/%s:%d no default name\n",
1117 prog
, cwd
, file
, n
);
1120 (void)strcpy(name
, lastname
);
1122 /* Extract name, converting to lowercase */
1123 for (cp2
= name
; !isspace(*cp
) && *cp
!= '\0'; ++cp
)
1125 *cp2
++ = tolower(*cp
);
1130 /* Check for domain shorthand */
1131 if (name
[0] == '@' && name
[1] == '\0')
1132 (void)strcpy(name
, domain
);
1135 /* Find next token */
1136 while (isspace(*cp
))
1139 /* Handle includes (gag) */
1140 if (name
[0] == '$' && strcasecmp(name
, "$include") == 0) {
1141 /* Extract filename */
1143 while (!isspace(*cp
) && *cp
!= '\0')
1147 /* Look for optional domain */
1148 while (isspace(*cp
))
1151 process(name
, domain
, zone
);
1154 /* Convert optional domain to lowercase */
1155 for (; !isspace(*cp
) && *cp
!= '\0'; ++cp
)
1159 process(name
, cp2
, cp2
);
1164 /* Handle $origin */
1165 if (name
[0] == '$' && strcasecmp(name
, "$origin") == 0) {
1166 /* Extract domain, converting to lowercase */
1167 for (cp2
= odomain
; !isspace(*cp
) && *cp
!= '\0'; ++cp
)
1169 *cp2
++ = tolower(*cp
);
1176 /* Are we doing an in-addr.arpa domain? */
1179 ccp
= domain
+ strlen(domain
) - (sizeof(inaddr
) - 1);
1180 if (ccp
>= domain
&& strcasecmp(ccp
, inaddr
) == 0 &&
1181 !parseinaddr(domain
, &net
, &mask
)) {
1184 "%s: %s/%s:%d bad in-addr.arpa domain\n",
1185 prog
, cwd
, file
, n
);
1192 if (name
[0] == '$' && strcasecmp(name
, "$ttl") == 0) {
1194 while (isdigit(*cp
))
1199 if (strchr("wdhms", ch
) != NULL
)
1201 while (isspace(*cp
))
1206 "%s: %s/%s:%d bad $ttl \"%s\"\n",
1207 prog
, cwd
, file
, n
, cp2
);
1209 (void)strcpy(name
, lastname
);
1213 /* Parse ttl or use default */
1218 } while (isdigit(*cp
));
1250 if (!isspace(*cp
)) {
1252 fprintf(stderr
, "%s: %s/%s:%d bad ttl\n",
1253 prog
, cwd
, file
, n
);
1257 /* Find next token */
1259 while (isspace(*cp
))
1262 ttl
= soaval
[SOA_MINIMUM
];
1264 /* Eat optional "in" */
1265 if ((cp
[0] == 'i' || cp
[0] == 'I') &&
1266 (cp
[1] == 'n' || cp
[1] == 'N') && isspace(cp
[2])) {
1267 /* Find next token */
1269 while (isspace(*cp
))
1271 } else if ((cp
[0] == 'c' || cp
[0] == 'C') &&
1272 isspace(cp
[5]) && strncasecmp(cp
, "chaos", 5) == 0) {
1273 /* Find next token */
1275 while (isspace(*cp
))
1279 /* Find end of record type, converting to lowercase */
1281 for (rtype
= cp
; !isspace(*cp
) && *cp
!= '\0'; ++cp
)
1286 /* Find "the rest" */
1287 while (isspace(*cp
))
1290 /* Check for non-ptr names with dots but no trailing dot */
1291 if (!isdigit(*name
) &&
1292 checkdots(name
) && strcmp(domain
, ".") != 0) {
1295 "%s: %s/%s:%d \"%s\" name missing trailing dot: %s\n",
1296 prog
, cwd
, file
, n
, rtype
, name
);
1299 /* Check for FQDNs outside the zone */
1300 cp2
= name
+ strlen(name
) - 1;
1301 if (cp2
>= name
&& *cp2
== '.' && strchr(name
, '.') != NULL
) {
1302 cp2
= name
+ strlen(name
) - strlen(zone
);
1303 if (cp2
>= name
&& strcasecmp(cp2
, zone
) != 0) {
1306 "%s: %s/%s:%d \"%s\" outside zone %s\n",
1307 prog
, cwd
, file
, n
, name
, zone
);
1311 #define CHECK4(p, a, b, c, d) \
1312 (p[0] == (a) && p[1] == (b) && p[2] == (c) && p[3] == (d) && p[4] == '\0')
1313 #define CHECK3(p, a, b, c) \
1314 (p[0] == (a) && p[1] == (b) && p[2] == (c) && p[3] == '\0')
1315 #define CHECK2(p, a, b) \
1316 (p[0] == (a) && p[1] == (b) && p[2] == '\0')
1317 #define CHECKDOT(p) \
1318 (p[0] == '.' && p[1] == '\0')
1320 if (rtype
[0] == 'a' && rtype
[1] == '\0') {
1321 /* Handle "a" record */
1322 add_domain(name
, domain
);
1323 addr
= htonl(inet_addr(cp
));
1324 if ((int)addr
== -1) {
1326 cp2
= cp
+ strlen(cp
) - 1;
1327 if (cp2
>= cp
&& *cp2
== '\n')
1330 "%s: %s/%s:%d bad \"a\" record ip addr \"%s\"\n",
1331 prog
, cwd
, file
, n
, cp
);
1334 errors
+= updateitem(name
, addr
, REC_A
, ttl
, 0);
1335 } else if (CHECK4(rtype
, 'a', 'a', 'a', 'a')) {
1336 /* Just eat for now */
1338 } else if (CHECK3(rtype
, 'p', 't', 'r')) {
1339 /* Handle "ptr" record */
1340 add_domain(name
, domain
);
1341 if (strcmp(cp
, "@") == 0)
1342 (void)strcpy(cp
, zone
);
1343 if (checkdots(cp
)) {
1345 fprintf(stderr
, dotfmt
,
1346 prog
, cwd
, file
, n
, rtype
, cp
);
1348 add_domain(cp
, domain
);
1350 addr
= parseptr(name
, net
, mask
, &errstr
);
1351 if (errstr
!= NULL
) {
1354 "%s: %s/%s:%d bad \"ptr\" record (%s) ip addr \"%s\"\n",
1355 prog
, cwd
, file
, n
, errstr
, name
);
1358 errors
+= updateitem(cp
, addr
, REC_PTR
, 0, 0);
1359 } else if (CHECK3(rtype
, 's', 'o', 'a')) {
1360 /* Handle "soa" record */
1361 if (!CHECKDOT(name
)) {
1362 add_domain(name
, domain
);
1363 errors
+= updateitem(name
, 0, REC_SOA
, 0, 0);
1366 if (!parsesoa(cp
, &errstr
))
1368 if (errstr
!= NULL
) {
1371 "%s: %s/%s:%d bad \"soa\" record (%s)\n",
1372 prog
, cwd
, file
, n
, errstr
);
1375 } else if (CHECK3(rtype
, 'w', 'k', 's')) {
1376 /* Handle "wks" record */
1377 addr
= htonl(inet_addr(cp
));
1378 if ((int)addr
== -1) {
1381 while (!isspace(*cp2
) && *cp2
!= '\0')
1385 "%s: %s/%s:%d bad \"wks\" record ip addr \"%s\"\n",
1386 prog
, cwd
, file
, n
, cp
);
1389 /* Step over ip address */
1390 while (*cp
== '.' || isdigit(*cp
))
1392 while (isspace(*cp
))
1394 /* Make sure services are legit */
1396 n
+= checkwks(f
, cp
, &smtp
, &errstr
);
1397 if (errstr
!= NULL
) {
1400 "%s: %s/%s:%d bad \"wks\" record (%s)\n",
1401 prog
, cwd
, file
, n
, errstr
);
1404 add_domain(name
, domain
);
1405 errors
+= updateitem(name
, addr
, REC_WKS
,
1406 0, smtp
? FLG_SMTPWKS
: 0);
1407 /* XXX check to see if ip address records exists? */
1408 } else if (rtype
[0] == 'h' && strcmp(rtype
, "hinfo") == 0) {
1409 /* Handle "hinfo" record */
1410 add_domain(name
, domain
);
1411 errors
+= updateitem(name
, 0, REC_HINFO
, 0, 0);
1413 cp
= parsequoted(cp
);
1417 "%s: %s/%s:%d \"hinfo\" missing quote: %s\n",
1418 prog
, cwd
, file
, n
, cp2
);
1421 if (!isspace(*cp
)) {
1424 "%s: %s/%s:%d \"hinfo\" missing white space: %s\n",
1425 prog
, cwd
, file
, n
, cp2
);
1429 while (isspace(*cp
))
1434 "%s: %s/%s:%d \"hinfo\" missing keyword: %s\n",
1435 prog
, cwd
, file
, n
, cp2
);
1438 cp
= parsequoted(cp
);
1442 "%s: %s/%s:%d \"hinfo\" missing quote: %s\n",
1443 prog
, cwd
, file
, n
, cp2
);
1449 "%s: %s/%s:%d \"hinfo\" garbage after keywords: %s\n",
1450 prog
, cwd
, file
, n
, cp2
);
1453 } else if (CHECK2(rtype
, 'm', 'x')) {
1454 /* Handle "mx" record */
1455 add_domain(name
, domain
);
1456 errors
+= updateitem(name
, 0, REC_MX
, ttl
, 0);
1458 /* Look for priority */
1459 if (!isdigit(*cp
)) {
1462 "%s: %s/%s:%d bad \"mx\" priority: %s\n",
1463 prog
, cwd
, file
, n
, cp
);
1466 /* Skip over priority */
1468 while (isdigit(*cp
))
1470 while (isspace(*cp
))
1475 "%s: %s/%s:%d missing \"mx\" hostname\n",
1476 prog
, cwd
, file
, n
);
1478 if (strcmp(cp
, "@") == 0)
1479 (void)strcpy(cp
, zone
);
1480 if (checkdots(cp
)) {
1482 fprintf(stderr
, dotfmt
,
1483 prog
, cwd
, file
, n
, rtype
, cp
);
1486 /* Check to see if mx host exists */
1487 add_domain(cp
, domain
);
1489 if (*name
== *cp
&& strcmp(name
, cp
) == 0)
1490 flags
|= FLG_SELFMX
;
1491 errors
+= updateitem(cp
, 0, REC_REF
, 0, flags
);
1492 } else if (rtype
[0] == 'c' && strcmp(rtype
, "cname") == 0) {
1493 /* Handle "cname" record */
1494 add_domain(name
, domain
);
1495 errors
+= updateitem(name
, 0, REC_CNAME
, 0, 0);
1496 if (checkdots(cp
)) {
1498 fprintf(stderr
, dotfmt
,
1499 prog
, cwd
, file
, n
, rtype
, cp
);
1502 /* Make sure cname points somewhere */
1503 if (strcmp(cp
, "@") == 0)
1504 (void)strcpy(cp
, zone
);
1505 add_domain(cp
, domain
);
1506 errors
+= updateitem(cp
, 0, REC_REF
, 0, 0);
1507 } else if (CHECK3(rtype
, 's', 'r', 'v')) {
1508 /* Handle "srv" record */
1509 add_domain(name
, domain
);
1510 errors
+= updateitem(name
, 0, REC_SRV
, 0, 0);
1513 /* Skip over three values */
1514 for (i
= 0; i
< 3; ++i
) {
1515 if (!isdigit(*cp
)) {
1517 fprintf(stderr
, "%s: %s/%s:%d"
1518 " bad \"srv\" value: %s\n",
1519 prog
, cwd
, file
, n
, cp
);
1522 /* Skip over value */
1524 while (isdigit(*cp
))
1526 while (isspace(*cp
))
1530 /* Check to see if mx host exists */
1531 add_domain(cp
, domain
);
1532 errors
+= updateitem(cp
, 0, REC_REF
, 0, 0);
1533 } else if (CHECK3(rtype
, 't', 'x', 't')) {
1534 /* Handle "txt" record */
1535 add_domain(name
, domain
);
1536 errors
+= updateitem(name
, 0, REC_TXT
, 0, 0);
1538 cp
= parsequoted(cp
);
1542 "%s: %s/%s:%d \"txt\" missing quote: %s\n",
1543 prog
, cwd
, file
, n
, cp2
);
1546 while (isspace(*cp
))
1551 "%s: %s/%s:%d \"txt\" garbage after text: %s\n",
1552 prog
, cwd
, file
, n
, cp2
);
1555 } else if (CHECK2(rtype
, 'n', 's')) {
1556 /* Handle "ns" record */
1557 errors
+= updateitem(zone
, 0, REC_NS
, 0, 0);
1558 if (strcmp(cp
, "@") == 0)
1559 (void)strcpy(cp
, zone
);
1560 if (checkdots(cp
)) {
1562 fprintf(stderr
, dotfmt
,
1563 prog
, cwd
, file
, n
, rtype
, cp
);
1565 add_domain(cp
, domain
);
1566 errors
+= updateitem(cp
, 0, REC_REF
, 0, 0);
1567 } else if (CHECK2(rtype
, 'r', 'p')) {
1568 /* Handle "rp" record */
1569 add_domain(name
, domain
);
1570 errors
+= updateitem(name
, 0, REC_RP
, 0, 0);
1573 /* Step over mailbox name */
1574 /* XXX could add_domain() and check further */
1575 while (!isspace(*cp
) && *cp
!= '\0')
1580 "%s: %s/%s:%d \"rp\" missing text name: %s\n",
1581 prog
, cwd
, file
, n
, cp2
);
1587 /* Step over text name */
1588 while (!isspace(*cp
) && *cp
!= '\0')
1594 "%s: %s/%s:%d \"rp\" garbage after text name: %s\n",
1595 prog
, cwd
, file
, n
, cp2
);
1599 /* Make sure text name points somewhere (if not ".") */
1600 if (!CHECKDOT(cp3
)) {
1601 add_domain(cp3
, domain
);
1602 errors
+= updateitem(cp3
, 0, REC_REF
, 0, 0);
1604 } else if (rtype
[0] == 'a' && strcmp(rtype
, "allowdupa") == 0) {
1605 /* Handle "allow duplicate a" record */
1606 add_domain(name
, domain
);
1607 addr
= htonl(inet_addr(cp
));
1608 if ((int)addr
== -1) {
1610 cp2
= cp
+ strlen(cp
) - 1;
1611 if (cp2
>= cp
&& *cp2
== '\n')
1614 "%s: %s/%s:%d bad \"allowdupa\" record ip addr \"%s\"\n",
1615 prog
, cwd
, file
, n
, cp
);
1618 errors
+= updateitem(name
, addr
, 0, 0, FLG_ALLOWDUPA
);
1620 /* Unknown record type */
1623 "%s: %s/%s:%d unknown record type \"%s\"\n",
1624 prog
, cwd
, file
, n
, rtype
);
1625 add_domain(name
, domain
);
1626 errors
+= updateitem(name
, 0, REC_UNKNOWN
, 0, 0);
1628 (void)strcpy(lastname
, name
);
1634 /* Records we use to detect duplicates */
1635 static struct duprec
{
1640 { REC_HINFO
, "hinfo" },
1645 checkdups(register struct item
*ip
, register int records
)
1647 register struct duprec
*dp
;
1649 records
&= (ip
->records
& MASK_TEST_DUP
);
1652 for (dp
= duprec
; dp
->name
!= NULL
; ++dp
)
1653 if ((records
& dp
->record
) != 0) {
1655 fprintf(stderr
, "%s: multiple \"%s\" records for %s\n",
1656 prog
, dp
->name
, ip
->host
);
1657 records
&= ~dp
->record
;
1660 fprintf(stderr
, "%s: checkdups: records not zero (%d)\n",
1665 updateitem(register const char *host
, register u_int32_t addr
,
1666 register int records
, register u_int ttl
, register int flags
)
1668 register const char *ccp
;
1669 register int n
, errs
;
1671 register struct item
*ip
;
1677 ITEMHASH(host
, i
, ccp
);
1678 ip
= &items
[i
& (ITEMSIZE
- 1)];
1679 while (n
< ITEMSIZE
&& ip
->host
) {
1680 if ((addr
== 0 || addr
== ip
->addr
|| ip
->addr
== 0) &&
1681 *host
== *ip
->host
&& strcmp(host
, ip
->host
) == 0) {
1685 if ((records
& MASK_TEST_DUP
) != 0)
1686 checkdups(ip
, records
);
1687 ip
->records
|= records
;
1688 /* Only check differing ttl's for A and MX records */
1691 else if (ttl
!= 0 && ip
->ttl
!= ttl
) {
1693 "%s: differing ttls for %s (%u != %u)\n",
1694 prog
, ip
->host
, ttl
, ip
->ttl
);
1698 /* Not done if we wildcard matched the name */
1704 if (ip
>= &items
[ITEMSIZE
])
1708 if (n
>= ITEMSIZE
) {
1709 fprintf(stderr
, "%s: out of item slots (max %d)\n",
1714 /* Done if we were wildcarding the name (and found entries for it) */
1715 if (addr
== 0 && foundsome
)
1718 /* Didn't find it, make new entry */
1721 fprintf(stderr
, "%s: reusing bucket!\n", prog
);
1725 ip
->host
= savestr(host
);
1726 if ((records
& MASK_TEST_DUP
) != 0)
1727 checkdups(ip
, records
);
1728 ip
->records
|= records
;
1735 static const char *microlist
[] = {
1744 rfc1034host(register const char *host
, register int recs
)
1746 register const char *cp
, **p
;
1747 register int underok
;
1750 for (p
= microlist
; *p
!= NULL
;++p
)
1751 if ((cp
= strstr(host
, *p
)) != NULL
&&
1754 cp
[strlen(*p
)] == '.') {
1760 if (!(isalpha(*cp
) || isdigit(*cp
) || (*cp
== '_' && underok
))) {
1762 "%s: illegal hostname \"%s\" (starts with non-alpha/numeric)\n",
1766 for (++cp
; *cp
!= '.' && *cp
!= '\0'; ++cp
)
1767 if (!(isalpha(*cp
) || isdigit(*cp
) || *cp
== '-' ||
1768 (*cp
== '/' && (recs
& REC_SOA
) != 0))) {
1770 "%s: illegal hostname \"%s\" ('%c' illegal character)\n",
1774 if (--cp
>= host
&& *cp
== '-') {
1775 fprintf(stderr
, "%s: illegal hostname \"%s\" (ends with '-')\n",
1785 register int n
, records
, flags
;
1786 register struct item
*ip
, *lastaip
, **ipp
, **itemlist
;
1787 register u_int32_t addr
, lastaddr
, mask
;
1789 itemlist
= (struct item
**)calloc(itemcnt
, sizeof(*ipp
));
1790 if (itemlist
== NULL
) {
1791 fprintf(stderr
, "%s: nslint: calloc: %s\n",
1792 prog
, strerror(errno
));
1796 for (n
= 0, ip
= items
; n
< ITEMSIZE
; ++n
, ++ip
) {
1797 if (ip
->host
== NULL
)
1800 /* Save entries with addresses for later check */
1807 printf("%s\t%s\t0x%x\t0x%x\n",
1808 ip
->host
, intoa(ip
->addr
), ip
->records
, ip
->flags
);
1811 /* Check for illegal hostnames (rfc1034) */
1812 if (rfc1034host(ip
->host
, ip
->records
))
1815 /* Check for missing ptr records (ok if also an ns record) */
1816 records
= ip
->records
& MASK_CHECK_REC
;
1817 if ((ip
->records
& MASK_TEST_REC
) != 0)
1818 records
|= REC_OTHER
;
1821 case REC_A
| REC_OTHER
| REC_PTR
| REC_REF
:
1822 case REC_A
| REC_OTHER
| REC_PTR
:
1823 case REC_A
| REC_PTR
| REC_REF
:
1824 case REC_A
| REC_PTR
:
1826 /* These are O.K. */
1829 case REC_CNAME
| REC_REF
:
1831 fprintf(stderr
, "%s: \"cname\" referenced by other"
1832 " \"cname\" or \"mx\": %s\n", prog
, ip
->host
);
1835 case REC_OTHER
| REC_REF
:
1838 * This is only an error if there is an address
1839 * associated with the hostname; this means
1840 * there was a wks entry with bogus address.
1841 * Otherwise, we have an mx or hinfo.
1843 if (ip
->addr
!= 0) {
1846 "%s: \"wks\" without \"a\" and \"ptr\": %s -> %s\n",
1847 prog
, ip
->host
, intoa(ip
->addr
));
1854 "%s: name referenced without other records: %s\n",
1858 case REC_A
| REC_OTHER
| REC_REF
:
1859 case REC_A
| REC_OTHER
:
1860 case REC_A
| REC_REF
:
1863 fprintf(stderr
, "%s: missing \"ptr\": %s -> %s\n",
1864 prog
, ip
->host
, intoa(ip
->addr
));
1867 case REC_OTHER
| REC_PTR
| REC_REF
:
1868 case REC_OTHER
| REC_PTR
:
1869 case REC_PTR
| REC_REF
:
1872 fprintf(stderr
, "%s: missing \"a\": %s -> %s\n",
1873 prog
, ip
->host
, intoa(ip
->addr
));
1876 case REC_A
| REC_CNAME
| REC_OTHER
| REC_PTR
| REC_REF
:
1877 case REC_A
| REC_CNAME
| REC_OTHER
| REC_PTR
:
1878 case REC_A
| REC_CNAME
| REC_OTHER
| REC_REF
:
1879 case REC_A
| REC_CNAME
| REC_OTHER
:
1880 case REC_A
| REC_CNAME
| REC_PTR
| REC_REF
:
1881 case REC_A
| REC_CNAME
| REC_PTR
:
1882 case REC_A
| REC_CNAME
| REC_REF
:
1883 case REC_A
| REC_CNAME
:
1884 case REC_CNAME
| REC_OTHER
| REC_PTR
| REC_REF
:
1885 case REC_CNAME
| REC_OTHER
| REC_PTR
:
1886 case REC_CNAME
| REC_OTHER
| REC_REF
:
1887 case REC_CNAME
| REC_OTHER
:
1888 case REC_CNAME
| REC_PTR
| REC_REF
:
1889 case REC_CNAME
| REC_PTR
:
1891 fprintf(stderr
, "%s: \"cname\" %s has other records\n",
1896 /* Second level test */
1897 if ((ip
->records
& ~(REC_NS
| REC_TXT
)) == 0)
1899 /* Fall through... */
1904 "%s: records == 0x%x: can't happen (%s 0x%x)\n",
1905 prog
, records
, ip
->host
, ip
->records
);
1909 /* Check for smtp problems */
1910 flags
= ip
->flags
& MASK_TEST_SMTP
;
1912 if ((flags
& FLG_SELFMX
) != 0 && (ip
->records
& REC_A
) == 0) {
1915 "%s: self \"mx\" for %s missing \"a\" record\n",
1922 case FLG_SELFMX
| FLG_SMTPWKS
:
1923 /* These are O.K. */
1927 if ((ip
->records
& REC_WKS
) != 0) {
1930 "%s: smtp/tcp missing from \"wks\": %s\n",
1938 "%s: saw smtp/tcp without self \"mx\": %s\n",
1945 "%s: flags == 0x%x: can't happen (%s)\n",
1946 prog
, flags
, ip
->host
);
1949 /* Check for chained MX records */
1950 if ((ip
->flags
& (FLG_SELFMX
| FLG_MXREF
)) == FLG_MXREF
&&
1951 (ip
->records
& REC_MX
) != 0) {
1953 fprintf(stderr
, "%s: \"mx\" referenced by other"
1954 " \"mx\" record: %s\n", prog
, ip
->host
);
1958 /* Check for doubly booked addresses */
1960 qsort(itemlist
, n
, sizeof(itemlist
[0]), cmpaddr
);
1963 for (ipp
= itemlist
; n
> 0; ++ipp
, --n
) {
1964 addr
= (*ipp
)->addr
;
1965 if (lastaddr
== addr
&&
1966 ((*ipp
)->flags
& FLG_ALLOWDUPA
) == 0 &&
1967 (ip
->flags
& FLG_ALLOWDUPA
) == 0) {
1969 fprintf(stderr
, "%s: %s in use by %s and %s\n",
1970 prog
, intoa(addr
), (*ipp
)->host
, ip
->host
);
1976 /* Check for hosts with multiple addresses on the same subnet */
1978 qsort(itemlist
, n
, sizeof(itemlist
[0]), cmphost
);
1979 if (netlistcnt
> 0) {
1982 for (ipp
= itemlist
; n
> 0; ++ipp
, --n
) {
1984 if ((ip
->records
& REC_A
) == 0 ||
1985 (ip
->flags
& FLG_ALLOWDUPA
) != 0)
1987 if (lastaip
!= NULL
&&
1988 strcasecmp(ip
->host
, lastaip
->host
) == 0) {
1989 mask
= findmask(ip
->addr
);
1993 "%s: can't find mask for %s (%s)\n",
1994 prog
, ip
->host
, intoa(ip
->addr
));
1995 } else if ((lastaip
->addr
& mask
) ==
1996 (ip
->addr
& mask
) ) {
1999 "%s: multiple \"a\" records for %s on subnet %s",
2001 intoa(ip
->addr
& mask
));
2002 fprintf(stderr
, "\n\t(%s",
2003 intoa(lastaip
->addr
));
2004 fprintf(stderr
, " and %s)\n",
2013 printf("%s: %d/%d items used, %d error%s\n", prog
, itemcnt
,
2014 ITEMSIZE
, errors
, errors
== 1 ? "" : "s");
2015 return (errors
!= 0);
2018 /* Similar to inet_ntoa() */
2020 intoa(u_int32_t addr
)
2023 register u_int byte
;
2025 static char buf
[sizeof(".xxx.xxx.xxx.xxx")];
2027 cp
= &buf
[sizeof buf
];
2033 *--cp
= byte
% 10 + '0';
2036 *--cp
= byte
% 10 + '0';
2049 parseinaddr(register const char *cp
, register u_int32_t
*netp
,
2050 register u_int32_t
*maskp
)
2052 register int i
, bits
;
2053 register u_int32_t o
, net
, mask
;
2062 o
= o
* 10 + (*cp
++ - '0');
2063 } while (isdigit(*cp
));
2066 /* Check for classless delegation mask width */
2071 o
= o
* 10 + (*cp
++ - '0');
2072 } while (isdigit(*cp
));
2074 if (bits
<= 0 || bits
> 32)
2078 if (*cp
== '.' && isdigit(cp
[1])) {
2082 o
= o
* 10 + (*cp
++ - '0');
2083 } while (isdigit(*cp
));
2084 net
= (net
>> 8) | (o
<< 24);
2086 if (*cp
== '.' && isdigit(cp
[1])) {
2090 o
= o
* 10 + (*cp
++ - '0');
2091 } while (isdigit(*cp
));
2092 net
= (net
>> 8) | (o
<< 24);
2094 if (*cp
== '.' && isdigit(cp
[1])) {
2098 o
= o
* 10 + (*cp
++ - '0');
2099 } while (isdigit(*cp
));
2100 net
= (net
>> 8) | (o
<< 24);
2105 if (strcasecmp(cp
, inaddr
) != 0)
2108 /* Classless delegation */
2109 /* XXX check that calculated mask isn't smaller than octet mask? */
2111 for (mask
= 0, i
= 31; bits
> 0; --i
, --bits
)
2120 parseptr(register const char *cp
, u_int32_t net
, u_int32_t mask
,
2121 register char **errstrp
)
2123 register u_int32_t o
, addr
;
2128 while (isdigit(*cp
) && shift
< 32) {
2131 o
= o
* 10 + (*cp
++ - '0');
2132 } while (isdigit(*cp
));
2138 *errstrp
= "missing dot";
2145 *errstrp
= "more than 4 octets";
2149 if (shift
== 32 && strcasecmp(cp
, inaddr
+ 1) == 0)
2154 *errstrp
= "trailing junk";
2159 if ((~mask
& net
) != 0) {
2160 *errstrp
= "too many octets for net";
2164 return (net
| addr
);
2168 checkwks(register FILE *f
, register char *proto
, register int *smtpp
,
2169 register char **errstrp
)
2171 register int n
, sawparen
;
2172 register char *cp
, *serv
, **p
;
2173 static char errstr
[132];
2177 if (!protoserv_init
) {
2185 /* Terminate protocol */
2187 while (!isspace(*cp
) && *cp
!= '\0')
2198 while (isspace(*cp
))
2205 if (fgets(buf
, sizeof(buf
), f
) == NULL
) {
2206 *errstrp
= "mismatched parens";
2211 while (isspace(*cp
))
2214 /* Find end of service, converting to lowercase */
2215 for (serv
= cp
; !isspace(*cp
) && *cp
!= '\0'; ++cp
)
2220 if (sawparen
&& *cp
== ')') {
2221 /* XXX should check for trailing junk */
2225 (void)sprintf(psbuf
, "%s/%s", serv
, proto
);
2227 if (*serv
== 's' && strcmp(psbuf
, "tcp/smtp") == 0)
2230 for (p
= protoserv
; *p
!= NULL
; ++p
)
2231 if (*psbuf
== **p
&& strcmp(psbuf
, *p
) == 0) {
2235 sprintf(errstr
, "%s unknown", psbuf
);
2245 checkserv(register const char *serv
, register char **p
)
2247 for (; *p
!= NULL
; ++p
)
2248 if (*serv
== **p
&& strcmp(serv
, *p
) == 0)
2257 register struct servent
*sp
;
2260 protoserv_len
= 256;
2261 protoserv
= (char **)malloc(protoserv_len
* sizeof(*protoserv
));
2262 if (protoserv
== NULL
) {
2263 fprintf(stderr
, "%s: nslint: malloc: %s\n",
2264 prog
, strerror(errno
));
2268 while ((sp
= getservent()) != NULL
) {
2269 (void)sprintf(psbuf
, "%s/%s", sp
->s_name
, sp
->s_proto
);
2271 /* Convert to lowercase */
2272 for (cp
= psbuf
; *cp
!= '\0'; ++cp
)
2276 if (protoserv_last
+ 1 >= protoserv_len
) {
2277 protoserv_len
<<= 1;
2278 protoserv
= realloc(protoserv
,
2279 protoserv_len
* sizeof(*protoserv
));
2280 if (protoserv
== NULL
) {
2281 fprintf(stderr
, "%s: nslint: realloc: %s\n",
2282 prog
, strerror(errno
));
2286 protoserv
[protoserv_last
] = savestr(psbuf
);
2289 protoserv
[protoserv_last
] = NULL
;
2293 * Returns true if name contains a dot but not a trailing dot.
2294 * Special case: allow a single dot if the second part is not one
2295 * of the 3 or 4 letter top level domains or is any 2 letter TLD
2298 checkdots(register const char *name
)
2300 register const char *cp
, *cp2
;
2302 if ((cp
= strchr(name
, '.')) == NULL
)
2304 cp2
= name
+ strlen(name
) - 1;
2305 if (cp2
>= name
&& *cp2
== '.')
2308 /* Return true of more than one dot*/
2310 if (strchr(cp
, '.') != NULL
)
2313 if (strlen(cp
) == 2 ||
2314 strcasecmp(cp
, "gov") == 0 ||
2315 strcasecmp(cp
, "edu") == 0 ||
2316 strcasecmp(cp
, "com") == 0 ||
2317 strcasecmp(cp
, "net") == 0 ||
2318 strcasecmp(cp
, "org") == 0 ||
2319 strcasecmp(cp
, "mil") == 0 ||
2320 strcasecmp(cp
, "int") == 0 ||
2321 strcasecmp(cp
, "nato") == 0 ||
2322 strcasecmp(cp
, "arpa") == 0)
2328 cmpaddr(register const void *ip1
, register const void *ip2
)
2330 register u_int32_t a1
, a2
;
2332 a1
= (*(struct item
**)ip1
)->addr
;
2333 a2
= (*(struct item
**)ip2
)->addr
;
2344 cmphost(register const void *ip1
, register const void *ip2
)
2346 register const char *s1
, *s2
;
2348 s1
= (*(struct item
**)ip1
)->host
;
2349 s2
= (*(struct item
**)ip2
)->host
;
2351 return (strcasecmp(s1
, s2
));
2354 /* Returns a pointer after the next token or quoted string, else NULL */
2356 parsequoted(register char *cp
)
2361 while (*cp
!= '"' && *cp
!= '\0')
2367 while (!isspace(*cp
) && *cp
!= '\0')
2376 extern char version
[];
2378 fprintf(stderr
, "Version %s\n", version
);
2379 fprintf(stderr
, "usage: %s [-d] [-b named.boot] [-B nslint.boot]\n",
2381 fprintf(stderr
, " %s [-d] [-c named.conf] [-C nslint.conf]\n",