5
/* generic session cache API
17
/* unsigned scache_size(scache, size)
21
/* void scache_free(scache)
24
/* void scache_save_endp(scache, endp_ttl, endp_label, endp_prop, fd)
27
/* const char *endp_label;
28
/* const char *endp_prop;
31
/* int scache_find_endp(scache, endp_label, endp_prop)
33
/* const char *endp_label;
34
/* VSTRING *endp_prop;
36
/* void scache_save_dest(scache, dest_ttl, dest_label,
37
/* dest_prop, endp_label)
40
/* const char *dest_label;
41
/* const char *dest_prop;
42
/* const char *endp_label;
44
/* int scache_find_dest(dest_label, dest_prop, endp_prop)
46
/* const char *dest_label;
47
/* VSTRING *dest_prop;
48
/* VSTRING *endp_prop;
50
/* This module implements a generic session cache interface.
51
/* Specific cache types are described in scache_single(3),
52
/* scache_clnt(3) and scache_multi(3). These documents also
53
/* describe now to instantiate a specific session cache type.
55
/* The code maintains two types of association: a) physical
56
/* endpoint to file descriptor, and b) logical endpoint
57
/* to physical endpoint. Physical endpoints are stored and
58
/* looked up under their low-level session details such as
59
/* numerical addresses, while logical endpoints are stored
60
/* and looked up by the domain name that humans use. One logical
61
/* endpoint can refer to multiple physical endpoints, one
62
/* physical endpoint may be referenced by multiple logical
63
/* endpoints, and one physical endpoint may have multiple
66
/* scache_size() returns the number of logical destination
67
/* names, physical endpoint addresses, and cached sessions.
69
/* scache_free() destroys the specified session cache.
71
/* scache_save_endp() stores an open session under the specified
72
/* physical endpoint name.
74
/* scache_find_endp() looks up a saved session under the
75
/* specified physical endpoint name.
77
/* scache_save_dest() associates the specified physical endpoint
78
/* with the specified logical endpoint name.
80
/* scache_find_dest() looks up a saved session under the
81
/* specified physical endpoint name.
85
/* How long the session should be cached. When information
86
/* expires it is purged automatically.
88
/* The transport name and the physical endpoint name under
89
/* which the session is stored and looked up.
91
/* In the case of SMTP, the physical endpoint includes the numerical
92
/* IP address, address family information, and the numerical TCP port.
94
/* Application-specific data with endpoint attributes. It is up to
95
/* the application to passivate (flatten) and re-activate this content
96
/* upon storage and retrieval, respectively.
98
/* In the case of SMTP, the endpoint attributes specify the
99
/* server hostname, IP address, numerical TCP port, as well
100
/* as ESMTP features advertised by the server, and when information
101
/* expires. All this in some application-specific format that is easy
102
/* to unravel when re-activating a cached session.
104
/* How long the destination-to-endpoint binding should be
105
/* cached. When information expires it is purged automatically.
107
/* The transport name and the logical destination under which the
108
/* destination-to-endpoint binding is stored and looked up.
110
/* In the case of SMTP, the logical destination is the DNS
111
/* host or domain name with or without [], plus the numerical TCP port.
113
/* Application-specific attributes that describe features of
114
/* this logical to physical binding. It is up to the application
115
/* to passivate (flatten) and re-activate this content.
116
/* upon storage and retrieval, respectively
118
/* In case the of an SMTP logical destination to physical
119
/* endpoint binding, the attributes specify the logical
120
/* destination name, numerical port, whether the physical
121
/* endpoint is best mx host with respect to a logical or
122
/* fall-back destination, and when information expires.
124
/* File descriptor with session to be cached.
126
/* scache_find_endp() and scache_find_dest() return -1 when
127
/* the lookup fails, and a file descriptor upon success.
129
/* Other diagnostics: fatal error: memory allocation problem;
130
/* panic: internal consistency failure.
132
/* scache_single(3), single-session, in-memory cache
133
/* scache_clnt(3), session cache client
134
/* scache_multi(3), multi-session, in-memory cache
138
/* The Secure Mailer license must be distributed with this software.
141
/* IBM T.J. Watson Research
143
/* Yorktown Heights, NY 10598, USA
146
/* System library. */
148
#include <sys_defs.h>
152
/* Utility library. */
157
#include <vstring_vstream.h>
161
/* Global library. */
168
* Driver program for cache regression tests. Although all variants are
169
* relatively simple to verify by hand for single session storage, more
170
* sophisticated instrumentation is needed to demonstrate that the
171
* multi-session cache manager properly handles collisions in the time
172
* domain and in all the name space domains.
174
static SCACHE *scache;
175
static VSTRING *endp_prop;
176
static VSTRING *dest_prop;
177
static int verbose_level = 3;
180
* Cache mode lookup table. We don't do the client-server variant because
181
* that drags in a ton of configuration junk; the client-server protocol is
182
* relatively easy to verify manually.
186
SCACHE *(*create) (void);
189
static struct cache_type cache_types[] = {
190
"single", scache_single_create,
191
"multi", scache_multi_create,
195
#define STR(x) vstring_str(x)
197
/* cache_type - select session cache type */
199
static void cache_type(ARGV *argv)
201
struct cache_type *cp;
203
if (argv->argc != 2) {
204
msg_error("usage: %s mode", argv->argv[0]);
209
for (cp = cache_types; cp->mode != 0; cp++) {
210
if (strcmp(cp->mode, argv->argv[1]) == 0) {
211
scache = cp->create();
215
msg_error("unknown cache type: %s", argv->argv[1]);
218
/* handle_events - handle events while time advances */
220
static void handle_events(ARGV *argv)
226
if (argv->argc != 2 || (delay = atoi(argv->argv[1])) <= 0) {
227
msg_error("usage: %s time", argv->argv[0]);
230
before = event_time();
232
after = event_time();
233
if (after < before + delay)
234
sleep(before + delay - after);
237
/* save_endp - save endpoint->session binding */
239
static void save_endp(ARGV *argv)
245
|| (ttl = atoi(argv->argv[1])) <= 0
246
|| (fd = atoi(argv->argv[4])) <= 0) {
247
msg_error("usage: save_endp ttl endpoint endp_props fd");
251
msg_fatal("dup2(0, %d): %m", fd);
252
scache_save_endp(scache, ttl, argv->argv[2], argv->argv[3], fd);
255
/* find_endp - find endpoint->session binding */
257
static void find_endp(ARGV *argv)
261
if (argv->argc != 2) {
262
msg_error("usage: find_endp endpoint");
265
if ((fd = scache_find_endp(scache, argv->argv[1], endp_prop)) >= 0)
269
/* save_dest - save destination->endpoint binding */
271
static void save_dest(ARGV *argv)
275
if (argv->argc != 5 || (ttl = atoi(argv->argv[1])) <= 0) {
276
msg_error("usage: save_dest ttl destination dest_props endpoint");
279
scache_save_dest(scache, ttl, argv->argv[2], argv->argv[3], argv->argv[4]);
282
/* find_dest - find destination->endpoint->session binding */
284
static void find_dest(ARGV *argv)
288
if (argv->argc != 2) {
289
msg_error("usage: find_dest destination");
292
if ((fd = scache_find_dest(scache, argv->argv[1], dest_prop, endp_prop)) >= 0)
296
/* verbose - adjust noise level during cache manipulation */
298
static void verbose(ARGV *argv)
302
if (argv->argc != 2 || (level = atoi(argv->argv[1])) < 0) {
303
msg_error("usage: verbose level");
306
verbose_level = level;
310
* The command lookup table.
314
void (*action) (ARGV *);
318
#define FLAG_NEED_CACHE (1<<0)
320
static void help(ARGV *);
322
static struct action actions[] = {
323
"cache_type", cache_type, 0,
324
"save_endp", save_endp, FLAG_NEED_CACHE,
325
"find_endp", find_endp, FLAG_NEED_CACHE,
326
"save_dest", save_dest, FLAG_NEED_CACHE,
327
"find_dest", find_dest, FLAG_NEED_CACHE,
328
"sleep", handle_events, 0,
329
"verbose", verbose, 0,
334
/* help - list commands */
336
static void help(ARGV *argv)
340
vstream_printf("commands:");
341
for (ap = actions; ap->command != 0; ap++)
342
vstream_printf(" %s", ap->command);
343
vstream_printf("\n");
344
vstream_fflush(VSTREAM_OUT);
347
/* get_buffer - prompt for input or log input */
349
static int get_buffer(VSTRING *buf, VSTREAM *fp, int interactive)
354
vstream_printf("> ");
355
vstream_fflush(VSTREAM_OUT);
357
if ((status = vstring_get_nonl(buf, fp)) != VSTREAM_EOF) {
359
vstream_printf(">>> %s\n", STR(buf));
360
vstream_fflush(VSTREAM_OUT);
366
/* at last, the main program */
368
int main(int unused_argc, char **unused_argv)
370
VSTRING *buf = vstring_alloc(1);
373
int interactive = isatty(0);
375
endp_prop = vstring_alloc(1);
376
dest_prop = vstring_alloc(1);
378
vstream_fileno(VSTREAM_ERR) = 1;
380
while (get_buffer(buf, VSTREAM_IN, interactive) != VSTREAM_EOF) {
381
argv = argv_split(STR(buf), " \t\r\n");
382
if (argv->argc > 0 && argv->argv[0][0] != '#') {
383
msg_verbose = verbose_level;
384
for (ap = actions; ap->command != 0; ap++) {
385
if (strcmp(ap->command, argv->argv[0]) == 0) {
386
if ((ap->flags & FLAG_NEED_CACHE) != 0 && scache == 0)
387
msg_error("no session cache");
394
if (ap->command == 0)
395
msg_error("bad command: %s", argv->argv[0]);
400
vstring_free(endp_prop);
401
vstring_free(dest_prop);