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