1 /* $NetBSD: maps.c,v 1.3 2020/03/18 19:05:16 christos Exp $ */ 2 3 /*++ 4 /* NAME 5 /* maps 3 6 /* SUMMARY 7 /* multi-dictionary search 8 /* SYNOPSIS 9 /* #include <maps.h> 10 /* 11 /* MAPS *maps_create(title, map_names, flags) 12 /* const char *title; 13 /* const char *map_names; 14 /* int flags; 15 /* 16 /* const char *maps_find(maps, key, flags) 17 /* MAPS *maps; 18 /* const char *key; 19 /* int flags; 20 /* 21 /* const char *maps_file_find(maps, key, flags) 22 /* MAPS *maps; 23 /* const char *key; 24 /* int flags; 25 /* 26 /* MAPS *maps_free(maps) 27 /* MAPS *maps; 28 /* DESCRIPTION 29 /* This module implements multi-dictionary searches. it goes 30 /* through the high-level dictionary interface and does file 31 /* locking. Dictionaries are opened read-only, and in-memory 32 /* dictionary instances are shared. 33 /* 34 /* maps_create() takes list of type:name pairs and opens the 35 /* named dictionaries. 36 /* The result is a handle that must be specified along with all 37 /* other maps_xxx() operations. 38 /* See dict_open(3) for a description of flags. 39 /* This includes the flags that specify preferences for search 40 /* string case folding. 41 /* 42 /* maps_find() searches the specified list of dictionaries 43 /* in the specified order for the named key. The result is in 44 /* memory that is overwritten upon each call. 45 /* The flags argument is either 0 or specifies a filter: 46 /* for example, DICT_FLAG_FIXED | DICT_FLAG_PATTERN selects 47 /* dictionaries that have fixed keys or pattern keys. 48 /* 49 /* maps_file_find() implements maps_find() but also decodes 50 /* the base64 lookup result. This requires that the maps are 51 /* opened with DICT_FLAG_SRC_RHS_IS_FILE. 52 /* 53 /* maps_free() releases storage claimed by maps_create() 54 /* and conveniently returns a null pointer. 55 /* 56 /* Arguments: 57 /* .IP title 58 /* String used for diagnostics. Typically one specifies the 59 /* type of information stored in the lookup tables. 60 /* .IP map_names 61 /* Null-terminated string with type:name dictionary specifications, 62 /* separated by whitespace or commas. 63 /* .IP flags 64 /* With maps_create(), flags that are passed to dict_open(). 65 /* With maps_find(), flags that control searching behavior 66 /* as documented above. 67 /* .IP maps 68 /* A result from maps_create(). 69 /* .IP key 70 /* Null-terminated string with a lookup key. Table lookup is case 71 /* sensitive. 72 /* DIAGNOSTICS 73 /* Panic: inappropriate use; fatal errors: out of memory, unable 74 /* to open database. Warnings: null string lookup result. 75 /* 76 /* maps_find() returns a null pointer when the requested 77 /* information was not found, and logs a warning when the 78 /* lookup failed due to error. The maps->error value indicates 79 /* if the last lookup failed due to error. 80 /* BUGS 81 /* The dictionary name space is flat, so dictionary names allocated 82 /* by maps_create() may collide with dictionary names allocated by 83 /* other methods. 84 /* 85 /* This functionality could be implemented by allowing the user to 86 /* specify dictionary search paths to dict_lookup() or dict_eval(). 87 /* However, that would either require that the dict(3) module adopts 88 /* someone else's list notation syntax, or that the dict(3) module 89 /* imposes syntax restrictions onto other software, neither of which 90 /* is desirable. 91 /* LICENSE 92 /* .ad 93 /* .fi 94 /* The Secure Mailer license must be distributed with this software. 95 /* AUTHOR(S) 96 /* Wietse Venema 97 /* IBM T.J. Watson Research 98 /* P.O. Box 704 99 /* Yorktown Heights, NY 10598, USA 100 /*--*/ 101 102 /* System library. */ 103 104 #include <sys_defs.h> 105 #include <string.h> 106 107 /* Utility library. */ 108 109 #include <argv.h> 110 #include <mymalloc.h> 111 #include <msg.h> 112 #include <dict.h> 113 #include <stringops.h> 114 #include <split_at.h> 115 116 /* Global library. */ 117 118 #include "mail_conf.h" 119 #include "maps.h" 120 121 /* maps_create - initialize */ 122 123 MAPS *maps_create(const char *title, const char *map_names, int dict_flags) 124 { 125 const char *myname = "maps_create"; 126 char *temp; 127 char *bufp; 128 static char sep[] = CHARS_COMMA_SP; 129 static char parens[] = CHARS_BRACE; 130 MAPS *maps; 131 char *map_type_name; 132 VSTRING *map_type_name_flags; 133 DICT *dict; 134 135 /* 136 * Initialize. 137 */ 138 maps = (MAPS *) mymalloc(sizeof(*maps)); 139 maps->title = mystrdup(title); 140 maps->argv = argv_alloc(2); 141 maps->error = 0; 142 143 /* 144 * For each specified type:name pair, either register a new dictionary, 145 * or increment the reference count of an existing one. 146 */ 147 if (*map_names) { 148 bufp = temp = mystrdup(map_names); 149 map_type_name_flags = vstring_alloc(10); 150 151 #define OPEN_FLAGS O_RDONLY 152 153 while ((map_type_name = mystrtokq(&bufp, sep, parens)) != 0) { 154 vstring_sprintf(map_type_name_flags, "%s(%o,%s)", 155 map_type_name, OPEN_FLAGS, 156 dict_flags_str(dict_flags)); 157 if ((dict = dict_handle(vstring_str(map_type_name_flags))) == 0) 158 dict = dict_open(map_type_name, OPEN_FLAGS, dict_flags); 159 if ((dict->flags & dict_flags) != dict_flags) 160 msg_panic("%s: map %s has flags 0%o, want flags 0%o", 161 myname, map_type_name, dict->flags, dict_flags); 162 dict_register(vstring_str(map_type_name_flags), dict); 163 argv_add(maps->argv, vstring_str(map_type_name_flags), ARGV_END); 164 } 165 myfree(temp); 166 vstring_free(map_type_name_flags); 167 } 168 return (maps); 169 } 170 171 /* maps_find - search a list of dictionaries */ 172 173 const char *maps_find(MAPS *maps, const char *name, int flags) 174 { 175 const char *myname = "maps_find"; 176 char **map_name; 177 const char *expansion; 178 DICT *dict; 179 180 /* 181 * In case of return without map lookup (empty name or no maps). 182 */ 183 maps->error = 0; 184 185 /* 186 * Temp. workaround, for buggy callers that pass zero-length keys when 187 * given partial addresses. 188 */ 189 if (*name == 0) 190 return (0); 191 192 for (map_name = maps->argv->argv; *map_name; map_name++) { 193 if ((dict = dict_handle(*map_name)) == 0) 194 msg_panic("%s: dictionary not found: %s", myname, *map_name); 195 if (flags != 0 && (dict->flags & flags) == 0) 196 continue; 197 if ((expansion = dict_get(dict, name)) != 0) { 198 if (*expansion == 0) { 199 msg_warn("%s lookup of %s returns an empty string result", 200 maps->title, name); 201 msg_warn("%s should return NO RESULT in case of NOT FOUND", 202 maps->title); 203 maps->error = DICT_ERR_CONFIG; 204 return (0); 205 } 206 if (msg_verbose) 207 msg_info("%s: %s: %s: %s = %.100s%s", myname, maps->title, 208 *map_name, name, expansion, 209 strlen(expansion) > 100 ? "..." : ""); 210 return (expansion); 211 } else if ((maps->error = dict->error) != 0) { 212 msg_warn("%s:%s lookup error for \"%s\"", 213 dict->type, dict->name, name); 214 break; 215 } 216 } 217 if (msg_verbose) 218 msg_info("%s: %s: %s: %s", myname, maps->title, name, maps->error ? 219 "search aborted" : "not found"); 220 return (0); 221 } 222 223 /* maps_file_find - search a list of dictionaries and base64 decode */ 224 225 const char *maps_file_find(MAPS *maps, const char *name, int flags) 226 { 227 const char *myname = "maps_file_find"; 228 char **map_name; 229 const char *expansion; 230 DICT *dict; 231 VSTRING *unb64; 232 char *err; 233 234 /* 235 * In case of return without map lookup (empty name or no maps). 236 */ 237 maps->error = 0; 238 239 /* 240 * Temp. workaround, for buggy callers that pass zero-length keys when 241 * given partial addresses. 242 */ 243 if (*name == 0) 244 return (0); 245 246 for (map_name = maps->argv->argv; *map_name; map_name++) { 247 if ((dict = dict_handle(*map_name)) == 0) 248 msg_panic("%s: dictionary not found: %s", myname, *map_name); 249 if ((dict->flags & DICT_FLAG_SRC_RHS_IS_FILE) == 0) 250 msg_panic("%s: %s: opened without DICT_FLAG_SRC_RHS_IS_FILE", 251 myname, maps->title); 252 if (flags != 0 && (dict->flags & flags) == 0) 253 continue; 254 if ((expansion = dict_get(dict, name)) != 0) { 255 if (*expansion == 0) { 256 msg_warn("%s lookup of %s returns an empty string result", 257 maps->title, name); 258 msg_warn("%s should return NO RESULT in case of NOT FOUND", 259 maps->title); 260 maps->error = DICT_ERR_CONFIG; 261 return (0); 262 } 263 if (msg_verbose) 264 msg_info("%s: %s: %s: %s = %.100s%s", myname, maps->title, 265 *map_name, name, expansion, 266 strlen(expansion) > 100 ? "..." : ""); 267 if ((unb64 = dict_file_from_b64(dict, expansion)) == 0) { 268 err = dict_file_get_error(dict); 269 msg_warn("table %s:%s: key %s: %s", 270 dict->type, dict->name, name, err); 271 myfree(err); 272 maps->error = DICT_ERR_CONFIG; 273 return (0); 274 } 275 return (vstring_str(unb64)); 276 } else if ((maps->error = dict->error) != 0) { 277 msg_warn("%s:%s lookup error for \"%s\"", 278 dict->type, dict->name, name); 279 break; 280 } 281 } 282 if (msg_verbose) 283 msg_info("%s: %s: %s: %s", myname, maps->title, name, maps->error ? 284 "search aborted" : "not found"); 285 return (0); 286 } 287 288 /* maps_free - release storage */ 289 290 MAPS *maps_free(MAPS *maps) 291 { 292 char **map_name; 293 294 for (map_name = maps->argv->argv; *map_name; map_name++) { 295 if (msg_verbose) 296 msg_info("maps_free: %s", *map_name); 297 dict_unregister(*map_name); 298 } 299 myfree(maps->title); 300 argv_free(maps->argv); 301 myfree((void *) maps); 302 return (0); 303 } 304 305 #ifdef TEST 306 307 #include <vstring.h> 308 #include <vstream.h> 309 #include <vstring_vstream.h> 310 311 int main(int argc, char **argv) 312 { 313 VSTRING *buf = vstring_alloc(100); 314 MAPS *maps; 315 const char *result; 316 317 if (argc != 2) 318 msg_fatal("usage: %s maps", argv[0]); 319 msg_verbose = 2; 320 maps = maps_create("whatever", argv[1], DICT_FLAG_LOCK); 321 322 while (vstring_fgets_nonl(buf, VSTREAM_IN)) { 323 maps->error = 99; 324 vstream_printf("\"%s\": ", vstring_str(buf)); 325 if ((result = maps_find(maps, vstring_str(buf), 0)) != 0) { 326 vstream_printf("%s\n", result); 327 } else if (maps->error != 0) { 328 vstream_printf("lookup error\n"); 329 } else { 330 vstream_printf("not found\n"); 331 } 332 vstream_fflush(VSTREAM_OUT); 333 } 334 maps_free(maps); 335 vstring_free(buf); 336 return (0); 337 } 338 339 #endif 340