Home | History | Annotate | Line # | Download | only in tcpdchk
tcpdchk.c revision 1.12
      1  1.12      matt /*	$NetBSD: tcpdchk.c,v 1.12 2012/03/21 10:11:34 matt Exp $	*/
      2   1.2  christos 
      3   1.1       cjs  /*
      4   1.1       cjs   * tcpdchk - examine all tcpd access control rules and inetd.conf entries
      5   1.1       cjs   *
      6   1.1       cjs   * Usage: tcpdchk [-a] [-d] [-i inet_conf] [-v]
      7   1.1       cjs   *
      8   1.1       cjs   * -a: complain about implicit "allow" at end of rule.
      9   1.1       cjs   *
     10   1.1       cjs   * -d: rules in current directory.
     11   1.1       cjs   *
     12   1.1       cjs   * -i: location of inetd.conf file.
     13   1.1       cjs   *
     14   1.1       cjs   * -v: show all rules.
     15   1.1       cjs   *
     16   1.1       cjs   * Author: Wietse Venema, Eindhoven University of Technology, The Netherlands.
     17   1.1       cjs   */
     18   1.1       cjs 
     19   1.2  christos #include <sys/cdefs.h>
     20   1.1       cjs #ifndef lint
     21   1.2  christos #if 0
     22   1.8    itojun static char sccsid[] = "@(#) tcpdchk.c 1.8 97/02/12 02:13:25";
     23   1.2  christos #else
     24  1.12      matt __RCSID("$NetBSD: tcpdchk.c,v 1.12 2012/03/21 10:11:34 matt Exp $");
     25   1.2  christos #endif
     26   1.1       cjs #endif
     27   1.1       cjs 
     28   1.1       cjs /* System libraries. */
     29   1.1       cjs 
     30   1.1       cjs #include <sys/types.h>
     31   1.1       cjs #include <sys/stat.h>
     32   1.1       cjs #include <netinet/in.h>
     33   1.1       cjs #include <arpa/inet.h>
     34   1.1       cjs #include <stdio.h>
     35   1.1       cjs #include <syslog.h>
     36   1.1       cjs #include <setjmp.h>
     37   1.1       cjs #include <errno.h>
     38   1.1       cjs #include <netdb.h>
     39   1.1       cjs #include <string.h>
     40   1.2  christos #include <stdlib.h>
     41   1.2  christos #include <unistd.h>
     42   1.1       cjs 
     43   1.1       cjs #ifndef INADDR_NONE
     44   1.1       cjs #define INADDR_NONE     (-1)		/* XXX should be 0xffffffff */
     45   1.1       cjs #endif
     46   1.1       cjs 
     47   1.1       cjs #ifndef S_ISDIR
     48   1.1       cjs #define S_ISDIR(m)	(((m) & S_IFMT) == S_IFDIR)
     49   1.1       cjs #endif
     50   1.1       cjs 
     51   1.1       cjs /* Application-specific. */
     52   1.1       cjs 
     53   1.1       cjs #include "tcpd.h"
     54   1.1       cjs #include "inetcf.h"
     55   1.1       cjs #include "scaffold.h"
     56   1.1       cjs 
     57   1.5  christos #ifdef NO_NETGRENT
     58   1.5  christos 	/* SCO has no *netgrent() support */
     59   1.5  christos #else
     60   1.5  christos # ifdef NETGROUP
     61   1.5  christos #  include <netgroup.h>
     62   1.5  christos # endif
     63   1.5  christos #endif
     64   1.5  christos 
     65   1.1       cjs  /*
     66   1.1       cjs   * Stolen from hosts_access.c...
     67   1.1       cjs   */
     68  1.12      matt static const char sep[] = ", \t\n";
     69   1.1       cjs 
     70   1.1       cjs #define	BUFLEN 2048
     71   1.1       cjs 
     72   1.1       cjs int     resident = 0;
     73   1.1       cjs int     hosts_access_verbose = 0;
     74  1.12      matt const char *hosts_allow_table = HOSTS_ALLOW;
     75  1.12      matt const char *hosts_deny_table = HOSTS_DENY;
     76   1.1       cjs extern jmp_buf tcpd_buf;
     77   1.1       cjs 
     78   1.1       cjs  /*
     79   1.1       cjs   * Local stuff.
     80   1.1       cjs   */
     81  1.12      matt static void usage(void);
     82  1.12      matt static void parse_table(const char *, struct request_info *);
     83  1.12      matt static void print_list(char *, char *);
     84  1.12      matt static void check_daemon_list(char *);
     85  1.12      matt static void check_client_list(char *);
     86  1.12      matt static void check_daemon(char *);
     87  1.12      matt static void check_user(char *);
     88  1.10    itojun #ifdef INET6
     89  1.12      matt static int check_inet_addr(char *);
     90  1.10    itojun #endif
     91  1.12      matt static int check_host(char *);
     92  1.12      matt static int reserved_name(char *);
     93   1.2  christos 
     94  1.12      matt int main(int, char **);
     95   1.1       cjs 
     96   1.1       cjs #define PERMIT	1
     97   1.1       cjs #define DENY	0
     98   1.1       cjs 
     99   1.1       cjs #define YES	1
    100   1.1       cjs #define	NO	0
    101   1.1       cjs 
    102   1.1       cjs static int defl_verdict;
    103   1.1       cjs static char *myname;
    104   1.1       cjs static int allow_check;
    105   1.1       cjs static char *inetcf;
    106   1.1       cjs 
    107  1.12      matt int
    108  1.12      matt main(int argc, char **argv)
    109   1.1       cjs {
    110   1.1       cjs     struct request_info request;
    111   1.1       cjs     struct stat st;
    112   1.1       cjs     int     c;
    113   1.1       cjs 
    114   1.1       cjs     myname = argv[0];
    115   1.1       cjs 
    116   1.1       cjs     /*
    117   1.1       cjs      * Parse the JCL.
    118   1.1       cjs      */
    119   1.4     lukem     while ((c = getopt(argc, argv, "adi:v")) != -1) {
    120   1.1       cjs 	switch (c) {
    121   1.1       cjs 	case 'a':
    122   1.1       cjs 	    allow_check = 1;
    123   1.1       cjs 	    break;
    124   1.1       cjs 	case 'd':
    125   1.1       cjs 	    hosts_allow_table = "hosts.allow";
    126   1.1       cjs 	    hosts_deny_table = "hosts.deny";
    127   1.1       cjs 	    break;
    128   1.1       cjs 	case 'i':
    129   1.1       cjs 	    inetcf = optarg;
    130   1.1       cjs 	    break;
    131   1.1       cjs 	case 'v':
    132   1.1       cjs 	    hosts_access_verbose++;
    133   1.1       cjs 	    break;
    134   1.1       cjs 	default:
    135   1.1       cjs 	    usage();
    136   1.1       cjs 	    /* NOTREACHED */
    137   1.1       cjs 	}
    138   1.1       cjs     }
    139   1.1       cjs     if (argc != optind)
    140   1.1       cjs 	usage();
    141   1.1       cjs 
    142   1.1       cjs     /*
    143   1.1       cjs      * When confusion really strikes...
    144   1.1       cjs      */
    145   1.1       cjs     if (check_path(REAL_DAEMON_DIR, &st) < 0) {
    146   1.1       cjs 	tcpd_warn("REAL_DAEMON_DIR %s: %m", REAL_DAEMON_DIR);
    147   1.1       cjs     } else if (!S_ISDIR(st.st_mode)) {
    148   1.1       cjs 	tcpd_warn("REAL_DAEMON_DIR %s is not a directory", REAL_DAEMON_DIR);
    149   1.1       cjs     }
    150   1.1       cjs 
    151   1.1       cjs     /*
    152   1.1       cjs      * Process the inet configuration file (or its moral equivalent). This
    153   1.1       cjs      * information is used later to find references in hosts.allow/deny to
    154   1.1       cjs      * unwrapped services, and other possible problems.
    155   1.1       cjs      */
    156   1.1       cjs     inetcf = inet_cfg(inetcf);
    157   1.1       cjs     if (hosts_access_verbose)
    158   1.1       cjs 	printf("Using network configuration file: %s\n", inetcf);
    159   1.1       cjs 
    160   1.1       cjs     /*
    161   1.1       cjs      * These are not run from inetd but may have built-in access control.
    162   1.1       cjs      */
    163   1.1       cjs     inet_set("portmap", WR_NOT);
    164   1.1       cjs     inet_set("rpcbind", WR_NOT);
    165   1.1       cjs 
    166   1.1       cjs     /*
    167   1.1       cjs      * Check accessibility of access control files.
    168   1.1       cjs      */
    169   1.1       cjs     (void) check_path(hosts_allow_table, &st);
    170   1.1       cjs     (void) check_path(hosts_deny_table, &st);
    171   1.1       cjs 
    172   1.1       cjs     /*
    173   1.1       cjs      * Fake up an arbitrary service request.
    174   1.1       cjs      */
    175   1.1       cjs     request_init(&request,
    176   1.1       cjs 		 RQ_DAEMON, "daemon_name",
    177   1.1       cjs 		 RQ_SERVER_NAME, "server_hostname",
    178   1.1       cjs 		 RQ_SERVER_ADDR, "server_addr",
    179   1.1       cjs 		 RQ_USER, "user_name",
    180   1.1       cjs 		 RQ_CLIENT_NAME, "client_hostname",
    181   1.1       cjs 		 RQ_CLIENT_ADDR, "client_addr",
    182   1.1       cjs 		 RQ_FILE, 1,
    183   1.1       cjs 		 0);
    184   1.1       cjs 
    185   1.1       cjs     /*
    186   1.1       cjs      * Examine all access-control rules.
    187   1.1       cjs      */
    188   1.1       cjs     defl_verdict = PERMIT;
    189   1.1       cjs     parse_table(hosts_allow_table, &request);
    190   1.1       cjs     defl_verdict = DENY;
    191   1.1       cjs     parse_table(hosts_deny_table, &request);
    192   1.1       cjs     return (0);
    193   1.1       cjs }
    194   1.1       cjs 
    195   1.1       cjs /* usage - explain */
    196   1.1       cjs 
    197  1.12      matt static void
    198  1.12      matt usage(void)
    199   1.1       cjs {
    200   1.1       cjs     fprintf(stderr, "usage: %s [-a] [-d] [-i inet_conf] [-v]\n", myname);
    201   1.1       cjs     fprintf(stderr, "	-a: report rules with implicit \"ALLOW\" at end\n");
    202   1.1       cjs     fprintf(stderr, "	-d: use allow/deny files in current directory\n");
    203   1.1       cjs     fprintf(stderr, "	-i: location of inetd.conf file\n");
    204   1.1       cjs     fprintf(stderr, "	-v: list all rules\n");
    205   1.1       cjs     exit(1);
    206   1.1       cjs }
    207   1.1       cjs 
    208   1.1       cjs /* parse_table - like table_match(), but examines _all_ entries */
    209   1.1       cjs 
    210  1.12      matt static void
    211  1.12      matt parse_table(const char *table, struct request_info *request)
    212   1.1       cjs {
    213   1.1       cjs     FILE   *fp;
    214  1.12      matt     volatile int     real_verdict;
    215   1.1       cjs     char    sv_list[BUFLEN];		/* becomes list of daemons */
    216   1.1       cjs     char   *cl_list;			/* becomes list of requests */
    217   1.1       cjs     char   *sh_cmd;			/* becomes optional shell command */
    218   1.1       cjs     int     verdict;
    219  1.12      matt     volatile struct tcpd_context saved_context;
    220   1.1       cjs 
    221   1.1       cjs     saved_context = tcpd_context;		/* stupid compilers */
    222   1.1       cjs 
    223   1.2  christos     if ((fp = fopen(table, "r")) != NULL) {
    224   1.1       cjs 	tcpd_context.file = table;
    225   1.1       cjs 	tcpd_context.line = 0;
    226   1.1       cjs 	while (xgets(sv_list, sizeof(sv_list), fp)) {
    227   1.1       cjs 	    if (sv_list[strlen(sv_list) - 1] != '\n') {
    228   1.1       cjs 		tcpd_warn("missing newline or line too long");
    229   1.1       cjs 		continue;
    230   1.1       cjs 	    }
    231   1.1       cjs 	    if (sv_list[0] == '#' || sv_list[strspn(sv_list, " \t\r\n")] == 0)
    232   1.1       cjs 		continue;
    233   1.1       cjs 	    if ((cl_list = split_at(sv_list, ':')) == 0) {
    234   1.1       cjs 		tcpd_warn("missing \":\" separator");
    235   1.1       cjs 		continue;
    236   1.1       cjs 	    }
    237   1.1       cjs 	    sh_cmd = split_at(cl_list, ':');
    238   1.1       cjs 
    239   1.1       cjs 	    if (hosts_access_verbose)
    240   1.1       cjs 		printf("\n>>> Rule %s line %d:\n",
    241   1.1       cjs 		       tcpd_context.file, tcpd_context.line);
    242   1.1       cjs 
    243   1.1       cjs 	    if (hosts_access_verbose)
    244   1.1       cjs 		print_list("daemons:  ", sv_list);
    245   1.1       cjs 	    check_daemon_list(sv_list);
    246   1.1       cjs 
    247   1.1       cjs 	    if (hosts_access_verbose)
    248   1.1       cjs 		print_list("clients:  ", cl_list);
    249   1.1       cjs 	    check_client_list(cl_list);
    250   1.1       cjs 
    251   1.1       cjs #ifdef PROCESS_OPTIONS
    252   1.1       cjs 	    real_verdict = defl_verdict;
    253   1.1       cjs 	    if (sh_cmd) {
    254   1.8    itojun 		verdict = setjmp(tcpd_buf);
    255   1.8    itojun 		if (verdict != 0) {
    256   1.1       cjs 		    real_verdict = (verdict == AC_PERMIT);
    257   1.1       cjs 		} else {
    258   1.1       cjs 		    dry_run = 1;
    259   1.1       cjs 		    process_options(sh_cmd, request);
    260   1.1       cjs 		    if (dry_run == 1 && real_verdict && allow_check)
    261   1.1       cjs 			tcpd_warn("implicit \"allow\" at end of rule");
    262   1.1       cjs 		}
    263   1.1       cjs 	    } else if (defl_verdict && allow_check) {
    264   1.1       cjs 		tcpd_warn("implicit \"allow\" at end of rule");
    265   1.1       cjs 	    }
    266   1.1       cjs 	    if (hosts_access_verbose)
    267   1.1       cjs 		printf("access:   %s\n", real_verdict ? "granted" : "denied");
    268   1.1       cjs #else
    269   1.1       cjs 	    if (sh_cmd)
    270   1.1       cjs 		shell_cmd(percent_x(buf, sizeof(buf), sh_cmd, request));
    271   1.1       cjs 	    if (hosts_access_verbose)
    272   1.1       cjs 		printf("access:   %s\n", defl_verdict ? "granted" : "denied");
    273   1.1       cjs #endif
    274   1.1       cjs 	}
    275   1.1       cjs 	(void) fclose(fp);
    276   1.1       cjs     } else if (errno != ENOENT) {
    277   1.1       cjs 	tcpd_warn("cannot open %s: %m", table);
    278   1.1       cjs     }
    279   1.1       cjs     tcpd_context = saved_context;
    280   1.1       cjs }
    281   1.1       cjs 
    282   1.1       cjs /* print_list - pretty-print a list */
    283   1.1       cjs 
    284   1.1       cjs static void print_list(title, list)
    285   1.1       cjs char   *title;
    286   1.1       cjs char   *list;
    287   1.1       cjs {
    288   1.1       cjs     char    buf[BUFLEN];
    289   1.1       cjs     char   *cp;
    290   1.1       cjs     char   *next;
    291   1.1       cjs 
    292   1.1       cjs     fputs(title, stdout);
    293  1.11    itojun     strlcpy(buf, list, sizeof(buf));
    294   1.1       cjs 
    295   1.1       cjs     for (cp = strtok(buf, sep); cp != 0; cp = next) {
    296   1.1       cjs 	fputs(cp, stdout);
    297   1.1       cjs 	next = strtok((char *) 0, sep);
    298   1.1       cjs 	if (next != 0)
    299   1.1       cjs 	    fputs(" ", stdout);
    300   1.1       cjs     }
    301   1.1       cjs     fputs("\n", stdout);
    302   1.1       cjs }
    303   1.1       cjs 
    304   1.1       cjs /* check_daemon_list - criticize daemon list */
    305   1.1       cjs 
    306   1.1       cjs static void check_daemon_list(list)
    307   1.1       cjs char   *list;
    308   1.1       cjs {
    309   1.1       cjs     char    buf[BUFLEN];
    310   1.1       cjs     char   *cp;
    311   1.1       cjs     char   *host;
    312   1.1       cjs     int     daemons = 0;
    313   1.1       cjs 
    314  1.11    itojun     strlcpy(buf, list, sizeof(buf));
    315   1.1       cjs 
    316   1.1       cjs     for (cp = strtok(buf, sep); cp != 0; cp = strtok((char *) 0, sep)) {
    317   1.1       cjs 	if (STR_EQ(cp, "EXCEPT")) {
    318   1.1       cjs 	    daemons = 0;
    319   1.1       cjs 	} else {
    320   1.1       cjs 	    daemons++;
    321   1.1       cjs 	    if ((host = split_at(cp + 1, '@')) != 0 && check_host(host) > 1) {
    322   1.1       cjs 		tcpd_warn("host %s has more than one address", host);
    323   1.1       cjs 		tcpd_warn("(consider using an address instead)");
    324   1.1       cjs 	    }
    325   1.1       cjs 	    check_daemon(cp);
    326   1.1       cjs 	}
    327   1.1       cjs     }
    328   1.1       cjs     if (daemons == 0)
    329   1.1       cjs 	tcpd_warn("daemon list is empty or ends in EXCEPT");
    330   1.1       cjs }
    331   1.1       cjs 
    332   1.1       cjs /* check_client_list - criticize client list */
    333   1.1       cjs 
    334   1.1       cjs static void check_client_list(list)
    335   1.1       cjs char   *list;
    336   1.1       cjs {
    337   1.1       cjs     char    buf[BUFLEN];
    338   1.1       cjs     char   *cp;
    339   1.1       cjs     char   *host;
    340   1.1       cjs     int     clients = 0;
    341   1.9    itojun #ifdef INET6
    342   1.9    itojun     int l;
    343   1.9    itojun #endif
    344   1.1       cjs 
    345  1.11    itojun     strlcpy(buf, list, sizeof(buf));
    346   1.1       cjs 
    347   1.1       cjs     for (cp = strtok(buf, sep); cp != 0; cp = strtok((char *) 0, sep)) {
    348   1.9    itojun #ifdef INET6
    349   1.9    itojun 	l = strlen(cp);
    350   1.9    itojun 	if (cp[0] == '[' && cp[l - 1] == ']') {
    351   1.9    itojun 	    cp[l - 1] = '\0';
    352   1.9    itojun 	    cp++;
    353   1.9    itojun 	}
    354   1.9    itojun #endif
    355   1.1       cjs 	if (STR_EQ(cp, "EXCEPT")) {
    356   1.1       cjs 	    clients = 0;
    357   1.1       cjs 	} else {
    358   1.1       cjs 	    clients++;
    359   1.2  christos 	    if ((host = split_at(cp + 1, '@')) != NULL) {	/* user@host */
    360   1.1       cjs 		check_user(cp);
    361   1.1       cjs 		check_host(host);
    362   1.1       cjs 	    } else {
    363   1.1       cjs 		check_host(cp);
    364   1.1       cjs 	    }
    365   1.1       cjs 	}
    366   1.1       cjs     }
    367   1.1       cjs     if (clients == 0)
    368   1.1       cjs 	tcpd_warn("client list is empty or ends in EXCEPT");
    369   1.1       cjs }
    370   1.1       cjs 
    371   1.1       cjs /* check_daemon - criticize daemon pattern */
    372   1.1       cjs 
    373   1.1       cjs static void check_daemon(pat)
    374   1.1       cjs char   *pat;
    375   1.1       cjs {
    376   1.1       cjs     if (pat[0] == '@') {
    377   1.1       cjs 	tcpd_warn("%s: daemon name begins with \"@\"", pat);
    378   1.1       cjs     } else if (pat[0] == '.') {
    379   1.1       cjs 	tcpd_warn("%s: daemon name begins with dot", pat);
    380   1.1       cjs     } else if (pat[strlen(pat) - 1] == '.') {
    381   1.1       cjs 	tcpd_warn("%s: daemon name ends in dot", pat);
    382   1.1       cjs     } else if (STR_EQ(pat, "ALL") || STR_EQ(pat, unknown)) {
    383   1.1       cjs 	 /* void */ ;
    384   1.1       cjs     } else if (STR_EQ(pat, "FAIL")) {		/* obsolete */
    385   1.1       cjs 	tcpd_warn("FAIL is no longer recognized");
    386   1.1       cjs 	tcpd_warn("(use EXCEPT or DENY instead)");
    387   1.1       cjs     } else if (reserved_name(pat)) {
    388   1.1       cjs 	tcpd_warn("%s: daemon name may be reserved word", pat);
    389   1.1       cjs     } else {
    390   1.1       cjs 	switch (inet_get(pat)) {
    391   1.1       cjs 	case WR_UNKNOWN:
    392   1.1       cjs 	    tcpd_warn("%s: no such process name in %s", pat, inetcf);
    393   1.1       cjs 	    inet_set(pat, WR_YES);		/* shut up next time */
    394   1.1       cjs 	    break;
    395   1.1       cjs 	case WR_NOT:
    396   1.1       cjs 	    tcpd_warn("%s: service possibly not wrapped", pat);
    397   1.1       cjs 	    inet_set(pat, WR_YES);
    398   1.1       cjs 	    break;
    399   1.1       cjs 	}
    400   1.1       cjs     }
    401   1.1       cjs }
    402   1.1       cjs 
    403   1.1       cjs /* check_user - criticize user pattern */
    404   1.1       cjs 
    405   1.1       cjs static void check_user(pat)
    406   1.1       cjs char   *pat;
    407   1.1       cjs {
    408   1.1       cjs     if (pat[0] == '@') {			/* @netgroup */
    409   1.1       cjs 	tcpd_warn("%s: user name begins with \"@\"", pat);
    410   1.1       cjs     } else if (pat[0] == '.') {
    411   1.1       cjs 	tcpd_warn("%s: user name begins with dot", pat);
    412   1.1       cjs     } else if (pat[strlen(pat) - 1] == '.') {
    413   1.1       cjs 	tcpd_warn("%s: user name ends in dot", pat);
    414   1.1       cjs     } else if (STR_EQ(pat, "ALL") || STR_EQ(pat, unknown)
    415   1.1       cjs 	       || STR_EQ(pat, "KNOWN")) {
    416   1.1       cjs 	 /* void */ ;
    417   1.1       cjs     } else if (STR_EQ(pat, "FAIL")) {		/* obsolete */
    418   1.1       cjs 	tcpd_warn("FAIL is no longer recognized");
    419   1.1       cjs 	tcpd_warn("(use EXCEPT or DENY instead)");
    420   1.1       cjs     } else if (reserved_name(pat)) {
    421   1.1       cjs 	tcpd_warn("%s: user name may be reserved word", pat);
    422   1.1       cjs     }
    423   1.1       cjs }
    424   1.1       cjs 
    425  1.10    itojun #ifdef INET6
    426  1.10    itojun static int check_inet_addr(pat)
    427  1.10    itojun char	*pat;
    428  1.10    itojun {
    429  1.10    itojun 	struct addrinfo *res;
    430  1.10    itojun 
    431  1.10    itojun 	res = find_inet_addr(pat, AI_NUMERICHOST);
    432  1.10    itojun 	if (res) {
    433  1.10    itojun 		freeaddrinfo(res);
    434  1.10    itojun 		return 1;
    435  1.10    itojun 	} else
    436  1.10    itojun 		return 0;
    437  1.10    itojun }
    438  1.10    itojun #endif
    439  1.10    itojun 
    440   1.1       cjs /* check_host - criticize host pattern */
    441   1.1       cjs static int check_host(pat)
    442   1.1       cjs char   *pat;
    443   1.1       cjs {
    444   1.1       cjs     char   *mask;
    445   1.1       cjs     int     addr_count = 1;
    446   1.1       cjs 
    447   1.1       cjs     if (pat[0] == '@') {			/* @netgroup */
    448   1.1       cjs #ifdef NO_NETGRENT
    449   1.1       cjs 	/* SCO has no *netgrent() support */
    450   1.1       cjs #else
    451   1.1       cjs #ifdef NETGROUP
    452   1.5  christos 	const char   *machinep;
    453   1.5  christos 	const char   *userp;
    454   1.5  christos 	const char   *domainp;
    455   1.1       cjs 
    456   1.1       cjs 	setnetgrent(pat + 1);
    457   1.1       cjs 	if (getnetgrent(&machinep, &userp, &domainp) == 0)
    458   1.1       cjs 	    tcpd_warn("%s: unknown or empty netgroup", pat + 1);
    459   1.1       cjs 	endnetgrent();
    460   1.1       cjs #else
    461   1.1       cjs 	tcpd_warn("netgroup support disabled");
    462   1.1       cjs #endif
    463   1.1       cjs #endif
    464   1.2  christos     } else if ((mask = split_at(pat, '/')) != NULL) {	/* network/netmask */
    465   1.9    itojun #ifdef INET6
    466  1.10    itojun 	char *ep;
    467   1.9    itojun #endif
    468   1.9    itojun 	if (dot_quad_addr(pat, NULL) != INADDR_NONE
    469   1.9    itojun 	    || dot_quad_addr(mask, NULL) != INADDR_NONE)
    470   1.9    itojun 	    ; /*okay*/
    471   1.9    itojun #ifdef INET6
    472  1.10    itojun 	else if (check_inet_addr(pat) && check_inet_addr(mask))
    473   1.9    itojun 	    ; /*okay*/
    474  1.10    itojun 	else if (check_inet_addr(pat) &&
    475  1.10    itojun 	    (ep = NULL, strtoul(mask, &ep, 10), ep && !*ep))
    476   1.9    itojun 	    ; /*okay*/
    477   1.9    itojun #endif
    478   1.9    itojun 	else
    479   1.1       cjs 	    tcpd_warn("%s/%s: bad net/mask pattern", pat, mask);
    480   1.1       cjs     } else if (STR_EQ(pat, "FAIL")) {		/* obsolete */
    481   1.1       cjs 	tcpd_warn("FAIL is no longer recognized");
    482   1.1       cjs 	tcpd_warn("(use EXCEPT or DENY instead)");
    483   1.1       cjs     } else if (reserved_name(pat)) {		/* other reserved */
    484   1.1       cjs 	 /* void */ ;
    485   1.1       cjs     } else if (NOT_INADDR(pat)) {		/* internet name */
    486   1.1       cjs 	if (pat[strlen(pat) - 1] == '.') {
    487   1.1       cjs 	    tcpd_warn("%s: domain or host name ends in dot", pat);
    488   1.1       cjs 	} else if (pat[0] != '.') {
    489   1.1       cjs 	    addr_count = check_dns(pat);
    490   1.1       cjs 	}
    491   1.1       cjs     } else {					/* numeric form */
    492   1.1       cjs 	if (STR_EQ(pat, "0.0.0.0") || STR_EQ(pat, "255.255.255.255")) {
    493   1.1       cjs 	    /* void */ ;
    494   1.1       cjs 	} else if (pat[0] == '.') {
    495   1.1       cjs 	    tcpd_warn("%s: network number begins with dot", pat);
    496   1.1       cjs 	} else if (pat[strlen(pat) - 1] != '.') {
    497   1.1       cjs 	    check_dns(pat);
    498   1.1       cjs 	}
    499   1.1       cjs     }
    500   1.1       cjs     return (addr_count);
    501   1.1       cjs }
    502   1.1       cjs 
    503   1.1       cjs /* reserved_name - determine if name is reserved */
    504   1.1       cjs 
    505   1.1       cjs static int reserved_name(pat)
    506   1.1       cjs char   *pat;
    507   1.1       cjs {
    508   1.1       cjs     return (STR_EQ(pat, unknown)
    509   1.1       cjs 	    || STR_EQ(pat, "KNOWN")
    510   1.1       cjs 	    || STR_EQ(pat, paranoid)
    511   1.1       cjs 	    || STR_EQ(pat, "ALL")
    512   1.1       cjs 	    || STR_EQ(pat, "LOCAL"));
    513   1.1       cjs }
    514