Cleanup: the dictionary routines now take an extra flag
argument to control such things as warning about duplicates,
- and appending null bytes to key/value. This was needed for
- a clean implementation of NIS master alias maps support.
+ and appending null bytes to key/value. The latter was needed
+ for a clean implementation of NIS master alias maps support.
Feature: POSIX regular expressions by Lamont Jones.
+19990328
+
+ Code cleanup: dictionaries now have flags that say whether
+ lookup keys are fixed strings or whether keys are subjected
+ to pattern matching. This is needed to avoid passing partial
+ addresses to regexp-based lookup tables (user, @domain,
+ user@, domain). Files: util/dict*.c.
+
+ Bugfix: fixed memory leaks and core dumps in the regexp
+ and pcre routines (neither handled an empty pattern file).
+
+19990329
+
+ Code cleanup: the dictionary I/O routines now do their own
+ locking depending on dictionary flag settings. This means
+ that the low-level dict_get() interface can now be used
+ for safe dictionary lookups. This is needed for 19990328's
+ partial lookup key support. Files: util/dict*.c. global/maps.c.
+
+ Feature: regular expression matches are no longer limited
+ to user@domain address forms in access/canonical/virtual
+ maps, but can also be used for domains in transport maps.
+ This needed the partial lookup key support to avoid passing
+ partial addresses to regexp-based lookup tables (user,
+ @domain, user@, domain). Files: global/maps.c
+ globl/mail_addr_find.c.
+
+ Feature: new dictionary types can be registered with
+ dict_open_register(). File: util/dict_open.c.
+
Future:
Planned: must be able to list the same hash table in
cleanup.o: cleanup.h
cleanup.o: ../include/argv.h
cleanup.o: ../include/maps.h
+cleanup.o: ../include/dict.h
cleanup.o: ../include/tok822.h
cleanup.o: ../include/resolve_clnt.h
cleanup.o: ../include/been_here.h
cleanup_envelope.o: cleanup.h
cleanup_envelope.o: ../include/argv.h
cleanup_envelope.o: ../include/maps.h
+cleanup_envelope.o: ../include/dict.h
cleanup_envelope.o: ../include/been_here.h
cleanup_envelope.o: ../include/mail_stream.h
cleanup_extracted.o: cleanup_extracted.c
cleanup_extracted.o: ../include/rec_type.h
cleanup_extracted.o: cleanup.h
cleanup_extracted.o: ../include/maps.h
+cleanup_extracted.o: ../include/dict.h
cleanup_extracted.o: ../include/tok822.h
cleanup_extracted.o: ../include/resolve_clnt.h
cleanup_extracted.o: ../include/been_here.h
cleanup_masquerade.o: cleanup.h
cleanup_masquerade.o: ../include/vstream.h
cleanup_masquerade.o: ../include/maps.h
+cleanup_masquerade.o: ../include/dict.h
cleanup_masquerade.o: ../include/been_here.h
cleanup_masquerade.o: ../include/mail_stream.h
cleanup_message.o: cleanup_message.c
cleanup_message.o: ../include/is_header.h
cleanup_message.o: cleanup.h
cleanup_message.o: ../include/maps.h
+cleanup_message.o: ../include/dict.h
cleanup_message.o: ../include/been_here.h
cleanup_message.o: ../include/mail_stream.h
cleanup_out.o: cleanup_out.c
cleanup_out.o: cleanup.h
cleanup_out.o: ../include/argv.h
cleanup_out.o: ../include/maps.h
+cleanup_out.o: ../include/dict.h
cleanup_out.o: ../include/tok822.h
cleanup_out.o: ../include/resolve_clnt.h
cleanup_out.o: ../include/been_here.h
cleanup_out_recipient.o: ../include/vbuf.h
cleanup_out_recipient.o: ../include/vstream.h
cleanup_out_recipient.o: ../include/maps.h
+cleanup_out_recipient.o: ../include/dict.h
cleanup_out_recipient.o: ../include/tok822.h
cleanup_out_recipient.o: ../include/resolve_clnt.h
cleanup_out_recipient.o: ../include/mail_stream.h
cleanup_rewrite.o: ../include/vstream.h
cleanup_rewrite.o: ../include/argv.h
cleanup_rewrite.o: ../include/maps.h
+cleanup_rewrite.o: ../include/dict.h
cleanup_rewrite.o: ../include/been_here.h
cleanup_rewrite.o: ../include/mail_stream.h
cleanup_skip.o: cleanup_skip.c
cleanup_skip.o: cleanup.h
cleanup_skip.o: ../include/argv.h
cleanup_skip.o: ../include/maps.h
+cleanup_skip.o: ../include/dict.h
cleanup_skip.o: ../include/tok822.h
cleanup_skip.o: ../include/resolve_clnt.h
cleanup_skip.o: ../include/been_here.h
cleanup_state.o: cleanup.h
cleanup_state.o: ../include/argv.h
cleanup_state.o: ../include/maps.h
+cleanup_state.o: ../include/dict.h
cleanup_state.o: ../include/tok822.h
cleanup_state.o: ../include/resolve_clnt.h
cleanup_state.o: ../include/mail_stream.h
{
if (*var_canonical_maps)
cleanup_comm_canon_maps =
- maps_create(VAR_CANONICAL_MAPS, var_canonical_maps);
+ maps_create(VAR_CANONICAL_MAPS, var_canonical_maps, DICT_FLAG_LOCK);
if (*var_send_canon_maps)
cleanup_send_canon_maps =
- maps_create(VAR_SEND_CANON_MAPS, var_send_canon_maps);
+ maps_create(VAR_SEND_CANON_MAPS, var_send_canon_maps,
+ DICT_FLAG_LOCK);
if (*var_rcpt_canon_maps)
cleanup_rcpt_canon_maps =
- maps_create(VAR_RCPT_CANON_MAPS, var_rcpt_canon_maps);
+ maps_create(VAR_RCPT_CANON_MAPS, var_rcpt_canon_maps,
+ DICT_FLAG_LOCK);
if (*var_virtual_maps)
- cleanup_virtual_maps = maps_create(VAR_VIRTUAL_MAPS, var_virtual_maps);
+ cleanup_virtual_maps = maps_create(VAR_VIRTUAL_MAPS, var_virtual_maps,
+ DICT_FLAG_LOCK);
if (*var_masq_domains)
cleanup_masq_domains = argv_split(var_masq_domains, " ,\t\r\n");
}
/*
* Try user+foo@domain and user@domain.
+ *
+ * Specify what keys are partial or full, to avoid matching partial
+ * addresses with regular expressions.
*/
- if ((result = maps_find(path, full_key)) == 0 && dict_errno == 0
- && bare_key != 0 && (result = maps_find(path, bare_key)) != 0
+#define FULL 0
+#define PARTIAL DICT_FLAG_FIXED
+
+ if ((result = maps_find(path, full_key, FULL)) == 0 && dict_errno == 0
+ && bare_key != 0 && (result = maps_find(path, bare_key, PARTIAL)) != 0
&& extp != 0) {
*extp = saved_ext;
saved_ext = 0;
&& (strcasecmp(ratsign + 1, var_myorigin) == 0
|| resolve_local(ratsign + 1))) {
*ratsign = 0;
- result = maps_find(path, full_key);
+ result = maps_find(path, full_key, PARTIAL);
if (result == 0 && dict_errno == 0 && bare_key != 0) {
if ((ratsign = strrchr(bare_key, '@')) == 0)
msg_panic("%s: bare key botch", myname);
*ratsign = 0;
- if ((result = maps_find(path, bare_key)) != 0 && extp != 0) {
+ if ((result = maps_find(path, bare_key, PARTIAL)) != 0 && extp != 0) {
*extp = saved_ext;
saved_ext = 0;
}
* Try @domain.
*/
if (result == 0 && dict_errno == 0 && ratsign)
- result = maps_find(path, ratsign);
+ result = maps_find(path, ratsign, PARTIAL);
/*
* Clean up.
*/
if (argc != 2)
msg_fatal("usage: %s database", argv[0]);
+ msg_verbose = 1;
/*
* Initialize.
*/
read_config();
- path = maps_create(argv[0], argv[1]);
+ path = maps_create(argv[0], argv[1], DICT_FLAG_LOCK);
while (vstring_fgets_nonl(buffer, VSTREAM_IN)) {
extent = 0;
result = mail_addr_find(path, STR(buffer), &extent);
vstream_printf("%s -> %s (%s)\n", STR(buffer), result ? result :
dict_errno ? "(try again)" :
- "(not found)", extent ? extent : "null");
+ "(not found)", extent ? extent : "null extension");
vstream_fflush(VSTREAM_OUT);
if (extent)
myfree(extent);
msg_verbose = 1;
if (chdir(var_queue_dir) < 0)
msg_fatal("chdir %s: %m", var_queue_dir);
- path = maps_create(argv[0], argv[1]);
+ path = maps_create(argv[0], argv[1], DICT_FLAG_LOCK);
while (vstring_fgets_nonl(buffer, VSTREAM_IN)) {
if ((result = mail_addr_map(path, STR(buffer))) != 0)
argv_free(result);
* Version of this program.
*/
#define VAR_MAIL_VERSION "mail_version"
-#define DEF_MAIL_VERSION "Snapshot-19990328"
+#define DEF_MAIL_VERSION "Snapshot-19990329"
extern char *var_mail_version;
/* LICENSE
/* named dictionaries.
/* The result is a handle that must be specified along with all
/* other maps_xxx() operations.
-/* see dict_open(3) for a description of flags.
+/* See dict_open(3) for a description of flags.
/*
/* maps_find() searches the specified list of dictionaries
/* in the specified order for the named key. The result is in
/* maps_create - initialize */
-MAPS *maps_create(const char *title, const char *map_names)
+MAPS *maps_create(const char *title, const char *map_names, int flags)
{
char *myname = "maps_create";
char *temp = mystrdup(map_names);
if (msg_verbose)
msg_info("%s: %s", myname, map_type_name);
if ((dict = dict_handle(map_type_name)) == 0)
- dict = dict_open(map_type_name, O_RDONLY, 0);
+ dict = dict_open(map_type_name, O_RDONLY, flags);
+ else if ((dict->flags & flags) != flags)
+ msg_warn("%s: map %s has flags 0%o, want flags 0%o",
+ myname, map_type_name, dict->flags, flags);
dict_register(map_type_name, dict);
argv_add(maps->argv, map_type_name, ARGV_END);
}
/* maps_find - search a list of dictionaries */
-const char *maps_find(MAPS *maps, const char *name)
+const char *maps_find(MAPS *maps, const char *name, int flags)
{
char *myname = "maps_find";
char **map_name;
const char *expansion;
+ DICT *dict;
for (map_name = maps->argv->argv; *map_name; map_name++) {
- if ((expansion = dict_lookup(*map_name, name)) != 0) {
+ if ((dict = dict_handle(*map_name)) == 0)
+ msg_panic("%s: dictionary not found: %s", myname, *map_name);
+ if (flags != 0 && (dict->flags & flags) == 0)
+ continue;
+ if ((expansion = dict_get(dict, name)) != 0) {
if (msg_verbose)
msg_info("%s: %s: %s = %s", myname, *map_name, name, expansion);
return (expansion);
if (argc != 2)
msg_fatal("usage: %s maps", argv[0]);
msg_verbose = 2;
- maps = maps_create("whatever", argv[1]);
+ maps = maps_create("whatever", argv[1], DICT_FLAG_LOCK);
while (vstring_fgets_nonl(buf, VSTREAM_IN)) {
- if ((result = maps_find(maps, vstring_str(buf))) != 0) {
+ if ((result = maps_find(maps, vstring_str(buf), 0)) != 0) {
vstream_printf("%s\n", result);
} else if (dict_errno != 0) {
msg_fatal("lookup error: %m");
/* DESCRIPTION
/* .nf
+ /*
+ * Utility library.
+ */
+#include <dict.h>
+
/*
* Dictionary name storage. We're borrowing from the argv(3) module.
*/
struct ARGV *argv;
} MAPS;
-extern MAPS *maps_create(const char *, const char *);
-extern const char *maps_find(MAPS *, const char *);
+extern MAPS *maps_create(const char *, const char *, int);
+extern const char *maps_find(MAPS *, const char *, int);
extern MAPS *maps_free(MAPS *);
/* LICENSE
* Do this only once.
*/
if (maps == 0)
- maps = maps_create("aliases", var_alias_maps);
+ maps = maps_create("aliases", var_alias_maps, DICT_FLAG_LOCK);
/*
* DUPLICATE/LOOP ELIMINATION
concatenate("owner-", state.msg_attr.local, (char *) 0)))
expansion = mystrdup(alias_result);
- if (OWNER_ASSIGN(owner) != 0 && maps_find(maps, owner)) {
+ if (OWNER_ASSIGN(owner) != 0 && maps_find(maps, owner, 0)) {
canon_owner = canon_addr_internal(vstring_alloc(10), owner);
SET_OWNER_ATTR(state.msg_attr, STR(canon_owner), state.level);
} else {
qmgr.o: qmgr.h
qmgr.o: ../include/scan_dir.h
qmgr.o: ../include/maps.h
+qmgr.o: ../include/dict.h
qmgr_active.o: qmgr_active.c
qmgr_active.o: ../include/sys_defs.h
qmgr_active.o: ../include/msg.h
qmgr_active.o: qmgr.h
qmgr_active.o: ../include/scan_dir.h
qmgr_active.o: ../include/maps.h
+qmgr_active.o: ../include/dict.h
qmgr_bounce.o: qmgr_bounce.c
qmgr_bounce.o: ../include/sys_defs.h
qmgr_bounce.o: ../include/bounce.h
qmgr_bounce.o: qmgr.h
qmgr_bounce.o: ../include/scan_dir.h
qmgr_bounce.o: ../include/maps.h
+qmgr_bounce.o: ../include/dict.h
qmgr_defer.o: qmgr_defer.c
qmgr_defer.o: ../include/sys_defs.h
qmgr_defer.o: ../include/msg.h
qmgr_defer.o: qmgr.h
qmgr_defer.o: ../include/scan_dir.h
qmgr_defer.o: ../include/maps.h
+qmgr_defer.o: ../include/dict.h
qmgr_deliver.o: qmgr_deliver.c
qmgr_deliver.o: ../include/sys_defs.h
qmgr_deliver.o: ../include/msg.h
qmgr_deliver.o: qmgr.h
qmgr_deliver.o: ../include/scan_dir.h
qmgr_deliver.o: ../include/maps.h
+qmgr_deliver.o: ../include/dict.h
qmgr_enable.o: qmgr_enable.c
qmgr_enable.o: ../include/sys_defs.h
qmgr_enable.o: ../include/msg.h
qmgr_enable.o: qmgr.h
qmgr_enable.o: ../include/scan_dir.h
qmgr_enable.o: ../include/maps.h
+qmgr_enable.o: ../include/dict.h
qmgr_entry.o: qmgr_entry.c
qmgr_entry.o: ../include/sys_defs.h
qmgr_entry.o: ../include/msg.h
qmgr_entry.o: qmgr.h
qmgr_entry.o: ../include/scan_dir.h
qmgr_entry.o: ../include/maps.h
+qmgr_entry.o: ../include/dict.h
qmgr_message.o: qmgr_message.c
qmgr_message.o: ../include/sys_defs.h
qmgr_message.o: ../include/msg.h
qmgr_move.o: ../include/mail_scan_dir.h
qmgr_move.o: qmgr.h
qmgr_move.o: ../include/maps.h
+qmgr_move.o: ../include/dict.h
qmgr_queue.o: qmgr_queue.c
qmgr_queue.o: ../include/sys_defs.h
qmgr_queue.o: ../include/msg.h
qmgr_queue.o: ../include/vbuf.h
qmgr_queue.o: ../include/scan_dir.h
qmgr_queue.o: ../include/maps.h
+qmgr_queue.o: ../include/dict.h
qmgr_rcpt_list.o: qmgr_rcpt_list.c
qmgr_rcpt_list.o: ../include/sys_defs.h
qmgr_rcpt_list.o: ../include/mymalloc.h
qmgr_rcpt_list.o: ../include/vbuf.h
qmgr_rcpt_list.o: ../include/scan_dir.h
qmgr_rcpt_list.o: ../include/maps.h
+qmgr_rcpt_list.o: ../include/dict.h
qmgr_scan.o: qmgr_scan.c
qmgr_scan.o: ../include/sys_defs.h
qmgr_scan.o: ../include/msg.h
qmgr_scan.o: ../include/vstream.h
qmgr_scan.o: ../include/vbuf.h
qmgr_scan.o: ../include/maps.h
+qmgr_scan.o: ../include/dict.h
qmgr_transport.o: qmgr_transport.c
qmgr_transport.o: ../include/sys_defs.h
qmgr_transport.o: ../include/msg.h
qmgr_transport.o: qmgr.h
qmgr_transport.o: ../include/scan_dir.h
qmgr_transport.o: ../include/maps.h
+qmgr_transport.o: ../include/dict.h
static void qmgr_pre_init(void)
{
if (*var_relocated_maps)
- qmgr_relocated = maps_create("relocated", var_relocated_maps);
+ qmgr_relocated = maps_create("relocated", var_relocated_maps,
+ DICT_FLAG_LOCK);
if (*var_virtual_maps)
- qmgr_virtual = maps_create("virtual", var_virtual_maps);
+ qmgr_virtual = maps_create("virtual", var_virtual_maps,
+ DICT_FLAG_LOCK);
}
/* qmgr_post_init - post-jail initialization */
&& qmgr_virtual != 0
&& (at = strrchr(recipient->address, '@')) != 0) {
domain = lowercase(mystrdup(at + 1));
- junk = maps_find(qmgr_virtual, domain);
+ junk = maps_find(qmgr_virtual, domain, 0);
myfree(domain);
if (junk) {
qmgr_bounce_recipient(message, recipient,
while ((name = mystrtok(&bp, " \t\r\n,")) != 0) {
argv_add(argv, name, (char *) 0);
if (strchr(name, ':') && dict_handle(name) == 0)
- dict_register(name, dict_open(name, 0, 0));
+ dict_register(name, dict_open(name, O_RDONLY, DICT_FLAG_LOCK));
}
argv_terminate(argv);
/* check_access - table lookup without substring magic */
-static int check_access(SMTPD_STATE *state, char *table, char *name)
+static int check_access(SMTPD_STATE *state, char *table, char *name, int flags)
{
char *myname = "check_access";
char *low_name = lowercase(mystrdup(name));
const char *value;
+ DICT *dict;
#define CHK_ACCESS_RETURN(x) { myfree(low_name); return(x); }
+#define FULL 0
+#define PARTIAL DICT_FLAG_FIXED
if (msg_verbose)
msg_info("%s: %s", myname, name);
- if ((value = dict_lookup(table, low_name)) != 0)
- CHK_ACCESS_RETURN(check_table_result(state, table, value, name));
- if (dict_errno != 0)
- msg_fatal("%s: table lookup problem", table);
+ if ((dict = dict_handle(table)) == 0)
+ msg_panic("%s: dictionary not found: %s", myname, table);
+ if (flags == 0 || (flags & dict->flags) != 0) {
+ if ((value = dict_get(dict, low_name)) != 0)
+ CHK_ACCESS_RETURN(check_table_result(state, table, value, name));
+ if (dict_errno != 0)
+ msg_fatal("%s: table lookup problem", table);
+ }
CHK_ACCESS_RETURN(SMTPD_CHECK_DUNNO);
}
/* check_domain_access - domainname-based table lookup */
-static int check_domain_access(SMTPD_STATE *state, char *table, char *domain)
+static int check_domain_access(SMTPD_STATE *state, char *table,
+ char *domain, int flags)
{
char *myname = "check_domain_access";
char *low_domain = lowercase(mystrdup(domain));
char *name;
char *next;
const char *value;
+ DICT *dict;
if (msg_verbose)
msg_info("%s: %s", myname, domain);
#define CHK_DOMAIN_RETURN(x) { myfree(low_domain); return(x); }
for (name = low_domain; (next = strchr(name, '.')) != 0; name = next + 1) {
- if ((value = dict_lookup(table, name)) != 0)
+ if ((dict = dict_handle(table)) == 0)
+ msg_panic("%s: dictionary not found: %s", myname, table);
+ if (flags != 0 && (flags & dict->flags) == 0)
+ continue;
+ if ((value = dict_get(dict, name)) != 0)
CHK_DOMAIN_RETURN(check_table_result(state, table, value, domain));
if (dict_errno != 0)
msg_fatal("%s: table lookup problem", table);
+ flags = PARTIAL;
}
CHK_DOMAIN_RETURN(SMTPD_CHECK_DUNNO);
}
/* check_addr_access - address-based table lookup */
-static int check_addr_access(SMTPD_STATE *state, char *table, char *address)
+static int check_addr_access(SMTPD_STATE *state, char *table, char *address,
+ int flags)
{
char *myname = "check_addr_access";
char *addr;
const char *value;
+ DICT *dict;
if (msg_verbose)
msg_info("%s: %s", myname, address);
addr = STR(vstring_strcpy(error_text, address));
do {
- if ((value = dict_lookup(table, addr)) != 0)
+ if ((dict = dict_handle(table)) == 0)
+ msg_panic("%s: dictionary not found: %s", myname, table);
+ if (flags != 0 && (flags & dict->flags) == 0)
+ continue;
+ if ((value = dict_get(dict, addr)) != 0)
return (check_table_result(state, table, value, address));
if (dict_errno != 0)
msg_fatal("%s: table lookup problem", table);
+ flags = PARTIAL;
} while (split_at_right(addr, '.'));
return (SMTPD_CHECK_DUNNO);
/* check_namadr_access - OK/FAIL based on host name/address lookup */
static int check_namadr_access(SMTPD_STATE *state, char *table,
- char *name, char *addr)
+ char *name, char *addr, int flags)
{
char *myname = "check_namadr_access";
int status;
* Look up the host name, or parent domains thereof. XXX A domain
* wildcard may pre-empt a more specific address table entry.
*/
- if ((status = check_domain_access(state, table, name)) != 0)
+ if ((status = check_domain_access(state, table, name, flags)) != 0)
return (status);
/*
* Look up the network address, or parent networks thereof.
*/
- if ((status = check_addr_access(state, table, addr)) != 0)
+ if ((status = check_addr_access(state, table, addr, flags)) != 0)
return (status);
/*
/*
* Look up the full address.
*/
- if ((status = check_access(state, table, STR(reply.recipient))) != 0)
+ if ((status = check_access(state, table, STR(reply.recipient), FULL)) != 0)
return (status);
/*
* Look up the domain name, or parent domains thereof.
*/
- if ((status = check_domain_access(state, table, ratsign + 1)) != 0)
+ if ((status = check_domain_access(state, table, ratsign + 1, PARTIAL)) != 0)
return (status);
/*
*/
local_at = mystrndup(STR(reply.recipient),
ratsign - STR(reply.recipient) + 1);
- status = check_access(state, table, local_at);
+ status = check_access(state, table, local_at, PARTIAL);
myfree(local_at);
if (status != 0)
return (status);
return (1);
}
if (is_map_command(name, CHECK_CLIENT_ACL, cpp)) {
- *status = check_namadr_access(state, **cpp, state->name, state->addr);
+ *status = check_namadr_access(state, **cpp, state->name, state->addr, FULL);
return (1);
}
if (strcasecmp(name, REJECT_MAPS_RBL) == 0) {
*/
if (state->helo_name) {
if (is_map_command(name, CHECK_HELO_ACL, cpp) && state->helo_name) {
- *status = check_domain_access(state, **cpp, state->helo_name);
+ *status = check_domain_access(state, **cpp, state->helo_name, FULL);
return (1);
}
if (strcasecmp(name, REJECT_INVALID_HOSTNAME) == 0) {
*/
for (cpp = client_restrctions->argv; (name = *cpp) != 0; cpp++) {
if (strchr(name, ':') != 0) {
- status = check_namadr_access(state, name, state->name, state->addr);
+ status = check_namadr_access(state, name, state->name, state->addr, FULL);
} else if (generic_checks(state, name, &cpp, &status, state->addr) == 0) {
msg_warn("unknown %s check: \"%s\"", VAR_CLIENT_CHECKS, name);
break;
state->helo_name = mystrdup(helohost);
for (cpp = helo_restrctions->argv; (name = *cpp) != 0; cpp++) {
if (strchr(name, ':') != 0) {
- status = check_domain_access(state, name, helohost);
+ status = check_domain_access(state, name, helohost, FULL);
} else if (generic_checks(state, name, &cpp, &status, helohost) == 0) {
msg_warn("unknown %s check: \"%s\"", VAR_HELO_CHECKS, name);
break;
*/
for (cpp = etrn_restrctions->argv; (name = *cpp) != 0; cpp++) {
if (strchr(name, ':') != 0) {
- status = check_domain_access(state, name, domain);
+ status = check_domain_access(state, name, domain, FULL);
} else if (is_map_command(name, CHECK_ETRN_ACL, &cpp)) {
- status = check_domain_access(state, *cpp, domain);
+ status = check_domain_access(state, *cpp, domain, FULL);
} else if (generic_checks(state, name, &cpp, &status, domain) == 0) {
msg_warn("unknown %s check: \"%s\"", VAR_RCPT_CHECKS, name);
break;
{
if (transport_path)
msg_panic("transport_init: repeated call");
- transport_path = maps_create("transport", var_transport_maps);
+ transport_path = maps_create("transport", var_transport_maps,
+ DICT_FLAG_LOCK);
}
/* transport_lookup - map a transport domain */
char *transport;
int found = 0;
+#define FULL 0
+#define PARTIAL DICT_FLAG_FIXED
+
+ int maps_flag = FULL;
+
if (transport_path == 0)
msg_panic("transport_lookup: missing initialization");
*
* Before changing the DB lookup result, make a copy first, in order to
* avoid DB cache corruption.
+ *
+ * Specify if a key is partial or full, to avoid matching partial keys with
+ * regular expressions.
*/
for (name = low_domain; name != 0; name = strchr(name + 1, '.')) {
- if ((value = maps_find(transport_path, name)) != 0) {
+ if ((value = maps_find(transport_path, name, maps_flag)) != 0) {
saved_value = mystrdup(value);
if ((host = split_at(saved_value, ':')) == 0 || *host == 0)
host = domain;
} else if (dict_errno != 0) {
msg_fatal("transport table lookup problem");
}
+ maps_flag = PARTIAL;
}
myfree(low_domain);
return (found);
dict.o: vbuf.h
dict.o: vstring.h
dict.o: readline.h
-dict.o: myflock.h
dict.o: mac_parse.h
dict.o: dict.h
dict.o: dict_ht.h
dict_db.o: vbuf.h
dict_db.o: stringops.h
dict_db.o: iostuff.h
+dict_db.o: myflock.h
dict_db.o: dict.h
dict_db.o: vstream.h
dict_db.o: dict_db.h
/*
/* dict_update() updates the value of the named dictionary member.
/* The dictionary member and the named dictionary are instantiated
-/* on the fly. During the update, a file-based dictionary is locked
-/* for exclusive access. With file-based dictionaries, duplicate
-/* of duplicate entries depends on dictionary flag settings:
-/* .IP DICT_FLAG_DUP_WARN
-/* Log a warning and ignore the duplicate.
-/* .IP DICT_FLAG_DUP_IGNORE
-/* Silently ignore the duplicate.
-/* .PP
-/* The default is to terminate the program with a fatal error.
+/* on the fly.
/*
/* dict_lookup() returns the value of the named member (i.e. without
/* expanding macros in the member value). The \fIdict_name\fR argument
-/* specifies the dictionary to search. The dictionary is locked for
-/* shared access, when it is file based. The result is a null pointer
+/* specifies the dictionary to search. The result is a null pointer
/* when no value is found, otherwise the result is owned by the
/* underlying dictionary method. Make a copy if the result is to be
/* modified, or if the result is to survive multiple dict_lookup() calls.
#include "vstream.h"
#include "vstring.h"
#include "readline.h"
-#include "myflock.h"
#include "mac_parse.h"
#include "dict.h"
#include "dict_ht.h"
dict = node->dict;
if (msg_verbose > 1)
msg_info("%s: %s = %s", myname, member, value);
- if (dict->fd >= 0 && myflock(dict->fd, MYFLOCK_EXCLUSIVE) < 0)
- msg_fatal("%s: lock dictionary %s: %m", myname, dict_name);
dict->update(dict, member, value);
- if (dict->fd >= 0 && myflock(dict->fd, MYFLOCK_NONE) < 0)
- msg_fatal("%s: unlock dictionary %s: %m", myname, dict_name);
}
/* dict_lookup - look up dictionary entry */
DICT *dict;
const char *ret = 0;
- dict_errno = 0;
-
if ((node = dict_node(dict_name)) == 0) {
if (dict_unknown_allowed == 0)
msg_fatal("%s: unknown dictionary: %s", myname, dict_name);
} else {
dict = node->dict;
- if (dict->fd >= 0 && myflock(dict->fd, MYFLOCK_SHARED) < 0)
- msg_fatal("%s: lock dictionary %s: %m", myname, dict_name);
ret = dict->lookup(dict, member);
- if (dict->fd >= 0 && myflock(dict->fd, MYFLOCK_NONE) < 0)
- msg_fatal("%s: unlock dictionary %s: %m", myname, dict_name);
if (ret == 0 && dict_unknown_allowed == 0)
msg_fatal("dictionary %s: unknown member: %s", dict_name, member);
}
#define DICT_FLAG_TRY1NULL (1<<3) /* append 0 to key/value */
#define DICT_FLAG_FIXED (1<<4) /* fixed key map */
#define DICT_FLAG_PATTERN (1<<5) /* keys are patterns */
+#define DICT_FLAG_LOCK (1<<6) /* lock before access */
extern int dict_unknown_allowed;
extern int dict_errno;
*/
extern DICT *dict_open(const char *, int, int);
extern DICT *dict_open3(const char *, const char *, int, int);
-
+extern void dict_open_register(const char *, DICT *(*)(const char *, int, int));
#define dict_get(dp, key) (dp)->lookup((dp), (key))
#define dict_put(dp, key, val) (dp)->update((dp), (key), (val))
#define dict_close(dp) (dp)->close(dp)
#include "vstring.h"
#include "stringops.h"
#include "iostuff.h"
+#include "myflock.h"
#include "dict.h"
#include "dict_db.h"
typedef struct {
DICT dict; /* generic members */
- int flags; /* see below */
DB *db; /* open db file */
char *path; /* pathname */
} DICT_DB;
DBT db_value;
int status;
static VSTRING *buf;
+ const char *result = 0;
+
+ dict_errno = 0;
+
+ /*
+ * Acquire a shared lock.
+ */
+ if ((dict->flags & DICT_FLAG_LOCK) && myflock(dict->fd, MYFLOCK_SHARED) < 0)
+ msg_fatal("%s: lock dictionary: %m", dict_db->path);
/*
* See if this DB file was written with one null byte appended to key and
* value.
*/
- if (dict_db->flags & DICT_FLAG_TRY1NULL) {
+ if (dict->flags & DICT_FLAG_TRY1NULL) {
db_key.data = (void *) name;
db_key.size = strlen(name) + 1;
if ((status = db->get(db, &db_key, &db_value, 0)) < 0)
msg_fatal("error reading %s: %m", dict_db->path);
if (status == 0) {
- dict_db->flags &= ~DICT_FLAG_TRY0NULL;
- return (db_value.data);
+ dict->flags &= ~DICT_FLAG_TRY0NULL;
+ result = db_value.data;
}
}
* See if this DB file was written with no null byte appended to key and
* value.
*/
- if (dict_db->flags & DICT_FLAG_TRY0NULL) {
+ if (result == 0 && (dict->flags & DICT_FLAG_TRY0NULL)) {
db_key.data = (void *) name;
db_key.size = strlen(name);
if ((status = db->get(db, &db_key, &db_value, 0)) < 0)
if (buf == 0)
buf = vstring_alloc(10);
vstring_strncpy(buf, db_value.data, db_value.size);
- dict_db->flags &= ~DICT_FLAG_TRY1NULL;
- return (vstring_str(buf));
+ dict->flags &= ~DICT_FLAG_TRY1NULL;
+ result = vstring_str(buf);
}
}
- return (0);
+
+ /*
+ * Release the shared lock.
+ */
+ if ((dict->fd & DICT_FLAG_LOCK) && myflock(dict->fd, MYFLOCK_NONE) < 0)
+ msg_fatal("%s: unlock dictionary: %m", dict_db->path);
+
+ return (result);
}
/* dict_db_update - add or update database entry */
* If undecided about appending a null byte to key and value, choose a
* default depending on the platform.
*/
- if ((dict_db->flags & DICT_FLAG_TRY1NULL)
- && (dict_db->flags & DICT_FLAG_TRY0NULL)) {
+ if ((dict->flags & DICT_FLAG_TRY1NULL)
+ && (dict->flags & DICT_FLAG_TRY0NULL)) {
#ifdef DB_NO_TRAILING_NULL
- dict_db->flags = DICT_FLAG_TRY0NULL;
+ dict->flags &= ~DICT_FLAG_TRY1NULL;
+ dict->flags |= DICT_FLAG_TRY0NULL;
#else
- dict_db->flags = DICT_FLAG_TRY1NULL;
+ dict->flags &= ~DICT_FLAG_TRY0NULL;
+ dict->flags |= DICT_FLAG_TRY1NULL;
#endif
}
/*
* Optionally append a null byte to key and value.
*/
- if (dict_db->flags & DICT_FLAG_TRY1NULL) {
+ if (dict->flags & DICT_FLAG_TRY1NULL) {
db_key.size++;
db_value.size++;
}
+ /*
+ * Acquire an exclusive lock.
+ */
+ if ((dict->flags & DICT_FLAG_LOCK) && myflock(dict->fd, MYFLOCK_EXCLUSIVE) < 0)
+ msg_fatal("%s: lock dictionary: %m", dict_db->path);
+
/*
* Do the update.
*/
else
msg_fatal("%s: duplicate entry: \"%s\"", dict_db->path, name);
}
+
+ /*
+ * Release the exclusive lock.
+ */
+ if ((dict->flags & DICT_FLAG_LOCK) && myflock(dict->fd, MYFLOCK_NONE) < 0)
+ msg_fatal("%s: unlock dictionary: %m", dict_db->path);
}
/* dict_db_close - close data base */
return (dict_db_open(path, open_flags, DB_BTREE, (void *) &tweak, dict_flags));
}
+/**INDENT** Error@188: Unmatched #endif */
#endif
#include "htable.h"
#include "iostuff.h"
#include "vstring.h"
+#include "myflock.h"
#include "dict.h"
#include "dict_dbm.h"
typedef struct {
DICT dict; /* generic members */
- int flags; /* see below */
DBM *dbm; /* open database */
char *path; /* pathname */
} DICT_DBM;
datum dbm_key;
datum dbm_value;
static VSTRING *buf;
+ const char *result = 0;
+
+ dict_errno = 0;
+
+ /*
+ * Acquire an exclusive lock.
+ */
+ if ((dict->flags & DICT_FLAG_LOCK) && myflock(dict->fd, MYFLOCK_SHARED) < 0)
+ msg_fatal("%s: lock dictionary: %m", dict_dbm->path);
/*
* See if this DBM file was written with one null byte appended to key
* and value.
*/
- if (dict_dbm->flags & DICT_FLAG_TRY1NULL) {
+ if (dict->flags & DICT_FLAG_TRY1NULL) {
dbm_key.dptr = (void *) name;
dbm_key.dsize = strlen(name) + 1;
dbm_value = dbm_fetch(dict_dbm->dbm, dbm_key);
if (dbm_value.dptr != 0) {
- dict_dbm->flags &= ~DICT_FLAG_TRY0NULL;
- return (dbm_value.dptr);
+ dict->flags &= ~DICT_FLAG_TRY0NULL;
+ result = dbm_value.dptr;
}
}
* See if this DBM file was written with no null byte appended to key and
* value.
*/
- if (dict_dbm->flags & DICT_FLAG_TRY0NULL) {
+ if (result == 0 && (dict->flags & DICT_FLAG_TRY0NULL)) {
dbm_key.dptr = (void *) name;
dbm_key.dsize = strlen(name);
dbm_value = dbm_fetch(dict_dbm->dbm, dbm_key);
if (buf == 0)
buf = vstring_alloc(10);
vstring_strncpy(buf, dbm_value.dptr, dbm_value.dsize);
- dict_dbm->flags &= ~DICT_FLAG_TRY1NULL;
- return (vstring_str(buf));
+ dict->flags &= ~DICT_FLAG_TRY1NULL;
+ result = vstring_str(buf);
}
}
- return (0);
+
+ /*
+ * Release the exclusive lock.
+ */
+ if ((dict->flags & DICT_FLAG_LOCK) && myflock(dict->fd, MYFLOCK_NONE) < 0)
+ msg_fatal("%s: unlock dictionary: %m", dict_dbm->path);
+
+ return (result);
}
/* dict_dbm_update - add or update database entry */
* If undecided about appending a null byte to key and value, choose a
* default depending on the platform.
*/
- if ((dict_dbm->flags & DICT_FLAG_TRY1NULL)
- && (dict_dbm->flags & DICT_FLAG_TRY0NULL)) {
+ if ((dict->flags & DICT_FLAG_TRY1NULL)
+ && (dict->flags & DICT_FLAG_TRY0NULL)) {
#ifdef DBM_NO_TRAILING_NULL
- dict_dbm->flags = DICT_FLAG_TRY0NULL;
+ dict->flags &= ~DICT_FLAG_TRY1NULL;
+ dict->flags |= DICT_FLAG_TRY0NULL;
#else
- dict_dbm->flags = DICT_FLAG_TRY1NULL;
+ dict->flags &= ~DICT_FLAG_TRY0NULL;
+ dict->flags |= DICT_FLAG_TRY1NULL;
#endif
}
/*
* Optionally append a null byte to key and value.
*/
- if (dict_dbm->flags & DICT_FLAG_TRY1NULL) {
+ if (dict->flags & DICT_FLAG_TRY1NULL) {
dbm_key.dsize++;
dbm_value.dsize++;
}
+ /*
+ * Acquire an exclusive lock.
+ */
+ if ((dict->flags & DICT_FLAG_LOCK) && myflock(dict->fd, MYFLOCK_EXCLUSIVE) < 0)
+ msg_fatal("%s: lock dictionary: %m", dict_dbm->path);
+
/*
* Do the update.
*/
else
msg_fatal("%s: duplicate entry: \"%s\"", dict_dbm->path, name);
}
+
+ /*
+ * Release the exclusive lock.
+ */
+ if ((dict->flags & DICT_FLAG_LOCK) && myflock(dict->fd, MYFLOCK_NONE) < 0)
+ msg_fatal("%s: unlock dictionary: %m", dict_dbm->path);
}
/* dict_dbm_close - disassociate from data base */
static const char *dict_env_lookup(DICT *unused_dict, const char *name)
{
+ dict_errno = 0;
+
return (safe_getenv(name));
}
{
DICT_HT *dict_ht = (DICT_HT *) dict;
+ dict_errno = 0;
+
return (htable_find(dict_ht->table, name));
}
int rc = 0;
void (*saved_alarm) (int);
+ dict_errno = 0;
+
/*
* Initialize.
*/
ni_status r;
ni_id dir;
+ dict_errno = 0;
+
if (msg_verbose)
msg_info("ni_lookup %s %s=%s", path, key_prop, key_value);
static const char *dict_nisplus_lookup(DICT *unused_dict, const char *unused_name)
{
+ dict_errno = 0;
msg_warn("dict_nisplus_lookup: NISPLUS lookup not implemented");
return (0);
}
/*
/* void dict_close(dict)
/* DICT *dict;
+/*
+/* dict_open_register(type, open)
+/* char *type;
+/* DICT *(*open) (const char *, int, int);
/* DESCRIPTION
/* This module implements a low-level interface to multiple
/* physical dictionary types.
/* Ignore duplicate keys if the underlying database does not
/* support duplicate keys. The default is to terminate with a fatal
/* error.
+/* .IP DICT_FLAG_TRY0NULL
+/* With maps where this is appropriate, append no null byte to
+/* keys and values.
+/* When neither DICT_FLAG_TRY0NULL nor DICT_FLAG_TRY1NULL are
+/* specified, the software guesses what format to use for reading;
+/* and in the absence of definite information, a system-dependent
+/* default is chosen for writing.
+/* .IP DICT_FLAG_TRY1NULL
+/* With maps where this is appropriate, append one null byte to
+/* keys and values.
+/* When neither DICT_FLAG_TRY0NULL nor DICT_FLAG_TRY1NULL are
+/* specified, the software guesses what format to use for reading;
+/* and in the absence of definite information, a system-dependent
+/* default is chosen for writing.
+/* .IP DICT_FLAG_LOCK
+/* With maps where this is appropriate, acquire an exclusive lock
+/* before writing, and acquire a shared lock before reading.
/* .PP
/* The dictionary types are as follows:
/* .IP environ
/*
/* dict_get() retrieves the value stored in the named dictionary
/* under the given key. A null pointer means the value was not found.
-/* This routine does not manipulate any locks.
/*
/* dict_put() stores the specified key and value into the named
-/* dictionary. This routine does not manipulate any locks.
+/* dictionary.
/*
/* dict_close() closes the specified dictionary and cleans up the
/* associated data structures.
+/*
+/* dict_open_register() adds support for a new dictionary type.
/* DIAGNOSTICS
/* Fatal error: open error, unsupported dictionary type, attempt to
/* update non-writable dictionary.
#include <dict_regexp.h>
#include <stringops.h>
#include <split_at.h>
+#include <htable.h>
/*
* lookup table for available map types.
0,
};
+static HTABLE *dict_open_hash;
+
+/* dict_open_init - one-off initialization */
+
+static void dict_open_init(void)
+{
+ char *myname = "dict_open_init";
+ DICT_OPEN_INFO *dp;
+
+ if (dict_open_hash != 0)
+ msg_panic("%s: multiple initialization", myname);
+ dict_open_hash = htable_create(10);
+
+ for (dp = dict_open_info; dp->type; dp++)
+ htable_enter(dict_open_hash, dp->type, (char *) dp);
+}
+
/* dict_open - open dictionary */
DICT *dict_open(const char *dict_spec, int open_flags, int dict_flags)
{
char *myname = "dict_open";
DICT_OPEN_INFO *dp;
- DICT *dict = 0;
-
- for (dp = dict_open_info; dp->type; dp++) {
- if (strcasecmp(dp->type, dict_type) == 0) {
- if ((dict = dp->open(dict_name, open_flags, dict_flags)) == 0)
- msg_fatal("opening %s:%s %m", dict_type, dict_name);
- if (msg_verbose)
- msg_info("%s: %s:%s", myname, dict_type, dict_name);
- break;
- }
- }
- if (dp->type == 0)
+ DICT *dict;
+
+ if (dict_open_hash == 0)
+ dict_open_init();
+ if ((dp = (DICT_OPEN_INFO *) htable_find(dict_open_hash, dict_type)) == 0)
msg_fatal("unsupported dictionary type: %s", dict_type);
+ if ((dict = dp->open(dict_name, open_flags, dict_flags)) == 0)
+ msg_fatal("opening %s:%s %m", dict_type, dict_name);
+ if (msg_verbose)
+ msg_info("%s: %s:%s", myname, dict_type, dict_name);
return (dict);
}
+/* dict_open_register - register dictionary type */
+
+void dict_open_register(const char *type,
+ DICT *(*open) (const char *, int, int))
+{
+ char *myname = "dict_open_register";
+ DICT_OPEN_INFO *dp;
+
+ if (dict_open_hash == 0)
+ dict_open_init();
+ if (htable_find(dict_open_hash, type))
+ msg_panic("%s: dictionary type exists: %s", myname, type);
+ dp = (DICT_OPEN_INFO *) mymalloc(sizeof(*dp));
+ dp->type = mystrdup(type);
+ dp->open = open;
+ htable_enter(dict_open_hash, dp->type, (char *) dp);
+}
+
#ifdef TEST
/*
#include "msg_vstream.h"
#include "vstring_vstream.h"
+static NORETURN usage(char *myname)
+{
+ msg_fatal("usage: %s type:file read|write|create", myname);
+}
+
main(int argc, char **argv)
{
VSTRING *keybuf = vstring_alloc(1);
int open_flags;
char *key;
const char *value;
+ int ch;
msg_vstream_init(argv[0], VSTREAM_ERR);
- if (argc != 3)
- msg_fatal("usage: %s type:file read|write|create", argv[0]);
- if (strcasecmp(argv[2], "create") == 0)
+ while ((ch = GETOPT(argc, argv, "v")) > 0) {
+ switch (ch) {
+ default:
+ usage(argv[0]);
+ case 'v':
+ msg_verbose++;
+ break;
+ }
+ }
+ optind = OPTIND;
+ if (argc - optind != 2)
+ usage(argv[0]);
+ if (strcasecmp(argv[optind + 1], "create") == 0)
open_flags = O_CREAT | O_RDWR | O_TRUNC;
- else if (strcasecmp(argv[2], "write") == 0)
+ else if (strcasecmp(argv[optind + 1], "write") == 0)
open_flags = O_RDWR;
- else if (strcasecmp(argv[2], "read") == 0)
+ else if (strcasecmp(argv[optind + 1], "read") == 0)
open_flags = O_RDONLY;
else
msg_fatal("unknown access mode: %s", argv[2]);
- dict_name = argv[1];
+ dict_name = argv[optind];
dict = dict_open(dict_name, open_flags, 0);
while (vstring_fgets_nonl(keybuf, VSTREAM_IN)) {
if ((key = strtok(vstring_str(keybuf), " =")) == 0)
static VSTRING *buf;
char *at;
-/* msg_info("dict_pcre_lookup: %s: %s", dict_pcre->map, name );*/
-
- /*
- * XXX Require user@domain, to defeat partial address matching for smtp
- * access control, canonical and virtual mappings, and to prevent regexps
- * from being used as alias databases because one might inadvertantly
- * copy "|command" or /file/name or :include: to the result.
- */
- if (name[0] == '@' || (at = strrchr(name, '@')) == 0 || at[1] == 0)
- return (0);
+ dict_errno = 0;
+
+ if (msg_verbose)
+ msg_info("dict_pcre_lookup: %s: %s", dict_pcre->map, name);
/* Search for a matching expression */
ctxt.matches = 0;
char *at;
int error;
- /* msg_info("dict_regexp_lookup: %s: %s", dict_regexp->map, name ); */
-
- /*
- * XXX Require user@domain, to defeat partial address matching for smtp
- * access control, canonical and virtual mappings, and to prevent regexps
- * from being used as alias databases because one might inadvertently
- * copy "|command" or /file/name or :include: to the result.
- */
- if (name[0] == '@' || (at = strrchr(name, '@')) == 0 || at[1] == 0)
- return (0);
+ dict_errno = 0;
+
+ if (msg_verbose)
+ msg_info("dict_regexp_lookup: %s: %s", dict_regexp->map, name);
/* Search for a matching expression */
for (rule = dict_regexp->head; rule; rule = rule->next) {
};
struct dict_unix_lookup *lp;
+ dict_errno = 0;
+
dict_unix = (DICT_UNIX *) mymalloc(sizeof(*dict_unix));
for (lp = dict_unix_lookup; /* void */ ; lp++) {
if (lp->name == 0)
} else if (strchr(pattern, ':') != 0) { /* type:table */
for (cp = pattern; *cp == '!'; cp++)
/* void */ ;
- dict_register(pattern, dict_open(pattern, 0, 0));
+ if (dict_handle(pattern) == 0)
+ dict_register(pattern,
+ dict_open(pattern, O_RDONLY, DICT_FLAG_LOCK));
argv_add(list, pattern, (char *) 0);
} else { /* other pattern */
argv_add(list, pattern, (char *) 0);
#else
#define GETOPT(argc, argv, str) getopt((argc), (argv), (str))
#endif
+#define OPTIND (optind > 0 ? optind : 1)
#if defined(USE_FCNTL_LOCK) && defined(USE_FLOCK_LOCK)
#error "define USE_FCNTL_LOCK or USE_FLOCK_LOCK, not both"