Postfix3.3.1
match_ops.c
[詳解]
1 /*++
2 /* NAME
3 /* match_ops 3
4 /* SUMMARY
5 /* simple string or host pattern matching
6 /* SYNOPSIS
7 /* #include <match_list.h>
8 /*
9 /* int match_string(list, string, pattern)
10 /* MATCH_LIST *list;
11 /* const char *string;
12 /* const char *pattern;
13 /*
14 /* int match_hostname(list, name, pattern)
15 /* MATCH_LIST *list;
16 /* const char *name;
17 /* const char *pattern;
18 /*
19 /* int match_hostaddr(list, addr, pattern)
20 /* MATCH_LIST *list;
21 /* const char *addr;
22 /* const char *pattern;
23 /* DESCRIPTION
24 /* This module implements simple string and host name or address
25 /* matching. The matching process is case insensitive. If a pattern
26 /* has the form type:name, table lookup is used instead of string
27 /* or address comparison.
28 /*
29 /* match_string() matches the string against the pattern, requiring
30 /* an exact (case-insensitive) match. The flags argument is not used.
31 /*
32 /* match_hostname() matches the host name when the hostname matches
33 /* the pattern exactly, or when the pattern matches a parent domain
34 /* of the named host. The flags argument specifies the bit-wise OR
35 /* of zero or more of the following:
36 /* .IP MATCH_FLAG_PARENT
37 /* The hostname pattern foo.com matches itself and any name below
38 /* the domain foo.com. If this flag is cleared, foo.com matches itself
39 /* only, and .foo.com matches any name below the domain foo.com.
40 /* .IP MATCH_FLAG_RETURN
41 /* Log a warning, return "not found", and set list->error to
42 /* a non-zero dictionary error code, instead of raising a fatal
43 /* run-time error.
44 /* .RE
45 /* Specify MATCH_FLAG_NONE to request none of the above.
46 /*
47 /* match_hostaddr() matches a host address when the pattern is
48 /* identical to the host address, or when the pattern is a net/mask
49 /* that contains the address. The mask specifies the number of
50 /* bits in the network part of the pattern. The flags argument is
51 /* not used.
52 /* LICENSE
53 /* .ad
54 /* .fi
55 /* The Secure Mailer license must be distributed with this software.
56 /* AUTHOR(S)
57 /* Wietse Venema
58 /* IBM T.J. Watson Research
59 /* P.O. Box 704
60 /* Yorktown Heights, NY 10598, USA
61 /*
62 /* Wietse Venema
63 /* Google, Inc.
64 /* 111 8th Avenue
65 /* New York, NY 10011, USA
66 /*--*/
67 
68 /* System library. */
69 
70 #include <sys_defs.h>
71 #include <netinet/in.h>
72 #include <arpa/inet.h>
73 #include <string.h>
74 #include <stdlib.h>
75 
76 /* Utility library. */
77 
78 #include <msg.h>
79 #include <mymalloc.h>
80 #include <split_at.h>
81 #include <dict.h>
82 #include <match_list.h>
83 #include <stringops.h>
84 #include <cidr_match.h>
85 
86 #define MATCH_DICTIONARY(pattern) \
87  ((pattern)[0] != '[' && strchr((pattern), ':') != 0)
88 
89 /* match_error - return or raise fatal error */
90 
91 static int match_error(MATCH_LIST *list, const char *fmt,...)
92 {
93  VSTRING *buf = vstring_alloc(100);
94  va_list ap;
95 
96  /*
97  * Report, and maybe return.
98  */
99  va_start(ap, fmt);
100  vstring_vsprintf(buf, fmt, ap);
101  va_end(ap);
102  if (list->flags & MATCH_FLAG_RETURN) {
103  msg_warn("%s: %s", list->pname, vstring_str(buf));
104  } else {
105  msg_fatal("%s: %s", list->pname, vstring_str(buf));
106  }
107  vstring_free(buf);
108  return (0);
109 }
110 
111 /* match_string - match a string literal */
112 
113 int match_string(MATCH_LIST *list, const char *string, const char *pattern)
114 {
115  const char *myname = "match_string";
116  DICT *dict;
117 
118  if (msg_verbose)
119  msg_info("%s: %s: %s ~? %s", myname, list->pname, string, pattern);
120 
121  /*
122  * Try dictionary lookup: exact match.
123  */
124  if (MATCH_DICTIONARY(pattern)) {
125  if ((dict = dict_handle(pattern)) == 0)
126  msg_panic("%s: unknown dictionary: %s", myname, pattern);
127  if (dict_get(dict, string) != 0)
128  return (1);
129  if ((list->error = dict->error) != 0)
130  return (match_error(list, "%s:%s: table lookup problem",
131  dict->type, dict->name));
132  return (0);
133  }
134 
135  /*
136  * Try an exact string match. Note that the string and pattern are
137  * already casefolded.
138  */
139  if (strcmp(string, pattern) == 0) {
140  return (1);
141  }
142 
143  /*
144  * No match found.
145  */
146  return (0);
147 }
148 
149 /* match_hostname - match a host by name */
150 
151 int match_hostname(MATCH_LIST *list, const char *name, const char *pattern)
152 {
153  const char *myname = "match_hostname";
154  const char *pd;
155  const char *entry;
156  const char *next;
157  int match;
158  DICT *dict;
159 
160  if (msg_verbose)
161  msg_info("%s: %s: %s ~? %s", myname, list->pname, name, pattern);
162 
163  /*
164  * Try dictionary lookup: exact match and parent domains.
165  *
166  * Don't look up parent domain substrings with regexp maps etc.
167  */
168  if (MATCH_DICTIONARY(pattern)) {
169  if ((dict = dict_handle(pattern)) == 0)
170  msg_panic("%s: unknown dictionary: %s", myname, pattern);
171  match = 0;
172  for (entry = name; *entry != 0; entry = next) {
173  if (entry == name || (dict->flags & DICT_FLAG_FIXED)) {
174  match = (dict_get(dict, entry) != 0);
175  if (msg_verbose > 1)
176  msg_info("%s: %s: lookup %s:%s %s: %s",
177  myname, list->pname, dict->type, dict->name,
178  entry, match ? "found" : "notfound");
179  if (match != 0)
180  break;
181  if ((list->error = dict->error) != 0)
182  return (match_error(list, "%s:%s: table lookup problem",
183  dict->type, dict->name));
184  }
185  if ((next = strchr(entry + 1, '.')) == 0)
186  break;
187  if (list->flags & MATCH_FLAG_PARENT)
188  next += 1;
189  }
190  return (match);
191  }
192 
193  /*
194  * Try an exact match with the host name. Note that the name and the
195  * pattern are already casefolded.
196  */
197  if (strcmp(name, pattern) == 0) {
198  return (1);
199  }
200 
201  /*
202  * See if the pattern is a parent domain of the hostname. Note that the
203  * name and the pattern are already casefolded.
204  */
205  else {
206  if (list->flags & MATCH_FLAG_PARENT) {
207  pd = name + strlen(name) - strlen(pattern);
208  if (pd > name && pd[-1] == '.' && strcmp(pd, pattern) == 0)
209  return (1);
210  } else if (pattern[0] == '.') {
211  pd = name + strlen(name) - strlen(pattern);
212  if (pd > name && strcmp(pd, pattern) == 0)
213  return (1);
214  }
215  }
216  return (0);
217 }
218 
219 /* match_hostaddr - match host by address */
220 
221 int match_hostaddr(MATCH_LIST *list, const char *addr, const char *pattern)
222 {
223  const char *myname = "match_hostaddr";
224  char *saved_patt;
225  CIDR_MATCH match_info;
226  DICT *dict;
227  VSTRING *err;
228  int rc;
229 
230  if (msg_verbose)
231  msg_info("%s: %s: %s ~? %s", myname, list->pname, addr, pattern);
232 
233 #define V4_ADDR_STRING_CHARS "01234567890."
234 #define V6_ADDR_STRING_CHARS V4_ADDR_STRING_CHARS "abcdefABCDEF:"
235 
236  if (addr[strspn(addr, V6_ADDR_STRING_CHARS)] != 0)
237  return (0);
238 
239  /*
240  * Try dictionary lookup. This can be case insensitive.
241  */
242  if (MATCH_DICTIONARY(pattern)) {
243  if ((dict = dict_handle(pattern)) == 0)
244  msg_panic("%s: unknown dictionary: %s", myname, pattern);
245  if (dict_get(dict, addr) != 0)
246  return (1);
247  if ((list->error = dict->error) != 0)
248  return (match_error(list, "%s:%s: table lookup problem",
249  dict->type, dict->name));
250  return (0);
251  }
252 
253  /*
254  * Try an exact match with the host address. Note that the address and
255  * pattern are already casefolded.
256  */
257  if (pattern[0] != '[') {
258  if (strcmp(addr, pattern) == 0)
259  return (1);
260  } else {
261  size_t addr_len = strlen(addr);
262 
263  if (strncmp(addr, pattern + 1, addr_len) == 0
264  && strcmp(pattern + 1 + addr_len, "]") == 0)
265  return (1);
266  }
267 
268  /*
269  * Light-weight tests before we get into expensive operations.
270  *
271  * - Don't bother matching IPv4 against IPv6. Postfix transforms
272  * IPv4-in-IPv6 to native IPv4 form when IPv4 support is enabled in
273  * Postfix; if not, then Postfix has no business dealing with IPv4
274  * addresses anyway.
275  *
276  * - Don't bother unless the pattern is either an IPv6 address or net/mask.
277  *
278  * We can safely skip IPv4 address patterns because their form is
279  * unambiguous and they did not match in the strcmp() calls above.
280  *
281  * XXX We MUST skip (parent) domain names, which may appear in NAMADR_LIST
282  * input, to avoid triggering false cidr_match_parse() errors.
283  *
284  * The last two conditions below are for backwards compatibility with
285  * earlier Postfix versions: don't abort with fatal errors on junk that
286  * was silently ignored (principle of least astonishment).
287  */
288  if (!strchr(addr, ':') != !strchr(pattern, ':')
289  || pattern[strcspn(pattern, ":/")] == 0
290  || pattern[strspn(pattern, V4_ADDR_STRING_CHARS)] == 0
291  || pattern[strspn(pattern, V6_ADDR_STRING_CHARS "[]/")] != 0)
292  return (0);
293 
294  /*
295  * No escape from expensive operations: either we have a net/mask
296  * pattern, or we have an address that can have multiple valid
297  * representations (e.g., 0:0:0:0:0:0:0:1 versus ::1, etc.). The only way
298  * to find out if the address matches the pattern is to transform
299  * everything into to binary form, and to do the comparison there.
300  */
301  saved_patt = mystrdup(pattern);
302  err = cidr_match_parse(&match_info, saved_patt, CIDR_MATCH_TRUE,
303  (VSTRING *) 0);
304  myfree(saved_patt);
305  if (err != 0) {
306  list->error = DICT_ERR_RETRY;
307  rc = match_error(list, "%s", vstring_str(err));
308  vstring_free(err);
309  return (rc);
310  }
311  return (cidr_match_execute(&match_info, addr) != 0);
312 }
int msg_verbose
Definition: msg.c:177
void myfree(void *ptr)
Definition: mymalloc.c:207
#define MATCH_FLAG_RETURN
Definition: match_list.h:40
char * mystrdup(const char *str)
Definition: mymalloc.c:225
NORETURN msg_panic(const char *fmt,...)
Definition: msg.c:295
#define vstring_str(vp)
Definition: vstring.h:71
char * name
Definition: dict.h:80
#define DICT_FLAG_FIXED
Definition: dict.h:114
int flags
Definition: dict.h:81
#define DICT_ERR_RETRY
Definition: dict.h:178
int match_hostaddr(MATCH_LIST *list, const char *addr, const char *pattern)
Definition: match_ops.c:221
#define V6_ADDR_STRING_CHARS
#define V4_ADDR_STRING_CHARS
int const char * fmt
Definition: dict.h:78
char * type
Definition: dict.h:79
VSTRING * vstring_vsprintf(VSTRING *vp, const char *format, va_list ap)
Definition: vstring.c:614
int match_string(MATCH_LIST *list, const char *string, const char *pattern)
Definition: match_ops.c:113
#define dict_get(dp, key)
Definition: dict.h:236
#define MATCH_FLAG_PARENT
Definition: match_list.h:39
CIDR_MATCH * cidr_match_execute(CIDR_MATCH *list, const char *addr)
Definition: cidr_match.c:148
DICT * dict_handle(const char *dict_name)
Definition: dict.c:333
void msg_warn(const char *fmt,...)
Definition: msg.c:215
VSTRING * vstring_alloc(ssize_t len)
Definition: vstring.c:353
int error
Definition: dict.h:94
NORETURN msg_fatal(const char *fmt,...)
Definition: msg.c:249
char * pname
Definition: match_list.h:28
int match_hostname(MATCH_LIST *list, const char *name, const char *pattern)
Definition: match_ops.c:151
VSTRING * cidr_match_parse(CIDR_MATCH *ip, char *pattern, int match, VSTRING *why)
Definition: cidr_match.c:186
VSTRING * vstring_free(VSTRING *vp)
Definition: vstring.c:380
#define MATCH_DICTIONARY(pattern)
Definition: match_ops.c:86
#define CIDR_MATCH_TRUE
Definition: cidr_match.h:57
void msg_info(const char *fmt,...)
Definition: msg.c:199