Postfix3.3.1
match_list.c
[詳解]
1 /*++
2 /* NAME
3 /* match_list 3
4 /* SUMMARY
5 /* generic list-based pattern matching
6 /* SYNOPSIS
7 /* #include <match_list.h>
8 /*
9 /* MATCH_LIST *match_list_init(pname, flags, pattern_list, count, func,...)
10 /* const char *pname;
11 /* int flags;
12 /* const char *pattern_list;
13 /* int count;
14 /* int (*func)(int flags, const char *string, const char *pattern);
15 /*
16 /* int match_list_match(list, string,...)
17 /* MATCH_LIST *list;
18 /* const char *string;
19 /*
20 /* void match_list_free(list)
21 /* MATCH_LIST *list;
22 /* DESCRIPTION
23 /* This module implements a framework for tests for list
24 /* membership. The actual tests are done by user-supplied
25 /* functions.
26 /*
27 /* Patterns are separated by whitespace and/or commas. A pattern
28 /* is either a string, a file name (in which case the contents
29 /* of the file are substituted for the file name) or a type:name
30 /* lookup table specification. In order to reverse the result
31 /* of a pattern match, precede a pattern with an exclamation
32 /* point (!).
33 /*
34 /* match_list_init() performs initializations. When the global
35 /* util_utf8_enable variable is non-zero, and when the code
36 /* is compiled with EAI support, string comparison will use
37 /* caseless UTF-8 mode. Otherwise, only ASCII characters will
38 /* be casefolded.
39 /*
40 /* match_list_match() matches strings against the specified
41 /* pattern list, passing the first string to the first function
42 /* given to match_list_init(), the second string to the second
43 /* function, and so on.
44 /*
45 /* match_list_free() releases storage allocated by match_list_init().
46 /*
47 /* Arguments:
48 /* .IP pname
49 /* Parameter name or other identifying information that is
50 /* prepended to error messages.
51 /* .IP flags
52 /* Specifies the bit-wise OR of zero or more of the following:
53 /* .RS
54 /* .IP MATCH_FLAG_PARENT
55 /* The hostname pattern foo.com matches any name within the
56 /* domain foo.com. If this flag is cleared, foo.com matches
57 /* itself only, and .foo.com matches any name below the domain
58 /* foo.com.
59 /* .IP MATCH_FLAG_RETURN
60 /* Request that match_list_match() logs a warning and returns
61 /* zero (with list->error set to a non-zero dictionary error
62 /* code) instead of raising a fatal run-time error.
63 /* .RE
64 /* Specify MATCH_FLAG_NONE to request none of the above.
65 /* .IP pattern_list
66 /* A list of patterns.
67 /* .IP count
68 /* Specifies how many match functions follow.
69 /* .IP list
70 /* Pattern list produced by match_list_init().
71 /* .IP string
72 /* Search string.
73 /* DIAGNOSTICS
74 /* Fatal error: unable to open or read a match_list file; invalid
75 /* match_list pattern; casefold error (UTF-8 mode only).
76 /* SEE ALSO
77 /* host_match(3) match hosts by name or by address
78 /* LICENSE
79 /* .ad
80 /* .fi
81 /* The Secure Mailer license must be distributed with this software.
82 /* AUTHOR(S)
83 /* Wietse Venema
84 /* IBM T.J. Watson Research
85 /* P.O. Box 704
86 /* Yorktown Heights, NY 10598, USA
87 /*--*/
88 
89 /* System library. */
90 
91 #include <sys_defs.h>
92 #include <unistd.h>
93 #include <string.h>
94 #include <fcntl.h>
95 #include <stdlib.h>
96 #include <stdarg.h>
97 
98 /* Utility library. */
99 
100 #include <msg.h>
101 #include <mymalloc.h>
102 #include <vstring.h>
103 #include <vstream.h>
104 #include <vstring_vstream.h>
105 #include <stringops.h>
106 #include <argv.h>
107 #include <dict.h>
108 #include <match_list.h>
109 
110 /* Application-specific */
111 
112 #define MATCH_DICTIONARY(pattern) \
113  ((pattern)[0] != '[' && strchr((pattern), ':') != 0)
114 
115 /* match_list_parse - parse buffer, destroy buffer */
116 
117 static ARGV *match_list_parse(MATCH_LIST *match_list, ARGV *pat_list,
118  char *string, int init_match)
119 {
120  const char *myname = "match_list_parse";
121  VSTRING *buf = vstring_alloc(10);
122  VSTREAM *fp;
123  const char *delim = CHARS_COMMA_SP;
124  char *bp = string;
125  char *start;
126  char *item;
127  char *map_type_name_flags;
128  int match;
129 
130  /*
131  * We do not use DICT_FLAG_FOLD_FIX, because we casefold the search
132  * string at the beginning of a search, and we use strcmp() for string
133  * comparison. This works because string patterns are casefolded during
134  * match_list initialization, and databases are supposed to fold case
135  * upon creation.
136  */
137 #define OPEN_FLAGS O_RDONLY
138 #define DICT_FLAGS (DICT_FLAG_LOCK | DICT_FLAG_UTF8_REQUEST)
139 #define STR(x) vstring_str(x)
140 
141  /*
142  * /filename contents are expanded in-line. To support !/filename we
143  * prepend the negation operator to each item from the file.
144  *
145  * If there is an error, implement graceful degradation by inserting a
146  * pseudo table whose lookups fail with a warning message.
147  */
148  while ((start = mystrtokq(&bp, delim, CHARS_BRACE)) != 0) {
149  if (*start == '#') {
150  msg_warn("%s: comment at end of line is not supported: %s %s",
151  match_list->pname, start, bp);
152  break;
153  }
154  for (match = init_match, item = start; *item == '!'; item++)
155  match = !match;
156  if (*item == 0)
157  /* No graceful degradation for this... */
158  msg_fatal("%s: no pattern after '!'", match_list->pname);
159  if (*item == '/') { /* /file/name */
160  if ((fp = vstream_fopen(item, O_RDONLY, 0)) == 0) {
161  /* Replace unusable pattern with pseudo table. */
162  vstring_sprintf(buf, "%s:%s", DICT_TYPE_NOFILE, item);
163  if (dict_handle(STR(buf)) == 0)
164  dict_register(STR(buf),
167  "open file %s: %m", item));
168  argv_add(pat_list, STR(buf), (char *) 0);
169  } else {
170  while (vstring_fgets(buf, fp))
171  if (vstring_str(buf)[0] != '#')
172  pat_list = match_list_parse(match_list, pat_list,
173  vstring_str(buf), match);
174  if (vstream_fclose(fp))
175  msg_fatal("%s: read file %s: %m", myname, item);
176  }
177  } else if (MATCH_DICTIONARY(item)) { /* type:table */
178  vstring_sprintf(buf, "%s%s(%o,%s)", match ? "" : "!",
180  map_type_name_flags = STR(buf) + (match == 0);
181  if (dict_handle(map_type_name_flags) == 0)
182  dict_register(map_type_name_flags,
184  argv_add(pat_list, STR(buf), (char *) 0);
185  } else { /* other pattern */
186  casefold(match_list->fold_buf, match ?
187  item : STR(vstring_sprintf(buf, "!%s", item)));
188  argv_add(pat_list, STR(match_list->fold_buf), (char *) 0);
189  }
190  }
191  vstring_free(buf);
192  return (pat_list);
193 }
194 
195 /* match_list_init - initialize pattern list */
196 
197 MATCH_LIST *match_list_init(const char *pname, int flags,
198  const char *patterns, int match_count,...)
199 {
200  MATCH_LIST *list;
201  char *saved_patterns;
202  va_list ap;
203  int i;
204 
205  if (flags & ~MATCH_FLAG_ALL)
206  msg_panic("match_list_init: bad flags 0x%x", flags);
207 
208  list = (MATCH_LIST *) mymalloc(sizeof(*list));
209  list->pname = mystrdup(pname);
210  list->flags = flags;
211  list->match_count = match_count;
212  list->match_func =
213  (MATCH_LIST_FN *) mymalloc(match_count * sizeof(MATCH_LIST_FN));
214  list->match_args =
215  (const char **) mymalloc(match_count * sizeof(const char *));
216  va_start(ap, match_count);
217  for (i = 0; i < match_count; i++)
218  list->match_func[i] = va_arg(ap, MATCH_LIST_FN);
219  va_end(ap);
220  list->error = 0;
221  list->fold_buf = vstring_alloc(20);
222 
223 #define DO_MATCH 1
224 
225  saved_patterns = mystrdup(patterns);
226  list->patterns = match_list_parse(list, argv_alloc(1), saved_patterns,
227  DO_MATCH);
228  argv_terminate(list->patterns);
229  myfree(saved_patterns);
230  return (list);
231 }
232 
233 /* match_list_match - match strings against pattern list */
234 
236 {
237  const char *myname = "match_list_match";
238  char **cpp;
239  char *pat;
240  int match;
241  int i;
242  va_list ap;
243 
244  /*
245  * Iterate over all patterns in the list, stop at the first match.
246  */
247  va_start(ap, list);
248  for (i = 0; i < list->match_count; i++)
249  list->match_args[i] = va_arg(ap, const char *);
250  va_end(ap);
251 
252  list->error = 0;
253  for (cpp = list->patterns->argv; (pat = *cpp) != 0; cpp++) {
254  for (match = 1; *pat == '!'; pat++)
255  match = !match;
256  for (i = 0; i < list->match_count; i++) {
257  casefold(list->fold_buf, list->match_args[i]);
258  if (list->match_func[i] (list, STR(list->fold_buf), pat))
259  return (match);
260  else if (list->error != 0)
261  return (0);
262  }
263  }
264  if (msg_verbose)
265  for (i = 0; i < list->match_count; i++)
266  msg_info("%s: %s: no match", myname, list->match_args[i]);
267  return (0);
268 }
269 
270 /* match_list_free - release storage */
271 
273 {
274  /* XXX Should decrement map refcounts. */
275  myfree(list->pname);
276  argv_free(list->patterns);
277  myfree((void *) list->match_func);
278  myfree((void *) list->match_args);
279  vstring_free(list->fold_buf);
280  myfree((void *) list);
281 }
#define vstring_fgets(s, p)
int msg_verbose
Definition: msg.c:177
#define DO_MATCH
void myfree(void *ptr)
Definition: mymalloc.c:207
ARGV * patterns
Definition: match_list.h:30
#define CHARS_BRACE
Definition: sys_defs.h:1763
char * mystrdup(const char *str)
Definition: mymalloc.c:225
void dict_register(const char *dict_name, DICT *dict_info)
Definition: dict.c:312
ARGV * argv_free(ARGV *argvp)
Definition: argv.c:136
Definition: argv.h:17
NORETURN msg_panic(const char *fmt,...)
Definition: msg.c:295
#define vstring_str(vp)
Definition: vstring.h:71
int match_count
Definition: match_list.h:31
char ** argv
Definition: argv.h:20
VSTRING * fold_buf
Definition: match_list.h:34
void argv_add(ARGV *argvp,...)
Definition: argv.c:197
char * mystrtokq(char **src, const char *sep, const char *parens)
Definition: mystrtok.c:80
DICT * dict_open(const char *, int, int)
Definition: dict_open.c:421
ARGV * argv_alloc(ssize_t len)
Definition: argv.c:149
VSTREAM * vstream_fopen(const char *path, int flags, mode_t mode)
Definition: vstream.c:1241
#define casefold(dst, src)
Definition: stringops.h:67
int match_list_match(MATCH_LIST *list,...)
Definition: match_list.c:235
void match_list_free(MATCH_LIST *list)
Definition: match_list.c:272
#define OPEN_FLAGS
MATCH_LIST * match_list_init(const char *pname, int flags, const char *patterns, int match_count,...)
Definition: match_list.c:197
int(* MATCH_LIST_FN)(MATCH_LIST *, const char *, const char *)
Definition: match_list.h:25
MATCH_LIST_FN * match_func
Definition: match_list.h:32
int vstream_fclose(VSTREAM *stream)
Definition: vstream.c:1268
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
const char * dict_flags_str(int dict_flags)
Definition: dict.c:647
VSTRING * vstring_sprintf(VSTRING *vp, const char *format,...)
Definition: vstring.c:602
NORETURN msg_fatal(const char *fmt,...)
Definition: msg.c:249
char * pname
Definition: match_list.h:28
#define DICT_TYPE_NOFILE
Definition: dict.h:275
#define CHARS_COMMA_SP
Definition: sys_defs.h:1761
#define DICT_FLAGS
const char ** match_args
Definition: match_list.h:33
VSTRING * vstring_free(VSTRING *vp)
Definition: vstring.c:380
#define MATCH_DICTIONARY(pattern)
Definition: match_list.c:112
#define MATCH_FLAG_ALL
Definition: match_list.h:41
#define STR(x)
DICT * dict_surrogate(const char *dict_type, const char *dict_name, int open_flags, int dict_flags, const char *fmt,...)
void * mymalloc(ssize_t len)
Definition: mymalloc.c:150
void argv_terminate(ARGV *argvp)
Definition: argv.c:242
void msg_info(const char *fmt,...)
Definition: msg.c:199