1139749Simp /*
277542Swpaul  * This module implements a simple access control language that is based on
377542Swpaul  * host (or domain) names, NIS (host) netgroup names, IP addresses (or
477542Swpaul  * network numbers) and daemon process names. When a match is found the
577542Swpaul  * search is terminated, and depending on whether PROCESS_OPTIONS is defined,
677542Swpaul  * a list of options is executed or an optional shell command is executed.
777542Swpaul  *
877542Swpaul  * Host and user names are looked up on demand, provided that suitable endpoint
977542Swpaul  * information is available as sockaddr_in structures or TLI netbufs. As a
1077542Swpaul  * side effect, the pattern matching process may change the contents of
1177542Swpaul  * request structure fields.
1277542Swpaul  *
1377542Swpaul  * Diagnostics are reported through syslog(3).
1477542Swpaul  *
1577542Swpaul  * Compile with -DNETGROUP if your library provides support for netgroups.
1677542Swpaul  *
1777542Swpaul  * Author: Wietse Venema, Eindhoven University of Technology, The Netherlands.
1877542Swpaul  *
1977542Swpaul  * $FreeBSD: releng/10.3/contrib/tcp_wrappers/hosts_access.c 201782 2010-01-08 10:54:15Z sobomax $
2077542Swpaul  */
2177542Swpaul
2277542Swpaul#ifndef lint
2377542Swpaulstatic char sccsid[] = "@(#) hosts_access.c 1.21 97/02/12 02:13:22";
2477542Swpaul#endif
2577542Swpaul
2677542Swpaul/* System libraries. */
2777542Swpaul
2877542Swpaul#include <sys/types.h>
2977542Swpaul#ifdef INT32_T
3077542Swpaul    typedef uint32_t u_int32_t;
3177542Swpaul#endif
3277542Swpaul#include <sys/param.h>
3377542Swpaul#ifdef INET6
34119418Sobrien#include <sys/socket.h>
35119418Sobrien#endif
36119418Sobrien#include <netinet/in.h>
3777542Swpaul#include <arpa/inet.h>
3877542Swpaul#include <stdio.h>
3977542Swpaul#include <syslog.h>
4077542Swpaul#include <ctype.h>
4177542Swpaul#include <errno.h>
4277542Swpaul#include <setjmp.h>
4377542Swpaul#include <string.h>
4477542Swpaul#ifdef INET6
4577542Swpaul#include <netdb.h>
4677542Swpaul#endif
4777542Swpaul
4877542Swpaulextern char *fgets();
4977542Swpaulextern int errno;
5077542Swpaul
5177542Swpaul#ifndef	INADDR_NONE
5277542Swpaul#define	INADDR_NONE	(-1)		/* XXX should be 0xffffffff */
5377542Swpaul#endif
5477542Swpaul
5577542Swpaul/* Local stuff. */
5677542Swpaul
5777542Swpaul#include "tcpd.h"
5877542Swpaul
5977542Swpaul/* Error handling. */
6077542Swpaul
6177542Swpaulextern jmp_buf tcpd_buf;
6277542Swpaul
6377542Swpaul/* Delimiters for lists of daemons or clients. */
6477542Swpaul
6577542Swpaulstatic char sep[] = ", \t\r\n";
6677542Swpaul
6777542Swpaul/* Constants to be used in assignments only, not in comparisons... */
6877542Swpaul
6977542Swpaul#define	YES		1
7077542Swpaul#define	NO		0
7177542Swpaul
7277542Swpaul /*
7377542Swpaul  * These variables are globally visible so that they can be redirected in
7477542Swpaul  * verification mode.
7577542Swpaul  */
7677542Swpaul
7777542Swpaulchar   *hosts_allow_table = HOSTS_ALLOW;
78129879Sphkchar   *hosts_deny_table = HOSTS_DENY;
7977542Swpaulint     hosts_access_verbose = 0;
8077542Swpaul
8177542Swpaul /*
8277542Swpaul  * In a long-running process, we are not at liberty to just go away.
8377542Swpaul  */
8477542Swpaul
8577542Swpaulint     resident = (-1);		/* -1, 0: unknown; +1: yes */
86147256Sbrooks
8777542Swpaul/* Forward declarations. */
8877542Swpaul
8977542Swpaulstatic int table_match();
9077542Swpaulstatic int list_match();
9177542Swpaulstatic int server_match();
9277542Swpaulstatic int client_match();
9377542Swpaulstatic int host_match();
9477542Swpaulstatic int string_match();
9577542Swpaulstatic int masked_match();
9677542Swpaul#ifdef INET6
9777542Swpaulstatic int masked_match4();
9877542Swpaulstatic int masked_match6();
9977542Swpaul#endif
100119291Simp
101119291Simp/* Size of logical line buffer. */
10277542Swpaul
10377542Swpaul#define	BUFLEN 2048
10477542Swpaul
10577542Swpaul/* hosts_access - host access control facility */
10677542Swpaul
107151545Simpint     hosts_access(request)
10877542Swpaulstruct request_info *request;
10977542Swpaul{
11077542Swpaul    int     verdict;
11177542Swpaul
11277542Swpaul    /*
113242908Sdim     * If the (daemon, client) pair is matched by an entry in the file
11477542Swpaul     * /etc/hosts.allow, access is granted. Otherwise, if the (daemon,
11577542Swpaul     * client) pair is matched by an entry in the file /etc/hosts.deny,
11677542Swpaul     * access is denied. Otherwise, access is granted. A non-existent
11777542Swpaul     * access-control file is treated as an empty file.
11899498Salfred     *
11999498Salfred     * After a rule has been matched, the optional language extensions may
12099498Salfred     * decide to grant or refuse service anyway. Or, while a rule is being
12177542Swpaul     * processed, a serious error is found, and it seems better to play safe
12299498Salfred     * and deny service. All this is done by jumping back into the
12399498Salfred     * hosts_access() routine, bypassing the regular return from the
12499498Salfred     * table_match() function calls below.
12599498Salfred     */
12677542Swpaul
12799498Salfred    if (resident <= 0)
12899498Salfred	resident++;
12999498Salfred    verdict = setjmp(tcpd_buf);
13099498Salfred    if (verdict != 0)
13199498Salfred	return (verdict == AC_PERMIT);
13299498Salfred    if (table_match(hosts_allow_table, request))
13399498Salfred	return (YES);
13499498Salfred    if (table_match(hosts_deny_table, request))
135152727Sjhb	return (NO);
13699498Salfred    return (YES);
13799498Salfred}
138152727Sjhb
13999498Salfred/* table_match - match table entries with (daemon, client) pair */
140199560Sjhb
141173839Syongaristatic int table_match(table, request)
14299498Salfredchar   *table;
143152727Sjhbstruct request_info *request;
14499498Salfred{
14577542Swpaul    FILE   *fp;
14699498Salfred    char    sv_list[BUFLEN];		/* becomes list of daemons */
14799498Salfred    char   *cl_list;			/* becomes list of clients */
14877542Swpaul    char   *sh_cmd;			/* becomes optional shell command */
14999498Salfred    int     match = NO;
15099498Salfred    struct tcpd_context saved_context;
15199498Salfred    char   *cp;
15277542Swpaul
15399498Salfred    saved_context = tcpd_context;		/* stupid compilers */
15499498Salfred
15599498Salfred    /*
15699498Salfred     * Between the fopen() and fclose() calls, avoid jumps that may cause
15777542Swpaul     * file descriptor leaks.
15877542Swpaul     */
15977542Swpaul
16077542Swpaul    if ((fp = fopen(table, "r")) != 0) {
16177542Swpaul	tcpd_context.file = table;
16277542Swpaul	tcpd_context.line = 0;
16377542Swpaul	while (match == NO && xgets(sv_list, sizeof(sv_list), fp) != 0) {
16477542Swpaul	    if (sv_list[strlen(sv_list) - 1] != '\n') {
16577542Swpaul		tcpd_warn("missing newline or line too long");
16677542Swpaul		continue;
16777542Swpaul	    }
16877542Swpaul	    /* Ignore anything after unescaped # character */
16977542Swpaul	    for (cp = strchr(sv_list, '#'); cp != NULL;) {
17077542Swpaul		if (cp > sv_list && cp[-1] == '\\') {
17177542Swpaul		    cp = strchr(cp + 1, '#');
17277542Swpaul		    continue;
17377542Swpaul		}
17477542Swpaul		*cp = '\0';
17577542Swpaul		break;
17677542Swpaul	    }
17777542Swpaul	    if (sv_list[strspn(sv_list, " \t\r\n")] == 0)
178229093Shselasky		continue;
17977542Swpaul	    if ((cl_list = split_at(sv_list, ':')) == 0) {
18077542Swpaul		tcpd_warn("missing \":\" separator");
18177542Swpaul		continue;
18277542Swpaul	    }
18377542Swpaul	    sh_cmd = split_at(cl_list, ':');
18477542Swpaul	    match = list_match(sv_list, request, server_match)
18577542Swpaul		&& list_match(cl_list, request, client_match);
18677542Swpaul	}
18777542Swpaul	(void) fclose(fp);
18877542Swpaul    } else if (errno != ENOENT) {
189113506Smdodd	tcpd_warn("cannot open %s: %m", table);
19077542Swpaul    }
191113506Smdodd    if (match) {
192113506Smdodd	if (hosts_access_verbose > 1)
193113506Smdodd	    syslog(LOG_DEBUG, "matched:  %s line %d",
19477542Swpaul		   tcpd_context.file, tcpd_context.line);
19577542Swpaul	if (sh_cmd) {
19677542Swpaul#ifdef PROCESS_OPTIONS
19777542Swpaul	    process_options(sh_cmd, request);
19877542Swpaul#else
19977542Swpaul	    char    cmd[BUFSIZ];
20077542Swpaul	    shell_cmd(percent_x(cmd, sizeof(cmd), sh_cmd, request));
20177542Swpaul#endif
20277542Swpaul	}
20377542Swpaul    }
20477542Swpaul    tcpd_context = saved_context;
20577542Swpaul    return (match);
20677542Swpaul}
20777542Swpaul
20877542Swpaul/* list_match - match a request against a list of patterns with exceptions */
20977542Swpaul
21077542Swpaulstatic int list_match(list, request, match_fn)
21177542Swpaulchar   *list;
21299498Salfredstruct request_info *request;
21399498Salfredint   (*match_fn) ();
21477542Swpaul{
21577542Swpaul    char   *tok;
21677542Swpaul
21777542Swpaul    /*
21877542Swpaul     * Process tokens one at a time. We have exhausted all possible matches
21977542Swpaul     * when we reach an "EXCEPT" token or the end of the list. If we do find
22077542Swpaul     * a match, look for an "EXCEPT" list and recurse to determine whether
22177542Swpaul     * the match is affected by any exceptions.
22277542Swpaul     */
22377542Swpaul
22477542Swpaul    for (tok = strtok(list, sep); tok != 0; tok = strtok((char *) 0, sep)) {
22577542Swpaul	if (STR_EQ(tok, "EXCEPT"))		/* EXCEPT: give up */
22677542Swpaul	    return (NO);
22777542Swpaul	if (match_fn(tok, request)) {		/* YES: look for exceptions */
22877542Swpaul	    while ((tok = strtok((char *) 0, sep)) && STR_NE(tok, "EXCEPT"))
229162321Sglebius		 /* VOID */ ;
23077542Swpaul	    return (tok == 0 || list_match((char *) 0, request, match_fn) == 0);
23177542Swpaul	}
23277542Swpaul    }
23377542Swpaul    return (NO);
23477542Swpaul}
23577542Swpaul
23677542Swpaul/* server_match - match server information */
23777542Swpaul
23877542Swpaulstatic int server_match(tok, request)
23977542Swpaulchar   *tok;
24077542Swpaulstruct request_info *request;
24177542Swpaul{
24277542Swpaul    char   *host;
24377542Swpaul
24477542Swpaul    if ((host = split_at(tok + 1, '@')) == 0) {	/* plain daemon */
24577542Swpaul	return (string_match(tok, eval_daemon(request)));
24699498Salfred    } else {					/* daemon@host */
24799498Salfred	return (string_match(tok, eval_daemon(request))
24877542Swpaul		&& host_match(host, request->server));
24977542Swpaul    }
25077542Swpaul}
25177542Swpaul
25277542Swpaul/* client_match - match client information */
25377542Swpaul
25477542Swpaulstatic int client_match(tok, request)
25577542Swpaulchar   *tok;
25677542Swpaulstruct request_info *request;
25777542Swpaul{
25877542Swpaul    char   *host;
25977542Swpaul
26077542Swpaul    if ((host = split_at(tok + 1, '@')) == 0) {	/* plain host */
26177542Swpaul	return (host_match(tok, request->client));
26277542Swpaul    } else {					/* user@host */
26377542Swpaul	return (host_match(host, request->client)
26477542Swpaul		&& string_match(tok, eval_user(request)));
26577542Swpaul    }
26677542Swpaul}
26777542Swpaul
26877542Swpaul/* hostfile_match - look up host patterns from file */
26999498Salfred
27099498Salfredstatic int hostfile_match(path, host)
27177542Swpaulchar   *path;
27277542Swpaulstruct hosts_info *host;
27377542Swpaul{
27477542Swpaul    char    tok[BUFSIZ];
27577542Swpaul    int     match = NO;
27677542Swpaul    FILE   *fp;
27777542Swpaul
27877542Swpaul    if ((fp = fopen(path, "r")) != 0) {
27977542Swpaul	while (fscanf(fp, "%s", tok) == 1 && !(match = host_match(tok, host)))
28077542Swpaul	     /* void */ ;
28177542Swpaul	fclose(fp);
28277542Swpaul    } else if (errno != ENOENT) {
28377542Swpaul	tcpd_warn("open %s: %m", path);
28477542Swpaul    }
28577542Swpaul    return (match);
28677542Swpaul}
28777542Swpaul
28877542Swpaul/* host_match - match host name and/or address against pattern */
28977542Swpaul
29077542Swpaulstatic int host_match(tok, host)
29177542Swpaulchar   *tok;
29277542Swpaulstruct host_info *host;
29377542Swpaul{
294162321Sglebius    char   *mask;
29577542Swpaul
29677542Swpaul    /*
29777542Swpaul     * This code looks a little hairy because we want to avoid unnecessary
29877542Swpaul     * hostname lookups.
29977542Swpaul     *
30077542Swpaul     * The KNOWN pattern requires that both address AND name be known; some
30199498Salfred     * patterns are specific to host names or to host addresses; all other
30299498Salfred     * patterns are satisfied when either the address OR the name match.
30377542Swpaul     */
30477542Swpaul
30577542Swpaul    if (tok[0] == '@') {			/* netgroup: look it up */
30677542Swpaul#ifdef  NETGROUP
30777542Swpaul	static char *mydomain = 0;
30877542Swpaul	if (mydomain == 0)
30977542Swpaul	    yp_get_default_domain(&mydomain);
31077542Swpaul	return (innetgr(tok + 1, eval_hostname(host), (char *) 0, mydomain));
31177542Swpaul#else
31277542Swpaul	tcpd_warn("netgroup support is disabled");	/* not tcpd_jump() */
31377542Swpaul	return (NO);
31477542Swpaul#endif
31577542Swpaul    } else if (tok[0] == '/') {			/* /file hack */
31677542Swpaul	return (hostfile_match(tok, host));
31777542Swpaul    } else if (STR_EQ(tok, "KNOWN")) {		/* check address and name */
31877542Swpaul	char   *name = eval_hostname(host);
319162321Sglebius	return (STR_NE(eval_hostaddr(host), unknown) && HOSTNAME_KNOWN(name));
32077542Swpaul    } else if (STR_EQ(tok, "LOCAL")) {		/* local: no dots in name */
32177542Swpaul	char   *name = eval_hostname(host);
32277542Swpaul	return (strchr(name, '.') == 0 && HOSTNAME_KNOWN(name));
32377542Swpaul    } else if ((mask = split_at(tok, '/')) != 0) {	/* net/mask */
32477542Swpaul	return (masked_match(tok, mask, eval_hostaddr(host)));
32577542Swpaul    } else {					/* anything else */
32699498Salfred	return (string_match(tok, eval_hostaddr(host))
32799498Salfred	    || (NOT_INADDR(tok) && string_match(tok, eval_hostname(host))));
32877542Swpaul    }
32977542Swpaul}
33077542Swpaul
33177542Swpaul/* string_match - match string against pattern */
33277542Swpaul
33377542Swpaulstatic int string_match(tok, string)
33477542Swpaulchar   *tok;
33577542Swpaulchar   *string;
33677542Swpaul{
33777542Swpaul    int     n;
33895673Sphk
33977542Swpaul#ifdef INET6
34077542Swpaul    /* convert IPv4 mapped IPv6 address to IPv4 address */
34177542Swpaul    if (STRN_EQ(string, "::ffff:", 7)
34277542Swpaul	&& dot_quad_addr(string + 7) != INADDR_NONE) {
34377542Swpaul	string += 7;
34477542Swpaul    }
34577542Swpaul#endif
34677542Swpaul    if (tok[0] == '.') {			/* suffix */
34777542Swpaul	n = strlen(string) - strlen(tok);
34877542Swpaul	return (n > 0 && STR_EQ(tok, string + n));
34977542Swpaul    } else if (STR_EQ(tok, "ALL")) {		/* all: match any */
35077542Swpaul	return (YES);
35177542Swpaul    } else if (STR_EQ(tok, "KNOWN")) {		/* not unknown */
35277542Swpaul	return (STR_NE(string, unknown));
35377542Swpaul    } else if (tok[(n = strlen(tok)) - 1] == '.') {	/* prefix */
35477542Swpaul	return (STRN_EQ(tok, string, n));
35577542Swpaul    } else {					/* exact match */
35677542Swpaul#ifdef INET6
35777542Swpaul	struct addrinfo hints, *res;
35877542Swpaul	struct sockaddr_in6 pat, addr;
35977542Swpaul	int len, ret;
36077542Swpaul	char ch;
36177542Swpaul
36277542Swpaul	len = strlen(tok);
36377542Swpaul	if (*tok == '[' && tok[len - 1] == ']') {
36477542Swpaul	    ch = tok[len - 1];
36577542Swpaul	    tok[len - 1] = '\0';
36677542Swpaul	    memset(&hints, 0, sizeof(hints));
36799498Salfred	    hints.ai_family = AF_INET6;
36899498Salfred	    hints.ai_socktype = SOCK_STREAM;
36977542Swpaul	    hints.ai_flags = AI_PASSIVE | AI_NUMERICHOST;
37077542Swpaul	    if ((ret = getaddrinfo(tok + 1, NULL, &hints, &res)) == 0) {
37177542Swpaul		memcpy(&pat, res->ai_addr, sizeof(pat));
37277542Swpaul		freeaddrinfo(res);
37377542Swpaul	    }
37477542Swpaul	    tok[len - 1] = ch;
375147256Sbrooks	    if (ret != 0 || getaddrinfo(string, NULL, &hints, &res) != 0)
376152727Sjhb		return NO;
37777542Swpaul	    memcpy(&addr, res->ai_addr, sizeof(addr));
37877542Swpaul	    freeaddrinfo(res);
37977542Swpaul	    if (pat.sin6_scope_id != 0 &&
38077542Swpaul		addr.sin6_scope_id != pat.sin6_scope_id)
38177542Swpaul		return NO;
38277542Swpaul	    return (!memcmp(&pat.sin6_addr, &addr.sin6_addr,
38377542Swpaul			    sizeof(struct in6_addr)));
38477542Swpaul	    return (ret);
38577542Swpaul	}
38677542Swpaul#endif
38777542Swpaul	return (STR_EQ(tok, string));
38877542Swpaul    }
38977542Swpaul}
39077542Swpaul
39177542Swpaul/* masked_match - match address against netnumber/netmask */
392195049Srwatson
39377542Swpaul#ifdef INET6
39477542Swpaulstatic int masked_match(net_tok, mask_tok, string)
39577542Swpaulchar   *net_tok;
396130270Snaddychar   *mask_tok;
397130270Snaddychar   *string;
39877542Swpaul{
39977542Swpaul    return (masked_match4(net_tok, mask_tok, string) ||
40077542Swpaul	    masked_match6(net_tok, mask_tok, string));
40177542Swpaul}
40277542Swpaul
403195049Srwatsonstatic int masked_match4(net_tok, mask_tok, string)
40477542Swpaul#else
40577542Swpaulstatic int masked_match(net_tok, mask_tok, string)
40677542Swpaul#endif
40777542Swpaulchar   *net_tok;
40877542Swpaulchar   *mask_tok;
40977542Swpaulchar   *string;
41077542Swpaul{
41199498Salfred#ifdef INET6
41299498Salfred    u_int32_t net;
41377542Swpaul    u_int32_t mask;
41477542Swpaul    u_int32_t addr;
41577542Swpaul#else
41677542Swpaul    unsigned long net;
41777542Swpaul    unsigned long mask;
41877542Swpaul    unsigned long addr;
41977542Swpaul#endif
42077542Swpaul
42177542Swpaul    /*
42277542Swpaul     * Disallow forms other than dotted quad: the treatment that inet_addr()
42377542Swpaul     * gives to forms with less than four components is inconsistent with the
42477542Swpaul     * access control language. John P. Rouillard <rouilj@cs.umb.edu>.
425162321Sglebius     */
42677542Swpaul
42777542Swpaul    if ((addr = dot_quad_addr(string)) == INADDR_NONE)
42877542Swpaul	return (NO);
42977542Swpaul    if ((net = dot_quad_addr(net_tok)) == INADDR_NONE
43077542Swpaul	|| (mask = dot_quad_addr(mask_tok)) == INADDR_NONE) {
43177542Swpaul#ifndef INET6
43277542Swpaul	tcpd_warn("bad net/mask expression: %s/%s", net_tok, mask_tok);
43377542Swpaul#endif
43477542Swpaul	return (NO);				/* not tcpd_jump() */
43577542Swpaul    }
43677542Swpaul    return ((addr & mask) == net);
43799498Salfred}
43899498Salfred
43977542Swpaul#ifdef INET6
44077542Swpaulstatic int masked_match6(net_tok, mask_tok, string)
441230697Smariuschar   *net_tok;
44277542Swpaulchar   *mask_tok;
44377542Swpaulchar   *string;
44477542Swpaul{
44577542Swpaul    struct addrinfo hints, *res;
44677542Swpaul    struct sockaddr_in6 net, addr;
44777542Swpaul    u_int32_t mask;
44877542Swpaul    int len, mask_len, i = 0;
449143160Simp    char ch;
45077542Swpaul
45177542Swpaul    memset(&hints, 0, sizeof(hints));
45277542Swpaul    hints.ai_family = AF_INET6;
45377542Swpaul    hints.ai_socktype = SOCK_STREAM;
45477542Swpaul    hints.ai_flags = AI_PASSIVE | AI_NUMERICHOST;
45577542Swpaul    if (getaddrinfo(string, NULL, &hints, &res) != 0)
45677542Swpaul	return NO;
45777542Swpaul    memcpy(&addr, res->ai_addr, sizeof(addr));
45877542Swpaul    freeaddrinfo(res);
45977542Swpaul
46077542Swpaul    if (IN6_IS_ADDR_V4MAPPED(&addr.sin6_addr)) {
46199498Salfred	if ((*(u_int32_t *)&net.sin6_addr.s6_addr[12] = dot_quad_addr(net_tok)) == INADDR_NONE
46299498Salfred	 || (mask = dot_quad_addr(mask_tok)) == INADDR_NONE)
46377542Swpaul	    return (NO);
46477542Swpaul	return ((*(u_int32_t *)&addr.sin6_addr.s6_addr[12] & mask) == *(u_int32_t *)&net.sin6_addr.s6_addr[12]);
46577542Swpaul    }
46677542Swpaul
467150879Sjhb    /* match IPv6 address against netnumber/prefixlen */
468150879Sjhb    len = strlen(net_tok);
46977542Swpaul    if (*net_tok != '[' || net_tok[len - 1] != ']')
47077542Swpaul	return NO;
471162321Sglebius    ch = net_tok[len - 1];
472162321Sglebius    net_tok[len - 1] = '\0';
473152727Sjhb    if (getaddrinfo(net_tok + 1, NULL, &hints, &res) != 0) {
474152727Sjhb	net_tok[len - 1] = ch;
475152727Sjhb	return NO;
476150879Sjhb    }
47777542Swpaul    memcpy(&net, res->ai_addr, sizeof(net));
47877542Swpaul    freeaddrinfo(res);
47977542Swpaul    net_tok[len - 1] = ch;
48077542Swpaul    if ((mask_len = atoi(mask_tok)) < 0 || mask_len > 128)
48177542Swpaul	return NO;
48277542Swpaul
483127135Snjl    if (net.sin6_scope_id != 0 && addr.sin6_scope_id != net.sin6_scope_id)
48477542Swpaul	return NO;
48577542Swpaul    while (mask_len > 0) {
486150879Sjhb	if (mask_len < 32) {
48777542Swpaul	    mask = htonl(~(0xffffffff >> mask_len));
48877542Swpaul	    if ((*(u_int32_t *)&addr.sin6_addr.s6_addr[i] & mask) != (*(u_int32_t *)&net.sin6_addr.s6_addr[i] & mask))
48977542Swpaul		return NO;
49077542Swpaul	    break;
49177542Swpaul	}
49277542Swpaul	if (*(u_int32_t *)&addr.sin6_addr.s6_addr[i] != *(u_int32_t *)&net.sin6_addr.s6_addr[i])
49377542Swpaul	    return NO;
49477542Swpaul	i += 4;
49577542Swpaul	mask_len -= 32;
496127135Snjl    }
49777542Swpaul    return YES;
49877542Swpaul}
49977542Swpaul#endif /* INET6 */
500150879Sjhb