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