144743Smarkm /*
244743Smarkm  * This module implements a simple access control language that is based on
344743Smarkm  * host (or domain) names, NIS (host) netgroup names, IP addresses (or
444743Smarkm  * network numbers) and daemon process names. When a match is found the
544743Smarkm  * search is terminated, and depending on whether PROCESS_OPTIONS is defined,
644743Smarkm  * a list of options is executed or an optional shell command is executed.
744743Smarkm  *
844743Smarkm  * Host and user names are looked up on demand, provided that suitable endpoint
944743Smarkm  * information is available as sockaddr_in structures or TLI netbufs. As a
1044743Smarkm  * side effect, the pattern matching process may change the contents of
1144743Smarkm  * request structure fields.
1244743Smarkm  *
1344743Smarkm  * Diagnostics are reported through syslog(3).
1444743Smarkm  *
1544743Smarkm  * Compile with -DNETGROUP if your library provides support for netgroups.
1644743Smarkm  *
1744743Smarkm  * Author: Wietse Venema, Eindhoven University of Technology, The Netherlands.
1851495Ssheldonh  *
1951495Ssheldonh  * $FreeBSD$
2044743Smarkm  */
2144743Smarkm
2244743Smarkm#ifndef lint
2344743Smarkmstatic char sccsid[] = "@(#) hosts_access.c 1.21 97/02/12 02:13:22";
2444743Smarkm#endif
2544743Smarkm
2644743Smarkm/* System libraries. */
2744743Smarkm
2844743Smarkm#include <sys/types.h>
2956977Sshin#ifdef INT32_T
3056977Sshin    typedef uint32_t u_int32_t;
3156977Sshin#endif
3244743Smarkm#include <sys/param.h>
3356977Sshin#ifdef INET6
3456977Sshin#include <sys/socket.h>
3556977Sshin#endif
3644743Smarkm#include <netinet/in.h>
3744743Smarkm#include <arpa/inet.h>
3844743Smarkm#include <stdio.h>
3944743Smarkm#include <syslog.h>
4044743Smarkm#include <ctype.h>
4144743Smarkm#include <errno.h>
4244743Smarkm#include <setjmp.h>
4344743Smarkm#include <string.h>
4463158Sume#ifdef INET6
4563158Sume#include <netdb.h>
4663158Sume#endif
4744743Smarkm
4844743Smarkmextern char *fgets();
4944743Smarkmextern int errno;
5044743Smarkm
5144743Smarkm#ifndef	INADDR_NONE
5244743Smarkm#define	INADDR_NONE	(-1)		/* XXX should be 0xffffffff */
5344743Smarkm#endif
5444743Smarkm
5544743Smarkm/* Local stuff. */
5644743Smarkm
5744743Smarkm#include "tcpd.h"
5844743Smarkm
5944743Smarkm/* Error handling. */
6044743Smarkm
6144743Smarkmextern jmp_buf tcpd_buf;
6244743Smarkm
6344743Smarkm/* Delimiters for lists of daemons or clients. */
6444743Smarkm
6544743Smarkmstatic char sep[] = ", \t\r\n";
6644743Smarkm
6744743Smarkm/* Constants to be used in assignments only, not in comparisons... */
6844743Smarkm
6944743Smarkm#define	YES		1
7044743Smarkm#define	NO		0
7144743Smarkm
7244743Smarkm /*
7344743Smarkm  * These variables are globally visible so that they can be redirected in
7444743Smarkm  * verification mode.
7544743Smarkm  */
7644743Smarkm
7744743Smarkmchar   *hosts_allow_table = HOSTS_ALLOW;
7844743Smarkmchar   *hosts_deny_table = HOSTS_DENY;
7944743Smarkmint     hosts_access_verbose = 0;
8044743Smarkm
8144743Smarkm /*
8244743Smarkm  * In a long-running process, we are not at liberty to just go away.
8344743Smarkm  */
8444743Smarkm
8544743Smarkmint     resident = (-1);		/* -1, 0: unknown; +1: yes */
8644743Smarkm
8744743Smarkm/* Forward declarations. */
8844743Smarkm
8944743Smarkmstatic int table_match();
9044743Smarkmstatic int list_match();
9144743Smarkmstatic int server_match();
9244743Smarkmstatic int client_match();
9344743Smarkmstatic int host_match();
9444743Smarkmstatic int string_match();
9544743Smarkmstatic int masked_match();
9656977Sshin#ifdef INET6
9756977Sshinstatic int masked_match4();
9856977Sshinstatic int masked_match6();
9956977Sshin#endif
10044743Smarkm
10144743Smarkm/* Size of logical line buffer. */
10244743Smarkm
10344743Smarkm#define	BUFLEN 2048
10444743Smarkm
10544743Smarkm/* hosts_access - host access control facility */
10644743Smarkm
10744743Smarkmint     hosts_access(request)
10844743Smarkmstruct request_info *request;
10944743Smarkm{
11044743Smarkm    int     verdict;
11144743Smarkm
11244743Smarkm    /*
11344743Smarkm     * If the (daemon, client) pair is matched by an entry in the file
11444743Smarkm     * /etc/hosts.allow, access is granted. Otherwise, if the (daemon,
11544743Smarkm     * client) pair is matched by an entry in the file /etc/hosts.deny,
11644743Smarkm     * access is denied. Otherwise, access is granted. A non-existent
11744743Smarkm     * access-control file is treated as an empty file.
11844743Smarkm     *
11944743Smarkm     * After a rule has been matched, the optional language extensions may
12044743Smarkm     * decide to grant or refuse service anyway. Or, while a rule is being
12144743Smarkm     * processed, a serious error is found, and it seems better to play safe
12244743Smarkm     * and deny service. All this is done by jumping back into the
12344743Smarkm     * hosts_access() routine, bypassing the regular return from the
12444743Smarkm     * table_match() function calls below.
12544743Smarkm     */
12644743Smarkm
12744743Smarkm    if (resident <= 0)
12844743Smarkm	resident++;
12944743Smarkm    verdict = setjmp(tcpd_buf);
13044743Smarkm    if (verdict != 0)
13144743Smarkm	return (verdict == AC_PERMIT);
13244743Smarkm    if (table_match(hosts_allow_table, request))
13344743Smarkm	return (YES);
13444743Smarkm    if (table_match(hosts_deny_table, request))
13544743Smarkm	return (NO);
13644743Smarkm    return (YES);
13744743Smarkm}
13844743Smarkm
13944743Smarkm/* table_match - match table entries with (daemon, client) pair */
14044743Smarkm
14144743Smarkmstatic int table_match(table, request)
14244743Smarkmchar   *table;
14344743Smarkmstruct request_info *request;
14444743Smarkm{
14544743Smarkm    FILE   *fp;
14644743Smarkm    char    sv_list[BUFLEN];		/* becomes list of daemons */
14744743Smarkm    char   *cl_list;			/* becomes list of clients */
14844743Smarkm    char   *sh_cmd;			/* becomes optional shell command */
14944743Smarkm    int     match = NO;
15044743Smarkm    struct tcpd_context saved_context;
151201782Ssobomax    char   *cp;
15244743Smarkm
15344743Smarkm    saved_context = tcpd_context;		/* stupid compilers */
15444743Smarkm
15544743Smarkm    /*
15644743Smarkm     * Between the fopen() and fclose() calls, avoid jumps that may cause
15744743Smarkm     * file descriptor leaks.
15844743Smarkm     */
15944743Smarkm
16044743Smarkm    if ((fp = fopen(table, "r")) != 0) {
16144743Smarkm	tcpd_context.file = table;
16244743Smarkm	tcpd_context.line = 0;
16344743Smarkm	while (match == NO && xgets(sv_list, sizeof(sv_list), fp) != 0) {
16444743Smarkm	    if (sv_list[strlen(sv_list) - 1] != '\n') {
16544743Smarkm		tcpd_warn("missing newline or line too long");
16644743Smarkm		continue;
16744743Smarkm	    }
168201782Ssobomax	    /* Ignore anything after unescaped # character */
169201782Ssobomax	    for (cp = strchr(sv_list, '#'); cp != NULL;) {
170201782Ssobomax		if (cp > sv_list && cp[-1] == '\\') {
171201782Ssobomax		    cp = strchr(cp + 1, '#');
172201782Ssobomax		    continue;
173201782Ssobomax		}
174201782Ssobomax		*cp = '\0';
175201782Ssobomax		break;
176201782Ssobomax	    }
177201782Ssobomax	    if (sv_list[strspn(sv_list, " \t\r\n")] == 0)
17844743Smarkm		continue;
17944743Smarkm	    if ((cl_list = split_at(sv_list, ':')) == 0) {
18044743Smarkm		tcpd_warn("missing \":\" separator");
18144743Smarkm		continue;
18244743Smarkm	    }
18344743Smarkm	    sh_cmd = split_at(cl_list, ':');
18444743Smarkm	    match = list_match(sv_list, request, server_match)
18544743Smarkm		&& list_match(cl_list, request, client_match);
18644743Smarkm	}
18744743Smarkm	(void) fclose(fp);
18844743Smarkm    } else if (errno != ENOENT) {
18944743Smarkm	tcpd_warn("cannot open %s: %m", table);
19044743Smarkm    }
19144743Smarkm    if (match) {
19244743Smarkm	if (hosts_access_verbose > 1)
19344743Smarkm	    syslog(LOG_DEBUG, "matched:  %s line %d",
19444743Smarkm		   tcpd_context.file, tcpd_context.line);
19544743Smarkm	if (sh_cmd) {
19644743Smarkm#ifdef PROCESS_OPTIONS
19744743Smarkm	    process_options(sh_cmd, request);
19844743Smarkm#else
19944743Smarkm	    char    cmd[BUFSIZ];
20044743Smarkm	    shell_cmd(percent_x(cmd, sizeof(cmd), sh_cmd, request));
20144743Smarkm#endif
20244743Smarkm	}
20344743Smarkm    }
20444743Smarkm    tcpd_context = saved_context;
20544743Smarkm    return (match);
20644743Smarkm}
20744743Smarkm
20844743Smarkm/* list_match - match a request against a list of patterns with exceptions */
20944743Smarkm
21044743Smarkmstatic int list_match(list, request, match_fn)
21144743Smarkmchar   *list;
21244743Smarkmstruct request_info *request;
21344743Smarkmint   (*match_fn) ();
21444743Smarkm{
21544743Smarkm    char   *tok;
21644743Smarkm
21744743Smarkm    /*
21844743Smarkm     * Process tokens one at a time. We have exhausted all possible matches
21944743Smarkm     * when we reach an "EXCEPT" token or the end of the list. If we do find
22044743Smarkm     * a match, look for an "EXCEPT" list and recurse to determine whether
22144743Smarkm     * the match is affected by any exceptions.
22244743Smarkm     */
22344743Smarkm
22444743Smarkm    for (tok = strtok(list, sep); tok != 0; tok = strtok((char *) 0, sep)) {
22544743Smarkm	if (STR_EQ(tok, "EXCEPT"))		/* EXCEPT: give up */
22644743Smarkm	    return (NO);
22744743Smarkm	if (match_fn(tok, request)) {		/* YES: look for exceptions */
22844743Smarkm	    while ((tok = strtok((char *) 0, sep)) && STR_NE(tok, "EXCEPT"))
22944743Smarkm		 /* VOID */ ;
23044743Smarkm	    return (tok == 0 || list_match((char *) 0, request, match_fn) == 0);
23144743Smarkm	}
23244743Smarkm    }
23344743Smarkm    return (NO);
23444743Smarkm}
23544743Smarkm
23644743Smarkm/* server_match - match server information */
23744743Smarkm
23844743Smarkmstatic int server_match(tok, request)
23944743Smarkmchar   *tok;
24044743Smarkmstruct request_info *request;
24144743Smarkm{
24244743Smarkm    char   *host;
24344743Smarkm
24444743Smarkm    if ((host = split_at(tok + 1, '@')) == 0) {	/* plain daemon */
24544743Smarkm	return (string_match(tok, eval_daemon(request)));
24644743Smarkm    } else {					/* daemon@host */
24744743Smarkm	return (string_match(tok, eval_daemon(request))
24844743Smarkm		&& host_match(host, request->server));
24944743Smarkm    }
25044743Smarkm}
25144743Smarkm
25244743Smarkm/* client_match - match client information */
25344743Smarkm
25444743Smarkmstatic int client_match(tok, request)
25544743Smarkmchar   *tok;
25644743Smarkmstruct request_info *request;
25744743Smarkm{
25844743Smarkm    char   *host;
25944743Smarkm
26044743Smarkm    if ((host = split_at(tok + 1, '@')) == 0) {	/* plain host */
26144743Smarkm	return (host_match(tok, request->client));
26244743Smarkm    } else {					/* user@host */
26344743Smarkm	return (host_match(host, request->client)
26444743Smarkm		&& string_match(tok, eval_user(request)));
26544743Smarkm    }
26644743Smarkm}
26744743Smarkm
26851495Ssheldonh/* hostfile_match - look up host patterns from file */
26951495Ssheldonh
27051495Ssheldonhstatic int hostfile_match(path, host)
27151495Ssheldonhchar   *path;
27251495Ssheldonhstruct hosts_info *host;
27351495Ssheldonh{
27451495Ssheldonh    char    tok[BUFSIZ];
27551495Ssheldonh    int     match = NO;
27651495Ssheldonh    FILE   *fp;
27751495Ssheldonh
27851495Ssheldonh    if ((fp = fopen(path, "r")) != 0) {
27951495Ssheldonh	while (fscanf(fp, "%s", tok) == 1 && !(match = host_match(tok, host)))
28051495Ssheldonh	     /* void */ ;
28151495Ssheldonh	fclose(fp);
28251495Ssheldonh    } else if (errno != ENOENT) {
28351495Ssheldonh	tcpd_warn("open %s: %m", path);
28451495Ssheldonh    }
28551495Ssheldonh    return (match);
28651495Ssheldonh}
28751495Ssheldonh
28844743Smarkm/* host_match - match host name and/or address against pattern */
28944743Smarkm
29044743Smarkmstatic int host_match(tok, host)
29144743Smarkmchar   *tok;
29244743Smarkmstruct host_info *host;
29344743Smarkm{
29444743Smarkm    char   *mask;
29544743Smarkm
29644743Smarkm    /*
29744743Smarkm     * This code looks a little hairy because we want to avoid unnecessary
29844743Smarkm     * hostname lookups.
29944743Smarkm     *
30044743Smarkm     * The KNOWN pattern requires that both address AND name be known; some
30144743Smarkm     * patterns are specific to host names or to host addresses; all other
30244743Smarkm     * patterns are satisfied when either the address OR the name match.
30344743Smarkm     */
30444743Smarkm
30544743Smarkm    if (tok[0] == '@') {			/* netgroup: look it up */
30644743Smarkm#ifdef  NETGROUP
30744743Smarkm	static char *mydomain = 0;
30844743Smarkm	if (mydomain == 0)
30944743Smarkm	    yp_get_default_domain(&mydomain);
31044743Smarkm	return (innetgr(tok + 1, eval_hostname(host), (char *) 0, mydomain));
31144743Smarkm#else
31244743Smarkm	tcpd_warn("netgroup support is disabled");	/* not tcpd_jump() */
31344743Smarkm	return (NO);
31444743Smarkm#endif
31551495Ssheldonh    } else if (tok[0] == '/') {			/* /file hack */
31651495Ssheldonh	return (hostfile_match(tok, host));
31744743Smarkm    } else if (STR_EQ(tok, "KNOWN")) {		/* check address and name */
31844743Smarkm	char   *name = eval_hostname(host);
31944743Smarkm	return (STR_NE(eval_hostaddr(host), unknown) && HOSTNAME_KNOWN(name));
32044743Smarkm    } else if (STR_EQ(tok, "LOCAL")) {		/* local: no dots in name */
32144743Smarkm	char   *name = eval_hostname(host);
32244743Smarkm	return (strchr(name, '.') == 0 && HOSTNAME_KNOWN(name));
32344743Smarkm    } else if ((mask = split_at(tok, '/')) != 0) {	/* net/mask */
32444743Smarkm	return (masked_match(tok, mask, eval_hostaddr(host)));
32544743Smarkm    } else {					/* anything else */
32644743Smarkm	return (string_match(tok, eval_hostaddr(host))
32744743Smarkm	    || (NOT_INADDR(tok) && string_match(tok, eval_hostname(host))));
32844743Smarkm    }
32944743Smarkm}
33044743Smarkm
33144743Smarkm/* string_match - match string against pattern */
33244743Smarkm
33344743Smarkmstatic int string_match(tok, string)
33444743Smarkmchar   *tok;
33544743Smarkmchar   *string;
33644743Smarkm{
33744743Smarkm    int     n;
33844743Smarkm
33956977Sshin#ifdef INET6
34056977Sshin    /* convert IPv4 mapped IPv6 address to IPv4 address */
34156977Sshin    if (STRN_EQ(string, "::ffff:", 7)
34256977Sshin	&& dot_quad_addr(string + 7) != INADDR_NONE) {
34356977Sshin	string += 7;
34456977Sshin    }
34556977Sshin#endif
34644743Smarkm    if (tok[0] == '.') {			/* suffix */
34744743Smarkm	n = strlen(string) - strlen(tok);
34844743Smarkm	return (n > 0 && STR_EQ(tok, string + n));
34944743Smarkm    } else if (STR_EQ(tok, "ALL")) {		/* all: match any */
35044743Smarkm	return (YES);
35144743Smarkm    } else if (STR_EQ(tok, "KNOWN")) {		/* not unknown */
35244743Smarkm	return (STR_NE(string, unknown));
35344743Smarkm    } else if (tok[(n = strlen(tok)) - 1] == '.') {	/* prefix */
35444743Smarkm	return (STRN_EQ(tok, string, n));
35544743Smarkm    } else {					/* exact match */
35656977Sshin#ifdef INET6
35763158Sume	struct addrinfo hints, *res;
35863158Sume	struct sockaddr_in6 pat, addr;
35956977Sshin	int len, ret;
36056977Sshin	char ch;
36156977Sshin
36256977Sshin	len = strlen(tok);
36356977Sshin	if (*tok == '[' && tok[len - 1] == ']') {
36456977Sshin	    ch = tok[len - 1];
36556977Sshin	    tok[len - 1] = '\0';
36663158Sume	    memset(&hints, 0, sizeof(hints));
36763158Sume	    hints.ai_family = AF_INET6;
36863158Sume	    hints.ai_socktype = SOCK_STREAM;
36963158Sume	    hints.ai_flags = AI_PASSIVE | AI_NUMERICHOST;
37063158Sume	    if ((ret = getaddrinfo(tok + 1, NULL, &hints, &res)) == 0) {
37163158Sume		memcpy(&pat, res->ai_addr, sizeof(pat));
37263158Sume		freeaddrinfo(res);
37363158Sume	    }
37456977Sshin	    tok[len - 1] = ch;
37563158Sume	    if (ret != 0 || getaddrinfo(string, NULL, &hints, &res) != 0)
37656977Sshin		return NO;
37763158Sume	    memcpy(&addr, res->ai_addr, sizeof(addr));
37863158Sume	    freeaddrinfo(res);
37963158Sume	    if (pat.sin6_scope_id != 0 &&
38063158Sume		addr.sin6_scope_id != pat.sin6_scope_id)
38163158Sume		return NO;
38263158Sume	    return (!memcmp(&pat.sin6_addr, &addr.sin6_addr,
38363158Sume			    sizeof(struct in6_addr)));
38463158Sume	    return (ret);
38556977Sshin	}
38656977Sshin#endif
38744743Smarkm	return (STR_EQ(tok, string));
38844743Smarkm    }
38944743Smarkm}
39044743Smarkm
39144743Smarkm/* masked_match - match address against netnumber/netmask */
39244743Smarkm
39356977Sshin#ifdef INET6
39444743Smarkmstatic int masked_match(net_tok, mask_tok, string)
39544743Smarkmchar   *net_tok;
39644743Smarkmchar   *mask_tok;
39744743Smarkmchar   *string;
39844743Smarkm{
39956977Sshin    return (masked_match4(net_tok, mask_tok, string) ||
40056977Sshin	    masked_match6(net_tok, mask_tok, string));
40156977Sshin}
40256977Sshin
40356977Sshinstatic int masked_match4(net_tok, mask_tok, string)
40456977Sshin#else
40556977Sshinstatic int masked_match(net_tok, mask_tok, string)
40656977Sshin#endif
40756977Sshinchar   *net_tok;
40856977Sshinchar   *mask_tok;
40956977Sshinchar   *string;
41056977Sshin{
41156977Sshin#ifdef INET6
41256977Sshin    u_int32_t net;
41356977Sshin    u_int32_t mask;
41456977Sshin    u_int32_t addr;
41556977Sshin#else
41644743Smarkm    unsigned long net;
41744743Smarkm    unsigned long mask;
41844743Smarkm    unsigned long addr;
41956977Sshin#endif
42044743Smarkm
42144743Smarkm    /*
42244743Smarkm     * Disallow forms other than dotted quad: the treatment that inet_addr()
42344743Smarkm     * gives to forms with less than four components is inconsistent with the
42444743Smarkm     * access control language. John P. Rouillard <rouilj@cs.umb.edu>.
42544743Smarkm     */
42644743Smarkm
42744743Smarkm    if ((addr = dot_quad_addr(string)) == INADDR_NONE)
42844743Smarkm	return (NO);
42944743Smarkm    if ((net = dot_quad_addr(net_tok)) == INADDR_NONE
43044743Smarkm	|| (mask = dot_quad_addr(mask_tok)) == INADDR_NONE) {
43156977Sshin#ifndef INET6
43244743Smarkm	tcpd_warn("bad net/mask expression: %s/%s", net_tok, mask_tok);
43356977Sshin#endif
43444743Smarkm	return (NO);				/* not tcpd_jump() */
43544743Smarkm    }
43644743Smarkm    return ((addr & mask) == net);
43744743Smarkm}
43856977Sshin
43956977Sshin#ifdef INET6
44056977Sshinstatic int masked_match6(net_tok, mask_tok, string)
44156977Sshinchar   *net_tok;
44256977Sshinchar   *mask_tok;
44356977Sshinchar   *string;
44456977Sshin{
44563158Sume    struct addrinfo hints, *res;
44663158Sume    struct sockaddr_in6 net, addr;
44756977Sshin    u_int32_t mask;
44856977Sshin    int len, mask_len, i = 0;
44956977Sshin    char ch;
45056977Sshin
45163158Sume    memset(&hints, 0, sizeof(hints));
45263158Sume    hints.ai_family = AF_INET6;
45363158Sume    hints.ai_socktype = SOCK_STREAM;
45463158Sume    hints.ai_flags = AI_PASSIVE | AI_NUMERICHOST;
45563158Sume    if (getaddrinfo(string, NULL, &hints, &res) != 0)
45663158Sume	return NO;
45763158Sume    memcpy(&addr, res->ai_addr, sizeof(addr));
45863158Sume    freeaddrinfo(res);
45956977Sshin
46063158Sume    if (IN6_IS_ADDR_V4MAPPED(&addr.sin6_addr)) {
46163158Sume	if ((*(u_int32_t *)&net.sin6_addr.s6_addr[12] = dot_quad_addr(net_tok)) == INADDR_NONE
46256977Sshin	 || (mask = dot_quad_addr(mask_tok)) == INADDR_NONE)
46356977Sshin	    return (NO);
46463158Sume	return ((*(u_int32_t *)&addr.sin6_addr.s6_addr[12] & mask) == *(u_int32_t *)&net.sin6_addr.s6_addr[12]);
46556977Sshin    }
46656977Sshin
46756977Sshin    /* match IPv6 address against netnumber/prefixlen */
46856977Sshin    len = strlen(net_tok);
46956977Sshin    if (*net_tok != '[' || net_tok[len - 1] != ']')
47056977Sshin	return NO;
47156977Sshin    ch = net_tok[len - 1];
47256977Sshin    net_tok[len - 1] = '\0';
47363158Sume    if (getaddrinfo(net_tok + 1, NULL, &hints, &res) != 0) {
47456977Sshin	net_tok[len - 1] = ch;
47556977Sshin	return NO;
47656977Sshin    }
47763158Sume    memcpy(&net, res->ai_addr, sizeof(net));
47863158Sume    freeaddrinfo(res);
47956977Sshin    net_tok[len - 1] = ch;
48056977Sshin    if ((mask_len = atoi(mask_tok)) < 0 || mask_len > 128)
48156977Sshin	return NO;
48256977Sshin
48363158Sume    if (net.sin6_scope_id != 0 && addr.sin6_scope_id != net.sin6_scope_id)
48463158Sume	return NO;
48556977Sshin    while (mask_len > 0) {
48656977Sshin	if (mask_len < 32) {
48756977Sshin	    mask = htonl(~(0xffffffff >> mask_len));
48863158Sume	    if ((*(u_int32_t *)&addr.sin6_addr.s6_addr[i] & mask) != (*(u_int32_t *)&net.sin6_addr.s6_addr[i] & mask))
48956977Sshin		return NO;
49056977Sshin	    break;
49156977Sshin	}
49263158Sume	if (*(u_int32_t *)&addr.sin6_addr.s6_addr[i] != *(u_int32_t *)&net.sin6_addr.s6_addr[i])
49356977Sshin	    return NO;
49456977Sshin	i += 4;
49556977Sshin	mask_len -= 32;
49656977Sshin    }
49756977Sshin    return YES;
49856977Sshin}
49956977Sshin#endif /* INET6 */
500