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