1/*++
2/* NAME
3/*	cidr_match 3
4/* SUMMARY
5/*	CIDR-style pattern matching
6/* SYNOPSIS
7/*	#include <cidr_match.h>
8/*
9/*	VSTRING *cidr_match_parse(info, pattern, why)
10/*	CIDR_MATCH *info;
11/*	char	*pattern;
12/*	VSTRING	*why;
13/*
14/*	int	cidr_match_execute(info, address)
15/*	CIDR_MATCH *info;
16/*	const char *address;
17/* DESCRIPTION
18/*	This module parses address or address/length patterns and
19/*	provides simple address matching. The implementation is
20/*	such that parsing and execution can be done without dynamic
21/*	memory allocation. The purpose is to minimize overhead when
22/*	called by functions that parse and execute on the fly, such
23/*	as match_hostaddr().
24/*
25/*	cidr_match_parse() parses an address or address/mask
26/*	expression and stores the result into the info argument.
27/*	The result is non-zero in case of problems: either the
28/*	value of the why argument, or a newly allocated VSTRING
29/*	(the caller should give the latter to vstring_free()).
30/*	The pattern argument is destroyed.
31/*
32/*	cidr_match_execute() matches the specified address against
33/*	a list of parsed expressions, and returns the matching
34/*	expression's data structure.
35/* SEE ALSO
36/*	dict_cidr(3) CIDR-style lookup table
37/* AUTHOR(S)
38/*	Wietse Venema
39/*	IBM T.J. Watson Research
40/*	P.O. Box 704
41/*	Yorktown Heights, NY 10598, USA
42/*--*/
43
44/* System library. */
45
46#include <sys_defs.h>
47#include <stdlib.h>
48#include <unistd.h>
49#include <string.h>
50#include <ctype.h>
51#include <sys/socket.h>
52#include <netinet/in.h>
53#include <arpa/inet.h>
54
55/* Utility library. */
56
57#include <msg.h>
58#include <vstring.h>
59#include <stringops.h>
60#include <split_at.h>
61#include <myaddrinfo.h>
62#include <mask_addr.h>
63#include <cidr_match.h>
64
65/* Application-specific. */
66
67 /*
68  * This is how we figure out the address family, address bit count and
69  * address byte count for a CIDR_MATCH entry.
70  */
71#ifdef HAS_IPV6
72#define CIDR_MATCH_ADDR_FAMILY(a) (strchr((a), ':') ? AF_INET6 : AF_INET)
73#define CIDR_MATCH_ADDR_BIT_COUNT(f) \
74    ((f) == AF_INET6 ? MAI_V6ADDR_BITS : \
75     (f) == AF_INET ? MAI_V4ADDR_BITS : \
76     (msg_panic("%s: bad address family %d", myname, (f)), 0))
77#define CIDR_MATCH_ADDR_BYTE_COUNT(f) \
78    ((f) == AF_INET6 ? MAI_V6ADDR_BYTES : \
79     (f) == AF_INET ? MAI_V4ADDR_BYTES : \
80     (msg_panic("%s: bad address family %d", myname, (f)), 0))
81#else
82#define CIDR_MATCH_ADDR_FAMILY(a) (AF_INET)
83#define CIDR_MATCH_ADDR_BIT_COUNT(f) \
84    ((f) == AF_INET ? MAI_V4ADDR_BITS : \
85     (msg_panic("%s: bad address family %d", myname, (f)), 0))
86#define CIDR_MATCH_ADDR_BYTE_COUNT(f) \
87    ((f) == AF_INET ? MAI_V4ADDR_BYTES : \
88     (msg_panic("%s: bad address family %d", myname, (f)), 0))
89#endif
90
91/* cidr_match_execute - match address against compiled CIDR pattern list */
92
93CIDR_MATCH *cidr_match_execute(CIDR_MATCH *list, const char *addr)
94{
95    unsigned char addr_bytes[CIDR_MATCH_ABYTES];
96    unsigned addr_family;
97    unsigned char *mp;
98    unsigned char *np;
99    unsigned char *ap;
100    CIDR_MATCH *entry;
101
102    addr_family = CIDR_MATCH_ADDR_FAMILY(addr);
103    if (inet_pton(addr_family, addr, addr_bytes) != 1)
104	return (0);
105
106    for (entry = list; entry; entry = entry->next) {
107	if (entry->addr_family == addr_family) {
108	    /* Unoptimized case: netmask with some or all bits zero. */
109	    if (entry->mask_shift < entry->addr_bit_count) {
110		for (np = entry->net_bytes, mp = entry->mask_bytes,
111		     ap = addr_bytes; /* void */ ; np++, mp++, ap++) {
112		    if (ap >= addr_bytes + entry->addr_byte_count)
113			return (entry);
114		    if ((*ap & *mp) != *np)
115			break;
116		}
117	    }
118	    /* Optimized case: all 1 netmask (i.e. no netmask specified). */
119	    else {
120		for (np = entry->net_bytes,
121		     ap = addr_bytes; /* void */ ; np++, ap++) {
122		    if (ap >= addr_bytes + entry->addr_byte_count)
123			return (entry);
124		    if (*ap != *np)
125			break;
126		}
127	    }
128	}
129    }
130    return (0);
131}
132
133/* cidr_match_parse - parse CIDR pattern */
134
135VSTRING *cidr_match_parse(CIDR_MATCH *ip, char *pattern, VSTRING *why)
136{
137    const char *myname = "cidr_match_parse";
138    char   *mask_search;
139    char   *mask;
140    MAI_HOSTADDR_STR hostaddr;
141    unsigned char *np;
142    unsigned char *mp;
143
144    /*
145     * Strip [] from [addr/len] or [addr]/len, destroying the pattern. CIDR
146     * maps don't need [] to eliminate syntax ambiguity, but matchlists need
147     * it. While stripping [], figure out where we should start looking for
148     * /mask information.
149     */
150    if (*pattern == '[') {
151	pattern++;
152	if ((mask_search = split_at(pattern, ']')) == 0) {
153	    vstring_sprintf(why ? why : (why = vstring_alloc(20)),
154			    "missing ']' character after \"[%s\"", pattern);
155	    return (why);
156	} else if (*mask_search != '/') {
157	    if (*mask_search != 0) {
158		vstring_sprintf(why ? why : (why = vstring_alloc(20)),
159				"garbage after \"[%s]\"", pattern);
160		return (why);
161	    }
162	    mask_search = pattern;
163	}
164    } else
165	mask_search = pattern;
166
167    /*
168     * Parse the pattern into network and mask, destroying the pattern.
169     */
170    if ((mask = split_at(mask_search, '/')) != 0) {
171	ip->addr_family = CIDR_MATCH_ADDR_FAMILY(pattern);
172	ip->addr_bit_count = CIDR_MATCH_ADDR_BIT_COUNT(ip->addr_family);
173	ip->addr_byte_count = CIDR_MATCH_ADDR_BYTE_COUNT(ip->addr_family);
174	if (!alldig(mask)
175	    || (ip->mask_shift = atoi(mask)) > ip->addr_bit_count
176	    || inet_pton(ip->addr_family, pattern, ip->net_bytes) != 1) {
177	    vstring_sprintf(why ? why : (why = vstring_alloc(20)),
178			  "bad net/mask pattern: \"%s/%s\"", pattern, mask);
179	    return (why);
180	}
181	if (ip->mask_shift > 0) {
182	    /* Allow for bytes > 8. */
183	    memset(ip->mask_bytes, (unsigned char) -1, ip->addr_byte_count);
184	    mask_addr(ip->mask_bytes, ip->addr_byte_count, ip->mask_shift);
185	} else
186	    memset(ip->mask_bytes, 0, ip->addr_byte_count);
187
188	/*
189	 * Sanity check: all host address bits must be zero.
190	 */
191	for (np = ip->net_bytes, mp = ip->mask_bytes;
192	     np < ip->net_bytes + ip->addr_byte_count; np++, mp++) {
193	    if (*np & ~(*mp)) {
194		mask_addr(ip->net_bytes, ip->addr_byte_count, ip->mask_shift);
195		if (inet_ntop(ip->addr_family, ip->net_bytes, hostaddr.buf,
196			      sizeof(hostaddr.buf)) == 0)
197		    msg_fatal("inet_ntop: %m");
198		vstring_sprintf(why ? why : (why = vstring_alloc(20)),
199				"non-null host address bits in \"%s/%s\", "
200				"perhaps you should use \"%s/%d\" instead",
201				pattern, mask, hostaddr.buf, ip->mask_shift);
202		return (why);
203	    }
204	}
205    }
206
207    /*
208     * No /mask specified. Treat a bare network address as /allbits.
209     */
210    else {
211	ip->addr_family = CIDR_MATCH_ADDR_FAMILY(pattern);
212	ip->addr_bit_count = CIDR_MATCH_ADDR_BIT_COUNT(ip->addr_family);
213	ip->addr_byte_count = CIDR_MATCH_ADDR_BYTE_COUNT(ip->addr_family);
214	if (inet_pton(ip->addr_family, pattern, ip->net_bytes) != 1) {
215	    vstring_sprintf(why ? why : (why = vstring_alloc(20)),
216			    "bad address pattern: \"%s\"", pattern);
217	    return (why);
218	}
219	ip->mask_shift = ip->addr_bit_count;
220	/* Allow for bytes > 8. */
221	memset(ip->mask_bytes, (unsigned char) -1, ip->addr_byte_count);
222    }
223
224    /*
225     * Wrap up the result.
226     */
227    ip->next = 0;
228
229    return (0);
230}
231