summaryrefslogtreecommitdiffstats
path: root/src/global/scache.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/global/scache.c')
-rw-r--r--src/global/scache.c407
1 files changed, 407 insertions, 0 deletions
diff --git a/src/global/scache.c b/src/global/scache.c
new file mode 100644
index 0000000..a3c12f1
--- /dev/null
+++ b/src/global/scache.c
@@ -0,0 +1,407 @@
+/*++
+/* NAME
+/* scache 3
+/* SUMMARY
+/* generic session cache API
+/* SYNOPSIS
+/* #include <scache.h>
+/* DESCRIPTION
+/* typedef struct {
+/* .in +4
+/* int dest_count;
+/* int endp_count;
+/* int sess_count;
+/* .in -4
+/* } SCACHE_SIZE;
+/*
+/* unsigned scache_size(scache, size)
+/* SCACHE *scache;
+/* SCACHE_SIZE *size;
+/*
+/* void scache_free(scache)
+/* SCACHE *scache;
+/*
+/* void scache_save_endp(scache, endp_ttl, endp_label, endp_prop, fd)
+/* SCACHE *scache;
+/* int endp_ttl;
+/* const char *endp_label;
+/* const char *endp_prop;
+/* int fd;
+/*
+/* int scache_find_endp(scache, endp_label, endp_prop)
+/* SCACHE *scache;
+/* const char *endp_label;
+/* VSTRING *endp_prop;
+/*
+/* void scache_save_dest(scache, dest_ttl, dest_label,
+/* dest_prop, endp_label)
+/* SCACHE *scache;
+/* int dest_ttl;
+/* const char *dest_label;
+/* const char *dest_prop;
+/* const char *endp_label;
+/*
+/* int scache_find_dest(dest_label, dest_prop, endp_prop)
+/* SCACHE *scache;
+/* const char *dest_label;
+/* VSTRING *dest_prop;
+/* VSTRING *endp_prop;
+/* DESCRIPTION
+/* This module implements a generic session cache interface.
+/* Specific cache types are described in scache_single(3),
+/* scache_clnt(3) and scache_multi(3). These documents also
+/* describe now to instantiate a specific session cache type.
+/*
+/* The code maintains two types of association: a) physical
+/* endpoint to file descriptor, and b) logical endpoint
+/* to physical endpoint. Physical endpoints are stored and
+/* looked up under their low-level session details such as
+/* numerical addresses, while logical endpoints are stored
+/* and looked up by the domain name that humans use. One logical
+/* endpoint can refer to multiple physical endpoints, one
+/* physical endpoint may be referenced by multiple logical
+/* endpoints, and one physical endpoint may have multiple
+/* sessions.
+/*
+/* scache_size() returns the number of logical destination
+/* names, physical endpoint addresses, and cached sessions.
+/*
+/* scache_free() destroys the specified session cache.
+/*
+/* scache_save_endp() stores an open session under the specified
+/* physical endpoint name.
+/*
+/* scache_find_endp() looks up a saved session under the
+/* specified physical endpoint name.
+/*
+/* scache_save_dest() associates the specified physical endpoint
+/* with the specified logical endpoint name.
+/*
+/* scache_find_dest() looks up a saved session under the
+/* specified physical endpoint name.
+/*
+/* Arguments:
+/* .IP endp_ttl
+/* How long the session should be cached. When information
+/* expires it is purged automatically.
+/* .IP endp_label
+/* The transport name and the physical endpoint name under
+/* which the session is stored and looked up.
+/*
+/* In the case of SMTP, the physical endpoint includes the numerical
+/* IP address, address family information, and the numerical TCP port.
+/* .IP endp_prop
+/* Application-specific data with endpoint attributes. It is up to
+/* the application to passivate (flatten) and re-activate this content
+/* upon storage and retrieval, respectively.
+/* .sp
+/* In the case of SMTP, the endpoint attributes specify the
+/* server hostname, IP address, numerical TCP port, as well
+/* as ESMTP features advertised by the server, and when information
+/* expires. All this in some application-specific format that is easy
+/* to unravel when re-activating a cached session.
+/* .IP dest_ttl
+/* How long the destination-to-endpoint binding should be
+/* cached. When information expires it is purged automatically.
+/* .IP dest_label
+/* The transport name and the logical destination under which the
+/* destination-to-endpoint binding is stored and looked up.
+/*
+/* In the case of SMTP, the logical destination is the DNS
+/* host or domain name with or without [], plus the numerical TCP port.
+/* .IP dest_prop
+/* Application-specific attributes that describe features of
+/* this logical to physical binding. It is up to the application
+/* to passivate (flatten) and re-activate this content.
+/* upon storage and retrieval, respectively
+/* .sp
+/* In case the of an SMTP logical destination to physical
+/* endpoint binding, the attributes specify the logical
+/* destination name, numerical port, whether the physical
+/* endpoint is best mx host with respect to a logical or
+/* fall-back destination, and when information expires.
+/* .IP fd
+/* File descriptor with session to be cached.
+/* DIAGNOSTICS
+/* scache_find_endp() and scache_find_dest() return -1 when
+/* the lookup fails, and a file descriptor upon success.
+/*
+/* Other diagnostics: fatal error: memory allocation problem;
+/* panic: internal consistency failure.
+/* SEE ALSO
+/* scache_single(3), single-session, in-memory cache
+/* scache_clnt(3), session cache client
+/* scache_multi(3), multi-session, in-memory cache
+/* LICENSE
+/* .ad
+/* .fi
+/* The Secure Mailer license must be distributed with this software.
+/* AUTHOR(S)
+/* Wietse Venema
+/* IBM T.J. Watson Research
+/* P.O. Box 704
+/* Yorktown Heights, NY 10598, USA
+/*--*/
+
+/* System library. */
+
+#include <sys_defs.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+
+/* Utility library. */
+
+#include <msg.h>
+#include <vstream.h>
+#include <vstring.h>
+#include <vstring_vstream.h>
+#include <argv.h>
+#include <events.h>
+
+/* Global library. */
+
+#include <scache.h>
+
+#ifdef TEST
+
+ /*
+ * Driver program for cache regression tests. Although all variants are
+ * relatively simple to verify by hand for single session storage, more
+ * sophisticated instrumentation is needed to demonstrate that the
+ * multi-session cache manager properly handles collisions in the time
+ * domain and in all the name space domains.
+ */
+static SCACHE *scache;
+static VSTRING *endp_prop;
+static VSTRING *dest_prop;
+static int verbose_level = 3;
+
+ /*
+ * Cache mode lookup table. We don't do the client-server variant because
+ * that drags in a ton of configuration junk; the client-server protocol is
+ * relatively easy to verify manually.
+ */
+struct cache_type {
+ char *mode;
+ SCACHE *(*create) (void);
+};
+
+static struct cache_type cache_types[] = {
+ "single", scache_single_create,
+ "multi", scache_multi_create,
+ 0,
+};
+
+#define STR(x) vstring_str(x)
+
+/* cache_type - select session cache type */
+
+static void cache_type(ARGV *argv)
+{
+ struct cache_type *cp;
+
+ if (argv->argc != 2) {
+ msg_error("usage: %s mode", argv->argv[0]);
+ return;
+ }
+ if (scache != 0)
+ scache_free(scache);
+ for (cp = cache_types; cp->mode != 0; cp++) {
+ if (strcmp(cp->mode, argv->argv[1]) == 0) {
+ scache = cp->create();
+ return;
+ }
+ }
+ msg_error("unknown cache type: %s", argv->argv[1]);
+}
+
+/* handle_events - handle events while time advances */
+
+static void handle_events(ARGV *argv)
+{
+ int delay;
+ time_t before;
+ time_t after;
+
+ if (argv->argc != 2 || (delay = atoi(argv->argv[1])) <= 0) {
+ msg_error("usage: %s time", argv->argv[0]);
+ return;
+ }
+ before = event_time();
+ event_drain(delay);
+ after = event_time();
+ if (after < before + delay)
+ sleep(before + delay - after);
+}
+
+/* save_endp - save endpoint->session binding */
+
+static void save_endp(ARGV *argv)
+{
+ int ttl;
+ int fd;
+
+ if (argv->argc != 5
+ || (ttl = atoi(argv->argv[1])) <= 0
+ || (fd = atoi(argv->argv[4])) <= 0) {
+ msg_error("usage: save_endp ttl endpoint endp_props fd");
+ return;
+ }
+ if (DUP2(0, fd) < 0)
+ msg_fatal("dup2(0, %d): %m", fd);
+ scache_save_endp(scache, ttl, argv->argv[2], argv->argv[3], fd);
+}
+
+/* find_endp - find endpoint->session binding */
+
+static void find_endp(ARGV *argv)
+{
+ int fd;
+
+ if (argv->argc != 2) {
+ msg_error("usage: find_endp endpoint");
+ return;
+ }
+ if ((fd = scache_find_endp(scache, argv->argv[1], endp_prop)) >= 0)
+ close(fd);
+}
+
+/* save_dest - save destination->endpoint binding */
+
+static void save_dest(ARGV *argv)
+{
+ int ttl;
+
+ if (argv->argc != 5 || (ttl = atoi(argv->argv[1])) <= 0) {
+ msg_error("usage: save_dest ttl destination dest_props endpoint");
+ return;
+ }
+ scache_save_dest(scache, ttl, argv->argv[2], argv->argv[3], argv->argv[4]);
+}
+
+/* find_dest - find destination->endpoint->session binding */
+
+static void find_dest(ARGV *argv)
+{
+ int fd;
+
+ if (argv->argc != 2) {
+ msg_error("usage: find_dest destination");
+ return;
+ }
+ if ((fd = scache_find_dest(scache, argv->argv[1], dest_prop, endp_prop)) >= 0)
+ close(fd);
+}
+
+/* verbose - adjust noise level during cache manipulation */
+
+static void verbose(ARGV *argv)
+{
+ int level;
+
+ if (argv->argc != 2 || (level = atoi(argv->argv[1])) < 0) {
+ msg_error("usage: verbose level");
+ return;
+ }
+ verbose_level = level;
+}
+
+ /*
+ * The command lookup table.
+ */
+struct action {
+ char *command;
+ void (*action) (ARGV *);
+ int flags;
+};
+
+#define FLAG_NEED_CACHE (1<<0)
+
+static void help(ARGV *);
+
+static struct action actions[] = {
+ "cache_type", cache_type, 0,
+ "save_endp", save_endp, FLAG_NEED_CACHE,
+ "find_endp", find_endp, FLAG_NEED_CACHE,
+ "save_dest", save_dest, FLAG_NEED_CACHE,
+ "find_dest", find_dest, FLAG_NEED_CACHE,
+ "sleep", handle_events, 0,
+ "verbose", verbose, 0,
+ "?", help, 0,
+ 0,
+};
+
+/* help - list commands */
+
+static void help(ARGV *argv)
+{
+ struct action *ap;
+
+ vstream_printf("commands:");
+ for (ap = actions; ap->command != 0; ap++)
+ vstream_printf(" %s", ap->command);
+ vstream_printf("\n");
+ vstream_fflush(VSTREAM_OUT);
+}
+
+/* get_buffer - prompt for input or log input */
+
+static int get_buffer(VSTRING *buf, VSTREAM *fp, int interactive)
+{
+ int status;
+
+ if (interactive) {
+ vstream_printf("> ");
+ vstream_fflush(VSTREAM_OUT);
+ }
+ if ((status = vstring_get_nonl(buf, fp)) != VSTREAM_EOF) {
+ if (!interactive) {
+ vstream_printf(">>> %s\n", STR(buf));
+ vstream_fflush(VSTREAM_OUT);
+ }
+ }
+ return (status);
+}
+
+/* at last, the main program */
+
+int main(int unused_argc, char **unused_argv)
+{
+ VSTRING *buf = vstring_alloc(1);
+ ARGV *argv;
+ struct action *ap;
+ int interactive = isatty(0);
+
+ endp_prop = vstring_alloc(1);
+ dest_prop = vstring_alloc(1);
+
+ vstream_fileno(VSTREAM_ERR) = 1;
+
+ while (get_buffer(buf, VSTREAM_IN, interactive) != VSTREAM_EOF) {
+ argv = argv_split(STR(buf), CHARS_SPACE);
+ if (argv->argc > 0 && argv->argv[0][0] != '#') {
+ msg_verbose = verbose_level;
+ for (ap = actions; ap->command != 0; ap++) {
+ if (strcmp(ap->command, argv->argv[0]) == 0) {
+ if ((ap->flags & FLAG_NEED_CACHE) != 0 && scache == 0)
+ msg_error("no session cache");
+ else
+ ap->action(argv);
+ break;
+ }
+ }
+ msg_verbose = 0;
+ if (ap->command == 0)
+ msg_error("bad command: %s", argv->argv[0]);
+ }
+ argv_free(argv);
+ }
+ scache_free(scache);
+ vstring_free(endp_prop);
+ vstring_free(dest_prop);
+ vstring_free(buf);
+ exit(0);
+}
+
+#endif