Feature: TLS support for client-side and server-side SNI
in the Postfix SMTP server, SMTP client, and tlsproxy.
-20181228
-
- Cleanup: generic wrapper infrastructure for Postfix maps
- (dictionaries) that is the basis for UTF8 checks and for
- base64 decoding of lookup results. Files: global/mkmap_open.c,
- postmap/postmap.c, util/dict.c, util/dict.h, util/dict_alloc.c,
- util/dict_file.c, util/dict_open.c, util/dict_pipe.c,
- util/dict_union.c, util/dict_utf8.c, util/dict_wrapper.c.
+20181229
+
+ Explicit maps_file_find() and dict_file_lookup() methods
+ that decode base64 content. Decoding content is not built
+ into the dict->lookup() method, because that would complicate
+ the implementation of map nesting (inline, thash), map
+ composition (pipemap, unionmap), and map proxying. For
+ consistency, decoding base64 file content is also not built
+ into the maps_find() method. Files: util/dict.h.
+ util/dict_file.c, global/maps.[hc], postmap/postmap.c.
* Patches change both the patchlevel and the release date. Snapshots have no
* patchlevel; they change the release date only.
*/
-#define MAIL_RELEASE_DATE "20181228"
+#define MAIL_RELEASE_DATE "20181229"
#define MAIL_VERSION_NUMBER "3.4"
#ifdef SNAPSHOT
/* const char *key;
/* int flags;
/*
+/* const char *maps_file_find(maps, key, flags)
+/* MAPS *maps;
+/* const char *key;
+/* int flags;
+/*
/* MAPS *maps_free(maps)
/* MAPS *maps;
/* DESCRIPTION
/* for example, DICT_FLAG_FIXED | DICT_FLAG_PATTERN selects
/* dictionaries that have fixed keys or pattern keys.
/*
+/* maps_file_find() implements maps_find() but also decodes
+/* the base64 lookup result. This requires that the maps are
+/* opened with DICT_FLAG_SRC_RHS_IS_FILE.
+/*
/* maps_free() releases storage claimed by maps_create()
/* and conveniently returns a null pointer.
/*
maps->title, name);
msg_warn("%s should return NO RESULT in case of NOT FOUND",
maps->title);
- maps->error = DICT_ERR_RETRY;
+ maps->error = DICT_ERR_CONFIG;
return (0);
}
if (msg_verbose)
- msg_info("%s: %s: %s: %s = %s", myname, maps->title,
- *map_name, name, expansion);
+ msg_info("%s: %s: %s: %s = %.100s%s", myname, maps->title,
+ *map_name, name, expansion,
+ strlen(expansion) > 100 ? "..." : "");
return (expansion);
} else if ((maps->error = dict->error) != 0) {
- msg_warn("%s:%s lookup error for \"%.100s\"",
+ msg_warn("%s:%s lookup error for \"%s\"",
+ dict->type, dict->name, name);
+ break;
+ }
+ }
+ if (msg_verbose)
+ msg_info("%s: %s: %s: %s", myname, maps->title, name, maps->error ?
+ "search aborted" : "not found");
+ return (0);
+}
+
+/* maps_file_find - search a list of dictionaries and base64 decode */
+
+const char *maps_file_find(MAPS *maps, const char *name, int flags)
+{
+ const char *myname = "maps_file_find";
+ char **map_name;
+ const char *expansion;
+ DICT *dict;
+ VSTRING *unb64;
+ char *err;
+
+ /*
+ * In case of return without map lookup (empty name or no maps).
+ */
+ maps->error = 0;
+
+ /*
+ * Temp. workaround, for buggy callers that pass zero-length keys when
+ * given partial addresses.
+ */
+ if (*name == 0)
+ return (0);
+
+ for (map_name = maps->argv->argv; *map_name; map_name++) {
+ if ((dict = dict_handle(*map_name)) == 0)
+ msg_panic("%s: dictionary not found: %s", myname, *map_name);
+ if ((dict->flags & DICT_FLAG_SRC_RHS_IS_FILE) == 0)
+ msg_panic("%s: %s: opened without DICT_FLAG_SRC_RHS_IS_FILE",
+ myname, maps->title);
+ if (flags != 0 && (dict->flags & flags) == 0)
+ continue;
+ if ((expansion = dict_get(dict, name)) != 0) {
+ if (*expansion == 0) {
+ msg_warn("%s lookup of %s returns an empty string result",
+ maps->title, name);
+ msg_warn("%s should return NO RESULT in case of NOT FOUND",
+ maps->title);
+ maps->error = DICT_ERR_CONFIG;
+ return (0);
+ }
+ if (msg_verbose)
+ msg_info("%s: %s: %s: %s = %.100s%s", myname, maps->title,
+ *map_name, name, expansion,
+ strlen(expansion) > 100 ? "..." : "");
+ if ((unb64 = dict_file_from_b64(dict, expansion)) == 0) {
+ err = dict_file_get_error(dict);
+ msg_warn("table %s:%s: key %s: %s",
+ dict->type, dict->name, name, err);
+ myfree(err);
+ maps->error = DICT_ERR_CONFIG;
+ return (0);
+ }
+ return (vstring_str(unb64));
+ } else if ((maps->error = dict->error) != 0) {
+ msg_warn("%s:%s lookup error for \"%s\"",
dict->type, dict->name, name);
break;
}
extern MAPS *maps_create(const char *, const char *, int);
extern const char *maps_find(MAPS *, const char *, int);
+extern const char *maps_file_find(MAPS *, const char *, int);
extern MAPS *maps_free(MAPS *);
/* LICENSE
*/
if ((mkmap->dict->flags & DICT_FLAG_UTF8_ACTIVE) == 0
&& DICT_NEED_UTF8_ACTIVATION(util_utf8_enable, dict_flags))
- dict_utf8_wrapper_activate(mkmap->dict);
-
- /* Insert wrapper for base64 decoding file content. */
- if ((mkmap->dict->flags & DICT_FLAG_UNB64_ACTIVE) == 0
- && (mkmap->dict->flags & DICT_FLAG_SRC_RHS_IS_FILE) != 0)
- dict_file_wrapper_activate(mkmap->dict);
+ mkmap->dict = dict_utf8_activate(mkmap->dict);
/*
* Resume signal delivery if multi-writer safe.
this-is-file1
this-is-file2
postmap: warning: table hash:file_test_map.db: key entry-4: malformed BASE64 value: postmap-entry-4
-postmap: fatal: table hash:file_test_map.db: query error: No such file or directory
+postmap: fatal: table hash:file_test_map.db: query error
dicts[n] = ((map_name = split_at(maps[n], ':')) != 0 ?
dict_open3(maps[n], map_name, O_RDONLY, dict_flags) :
dict_open3(var_db_type, maps[n], O_RDONLY, dict_flags));
- if ((value = dict_get(dicts[n], STR(keybuf))) != 0) {
+ value = ((dict_flags & DICT_FLAG_SRC_RHS_IS_FILE) ?
+ dict_file_lookup : dicts[n]->lookup)
+ (dicts[n], STR(keybuf));
+ if (value != 0) {
if (*value == 0) {
msg_warn("table %s:%s: key %s: empty string result is not allowed",
dicts[n]->type, dicts[n]->name, STR(keybuf));
msg_warn("table %s:%s should return NO RESULT in case of NOT FOUND",
dicts[n]->type, dicts[n]->name);
}
-#if 0
- if (dict_flags & DICT_FLAG_SRC_RHS_IS_FILE) {
- VSTRING *unb64;
- char *err;
-
- if ((unb64 = dict_file_from_b64(dicts[n], value)) == 0) {
- err = dict_file_get_error(dicts[n]);
- msg_fatal("table %s:%s: key %s: %s",
- dicts[n]->type, dicts[n]->name,
- STR(keybuf), err);
- }
- value = STR(unb64);
- }
-#endif
vstream_printf("%s %s\n", STR(keybuf), value);
found = 1;
break;
}
- if (dicts[n]->error)
+ switch (dicts[n]->error) {
+ case 0:
+ break;
+ case DICT_ERR_CONFIG:
+ msg_fatal("table %s:%s: query error",
+ dicts[n]->type, dicts[n]->name);
+ default:
msg_fatal("table %s:%s: query error: %m",
dicts[n]->type, dicts[n]->name);
+ }
}
}
} else {
const char *value;
dict = dict_open3(map_type, map_name, O_RDONLY, dict_flags);
- if ((value = dict_get(dict, key)) != 0) {
+ value = ((dict_flags & DICT_FLAG_SRC_RHS_IS_FILE) ?
+ dict_file_lookup : dict->lookup) (dict, key);
+ if (value != 0) {
if (*value == 0) {
msg_warn("table %s:%s: key %s: empty string result is not allowed",
map_type, map_name, key);
msg_warn("table %s:%s should return NO RESULT in case of NOT FOUND",
map_type, map_name);
}
-#if 0
- if (dict_flags & DICT_FLAG_SRC_RHS_IS_FILE) {
- VSTRING *unb64;
- char *err;
-
- if ((unb64 = dict_file_from_b64(dict, value)) == 0) {
- err = dict_file_get_error(dict);
- msg_fatal("table %s:%s: key %s: %s",
- dict->type, dict->name,
- key, err);
- }
- value = STR(unb64);
- }
-#endif
vstream_printf("%s\n", value);
}
- if (dict->error)
- msg_fatal("table %s:%s: query error: %m", dict->type, dict->name);
+ switch (dict->error) {
+ case 0:
+ break;
+ case DICT_ERR_CONFIG:
+ msg_fatal("table %s:%s: query error",
+ dict->type, dict->name);
+ default:
+ msg_fatal("table %s:%s: query error: %m",
+ dict->type, dict->name);
+ }
vstream_fflush(VSTREAM_OUT);
dict_close(dict);
return (value != 0);
msg_warn("table %s:%s should return NO RESULT in case of NOT FOUND",
map_type, map_name);
}
-#if 1
if (dict_flags & DICT_FLAG_SRC_RHS_IS_FILE) {
VSTRING *unb64;
char *err;
}
value = STR(unb64);
}
-#endif
vstream_printf("%s %s\n", key, value);
}
if (dict->error)
/* #include "smtpd.h"
/* #include "smtpd_sasl_proto.h"
/*
-/* void smtpd_sasl_auth_cmd(state, argc, argv)
+/* int smtpd_sasl_auth_cmd(state, argc, argv)
/* SMTPD_STATE *state;
/* int argc;
/* SMTPD_TOKEN *argv;
return SSL_TLSEXT_ERR_NOACK;
do {
- pem = maps_find(tls_server_sni_maps, sni, DICT_FLAG_SRC_RHS_IS_FILE);
+ /* Don't silently skip maps opened with the wrong flags. */
+ pem = maps_file_find(tls_server_sni_maps, sni, 0);
} while (!pem
&& !tls_server_sni_maps->error
&& (sni = strchr(sni + 1, '.')) != 0);
poll_fd.c timecmp.c slmdb.c dict_pipe.c dict_random.c \
valid_utf8_hostname.c midna_domain.c argv_splitq.c balpar.c dict_union.c \
extpar.c dict_inline.c casefold.c dict_utf8.c strcasecmp_utf8.c \
- split_qnameval.c argv_attr_print.c argv_attr_scan.c dict_file.c \
- dict_wrapper.c
+ split_qnameval.c argv_attr_print.c argv_attr_scan.c dict_file.c
OBJS = alldig.o allprint.o argv.o argv_split.o attr_clnt.o attr_print0.o \
attr_print64.o attr_print_plain.o attr_scan0.o attr_scan64.o \
attr_scan_plain.o auto_clnt.o base64_code.o basename.o binhash.o \
poll_fd.o timecmp.o $(NON_PLUGIN_MAP_OBJ) dict_pipe.o dict_random.o \
valid_utf8_hostname.o midna_domain.o argv_splitq.o balpar.o dict_union.o \
extpar.o dict_inline.o casefold.o dict_utf8.o strcasecmp_utf8.o \
- split_qnameval.o argv_attr_print.o argv_attr_scan.o dict_file.o \
- dict_wrapper.o
+ split_qnameval.o argv_attr_print.o argv_attr_scan.o dict_file.o
# MAP_OBJ is for maps that may be dynamically loaded with dynamicmaps.cf.
# When hard-linking these, makedefs sets NON_PLUGIN_MAP_OBJ=$(MAP_OBJ),
# otherwise it sets the PLUGIN_* macros.
dict_ht.o: vbuf.h
dict_ht.o: vstream.h
dict_ht.o: vstring.h
+dict_inline.cdict_static.o: dict_inline.cdict_static.c
dict_inline.o: argv.h
dict_inline.o: check_arg.h
dict_inline.o: dict.h
dict_utf8.o: vbuf.h
dict_utf8.o: vstream.h
dict_utf8.o: vstring.h
-dict_wrapper.o: argv.h
-dict_wrapper.o: check_arg.h
-dict_wrapper.o: dict.h
-dict_wrapper.o: dict_wrapper.c
-dict_wrapper.o: msg.h
-dict_wrapper.o: myflock.h
-dict_wrapper.o: mymalloc.h
-dict_wrapper.o: stringops.h
-dict_wrapper.o: sys_defs.h
-dict_wrapper.o: vbuf.h
-dict_wrapper.o: vstream.h
-dict_wrapper.o: vstring.h
dir_forest.o: check_arg.h
dir_forest.o: dir_forest.c
dir_forest.o: dir_forest.h
load_file.o: vstream.h
load_file.o: warn_stat.h
load_lib.o: load_lib.c
+load_lib.o: load_lib.h
+load_lib.o: msg.h
load_lib.o: sys_defs.h
lowercase.o: check_arg.h
lowercase.o: lowercase.c
"utf8_request", DICT_FLAG_UTF8_REQUEST, /* request UTF-8 activation */
"utf8_active", DICT_FLAG_UTF8_ACTIVE, /* UTF-8 is activated */
"src_rhs_is_file", DICT_FLAG_SRC_RHS_IS_FILE, /* value from file */
- "unb64_active", DICT_FLAG_UNB64_ACTIVE, /* base64 decode activated */
0,
};
DICT_OWNER owner; /* provenance */
int error; /* last operation only */
DICT_JMP_BUF *jbuf; /* exception handling */
- struct DICT_WRAPPER *wrapper; /* see below */
+ struct DICT_UTF8_BACKUP *utf8_backup; /* see below */
struct VSTRING *file_buf; /* dict_file_to_buf() */
struct VSTRING *file_b64; /* dict_file_to_b64() */
} DICT;
#define DICT_FLAG_UTF8_ACTIVE (1<<20) /* UTF-8 proxy layer is present */
#define DICT_FLAG_SRC_RHS_IS_FILE \
(1<<21) /* Map source RHS is a file */
-#define DICT_FLAG_UNB64_ACTIVE (1<<22) /* File decode proxy layer is present */
#define DICT_FLAG_UTF8_MASK (DICT_FLAG_UTF8_REQUEST)
extern void dict_type_override(DICT *, const char *);
/*
- * Wrappers for DICT methods. Usage: create an "trivial" wrapper object with
- * dict_wrapper_alloc(), then for each method that requires special
- * processing, specify a pointer to function that calls the 'next' wrapper's
- * method of the same type, with the 'next' wrapper as the first argument
- * (the 'self' pointer).
+ * Check and convert UTF-8 keys and values.
*/
-typedef struct DICT_WRAPPER {
- const char *name; /* for literal constant */
- const char *(*lookup) (struct DICT_WRAPPER *, DICT *, const char *);
- int (*update) (struct DICT_WRAPPER *, DICT *, const char *, const char *);
- int (*delete) (struct DICT_WRAPPER *, DICT *, const char *);
- struct DICT_WRAPPER *next;
-} DICT_WRAPPER;
-
-extern void dict_wrapper_prepend(DICT *, DICT_WRAPPER *);
-extern DICT_WRAPPER *dict_wrapper_alloc(ssize_t);
-extern void dict_wrapper_free(DICT_WRAPPER *);
+typedef struct DICT_UTF8_BACKUP {
+ const char *(*lookup) (struct DICT *, const char *);
+ int (*update) (struct DICT *, const char *, const char *);
+ int (*delete) (struct DICT *, const char *);
+} DICT_UTF8_BACKUP;
- /*
- * Things that build on DICT_WRAPPER.
- */
-extern void dict_utf8_wrapper_activate(DICT *);
-extern void dict_file_wrapper_activate(DICT *);
+extern DICT *dict_utf8_activate(DICT *);
/*
* Driver for interactive or scripted tests.
extern struct VSTRING *dict_file_from_b64(DICT *, const char *);
extern char *dict_file_get_error(DICT *);
extern void dict_file_purge_buffers(DICT *);
+extern const char *dict_file_lookup(DICT *dict, const char *);
/* LICENSE
/* .ad
dict->owner.uid = INT_MAX;
dict->error = DICT_ERR_NONE;
dict->jbuf = 0;
- dict->wrapper = 0;
+ dict->utf8_backup = 0;
dict->file_buf = 0;
dict->file_b64 = 0;
return dict;
myfree(dict->name);
if (dict->jbuf)
myfree((void *) dict->jbuf);
- if (dict->wrapper)
- dict_wrapper_free(dict->wrapper);
+ if (dict->utf8_backup)
+ myfree((void *) dict->utf8_backup);
if (dict->file_buf)
vstring_free(dict->file_buf);
if (dict->file_b64)
./dict_open: warning: cidr map dict_cidr_file.map, line 3: open dict_cidr_file3: No such file or directory: skipping this rule
owner=untrusted (uid=USER)
> get 1.1.1.1
-1.1.1.1=this-is-file1
-
+1.1.1.1=dGhpcy1pcy1maWxlMQo=
> get 2.2.2.2
-2.2.2.2=this-is-file2
-
+2.2.2.2=dGhpcy1pcy1maWxlMgo=
> get 3.3.3.3
3.3.3.3: not found
/* void dict_file_purge_buffers(
/* DICT *dict)
/*
-/* void dict_file_wrapper_activate(
+/* const char *dict_file_lookup(
/* DICT *dict)
/* DESCRIPTION
/* dict_file_to_buf() reads the content of the specified files,
/* it returns a desciption of the problem. Storage is owned
/* by the caller.
/*
-/* dict_file_wrapper_activate() activates a wrapper that
-/* automatically base64-decodes lookup results.
+/* dict_file_lookup() wraps the dictionary lookup method and
+/* decodes the base64 lookup result. The dictionary must be
+/* opened with DICT_FLAG_SRC_RHS_IS_FILE. Sets dict->error to
+/* DICT_ERR_CONFIG if the content is invalid. Decoding is not
+/* built into the dict->lookup() method, because that would
+/* complicate the implementation of map nesting (inline, thash),
+/* map composition (pipemap, unionmap), and map proxying.
/* DIAGNOSTICS
-/* In case of error the result value is a null pointer, and
+/* Panic: interface violation.
+/*
+/* In case of error the VSTRING result value is a null pointer, and
/* an error description can be retrieved with dict_file_get_error().
/* The storage is owned by the caller.
/* LICENSE
}
}
-/* dict_file_wrapper_lookup - wrap the lookup method */
+/* dict_file_lookup - look up and decode dictionary entry */
-static const char *dict_file_wrapper_lookup(DICT_WRAPPER *wrapper,
- DICT *dict, const char *key)
+const char *dict_file_lookup(DICT *dict, const char *key)
{
- DICT_WRAPPER *next_wrapper;
+ const char myname[] = "dict_file_lookup";
const char *res;
VSTRING *unb64;
char *err;
- next_wrapper = wrapper->next;
- if ((res = next_wrapper->lookup(next_wrapper, dict, key)) != 0) {
- if ((unb64 = dict_file_from_b64(dict, res)) == 0) {
- err = dict_file_get_error(dict);
- msg_warn("table %s:%s: key %s: %s",
- dict->type, dict->name, key, err);
- myfree(err);
- dict->error = DICT_ERR_CONFIG;
- res = 0;
- } else {
- res = vstring_str(unb64);
- }
+ if ((dict->flags & DICT_FLAG_SRC_RHS_IS_FILE) == 0)
+ msg_panic("%s: dictionary opened without DICT_FLAG_SRC_RHS_IS_FILE",
+ myname);
+ if ((res = dict->lookup(dict, key)) == 0)
+ return (0);
+ if ((unb64 = dict_file_from_b64(dict, res)) == 0) {
+ err = dict_file_get_error(dict);
+ msg_warn("table %s:%s: key %s: %s",
+ dict->type, dict->name,
+ key, err);
+ myfree(err);
+ dict->error = DICT_ERR_CONFIG;
+ return (0);
}
- return (res);
-}
-
-/* dict_file_wrapper_activate - wrap the lookup method */
-
-void dict_file_wrapper_activate(DICT *dict)
-{
- const char myname[] = "dict_file_wrapper_activate";
- DICT_WRAPPER *wrapper;
-
- /*
- * Sanity check.
- */
- if ((dict->flags & DICT_FLAG_UNB64_ACTIVE))
- msg_panic("%s: %s:%s Base64 decoding support is already activated",
- myname, dict->type, dict->name);
-
- /*
- * Interpose on the lookup method.
- */
- wrapper = dict_wrapper_alloc(sizeof(*wrapper));
- wrapper->name = "file";
- wrapper->lookup = dict_file_wrapper_lookup;
- dict_wrapper_prepend(dict, wrapper);
-
- /*
- * Leave our mark. See sanity check above.
- */
- dict->flags |= DICT_FLAG_UNB64_ACTIVE;
+ return STR(unb64);
}
foo: error
owner=trusted (uid=2147483647)
> get file1
-file1=this-is-file1
-
+file1=dGhpcy1pcy1maWxlMQo=
> get file2
-file2=this-is-file2
-
+file2=dGhpcy1pcy1maWxlMgo=
> get file3
file3: not found
msg_fatal("%s:%s: unable to get exclusive lock: %m",
dict_type, dict_name);
}
- /* Insert wrapper for UTF-8 syntax checks and casefolding. */
+ /* Last step: insert proxy for UTF-8 syntax checks and casefolding. */
if ((dict->flags & DICT_FLAG_UTF8_ACTIVE) == 0
&& DICT_NEED_UTF8_ACTIVATION(util_utf8_enable, dict_flags))
- dict_utf8_wrapper_activate(dict);
-
- /* Insert wrapper for base64 decoding file content. */
- if ((dict->flags & DICT_FLAG_UNB64_ACTIVE) == 0
- && dict->flags & DICT_FLAG_SRC_RHS_IS_FILE)
- dict_file_wrapper_activate(dict);
-
+ dict = dict_utf8_activate(dict);
return (dict);
}
./dict_open: warning: pcre map dict_pcre_file.map, line 6: empty pathname list: >>,<<': skipping this rule
owner=untrusted (uid=USER)
> get file1
-file1=this-is-file1
-
+file1=dGhpcy1pcy1maWxlMQo=
> get file2
-file2=this-is-file2
-
+file2=dGhpcy1pcy1maWxlMgo=
> get file3
file3: not found
> get files12
-files12=this-is-file1
-
-this-is-file2
-
+files12=dGhpcy1pcy1maWxlMQoKdGhpcy1pcy1maWxlMgo=
DICT_TYPE_PIPE, name,
DICT_TYPE_PIPE));
if ((dict = dict_handle(dict_type_name)) == 0)
- dict = dict_open(dict_type_name, open_flags,
- dict_flags & ~DICT_FLAG_SRC_RHS_IS_FILE);
+ dict = dict_open(dict_type_name, open_flags, dict_flags);
dict_register(dict_type_name, dict);
DICT_OWNER_AGGREGATE_UPDATE(aggr_owner, dict->owner);
if (cpp == argv->argv)
foo: error
owner=trusted (uid=0)
> get foo
-foo=this-is-file1
-
+foo=dGhpcy1pcy1maWxlMQo=
> get bar
-bar=this-is-file1
-
+bar=dGhpcy1pcy1maWxlMQo=
./dict_open: warning: regexp map dict_regexp_file.map, line 3: open dict_regexp_file3: No such file or directory: skipping this rule
owner=untrusted (uid=USER)
> get file1
-file1=this-is-file1
-
+file1=dGhpcy1pcy1maWxlMQo=
> get file2
-file2=this-is-file2
-
+file2=dGhpcy1pcy1maWxlMgo=
> get file3
file3: not found
foo: error
owner=trusted (uid=2147483647)
> get file1
-file1=this-is-file1
-
+file1=dGhpcy1pcy1maWxlMQo=
> get file2
-file2=this-is-file1
-
+file2=dGhpcy1pcy1maWxlMQo=
DICT_TYPE_UNION, name,
DICT_TYPE_UNION));
if ((dict = dict_handle(dict_type_name)) == 0)
- dict = dict_open(dict_type_name, open_flags,
- dict_flags & ~DICT_FLAG_SRC_RHS_IS_FILE);
+ dict = dict_open(dict_type_name, open_flags, dict_flags);
dict_register(dict_type_name, dict);
DICT_OWNER_AGGREGATE_UPDATE(aggr_owner, dict->owner);
if (cpp == argv->argv)
/* SYNOPSIS
/* #include <dict.h>
/*
-/* void dict_utf8_wrapper_activate(
+/* DICT *dict_utf8_activate(
/* DICT *dict)
/* DESCRIPTION
-/* dict_utf8_wrapper_activate() wraps a dictionary's lookup/update/delete
+/* dict_utf8_activate() wraps a dictionary's lookup/update/delete
/* methods with code that enforces UTF-8 checks on keys and
/* values, and that logs a warning when incorrect UTF-8 is
-/* encountered.
+/* encountered. The original dictionary handle becomes invalid.
/*
/* The wrapper code enforces a policy that maximizes application
/* robustness (it avoids the need for new error-handling code
/* skipped while reporting a non-error status, and lookup
/* results that contain a non-UTF-8 value are blocked while
/* reporting a configuration error.
+/* BUGS
+/* dict_utf8_activate() does not nest.
/* LICENSE
/* .ad
/* .fi
/* dict_utf8_lookup - UTF-8 lookup method wrapper */
-static const char *dict_utf8_lookup(DICT_WRAPPER *wrapper, DICT *dict,
- const char *key)
+static const char *dict_utf8_lookup(DICT *dict, const char *key)
{
- DICT_WRAPPER *next_wrapper;
+ DICT_UTF8_BACKUP *backup;
const char *utf8_err;
const char *fold_res;
const char *value;
*/
saved_flags = (dict->flags & DICT_FLAG_FOLD_ANY);
dict->flags &= ~DICT_FLAG_FOLD_ANY;
- next_wrapper = wrapper->next;
- value = next_wrapper->lookup(next_wrapper, dict, fold_res);
+ backup = dict->utf8_backup;
+ value = backup->lookup(dict, fold_res);
dict->flags |= saved_flags;
/*
/* dict_utf8_update - UTF-8 update method wrapper */
-static int dict_utf8_update(DICT_WRAPPER *wrapper, DICT *dict,
- const char *key, const char *value)
+static int dict_utf8_update(DICT *dict, const char *key, const char *value)
{
- DICT_WRAPPER *next_wrapper;
+ DICT_UTF8_BACKUP *backup;
const char *utf8_err;
const char *fold_res;
int saved_flags;
else {
saved_flags = (dict->flags & DICT_FLAG_FOLD_ANY);
dict->flags &= ~DICT_FLAG_FOLD_ANY;
- next_wrapper = wrapper->next;
- status = next_wrapper->update(next_wrapper, dict, fold_res, value);
+ backup = dict->utf8_backup;
+ status = backup->update(dict, fold_res, value);
dict->flags |= saved_flags;
return (status);
}
/* dict_utf8_delete - UTF-8 delete method wrapper */
-static int dict_utf8_delete(DICT_WRAPPER *wrapper, DICT *dict, const char *key)
+static int dict_utf8_delete(DICT *dict, const char *key)
{
- DICT_WRAPPER *next_wrapper;
+ DICT_UTF8_BACKUP *backup;
const char *utf8_err;
const char *fold_res;
int saved_flags;
else {
saved_flags = (dict->flags & DICT_FLAG_FOLD_ANY);
dict->flags &= ~DICT_FLAG_FOLD_ANY;
- next_wrapper = wrapper->next;
- status = next_wrapper->delete(next_wrapper, dict, fold_res);
+ backup = dict->utf8_backup;
+ status = backup->delete(dict, fold_res);
dict->flags |= saved_flags;
return (status);
}
}
-/* dict_utf8_wrapper_activate - wrap legacy dict object for UTF-8 processing */
+/* dict_utf8_activate - wrap a legacy dict object for UTF-8 processing */
-void dict_utf8_wrapper_activate(DICT *dict)
+DICT *dict_utf8_activate(DICT *dict)
{
- const char myname[] = "dict_utf8_wrapper_activate";
- DICT_WRAPPER *wrapper;
+ const char myname[] = "dict_utf8_activate";
+ DICT_UTF8_BACKUP *backup;
/*
* Sanity check.
if ((dict->flags & DICT_FLAG_UTF8_REQUEST) == 0)
msg_panic("%s: %s:%s does not request Unicode support",
myname, dict->type, dict->name);
- if ((dict->flags & DICT_FLAG_UTF8_ACTIVE))
+ if ((dict->flags & DICT_FLAG_UTF8_ACTIVE) || dict->utf8_backup != 0)
msg_panic("%s: %s:%s Unicode support is already activated",
myname, dict->type, dict->name);
/*
- * Interpose on the lookup/update/delete methods.
+ * Unlike dict_debug(3) we do not put a proxy dict object in front of the
+ * encapsulated object, because then we would have to bidirectionally
+ * propagate changes in the data members (errors, flags, jbuf, and so on)
+ * between proxy object and encapsulated object.
+ *
+ * Instead we attach ourselves behind the encapsulated dict object, and
+ * redirect some function pointers to ourselves.
*/
- wrapper = dict_wrapper_alloc(sizeof(*wrapper));
- wrapper->name = "utf8";
- wrapper->lookup = dict_utf8_lookup;
- wrapper->update = dict_utf8_update;
- wrapper->delete = dict_utf8_delete;
- dict_wrapper_prepend(dict, wrapper);
+ backup = dict->utf8_backup = (DICT_UTF8_BACKUP *) mymalloc(sizeof(*backup));
+
+ /*
+ * Interpose on the lookup/update/delete methods. It is a conscious
+ * decision not to tinker with the iterator or destructor.
+ */
+ backup->lookup = dict->lookup;
+ backup->update = dict->update;
+ backup->delete = dict->delete;
+
+ dict->lookup = dict_utf8_lookup;
+ dict->update = dict_utf8_update;
+ dict->delete = dict_utf8_delete;
/*
* Leave our mark. See sanity check above.
*/
dict->flags |= DICT_FLAG_UTF8_ACTIVE;
+
+ return (dict);
}
+++ /dev/null
-/*++
-/* NAME
-/* dict_wrapper 3
-/* SUMMARY
-/* dictionary method wrappers
-/* SYNOPSIS
-/* #include <dict.h>
-/*
-/* void dict_wrapper_prepend(
-/* DICT *dict,
-/* DICT_WRAPPER *wrapper)
-/*
-/* DICT_WRAPPER *dict_wrapper_alloc(
-/* ssize_t size)
-/*
-/* void dict_wrapper_free(
-/* DICT_WRAPPER *wrapper)
-/* DESCRIPTION
-/* dict_wrapper_prepend() prepends the specified dictionary
-/* lookup/update/delete wrappers to a chain that is evaluated
-/* in reverse order. dict_wrapper_prepend() takes ownership
-/* of the wrapper.
-/*
-/* dict_wrapper_alloc() allocates memory for a dictionary
-/* wrapper object and initializes all wrapper methods to
-/* empty (no override).
-/*
-/* dict_wrapper_free() destroys a chain of dictionary wrappers.
-/* LICENSE
-/* .ad
-/* .fi
-/* The Secure Mailer license must be distributed with this software.
-/* AUTHOR(S)
-/* Wietse Venema
-/* Google, Inc.
-/* 111 8th Avenue
-/* New York, NY 10011, USA
-/*--*/
-
- /*
- * System library.
- */
-#include <sys_defs.h>
-#include <string.h>
-
- /*
- * Utility library.
- */
-#include <msg.h>
-#include <stringops.h>
-#include <dict.h>
-#include <mymalloc.h>
-#include <msg.h>
-
- /*
- * The final DICT_WRAPPER is installed first, and also contains the original
- * DICT's methods.
- */
-typedef struct {
- DICT_WRAPPER wrapper; /* parent class, must be first */
- const char *(*saved_lookup) (DICT *, const char *);
- int (*saved_update) (DICT *, const char *, const char *);
- int (*saved_delete) (DICT *, const char *);
-} DICT_FINAL_WRAPPER;
-
- /*
- * Functions that override DICT methods, and that call into the head of
- * the dict wrapper chain.
- */
-
-/* dict_wrapper_lookup - DICT method to call into wrapper chain head */
-
-static const char *dict_wrapper_lookup(DICT *dict, const char *key)
-{
- DICT_WRAPPER *head_wrapper = dict->wrapper;
-
- return (head_wrapper->lookup(head_wrapper, dict, key));
-}
-
-/* dict_wrapper_update - DICT method to call into wrapper chain head */
-
-static int dict_wrapper_update(DICT *dict, const char *key, const char *value)
-{
- DICT_WRAPPER *head_wrapper = dict->wrapper;
-
- return (head_wrapper->update(head_wrapper, dict, key, value));
-}
-
-/* dict_wrapper_delete - DICT method to call into wrapper chain head */
-
-static int dict_wrapper_delete(DICT *dict, const char *key)
-{
- DICT_WRAPPER *head_wrapper = dict->wrapper;
-
- return (head_wrapper->delete(head_wrapper, dict, key));
-}
-
- /*
- * Empty methods for wrappers that override only some methods. These ensure
- * that the next wrapper's methods are called with the right 'self' pointer.
- */
-
-/* empty_wrapper_lookup - wrapper method to call into next wrapper */
-
-static const char *empty_wrapper_lookup(DICT_WRAPPER *wrapper, DICT *dict,
- const char *key)
-{
- DICT_WRAPPER *next_wrapper = wrapper->next;
-
- return (next_wrapper->lookup(next_wrapper, dict, key));
-}
-
-/* empty_wrapper_update - wrapper method to call into next wrapper */
-
-static int empty_wrapper_update(DICT_WRAPPER *wrapper, DICT *dict,
- const char *key, const char *value)
-{
- DICT_WRAPPER *next_wrapper = wrapper->next;
-
- return (next_wrapper->update(next_wrapper, dict, key, value));
-}
-
-/* empty_wrapper_delete - wrapper method to call into next wrapper */
-
-static int empty_wrapper_delete(DICT_WRAPPER *wrapper, DICT *dict,
- const char *key)
-{
- DICT_WRAPPER *next_wrapper = wrapper->next;
-
- return (next_wrapper->delete(next_wrapper, dict, key));
-}
-
- /*
- * Wrapper methods for the final dict wrapper in the chain. These call into
- * the saved DICT methods.
- */
-
-/* final_wrapper_lookup - wrapper method to call saved DICT method */
-
-static const char *final_wrapper_lookup(DICT_WRAPPER *wrapper, DICT *dict,
- const char *key)
-{
- DICT_FINAL_WRAPPER *final_wrapper = (DICT_FINAL_WRAPPER *) wrapper;
-
- return (final_wrapper->saved_lookup(dict, key));
-}
-
-/* final_wrapper_update - wrapper method to call saved DICT method */
-
-static int final_wrapper_update(DICT_WRAPPER *wrapper, DICT *dict,
- const char *key, const char *value)
-{
- DICT_FINAL_WRAPPER *final_wrapper = (DICT_FINAL_WRAPPER *) wrapper;
-
- return (final_wrapper->saved_update(dict, key, value));
-}
-
-/* final_wrapper_delete - wrapper method to call saved DICT method */
-
-static int final_wrapper_delete(DICT_WRAPPER *wrapper, DICT *dict,
- const char *key)
-{
- DICT_FINAL_WRAPPER *final_wrapper = (DICT_FINAL_WRAPPER *) wrapper;
-
- return (final_wrapper->saved_delete(dict, key));
-}
-
- /*
- * Finally, the functions that build the wrapper chain.
- */
-
-/* dict_wrapper_activate - wrap a DICT object for additional processing */
-
-static void dict_wrapper_activate(DICT *dict)
-{
- const char myname[] = "dict_wrapper_activate";
- DICT_FINAL_WRAPPER *final_wrapper;
-
- /*
- * Sanity check.
- */
- if (dict->wrapper != 0)
- msg_panic("%s: %s:%s wrapper support is already activated",
- myname, dict->type, dict->name);
-
- /*
- * Install the final wrapper object that calls the original DICT's
- * methods, and redirect DICT's method calls to ourselves. All other
- * dictionary wrappers will be prepended to a chain that ends in the
- * final wrapper object.
- */
- final_wrapper = (DICT_FINAL_WRAPPER *) mymalloc(sizeof(*final_wrapper));
- final_wrapper->wrapper.name = "final";
- final_wrapper->wrapper.lookup = final_wrapper_lookup;
- final_wrapper->wrapper.update = final_wrapper_update;
- final_wrapper->wrapper.delete = final_wrapper_delete;
- final_wrapper->wrapper.next = 0;
- dict->wrapper = &final_wrapper->wrapper;
-
- /*
- * Interpose on the DICT's lookup/update/delete methods.
- */
- final_wrapper->saved_lookup = dict->lookup;
- final_wrapper->saved_update = dict->update;
- final_wrapper->saved_delete = dict->delete;
-
- dict->lookup = dict_wrapper_lookup;
- dict->update = dict_wrapper_update;
- dict->delete = dict_wrapper_delete;
-}
-
-/* dict_wrapper_alloc - allocate and initialize dictionary wrapper */
-
-DICT_WRAPPER *dict_wrapper_alloc(ssize_t size)
-{
- DICT_WRAPPER *wrapper;
-
- wrapper = (DICT_WRAPPER *) mymalloc(size);
- wrapper->lookup = empty_wrapper_lookup;
- wrapper->update = empty_wrapper_update;
- wrapper->delete = empty_wrapper_delete;
- return (wrapper);
-}
-
-/* dict_wrapper_prepend - prepend dict method overrides */
-
-void dict_wrapper_prepend(DICT *dict, DICT_WRAPPER *wrapper)
-{
- if (dict->wrapper == 0)
- dict_wrapper_activate(dict);
-
- wrapper->next = dict->wrapper;
- dict->wrapper = wrapper;
-}
-
-/* dict_wrapper_free - wrapper destructor */
-
-void dict_wrapper_free(DICT_WRAPPER *wrapper)
-{
- if (wrapper->next)
- dict_wrapper_free(wrapper->next);
- myfree((void *) wrapper);
-}