diff options
Diffstat (limited to 'src/global/scache.c')
-rw-r--r-- | src/global/scache.c | 407 |
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 |