1 /* $NetBSD: maps.c,v 1.6 2026/05/09 18:49: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 /* Wietse Venema 102 /* Google, Inc. 103 /* 111 8th Avenue 104 /* New York, NY 10011, USA 105 /*--*/ 106 107 /* System library. */ 108 109 #include <sys_defs.h> 110 #include <string.h> 111 112 /* Utility library. */ 113 114 #include <argv.h> 115 #include <mymalloc.h> 116 #include <msg.h> 117 #include <dict.h> 118 #include <stringops.h> 119 #include <split_at.h> 120 121 /* Global library. */ 122 123 #include "mail_conf.h" 124 #include "maps.h" 125 126 /* maps_create - initialize */ 127 128 MAPS *maps_create(const char *title, const char *map_names, int dict_flags) 129 { 130 const char *myname = "maps_create"; 131 char *temp; 132 char *bufp; 133 static char sep[] = CHARS_COMMA_SP; 134 static char parens[] = CHARS_BRACE; 135 MAPS *maps; 136 char *map_type_name; 137 DICT *dict; 138 139 /* 140 * Initialize. 141 */ 142 maps = (MAPS *) mymalloc(sizeof(*maps)); 143 maps->title = mystrdup(title); 144 maps->argv = argv_alloc(2); 145 maps->error = 0; 146 147 /* 148 * For each specified type:name pair, either register a new dictionary, 149 * or increment the reference count of an existing one. 150 */ 151 if (*map_names) { 152 bufp = temp = mystrdup(map_names); 153 154 #define OPEN_FLAGS O_RDONLY 155 156 while ((map_type_name = mystrtokq(&bufp, sep, parens)) != 0) { 157 dict = dict_open(map_type_name, OPEN_FLAGS, dict_flags); 158 if ((dict->flags & dict_flags) != dict_flags) 159 msg_panic("%s: map %s has flags 0%o, want flags 0%o", 160 myname, map_type_name, dict->flags, dict_flags); 161 argv_add(maps->argv, dict->reg_name, ARGV_END); 162 } 163 myfree(temp); 164 } 165 return (maps); 166 } 167 168 /* maps_find - search a list of dictionaries */ 169 170 const char *maps_find(MAPS *maps, const char *name, int flags) 171 { 172 const char *myname = "maps_find"; 173 char **map_name; 174 const char *expansion; 175 DICT *dict; 176 177 /* 178 * In case of return without map lookup (empty name or no maps). 179 */ 180 maps->error = 0; 181 182 /* 183 * Temp. workaround, for buggy callers that pass zero-length keys when 184 * given partial addresses. 185 */ 186 if (*name == 0) 187 return (0); 188 189 for (map_name = maps->argv->argv; *map_name; map_name++) { 190 if ((dict = dict_handle(*map_name)) == 0) 191 msg_panic("%s: dictionary not found: %s", myname, *map_name); 192 if (flags != 0 && (dict->flags & flags) == 0) { 193 if (msg_verbose) 194 msg_info("%s: %s: skipping %s lookup for %s", 195 myname, maps->title, *map_name, name); 196 continue; 197 } 198 if ((expansion = dict_get(dict, name)) != 0) { 199 if (*expansion == 0) { 200 msg_warn("%s lookup of %s returns an empty string result", 201 maps->title, name); 202 msg_warn("%s should return NO RESULT in case of NOT FOUND", 203 maps->title); 204 maps->error = DICT_ERR_CONFIG; 205 return (0); 206 } 207 if (msg_verbose) 208 msg_info("%s: %s: %s: %s = %.100s%s", myname, maps->title, 209 *map_name, name, expansion, 210 strlen(expansion) > 100 ? "..." : ""); 211 return (expansion); 212 } else if ((maps->error = dict->error) != 0) { 213 msg_warn("%s:%s lookup error for \"%s\"", 214 dict->type, dict->name, name); 215 break; 216 } 217 } 218 if (msg_verbose) 219 msg_info("%s: %s: %s: %s", myname, maps->title, name, maps->error ? 220 "search aborted" : "not found"); 221 return (0); 222 } 223 224 /* maps_file_find - search a list of dictionaries and base64 decode */ 225 226 const char *maps_file_find(MAPS *maps, const char *name, int flags) 227 { 228 const char *myname = "maps_file_find"; 229 char **map_name; 230 const char *expansion; 231 DICT *dict; 232 VSTRING *unb64; 233 char *err; 234 235 /* 236 * In case of return without map lookup (empty name or no maps). 237 */ 238 maps->error = 0; 239 240 /* 241 * Temp. workaround, for buggy callers that pass zero-length keys when 242 * given partial addresses. 243 */ 244 if (*name == 0) 245 return (0); 246 247 for (map_name = maps->argv->argv; *map_name; map_name++) { 248 if ((dict = dict_handle(*map_name)) == 0) 249 msg_panic("%s: dictionary not found: %s", myname, *map_name); 250 if ((dict->flags & DICT_FLAG_SRC_RHS_IS_FILE) == 0) 251 msg_panic("%s: %s: opened without DICT_FLAG_SRC_RHS_IS_FILE", 252 myname, maps->title); 253 if (flags != 0 && (dict->flags & flags) == 0) { 254 if (msg_verbose) 255 msg_info("%s: %s: skipping %s lookup for %s", 256 myname, maps->title, *map_name, name); 257 continue; 258 } 259 if ((expansion = dict_get(dict, name)) != 0) { 260 if (*expansion == 0) { 261 msg_warn("%s lookup of %s returns an empty string result", 262 maps->title, name); 263 msg_warn("%s should return NO RESULT in case of NOT FOUND", 264 maps->title); 265 maps->error = DICT_ERR_CONFIG; 266 return (0); 267 } 268 if (msg_verbose) 269 msg_info("%s: %s: %s: %s = %.100s%s", myname, maps->title, 270 *map_name, name, expansion, 271 strlen(expansion) > 100 ? "..." : ""); 272 if ((unb64 = dict_file_from_b64(dict, expansion)) == 0) { 273 err = dict_file_get_error(dict); 274 msg_warn("table %s:%s: key %s: %s", 275 dict->type, dict->name, name, err); 276 myfree(err); 277 maps->error = DICT_ERR_CONFIG; 278 return (0); 279 } 280 return (vstring_str(unb64)); 281 } else if ((maps->error = dict->error) != 0) { 282 msg_warn("%s:%s lookup error for \"%s\"", 283 dict->type, dict->name, name); 284 break; 285 } 286 } 287 if (msg_verbose) 288 msg_info("%s: %s: %s: %s", myname, maps->title, name, maps->error ? 289 "search aborted" : "not found"); 290 return (0); 291 } 292 293 /* maps_free - release storage */ 294 295 MAPS *maps_free(MAPS *maps) 296 { 297 const char *myname = "maps_free"; 298 DICT *dict; 299 char **map_name; 300 301 for (map_name = maps->argv->argv; *map_name; map_name++) { 302 if (msg_verbose) 303 msg_info("maps_free: %s", *map_name); 304 if ((dict = dict_handle(*map_name)) == 0) 305 msg_panic("%s: dictionary not found: %s", myname, *map_name); 306 dict_close(dict); 307 } 308 myfree(maps->title); 309 argv_free(maps->argv); 310 myfree((void *) maps); 311 return (0); 312 } 313 314 #ifdef TEST 315 316 #include <vstring.h> 317 #include <vstream.h> 318 #include <vstring_vstream.h> 319 320 int main(int argc, char **argv) 321 { 322 VSTRING *buf = vstring_alloc(100); 323 MAPS *maps; 324 const char *result; 325 326 if (argc != 2) 327 msg_fatal("usage: %s maps", argv[0]); 328 msg_verbose = 2; 329 maps = maps_create("whatever", argv[1], DICT_FLAG_LOCK); 330 331 while (vstring_fgets_nonl(buf, VSTREAM_IN)) { 332 maps->error = 99; 333 vstream_printf("\"%s\": ", vstring_str(buf)); 334 if ((result = maps_find(maps, vstring_str(buf), 0)) != 0) { 335 vstream_printf("%s\n", result); 336 } else if (maps->error != 0) { 337 vstream_printf("lookup error\n"); 338 } else { 339 vstream_printf("not found\n"); 340 } 341 vstream_fflush(VSTREAM_OUT); 342 } 343 maps_free(maps); 344 vstring_free(buf); 345 return (0); 346 } 347 348 #endif 349