Postfix3.3.1
cidr_match.c
[詳解]
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, match, 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 /* AUXILIARY FUNCTIONS
18 /* VSTRING *cidr_match_parse_if(info, pattern, match, why)
19 /* CIDR_MATCH *info;
20 /* char *pattern;
21 /* VSTRING *why;
22 /*
23 /* void cidr_match_endif(info)
24 /* CIDR_MATCH *info;
25 /* DESCRIPTION
26 /* This module parses address or address/length patterns and
27 /* provides simple address matching. The implementation is
28 /* such that parsing and execution can be done without dynamic
29 /* memory allocation. The purpose is to minimize overhead when
30 /* called by functions that parse and execute on the fly, such
31 /* as match_hostaddr().
32 /*
33 /* cidr_match_parse() parses an address or address/mask
34 /* expression and stores the result into the info argument.
35 /* A non-zero (or zero) match argument requests a positive (or
36 /* negative) match. The symbolic constants CIDR_MATCH_TRUE and
37 /* CIDR_MATCH_FALSE may help to improve code readability.
38 /* The result is non-zero in case of problems: either the
39 /* value of the why argument, or a newly allocated VSTRING
40 /* (the caller should give the latter to vstring_free()).
41 /* The pattern argument is destroyed.
42 /*
43 /* cidr_match_parse_if() parses the address that follows an IF
44 /* token, and stores the result into the info argument.
45 /* The arguments are the same as for cidr_match_parse().
46 /*
47 /* cidr_match_endif() handles the occurrence of an ENDIF token,
48 /* and updates the info argument.
49 /*
50 /* cidr_match_execute() matches the specified address against
51 /* a list of parsed expressions, and returns the matching
52 /* expression's data structure.
53 /* SEE ALSO
54 /* dict_cidr(3) CIDR-style lookup table
55 /* AUTHOR(S)
56 /* Wietse Venema
57 /* IBM T.J. Watson Research
58 /* P.O. Box 704
59 /* Yorktown Heights, NY 10598, USA
60 /*
61 /* Wietse Venema
62 /* Google, Inc.
63 /* 111 8th Avenue
64 /* New York, NY 10011, USA
65 /*--*/
66 
67 /* System library. */
68 
69 #include <sys_defs.h>
70 #include <stdlib.h>
71 #include <unistd.h>
72 #include <string.h>
73 #include <ctype.h>
74 #include <sys/socket.h>
75 #include <netinet/in.h>
76 #include <arpa/inet.h>
77 
78 /* Utility library. */
79 
80 #include <msg.h>
81 #include <vstring.h>
82 #include <stringops.h>
83 #include <split_at.h>
84 #include <myaddrinfo.h>
85 #include <mask_addr.h>
86 #include <cidr_match.h>
87 
88 /* Application-specific. */
89 
90  /*
91  * This is how we figure out the address family, address bit count and
92  * address byte count for a CIDR_MATCH entry.
93  */
94 #ifdef HAS_IPV6
95 #define CIDR_MATCH_ADDR_FAMILY(a) (strchr((a), ':') ? AF_INET6 : AF_INET)
96 #define CIDR_MATCH_ADDR_BIT_COUNT(f) \
97  ((f) == AF_INET6 ? MAI_V6ADDR_BITS : \
98  (f) == AF_INET ? MAI_V4ADDR_BITS : \
99  (msg_panic("%s: bad address family %d", myname, (f)), 0))
100 #define CIDR_MATCH_ADDR_BYTE_COUNT(f) \
101  ((f) == AF_INET6 ? MAI_V6ADDR_BYTES : \
102  (f) == AF_INET ? MAI_V4ADDR_BYTES : \
103  (msg_panic("%s: bad address family %d", myname, (f)), 0))
104 #else
105 #define CIDR_MATCH_ADDR_FAMILY(a) (AF_INET)
106 #define CIDR_MATCH_ADDR_BIT_COUNT(f) \
107  ((f) == AF_INET ? MAI_V4ADDR_BITS : \
108  (msg_panic("%s: bad address family %d", myname, (f)), 0))
109 #define CIDR_MATCH_ADDR_BYTE_COUNT(f) \
110  ((f) == AF_INET ? MAI_V4ADDR_BYTES : \
111  (msg_panic("%s: bad address family %d", myname, (f)), 0))
112 #endif
113 
114 /* cidr_match_entry - match one entry */
115 
116 static inline int cidr_match_entry(CIDR_MATCH *entry,
117  unsigned char *addr_bytes)
118 {
119  unsigned char *mp;
120  unsigned char *np;
121  unsigned char *ap;
122 
123  /* Unoptimized case: netmask with some or all bits zero. */
124  if (entry->mask_shift < entry->addr_bit_count) {
125  for (np = entry->net_bytes, mp = entry->mask_bytes,
126  ap = addr_bytes; /* void */ ; np++, mp++, ap++) {
127  if (ap >= addr_bytes + entry->addr_byte_count)
128  return (entry->match);
129  if ((*ap & *mp) != *np)
130  break;
131  }
132  }
133  /* Optimized case: all 1 netmask (i.e. no netmask specified). */
134  else {
135  for (np = entry->net_bytes,
136  ap = addr_bytes; /* void */ ; np++, ap++) {
137  if (ap >= addr_bytes + entry->addr_byte_count)
138  return (entry->match);
139  if (*ap != *np)
140  break;
141  }
142  }
143  return (!entry->match);
144 }
145 
146 /* cidr_match_execute - match address against compiled CIDR pattern list */
147 
148 CIDR_MATCH *cidr_match_execute(CIDR_MATCH *list, const char *addr)
149 {
150  unsigned char addr_bytes[CIDR_MATCH_ABYTES];
151  unsigned addr_family;
152  CIDR_MATCH *entry;
153 
154  addr_family = CIDR_MATCH_ADDR_FAMILY(addr);
155  if (inet_pton(addr_family, addr, addr_bytes) != 1)
156  return (0);
157 
158  for (entry = list; entry; entry = entry->next) {
159 
160  switch (entry->op) {
161 
162  case CIDR_MATCH_OP_MATCH:
163  if (entry->addr_family == addr_family)
164  if (cidr_match_entry(entry, addr_bytes))
165  return (entry);
166  break;
167 
168  case CIDR_MATCH_OP_IF:
169  if (entry->addr_family == addr_family)
170  if (cidr_match_entry(entry, addr_bytes))
171  continue;
172  /* An IF without matching ENDIF has no end-of block entry. */
173  if ((entry = entry->block_end) == 0)
174  return (0);
175  /* FALLTHROUGH */
176 
177  case CIDR_MATCH_OP_ENDIF:
178  continue;
179  }
180  }
181  return (0);
182 }
183 
184 /* cidr_match_parse - parse CIDR pattern */
185 
186 VSTRING *cidr_match_parse(CIDR_MATCH *ip, char *pattern, int match,
187  VSTRING *why)
188 {
189  const char *myname = "cidr_match_parse";
190  char *mask_search;
191  char *mask;
192  MAI_HOSTADDR_STR hostaddr;
193  unsigned char *np;
194  unsigned char *mp;
195 
196  /*
197  * Strip [] from [addr/len] or [addr]/len, destroying the pattern. CIDR
198  * maps don't need [] to eliminate syntax ambiguity, but matchlists need
199  * it. While stripping [], figure out where we should start looking for
200  * /mask information.
201  */
202  if (*pattern == '[') {
203  pattern++;
204  if ((mask_search = split_at(pattern, ']')) == 0) {
205  vstring_sprintf(why ? why : (why = vstring_alloc(20)),
206  "missing ']' character after \"[%s\"", pattern);
207  return (why);
208  } else if (*mask_search != '/') {
209  if (*mask_search != 0) {
210  vstring_sprintf(why ? why : (why = vstring_alloc(20)),
211  "garbage after \"[%s]\"", pattern);
212  return (why);
213  }
214  mask_search = pattern;
215  }
216  } else
217  mask_search = pattern;
218 
219  /*
220  * Parse the pattern into network and mask, destroying the pattern.
221  */
222  if ((mask = split_at(mask_search, '/')) != 0) {
223  ip->addr_family = CIDR_MATCH_ADDR_FAMILY(pattern);
226  if (!alldig(mask)
227  || (ip->mask_shift = atoi(mask)) > ip->addr_bit_count
228  || inet_pton(ip->addr_family, pattern, ip->net_bytes) != 1) {
229  vstring_sprintf(why ? why : (why = vstring_alloc(20)),
230  "bad net/mask pattern: \"%s/%s\"", pattern, mask);
231  return (why);
232  }
233  if (ip->mask_shift > 0) {
234  /* Allow for bytes > 8. */
235  memset(ip->mask_bytes, ~0U, ip->addr_byte_count);
237  } else
238  memset(ip->mask_bytes, 0, ip->addr_byte_count);
239 
240  /*
241  * Sanity check: all host address bits must be zero.
242  */
243  for (np = ip->net_bytes, mp = ip->mask_bytes;
244  np < ip->net_bytes + ip->addr_byte_count; np++, mp++) {
245  if (*np & ~(*mp)) {
247  if (inet_ntop(ip->addr_family, ip->net_bytes, hostaddr.buf,
248  sizeof(hostaddr.buf)) == 0)
249  msg_fatal("inet_ntop: %m");
250  vstring_sprintf(why ? why : (why = vstring_alloc(20)),
251  "non-null host address bits in \"%s/%s\", "
252  "perhaps you should use \"%s/%d\" instead",
253  pattern, mask, hostaddr.buf, ip->mask_shift);
254  return (why);
255  }
256  }
257  }
258 
259  /*
260  * No /mask specified. Treat a bare network address as /allbits.
261  */
262  else {
263  ip->addr_family = CIDR_MATCH_ADDR_FAMILY(pattern);
266  if (inet_pton(ip->addr_family, pattern, ip->net_bytes) != 1) {
267  vstring_sprintf(why ? why : (why = vstring_alloc(20)),
268  "bad address pattern: \"%s\"", pattern);
269  return (why);
270  }
271  ip->mask_shift = ip->addr_bit_count;
272  /* Allow for bytes > 8. */
273  memset(ip->mask_bytes, ~0U, ip->addr_byte_count);
274  }
275 
276  /*
277  * Wrap up the result.
278  */
279  ip->op = CIDR_MATCH_OP_MATCH;
280  ip->match = match;
281  ip->next = 0;
282  ip->block_end = 0;
283 
284  return (0);
285 }
286 
287 /* cidr_match_parse_if - parse CIDR pattern after IF */
288 
289 VSTRING *cidr_match_parse_if(CIDR_MATCH *ip, char *pattern, int match,
290  VSTRING *why)
291 {
292  VSTRING *ret;
293 
294  if ((ret = cidr_match_parse(ip, pattern, match, why)) == 0)
295  ip->op = CIDR_MATCH_OP_IF;
296  return (ret);
297 }
298 
299 /* cidr_match_endif - handle ENDIF pattern */
300 
302 {
303  memset(ip, 0, sizeof(*ip));
304  ip->op = CIDR_MATCH_OP_ENDIF;
305  ip->next = 0; /* maybe not all bits 0 */
306  ip->block_end = 0;
307 }
void cidr_match_endif(CIDR_MATCH *ip)
Definition: cidr_match.c:301
void mask_addr(unsigned char *addr_bytes, unsigned addr_byte_count, unsigned network_bits)
Definition: mask_addr.c:50
unsigned char mask_shift
Definition: cidr_match.h:48
unsigned char addr_bit_count
Definition: cidr_match.h:47
#define CIDR_MATCH_OP_IF
Definition: cidr_match.h:54
#define CIDR_MATCH_ADDR_BIT_COUNT(f)
Definition: cidr_match.c:106
struct CIDR_MATCH * block_end
Definition: cidr_match.h:50
unsigned char net_bytes[CIDR_MATCH_ABYTES]
Definition: cidr_match.h:43
unsigned char addr_byte_count
Definition: cidr_match.h:46
#define CIDR_MATCH_ABYTES
Definition: cidr_match.h:34
int alldig(const char *string)
Definition: alldig.c:38
#define CIDR_MATCH_OP_MATCH
Definition: cidr_match.h:53
VSTRING * cidr_match_parse_if(CIDR_MATCH *ip, char *pattern, int match, VSTRING *why)
Definition: cidr_match.c:289
char buf[MAI_HOSTADDR_STRSIZE]
Definition: myaddrinfo.h:146
CIDR_MATCH * cidr_match_execute(CIDR_MATCH *list, const char *addr)
Definition: cidr_match.c:148
const char * inet_ntop(int af, const void *src, char *dst, SOCKADDR_SIZE size)
Definition: sys_compat.c:325
VSTRING * vstring_alloc(ssize_t len)
Definition: vstring.c:353
int inet_pton(int af, const char *src, void *dst)
Definition: sys_compat.c:368
VSTRING * vstring_sprintf(VSTRING *vp, const char *format,...)
Definition: vstring.c:602
NORETURN msg_fatal(const char *fmt,...)
Definition: msg.c:249
unsigned char mask_bytes[CIDR_MATCH_ABYTES]
Definition: cidr_match.h:44
#define CIDR_MATCH_OP_ENDIF
Definition: cidr_match.h:55
#define CIDR_MATCH_ADDR_BYTE_COUNT(f)
Definition: cidr_match.c:109
struct CIDR_MATCH * next
Definition: cidr_match.h:49
VSTRING * cidr_match_parse(CIDR_MATCH *ip, char *pattern, int match, VSTRING *why)
Definition: cidr_match.c:186
unsigned char addr_family
Definition: cidr_match.h:45
char * split_at(char *string, int delimiter)
Definition: split_at.c:53
#define CIDR_MATCH_ADDR_FAMILY(a)
Definition: cidr_match.c:105