xref: /netbsd-src/external/ibm-public/postfix/dist/src/util/dict_open.c (revision a5847cc334d9a7029f6352b847e9e8d71a0f9e0c)
1 /*	$NetBSD: dict_open.c,v 1.1.1.4 2011/05/11 09:11:23 tron Exp $	*/
2 
3 /*++
4 /* NAME
5 /*	dict_open 3
6 /* SUMMARY
7 /*	low-level dictionary interface
8 /* SYNOPSIS
9 /*	#include <dict.h>
10 /*
11 /*	DICT	*dict_open(dict_spec, open_flags, dict_flags)
12 /*	const char *dict_spec;
13 /*	int	open_flags;
14 /*	int	dict_flags;
15 /*
16 /*	DICT	*dict_open3(dict_type, dict_name, open_flags, dict_flags)
17 /*	const char *dict_type;
18 /*	const char *dict_name;
19 /*	int	open_flags;
20 /*	int	dict_flags;
21 /*
22 /*	void	dict_put(dict, key, value)
23 /*	DICT	*dict;
24 /*	const char *key;
25 /*	const char *value;
26 /*
27 /*	const char *dict_get(dict, key)
28 /*	DICT	*dict;
29 /*	const char *key;
30 /*
31 /*	int	dict_del(dict, key)
32 /*	DICT	*dict;
33 /*	const char *key;
34 /*
35 /*	void	dict_seq(dict, func, key, value)
36 /*	DICT	*dict;
37 /*	int	func;
38 /*	const char **key;
39 /*	const char **value;
40 /*
41 /*	void	dict_close(dict)
42 /*	DICT	*dict;
43 /*
44 /*	dict_open_register(type, open)
45 /*	char	*type;
46 /*	DICT	*(*open) (const char *, int, int);
47 /*
48 /*	ARGV	*dict_mapnames()
49 /* DESCRIPTION
50 /*	This module implements a low-level interface to multiple
51 /*	physical dictionary types.
52 /*
53 /*	dict_open() takes a type:name pair that specifies a dictionary type
54 /*	and dictionary name, opens the dictionary, and returns a dictionary
55 /*	handle.  The \fIopen_flags\fR arguments are as in open(2). The
56 /*	\fIdict_flags\fR are the bit-wise OR of zero or more of the following:
57 /* .IP DICT_FLAG_DUP_WARN
58 /*	Warn about duplicate keys, if the underlying database does not
59 /*	support duplicate keys. The default is to terminate with a fatal
60 /*	error.
61 /* .IP DICT_FLAG_DUP_IGNORE
62 /*	Ignore duplicate keys if the underlying database does not
63 /*	support duplicate keys. The default is to terminate with a fatal
64 /*	error.
65 /* .IP DICT_FLAG_DUP_REPLACE
66 /*	Replace duplicate keys if the underlying database supports such
67 /*	an operation. The default is to terminate with a fatal error.
68 /* .IP DICT_FLAG_TRY0NULL
69 /*	With maps where this is appropriate, append no null byte to
70 /*	keys and values.
71 /*	When neither DICT_FLAG_TRY0NULL nor DICT_FLAG_TRY1NULL are
72 /*	specified, the software guesses what format to use for reading;
73 /*	and in the absence of definite information, a system-dependent
74 /*	default is chosen for writing.
75 /* .IP DICT_FLAG_TRY1NULL
76 /*	With maps where this is appropriate, append one null byte to
77 /*	keys and values.
78 /*	When neither DICT_FLAG_TRY0NULL nor DICT_FLAG_TRY1NULL are
79 /*	specified, the software guesses what format to use for reading;
80 /*	and in the absence of definite information, a system-dependent
81 /*	default is chosen for writing.
82 /* .IP DICT_FLAG_LOCK
83 /*	With maps where this is appropriate, acquire an exclusive lock
84 /*	before writing, and acquire a shared lock before reading.
85 /* .IP DICT_FLAG_OPEN_LOCK
86 /*	With maps where this is appropriate, acquire an exclusive
87 /*	lock upon open, and report a fatal run-time error if the
88 /*	table is already locked.
89 /* .IP DICT_FLAG_FOLD_FIX
90 /*	With databases whose lookup fields are fixed-case strings,
91 /*	fold the search key to lower case before accessing the
92 /*	database.  This includes hash:, cdb:, dbm:. nis:, ldap:,
93 /*	*sql.
94 /* .IP DICT_FLAG_FOLD_MUL
95 /*	With databases where one lookup field can match both upper
96 /*	and lower case, fold the search key to lower case before
97 /*	accessing the database. This includes regexp: and pcre:
98 /* .IP DICT_FLAG_FOLD_ANY
99 /*	Short-hand for (DICT_FLAG_FOLD_FIX | DICT_FLAG_FOLD_MUL).
100 /* .IP DICT_FLAG_SYNC_UPDATE
101 /*	With file-based maps, flush I/O buffers to file after each update.
102 /*	Thus feature is not supported with some file-based dictionaries.
103 /* .IP DICT_FLAG_NO_REGSUB
104 /*      Disallow regular expression substitution from left-hand side data
105 /*	into the right-hand side.
106 /* .IP DICT_FLAG_NO_PROXY
107 /*	Disallow access through the \fBproxymap\fR service.
108 /* .IP DICT_FLAG_NO_UNAUTH
109 /*	Disallow network lookup mechanisms that lack any form of
110 /*	authentication (example: tcp_table; even NIS can be secured
111 /*	to some extent by requiring that the server binds to a
112 /*	privileged port).
113 /* .IP DICT_FLAG_PARANOID
114 /*	A combination of all the paranoia flags: DICT_FLAG_NO_REGSUB,
115 /*	DICT_FLAG_NO_PROXY and DICT_FLAG_NO_UNAUTH.
116 /* .PP
117 /*	Specify DICT_FLAG_NONE for no special processing.
118 /*
119 /*	The dictionary types are as follows:
120 /* .IP environ
121 /*	The process environment array. The \fIdict_name\fR argument is ignored.
122 /* .IP dbm
123 /*	DBM file.
124 /* .IP hash
125 /*	Berkeley DB file in hash format.
126 /* .IP btree
127 /*	Berkeley DB file in btree format.
128 /* .IP nis
129 /*	NIS map. Only read access is supported.
130 /* .IP nisplus
131 /*	NIS+ map. Only read access is supported.
132 /* .IP netinfo
133 /*	NetInfo table. Only read access is supported.
134 /* .IP ldap
135 /*	LDAP ("light-weight" directory access protocol) database access.
136 /* .IP pcre
137 /*	PERL-compatible regular expressions.
138 /* .IP regexp
139 /*	POSIX-compatible regular expressions.
140 /* .IP texthash
141 /*	Flat text in postmap(1) input format.
142 /* .PP
143 /*	dict_open3() takes separate arguments for dictionary type and
144 /*	name, but otherwise performs the same functions as dict_open().
145 /*
146 /*	dict_get() retrieves the value stored in the named dictionary
147 /*	under the given key. A null pointer means the value was not found.
148 /*	As with dict_lookup(), the result is owned by the lookup table
149 /*	implementation. Make a copy if the result is to be modified,
150 /*	or if the result is to survive multiple table lookups.
151 /*
152 /*	dict_put() stores the specified key and value into the named
153 /*	dictionary.
154 /*
155 /*	dict_del() removes a dictionary entry, and returns zero
156 /*	in case of success.
157 /*
158 /*	dict_seq() iterates over all members in the named dictionary.
159 /*	func is define DICT_SEQ_FUN_FIRST (select first member) or
160 /*	DICT_SEQ_FUN_NEXT (select next member). A zero result means
161 /*	that an entry was found.
162 /*
163 /*	dict_close() closes the specified dictionary and cleans up the
164 /*	associated data structures.
165 /*
166 /*	dict_open_register() adds support for a new dictionary type.
167 /*
168 /*	dict_mapnames() returns a sorted list with the names of all available
169 /*	dictionary types.
170 /* DIAGNOSTICS
171 /*	Fatal error: open error, unsupported dictionary type, attempt to
172 /*	update non-writable dictionary.
173 /* LICENSE
174 /* .ad
175 /* .fi
176 /*	The Secure Mailer license must be distributed with this software.
177 /* AUTHOR(S)
178 /*	Wietse Venema
179 /*	IBM T.J. Watson Research
180 /*	P.O. Box 704
181 /*	Yorktown Heights, NY 10598, USA
182 /*--*/
183 
184 /* System library. */
185 
186 #include <sys_defs.h>
187 #include <string.h>
188 #include <stdlib.h>
189 
190 #ifdef STRCASECMP_IN_STRINGS_H
191 #include <strings.h>
192 #endif
193 
194 /* Utility library. */
195 
196 #include <argv.h>
197 #include <mymalloc.h>
198 #include <msg.h>
199 #include <dict.h>
200 #include <dict_cdb.h>
201 #include <dict_env.h>
202 #include <dict_unix.h>
203 #include <dict_tcp.h>
204 #include <dict_sdbm.h>
205 #include <dict_dbm.h>
206 #include <dict_db.h>
207 #include <dict_nis.h>
208 #include <dict_nisplus.h>
209 #include <dict_ni.h>
210 #include <dict_pcre.h>
211 #include <dict_regexp.h>
212 #include <dict_static.h>
213 #include <dict_cidr.h>
214 #include <dict_ht.h>
215 #include <dict_thash.h>
216 #include <stringops.h>
217 #include <split_at.h>
218 #include <htable.h>
219 #include <myflock.h>
220 
221  /*
222   * lookup table for available map types.
223   */
224 typedef struct {
225     char   *type;
226     struct DICT *(*open) (const char *, int, int);
227 } DICT_OPEN_INFO;
228 
229 static const DICT_OPEN_INFO dict_open_info[] = {
230 #ifdef HAS_CDB
231     DICT_TYPE_CDB, dict_cdb_open,
232 #endif
233     DICT_TYPE_ENVIRON, dict_env_open,
234     DICT_TYPE_HT, dict_ht_open,
235     DICT_TYPE_UNIX, dict_unix_open,
236     DICT_TYPE_TCP, dict_tcp_open,
237 #ifdef HAS_SDBM
238     DICT_TYPE_SDBM, dict_sdbm_open,
239 #endif
240 #ifdef HAS_DBM
241     DICT_TYPE_DBM, dict_dbm_open,
242 #endif
243 #ifdef HAS_DB
244     DICT_TYPE_HASH, dict_hash_open,
245     DICT_TYPE_BTREE, dict_btree_open,
246 #endif
247 #ifdef HAS_NIS
248     DICT_TYPE_NIS, dict_nis_open,
249 #endif
250 #ifdef HAS_NISPLUS
251     DICT_TYPE_NISPLUS, dict_nisplus_open,
252 #endif
253 #ifdef HAS_NETINFO
254     DICT_TYPE_NETINFO, dict_ni_open,
255 #endif
256 #ifdef HAS_PCRE
257     DICT_TYPE_PCRE, dict_pcre_open,
258 #endif
259 #ifdef HAS_POSIX_REGEXP
260     DICT_TYPE_REGEXP, dict_regexp_open,
261 #endif
262     DICT_TYPE_STATIC, dict_static_open,
263     DICT_TYPE_CIDR, dict_cidr_open,
264     DICT_TYPE_THASH, dict_thash_open,
265     0,
266 };
267 
268 static HTABLE *dict_open_hash;
269 
270 /* dict_open_init - one-off initialization */
271 
272 static void dict_open_init(void)
273 {
274     const char *myname = "dict_open_init";
275     const DICT_OPEN_INFO *dp;
276 
277     if (dict_open_hash != 0)
278 	msg_panic("%s: multiple initialization", myname);
279     dict_open_hash = htable_create(10);
280 
281     for (dp = dict_open_info; dp->type; dp++)
282 	htable_enter(dict_open_hash, dp->type, (char *) dp);
283 }
284 
285 /* dict_open - open dictionary */
286 
287 DICT   *dict_open(const char *dict_spec, int open_flags, int dict_flags)
288 {
289     char   *saved_dict_spec = mystrdup(dict_spec);
290     char   *dict_name;
291     DICT   *dict;
292 
293     if ((dict_name = split_at(saved_dict_spec, ':')) == 0)
294 	msg_fatal("open dictionary: expecting \"type:name\" form instead of \"%s\"",
295 		  dict_spec);
296 
297     dict = dict_open3(saved_dict_spec, dict_name, open_flags, dict_flags);
298     myfree(saved_dict_spec);
299     return (dict);
300 }
301 
302 
303 /* dict_open3 - open dictionary */
304 
305 DICT   *dict_open3(const char *dict_type, const char *dict_name,
306 		           int open_flags, int dict_flags)
307 {
308     const char *myname = "dict_open";
309     DICT_OPEN_INFO *dp;
310     DICT   *dict;
311 
312     if (*dict_type == 0 || *dict_name == 0)
313 	msg_fatal("open dictionary: expecting \"type:name\" form instead of \"%s:%s\"",
314 		  dict_type, dict_name);
315     if (dict_open_hash == 0)
316 	dict_open_init();
317     if ((dp = (DICT_OPEN_INFO *) htable_find(dict_open_hash, dict_type)) == 0)
318 	msg_fatal("unsupported dictionary type: %s", dict_type);
319     if ((dict = dp->open(dict_name, open_flags, dict_flags)) == 0)
320 	msg_fatal("opening %s:%s %m", dict_type, dict_name);
321     if (msg_verbose)
322 	msg_info("%s: %s:%s", myname, dict_type, dict_name);
323     /* XXX the choice between wait-for-lock or no-wait is hard-coded. */
324     if (dict->lock_fd >= 0 && (dict_flags & DICT_FLAG_OPEN_LOCK) != 0) {
325 	if (dict_flags & DICT_FLAG_LOCK)
326 	    msg_panic("%s: attempt to open %s:%s with both \"open\" lock and \"access\" lock",
327 		      myname, dict_type, dict_name);
328 	if (myflock(dict->lock_fd, INTERNAL_LOCK,
329 		    MYFLOCK_OP_EXCLUSIVE | MYFLOCK_OP_NOWAIT) < 0)
330 	    msg_fatal("%s:%s: unable to get exclusive lock: %m",
331 		      dict_type, dict_name);
332     }
333     return (dict);
334 }
335 
336 /* dict_open_register - register dictionary type */
337 
338 void    dict_open_register(const char *type,
339 			           DICT *(*open) (const char *, int, int))
340 {
341     const char *myname = "dict_open_register";
342     DICT_OPEN_INFO *dp;
343 
344     if (dict_open_hash == 0)
345 	dict_open_init();
346     if (htable_find(dict_open_hash, type))
347 	msg_panic("%s: dictionary type exists: %s", myname, type);
348     dp = (DICT_OPEN_INFO *) mymalloc(sizeof(*dp));
349     dp->type = mystrdup(type);
350     dp->open = open;
351     htable_enter(dict_open_hash, dp->type, (char *) dp);
352 }
353 
354 /* dict_sort_alpha_cpp - qsort() callback */
355 
356 static int dict_sort_alpha_cpp(const void *a, const void *b)
357 {
358     return (strcmp(((char **) a)[0], ((char **) b)[0]));
359 }
360 
361 /* dict_mapnames - return an ARGV of available map_names */
362 
363 ARGV   *dict_mapnames()
364 {
365     HTABLE_INFO **ht_info;
366     HTABLE_INFO **ht;
367     DICT_OPEN_INFO *dp;
368     ARGV   *mapnames;
369 
370     if (dict_open_hash == 0)
371 	dict_open_init();
372     mapnames = argv_alloc(dict_open_hash->used + 1);
373     for (ht_info = ht = htable_list(dict_open_hash); *ht; ht++) {
374 	dp = (DICT_OPEN_INFO *) ht[0]->value;
375 	argv_add(mapnames, dp->type, ARGV_END);
376     }
377     qsort((void *) mapnames->argv, mapnames->argc, sizeof(mapnames->argv[0]),
378 	  dict_sort_alpha_cpp);
379     myfree((char *) ht_info);
380     argv_terminate(mapnames);
381     return mapnames;
382 }
383 
384 #ifdef TEST
385 
386  /*
387   * Proof-of-concept test program. Create, update or read a database. When
388   * the input is a name=value pair, the database is updated, otherwise the
389   * program assumes that the input specifies a lookup key and prints the
390   * corresponding value.
391   */
392 
393 /* System library. */
394 
395 #include <stdlib.h>
396 #include <fcntl.h>
397 #include <unistd.h>
398 #include <signal.h>
399 
400 /* Utility library. */
401 
402 #include "vstring.h"
403 #include "vstream.h"
404 #include "msg_vstream.h"
405 #include "vstring_vstream.h"
406 
407 static NORETURN usage(char *myname)
408 {
409     msg_fatal("usage: %s type:file read|write|create [fold] [sync]", myname);
410 }
411 
412 int     main(int argc, char **argv)
413 {
414     VSTRING *keybuf = vstring_alloc(1);
415     VSTRING *inbuf = vstring_alloc(1);
416     DICT   *dict;
417     char   *dict_name;
418     int     open_flags;
419     char   *bufp;
420     char   *cmd;
421     const char *key;
422     const char *value;
423     int     ch;
424     int     dict_flags = DICT_FLAG_LOCK | DICT_FLAG_DUP_REPLACE;
425     int     n;
426 
427     signal(SIGPIPE, SIG_IGN);
428 
429     msg_vstream_init(argv[0], VSTREAM_ERR);
430     while ((ch = GETOPT(argc, argv, "v")) > 0) {
431 	switch (ch) {
432 	default:
433 	    usage(argv[0]);
434 	case 'v':
435 	    msg_verbose++;
436 	    break;
437 	}
438     }
439     optind = OPTIND;
440     if (argc - optind < 2)
441 	usage(argv[0]);
442     if (strcasecmp(argv[optind + 1], "create") == 0)
443 	open_flags = O_CREAT | O_RDWR | O_TRUNC;
444     else if (strcasecmp(argv[optind + 1], "write") == 0)
445 	open_flags = O_RDWR;
446     else if (strcasecmp(argv[optind + 1], "read") == 0)
447 	open_flags = O_RDONLY;
448     else
449 	msg_fatal("unknown access mode: %s", argv[2]);
450     for (n = 2; argv[optind + n]; n++) {
451 	if (strcasecmp(argv[optind + 2], "fold") == 0)
452 	    dict_flags |= DICT_FLAG_FOLD_ANY;
453 	else if (strcasecmp(argv[optind + 2], "sync") == 0)
454 	    dict_flags |= DICT_FLAG_SYNC_UPDATE;
455 	else
456 	    usage(argv[0]);
457     }
458     dict_name = argv[optind];
459     dict = dict_open(dict_name, open_flags, dict_flags);
460     dict_register(dict_name, dict);
461     while (vstring_fgets_nonl(inbuf, VSTREAM_IN)) {
462 	bufp = vstring_str(inbuf);
463 	if (!isatty(0)) {
464 	    vstream_printf("> %s\n", bufp);
465 	    vstream_fflush(VSTREAM_OUT);
466 	}
467 	if (*bufp == '#')
468 	    continue;
469 	if ((cmd = mystrtok(&bufp, " ")) == 0) {
470 	    vstream_printf("usage: del key|get key|put key=value|first|next\n");
471 	    vstream_fflush(VSTREAM_OUT);
472 	    continue;
473 	}
474 	if (dict_changed_name())
475 	    msg_warn("dictionary has changed");
476 	key = *bufp ? vstring_str(unescape(keybuf, mystrtok(&bufp, " ="))) : 0;
477 	value = mystrtok(&bufp, " =");
478 	if (strcmp(cmd, "del") == 0 && key && !value) {
479 	    if (dict_del(dict, key))
480 		vstream_printf("%s: not found\n", key);
481 	    else
482 		vstream_printf("%s: deleted\n", key);
483 	} else if (strcmp(cmd, "get") == 0 && key && !value) {
484 	    if ((value = dict_get(dict, key)) == 0) {
485 		vstream_printf("%s: %s\n", key,
486 			       dict_errno == DICT_ERR_RETRY ?
487 			       "soft error" : "not found");
488 	    } else {
489 		vstream_printf("%s=%s\n", key, value);
490 	    }
491 	} else if (strcmp(cmd, "put") == 0 && key && value) {
492 	    dict_put(dict, key, value);
493 	    vstream_printf("%s=%s\n", key, value);
494 	} else if (strcmp(cmd, "first") == 0 && !key && !value) {
495 	    if (dict_seq(dict, DICT_SEQ_FUN_FIRST, &key, &value) == 0)
496 		vstream_printf("%s=%s\n", key, value);
497 	    else
498 		vstream_printf("%s\n",
499 			       dict_errno == DICT_ERR_RETRY ?
500 			       "soft error" : "not found");
501 	} else if (strcmp(cmd, "next") == 0 && !key && !value) {
502 	    if (dict_seq(dict, DICT_SEQ_FUN_NEXT, &key, &value) == 0)
503 		vstream_printf("%s=%s\n", key, value);
504 	    else
505 		vstream_printf("%s\n",
506 			       dict_errno == DICT_ERR_RETRY ?
507 			       "soft error" : "not found");
508 	} else {
509 	    vstream_printf("usage: del key|get key|put key=value|first|next\n");
510 	}
511 	vstream_fflush(VSTREAM_OUT);
512     }
513     vstring_free(keybuf);
514     vstring_free(inbuf);
515     dict_close(dict);
516     return (0);
517 }
518 
519 #endif
520