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(flags, pattern_list, count, func,...) 10/* int flags; 11/* const char *pattern_list; 12/* int count; 13/* int (*func)(int flags, const char *string, const char *pattern); 14/* 15/* int match_list_match(list, string,...) 16/* MATCH_LIST *list; 17/* const char *string; 18/* 19/* void match_list_free(list) 20/* MATCH_LIST *list; 21/* DESCRIPTION 22/* This module implements a framework for tests for list membership. 23/* The actual tests are done by user-supplied functions. 24/* 25/* Patterns are separated by whitespace and/or commas. A pattern 26/* is either a string, a file name (in which case the contents 27/* of the file are substituted for the file name) or a type:name 28/* lookup table specification. In order to reverse the result of 29/* a pattern match, precede a pattern with an exclamation point (!). 30/* 31/* match_list_init() performs initializations. The flags argument 32/* specifies the bit-wise OR of zero or more of the following: 33/* .RS 34/* .IP MATCH_FLAG_PARENT 35/* The hostname pattern foo.com matches any name within the domain 36/* foo.com. If this flag is cleared, foo.com matches itself 37/* only, and .foo.com matches any name below the domain foo.com. 38/* .IP MATCH_FLAG_RETURN 39/* Request that match_list_match() logs a warning and returns 40/* zero (with list->error set to a non-zero dictionary error 41/* code) instead of raising a fatal run-time error. 42/* .RE 43/* Specify MATCH_FLAG_NONE to request none of the above. 44/* The pattern_list argument specifies a list of patterns. The third 45/* argument specifies how many match functions follow. 46/* 47/* match_list_match() matches strings against the specified pattern 48/* list, passing the first string to the first function given to 49/* match_list_init(), the second string to the second function, and 50/* so on. 51/* 52/* match_list_free() releases storage allocated by match_list_init(). 53/* DIAGNOSTICS 54/* Fatal error: unable to open or read a match_list file; invalid 55/* match_list pattern. 56/* SEE ALSO 57/* host_match(3) match hosts by name or by address 58/* LICENSE 59/* .ad 60/* .fi 61/* The Secure Mailer license must be distributed with this software. 62/* AUTHOR(S) 63/* Wietse Venema 64/* IBM T.J. Watson Research 65/* P.O. Box 704 66/* Yorktown Heights, NY 10598, USA 67/*--*/ 68 69/* System library. */ 70 71#include <sys_defs.h> 72#include <unistd.h> 73#include <string.h> 74#include <fcntl.h> 75#include <stdlib.h> 76#include <stdarg.h> 77 78/* Utility library. */ 79 80#include <msg.h> 81#include <mymalloc.h> 82#include <vstring.h> 83#include <vstream.h> 84#include <vstring_vstream.h> 85#include <stringops.h> 86#include <argv.h> 87#include <dict.h> 88#include <match_list.h> 89 90/* Application-specific */ 91 92#define MATCH_DICTIONARY(pattern) \ 93 ((pattern)[0] != '[' && strchr((pattern), ':') != 0) 94 95/* match_list_parse - parse buffer, destroy buffer */ 96 97static ARGV *match_list_parse(ARGV *list, char *string, int init_match) 98{ 99 const char *myname = "match_list_parse"; 100 VSTRING *buf = vstring_alloc(10); 101 VSTREAM *fp; 102 const char *delim = " ,\t\r\n"; 103 char *bp = string; 104 char *start; 105 char *item; 106 char *map_type_name_flags; 107 int match; 108 109#define OPEN_FLAGS O_RDONLY 110#define DICT_FLAGS (DICT_FLAG_LOCK | DICT_FLAG_FOLD_FIX) 111#define STR(x) vstring_str(x) 112 113 /* 114 * /filename contents are expanded in-line. To support !/filename we 115 * prepend the negation operator to each item from the file. 116 */ 117 while ((start = mystrtok(&bp, delim)) != 0) { 118 if (*start == '#') { 119 msg_warn("%s: comment at end of line is not supported: %s %s", 120 myname, start, bp); 121 break; 122 } 123 for (match = init_match, item = start; *item == '!'; item++) 124 match = !match; 125 if (*item == 0) 126 msg_fatal("%s: no pattern after '!'", myname); 127 if (*item == '/') { /* /file/name */ 128 if ((fp = vstream_fopen(item, O_RDONLY, 0)) == 0) { 129 vstring_sprintf(buf, "%s:%s", DICT_TYPE_NOFILE, item); 130 /* XXX Should increment existing map refcount. */ 131 if (dict_handle(STR(buf)) == 0) 132 dict_register(STR(buf), 133 dict_surrogate(DICT_TYPE_NOFILE, item, 134 OPEN_FLAGS, DICT_FLAGS, 135 "open file %s: %m", item)); 136 argv_add(list, STR(buf), (char *) 0); 137 } else { 138 while (vstring_fgets(buf, fp)) 139 if (vstring_str(buf)[0] != '#') 140 list = match_list_parse(list, vstring_str(buf), match); 141 if (vstream_fclose(fp)) 142 msg_fatal("%s: read file %s: %m", myname, item); 143 } 144 } else if (MATCH_DICTIONARY(item)) { /* type:table */ 145 vstring_sprintf(buf, "%s%s(%o,%s)", match ? "" : "!", 146 item, OPEN_FLAGS, dict_flags_str(DICT_FLAGS)); 147 map_type_name_flags = STR(buf) + (match == 0); 148 /* XXX Should increment existing map refcount. */ 149 if (dict_handle(map_type_name_flags) == 0) 150 dict_register(map_type_name_flags, 151 dict_open(item, OPEN_FLAGS, DICT_FLAGS)); 152 argv_add(list, STR(buf), (char *) 0); 153 } else { /* other pattern */ 154 argv_add(list, match ? item : 155 STR(vstring_sprintf(buf, "!%s", item)), (char *) 0); 156 } 157 } 158 vstring_free(buf); 159 return (list); 160} 161 162/* match_list_init - initialize pattern list */ 163 164MATCH_LIST *match_list_init(int flags, const char *patterns, int match_count,...) 165{ 166 MATCH_LIST *list; 167 char *saved_patterns; 168 va_list ap; 169 int i; 170 171 if (flags & ~MATCH_FLAG_ALL) 172 msg_panic("match_list_init: bad flags 0x%x", flags); 173 174 list = (MATCH_LIST *) mymalloc(sizeof(*list)); 175 list->flags = flags; 176 list->match_count = match_count; 177 list->match_func = 178 (MATCH_LIST_FN *) mymalloc(match_count * sizeof(MATCH_LIST_FN)); 179 list->match_args = 180 (const char **) mymalloc(match_count * sizeof(const char *)); 181 va_start(ap, match_count); 182 for (i = 0; i < match_count; i++) 183 list->match_func[i] = va_arg(ap, MATCH_LIST_FN); 184 va_end(ap); 185 list->error = 0; 186 187#define DO_MATCH 1 188 189 saved_patterns = mystrdup(patterns); 190 list->patterns = match_list_parse(argv_alloc(1), saved_patterns, DO_MATCH); 191 argv_terminate(list->patterns); 192 myfree(saved_patterns); 193 return (list); 194} 195 196/* match_list_match - match strings against pattern list */ 197 198int match_list_match(MATCH_LIST *list,...) 199{ 200 const char *myname = "match_list_match"; 201 char **cpp; 202 char *pat; 203 int match; 204 int i; 205 va_list ap; 206 207 /* 208 * Iterate over all patterns in the list, stop at the first match. 209 */ 210 va_start(ap, list); 211 for (i = 0; i < list->match_count; i++) 212 list->match_args[i] = va_arg(ap, const char *); 213 va_end(ap); 214 215 list->error = 0; 216 for (cpp = list->patterns->argv; (pat = *cpp) != 0; cpp++) { 217 for (match = 1; *pat == '!'; pat++) 218 match = !match; 219 for (i = 0; i < list->match_count; i++) 220 if (list->match_func[i] (list, list->match_args[i], pat)) 221 return (match); 222 else if (list->error != 0) 223 return (0); 224 } 225 if (msg_verbose) 226 for (i = 0; i < list->match_count; i++) 227 msg_info("%s: %s: no match", myname, list->match_args[i]); 228 return (0); 229} 230 231/* match_list_free - release storage */ 232 233void match_list_free(MATCH_LIST *list) 234{ 235 /* XXX Should decrement map refcounts. */ 236 argv_free(list->patterns); 237 myfree((char *) list->match_func); 238 myfree((char *) list->match_args); 239 myfree((char *) list); 240} 241