1/*++
2/* NAME
3/*	scache 3
4/* SUMMARY
5/*	generic session cache API
6/* SYNOPSIS
7/*	#include <scache.h>
8/* DESCRIPTION
9/*	typedef struct {
10/* .in +4
11/*		int	dest_count;
12/*		int	endp_count;
13/*		int	sess_count;
14/* .in -4
15/*	} SCACHE_SIZE;
16/*
17/*	unsigned scache_size(scache, size)
18/*	SCACHE	*scache;
19/*	SCACHE_SIZE *size;
20/*
21/*	void	scache_free(scache)
22/*	SCACHE	*scache;
23/*
24/*	void    scache_save_endp(scache, endp_ttl, endp_label, endp_prop, fd)
25/*	SCACHE	*scache;
26/*	int	endp_ttl;
27/*	const char *endp_label;
28/*	const char *endp_prop;
29/*	int	fd;
30/*
31/*	int     scache_find_endp(scache, endp_label, endp_prop)
32/*	SCACHE	*scache;
33/*	const char *endp_label;
34/*	VSTRING	*endp_prop;
35/*
36/*	void	scache_save_dest(scache, dest_ttl, dest_label,
37/*				dest_prop, endp_label)
38/*	SCACHE	*scache;
39/*	int	dest_ttl;
40/*	const char *dest_label;
41/*	const char *dest_prop;
42/*	const char *endp_label;
43/*
44/*	int	scache_find_dest(dest_label, dest_prop, endp_prop)
45/*	SCACHE	*scache;
46/*	const char *dest_label;
47/*	VSTRING	*dest_prop;
48/*	VSTRING	*endp_prop;
49/* DESCRIPTION
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.
54/*
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
64/*	sessions.
65/*
66/*	scache_size() returns the number of logical destination
67/*	names, physical endpoint addresses, and cached sessions.
68/*
69/*	scache_free() destroys the specified session cache.
70/*
71/*	scache_save_endp() stores an open session under the specified
72/*	physical endpoint name.
73/*
74/*	scache_find_endp() looks up a saved session under the
75/*	specified physical endpoint name.
76/*
77/*	scache_save_dest() associates the specified physical endpoint
78/*	with the specified logical endpoint name.
79/*
80/*	scache_find_dest() looks up a saved session under the
81/*	specified physical endpoint name.
82/*
83/*	Arguments:
84/* .IP endp_ttl
85/*	How long the session should be cached.  When information
86/*	expires it is purged automatically.
87/* .IP endp_label
88/*      The transport name and the physical endpoint name under
89/*      which the session is stored and looked up.
90/*
91/*	In the case of SMTP, the physical endpoint includes the numerical
92/*	IP address, address family information, and the numerical TCP port.
93/* .IP endp_prop
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.
97/* .sp
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.
103/* .IP dest_ttl
104/*	How long the destination-to-endpoint binding should be
105/*	cached. When information expires it is purged automatically.
106/* .IP dest_label
107/*	The transport name and the logical destination under which the
108/*	destination-to-endpoint binding is stored and looked up.
109/*
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.
112/* .IP dest_prop
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
117/* .sp
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.
123/* .IP fd
124/*	File descriptor with session to be cached.
125/* DIAGNOSTICS
126/*	scache_find_endp() and scache_find_dest() return -1 when
127/*	the lookup fails, and a file descriptor upon success.
128/*
129/*	Other diagnostics: fatal error: memory allocation problem;
130/*	panic: internal consistency failure.
131/* SEE ALSO
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
135/* LICENSE
136/* .ad
137/* .fi
138/*	The Secure Mailer license must be distributed with this software.
139/* AUTHOR(S)
140/*	Wietse Venema
141/*	IBM T.J. Watson Research
142/*	P.O. Box 704
143/*	Yorktown Heights, NY 10598, USA
144/*--*/
145
146/* System library. */
147
148#include <sys_defs.h>
149#include <stdlib.h>
150#include <unistd.h>
151#include <string.h>
152
153/* Utility library. */
154
155#include <msg.h>
156#include <vstream.h>
157#include <vstring.h>
158#include <vstring_vstream.h>
159#include <argv.h>
160#include <events.h>
161
162/* Global library. */
163
164#include <scache.h>
165
166#ifdef TEST
167
168 /*
169  * Driver program for cache regression tests. Although all variants are
170  * relatively simple to verify by hand for single session storage, more
171  * sophisticated instrumentation is needed to demonstrate that the
172  * multi-session cache manager properly handles collisions in the time
173  * domain and in all the name space domains.
174  */
175static SCACHE *scache;
176static VSTRING *endp_prop;
177static VSTRING *dest_prop;
178static int verbose_level = 3;
179
180 /*
181  * Cache mode lookup table. We don't do the client-server variant because
182  * that drags in a ton of configuration junk; the client-server protocol is
183  * relatively easy to verify manually.
184  */
185struct cache_type {
186    char   *mode;
187    SCACHE *(*create) (void);
188};
189
190static struct cache_type cache_types[] = {
191    "single", scache_single_create,
192    "multi", scache_multi_create,
193    0,
194};
195
196#define STR(x) vstring_str(x)
197
198/* cache_type - select session cache type */
199
200static void cache_type(ARGV *argv)
201{
202    struct cache_type *cp;
203
204    if (argv->argc != 2) {
205	msg_error("usage: %s mode", argv->argv[0]);
206	return;
207    }
208    if (scache != 0)
209	scache_free(scache);
210    for (cp = cache_types; cp->mode != 0; cp++) {
211	if (strcmp(cp->mode, argv->argv[1]) == 0) {
212	    scache = cp->create();
213	    return;
214	}
215    }
216    msg_error("unknown cache type: %s", argv->argv[1]);
217}
218
219/* handle_events - handle events while time advances */
220
221static void handle_events(ARGV *argv)
222{
223    int     delay;
224    time_t  before;
225    time_t  after;
226
227    if (argv->argc != 2 || (delay = atoi(argv->argv[1])) <= 0) {
228	msg_error("usage: %s time", argv->argv[0]);
229	return;
230    }
231    before = event_time();
232    event_drain(delay);
233    after = event_time();
234    if (after < before + delay)
235	sleep(before + delay - after);
236}
237
238/* save_endp - save endpoint->session binding */
239
240static void save_endp(ARGV *argv)
241{
242    int     ttl;
243    int     fd;
244
245    if (argv->argc != 5
246	|| (ttl = atoi(argv->argv[1])) <= 0
247	|| (fd = atoi(argv->argv[4])) <= 0) {
248	msg_error("usage: save_endp ttl endpoint endp_props fd");
249	return;
250    }
251    if (DUP2(0, fd) < 0)
252	msg_fatal("dup2(0, %d): %m", fd);
253    scache_save_endp(scache, ttl, argv->argv[2], argv->argv[3], fd);
254}
255
256/* find_endp - find endpoint->session binding */
257
258static void find_endp(ARGV *argv)
259{
260    int     fd;
261
262    if (argv->argc != 2) {
263	msg_error("usage: find_endp endpoint");
264	return;
265    }
266    if ((fd = scache_find_endp(scache, argv->argv[1], endp_prop)) >= 0)
267	close(fd);
268}
269
270/* save_dest - save destination->endpoint binding */
271
272static void save_dest(ARGV *argv)
273{
274    int     ttl;
275
276    if (argv->argc != 5 || (ttl = atoi(argv->argv[1])) <= 0) {
277	msg_error("usage: save_dest ttl destination dest_props endpoint");
278	return;
279    }
280    scache_save_dest(scache, ttl, argv->argv[2], argv->argv[3], argv->argv[4]);
281}
282
283/* find_dest - find destination->endpoint->session binding */
284
285static void find_dest(ARGV *argv)
286{
287    int     fd;
288
289    if (argv->argc != 2) {
290	msg_error("usage: find_dest destination");
291	return;
292    }
293    if ((fd = scache_find_dest(scache, argv->argv[1], dest_prop, endp_prop)) >= 0)
294	close(fd);
295}
296
297/* verbose - adjust noise level during cache manipulation */
298
299static void verbose(ARGV *argv)
300{
301    int     level;
302
303    if (argv->argc != 2 || (level = atoi(argv->argv[1])) < 0) {
304	msg_error("usage: verbose level");
305	return;
306    }
307    verbose_level = level;
308}
309
310 /*
311  * The command lookup table.
312  */
313struct action {
314    char   *command;
315    void    (*action) (ARGV *);
316    int     flags;
317};
318
319#define FLAG_NEED_CACHE	(1<<0)
320
321static void help(ARGV *);
322
323static struct action actions[] = {
324    "cache_type", cache_type, 0,
325    "save_endp", save_endp, FLAG_NEED_CACHE,
326    "find_endp", find_endp, FLAG_NEED_CACHE,
327    "save_dest", save_dest, FLAG_NEED_CACHE,
328    "find_dest", find_dest, FLAG_NEED_CACHE,
329    "sleep", handle_events, 0,
330    "verbose", verbose, 0,
331    "?", help, 0,
332    0,
333};
334
335/* help - list commands */
336
337static void help(ARGV *argv)
338{
339    struct action *ap;
340
341    vstream_printf("commands:");
342    for (ap = actions; ap->command != 0; ap++)
343	vstream_printf(" %s", ap->command);
344    vstream_printf("\n");
345    vstream_fflush(VSTREAM_OUT);
346}
347
348/* get_buffer - prompt for input or log input */
349
350static int get_buffer(VSTRING *buf, VSTREAM *fp, int interactive)
351{
352    int     status;
353
354    if (interactive) {
355	vstream_printf("> ");
356	vstream_fflush(VSTREAM_OUT);
357    }
358    if ((status = vstring_get_nonl(buf, fp)) != VSTREAM_EOF) {
359	if (!interactive) {
360	    vstream_printf(">>> %s\n", STR(buf));
361	    vstream_fflush(VSTREAM_OUT);
362	}
363    }
364    return (status);
365}
366
367/* at last, the main program */
368
369int     main(int unused_argc, char **unused_argv)
370{
371    VSTRING *buf = vstring_alloc(1);
372    ARGV   *argv;
373    struct action *ap;
374    int     interactive = isatty(0);
375
376    endp_prop = vstring_alloc(1);
377    dest_prop = vstring_alloc(1);
378
379    vstream_fileno(VSTREAM_ERR) = 1;
380
381    while (get_buffer(buf, VSTREAM_IN, interactive) != VSTREAM_EOF) {
382	argv = argv_split(STR(buf), " \t\r\n");
383	if (argv->argc > 0 && argv->argv[0][0] != '#') {
384	    msg_verbose = verbose_level;
385	    for (ap = actions; ap->command != 0; ap++) {
386		if (strcmp(ap->command, argv->argv[0]) == 0) {
387		    if ((ap->flags & FLAG_NEED_CACHE) != 0 && scache == 0)
388			msg_error("no session cache");
389		    else
390			ap->action(argv);
391		    break;
392		}
393	    }
394	    msg_verbose = 0;
395	    if (ap->command == 0)
396		msg_error("bad command: %s", argv->argv[0]);
397	}
398	argv_free(argv);
399    }
400    scache_free(scache);
401    vstring_free(endp_prop);
402    vstring_free(dest_prop);
403    vstring_free(buf);
404    exit(0);
405}
406
407#endif
408