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