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