1/*++
2/* NAME
3/*	scache_single 3
4/* SUMMARY
5/*	single-item session cache
6/* SYNOPSIS
7/*	#include <scache.h>
8/* DESCRIPTION
9/*	SCACHE *scache_single_create()
10/* DESCRIPTION
11/*	This module implements an in-memory, single-session cache.
12/*
13/*	scache_single_create() creates a session cache instance
14/*	that stores a single session.
15/* DIAGNOSTICS
16/*	Fatal error: memory allocation problem;
17/*	panic: internal consistency failure.
18/* SEE ALSO
19/*	scache(3), generic session cache API
20/* LICENSE
21/* .ad
22/* .fi
23/*	The Secure Mailer license must be distributed with this software.
24/* AUTHOR(S)
25/*	Wietse Venema
26/*	IBM T.J. Watson Research
27/*	P.O. Box 704
28/*	Yorktown Heights, NY 10598, USA
29/*--*/
30
31/* System library. */
32
33#include <sys_defs.h>
34#include <unistd.h>
35#include <string.h>
36
37/* Utility library. */
38
39#include <msg.h>
40#include <vstring.h>
41#include <mymalloc.h>
42#include <events.h>
43
44/*#define msg_verbose 1*/
45
46/* Global library. */
47
48#include <scache.h>
49
50/* Application-specific. */
51
52 /*
53  * Data structure for one saved connection. It is left up to the application
54  * to serialize attributes upon passivation, and to de-serialize them upon
55  * re-activation.
56  */
57typedef struct {
58    VSTRING *endp_label;		/* physical endpoint name */
59    VSTRING *endp_prop;			/* endpoint properties, serialized */
60    int     fd;				/* the session */
61} SCACHE_SINGLE_ENDP;
62
63 /*
64  * Data structure for a logical name to physical endpoint binding. It is
65  * left up to the application to serialize attributes upon passivation, and
66  * to de-serialize then upon re-activation.
67  */
68typedef struct {
69    VSTRING *dest_label;		/* logical destination name */
70    VSTRING *dest_prop;			/* binding properties, serialized */
71    VSTRING *endp_label;		/* physical endpoint name */
72} SCACHE_SINGLE_DEST;
73
74 /*
75  * SCACHE_SINGLE is a derived type from the SCACHE super-class.
76  */
77typedef struct {
78    SCACHE  scache[1];			/* super-class */
79    SCACHE_SINGLE_ENDP endp;		/* one cached session */
80    SCACHE_SINGLE_DEST dest;		/* one cached binding */
81} SCACHE_SINGLE;
82
83static void scache_single_expire_endp(int, char *);
84static void scache_single_expire_dest(int, char *);
85
86#define SCACHE_SINGLE_ENDP_BUSY(sp)	(VSTRING_LEN(sp->endp.endp_label) > 0)
87#define SCACHE_SINGLE_DEST_BUSY(sp)	(VSTRING_LEN(sp->dest.dest_label) > 0)
88
89#define STR(x) vstring_str(x)
90
91/* scache_single_free_endp - discard endpoint */
92
93static void scache_single_free_endp(SCACHE_SINGLE *sp)
94{
95    const char *myname = "scache_single_free_endp";
96
97    if (msg_verbose)
98	msg_info("%s: %s", myname, STR(sp->endp.endp_label));
99
100    event_cancel_timer(scache_single_expire_endp, (char *) sp);
101    if (sp->endp.fd >= 0 && close(sp->endp.fd) < 0)
102	msg_warn("close session endpoint %s: %m", STR(sp->endp.endp_label));
103    VSTRING_RESET(sp->endp.endp_label);
104    VSTRING_TERMINATE(sp->endp.endp_label);
105    VSTRING_RESET(sp->endp.endp_prop);
106    VSTRING_TERMINATE(sp->endp.endp_prop);
107    sp->endp.fd = -1;
108}
109
110/* scache_single_expire_endp - discard expired session */
111
112static void scache_single_expire_endp(int unused_event, char *context)
113{
114    SCACHE_SINGLE *sp = (SCACHE_SINGLE *) context;
115
116    scache_single_free_endp(sp);
117}
118
119/* scache_single_save_endp - save endpoint */
120
121static void scache_single_save_endp(SCACHE *scache, int endp_ttl,
122				            const char *endp_label,
123				            const char *endp_prop, int fd)
124{
125    SCACHE_SINGLE *sp = (SCACHE_SINGLE *) scache;
126    const char *myname = "scache_single_save_endp";
127
128    if (endp_ttl <= 0)
129	msg_panic("%s: bad endp_ttl: %d", myname, endp_ttl);
130
131    if (SCACHE_SINGLE_ENDP_BUSY(sp))
132	scache_single_free_endp(sp);		/* dump the cached fd */
133
134    vstring_strcpy(sp->endp.endp_label, endp_label);
135    vstring_strcpy(sp->endp.endp_prop, endp_prop);
136    sp->endp.fd = fd;
137    event_request_timer(scache_single_expire_endp, (char *) sp, endp_ttl);
138
139    if (msg_verbose)
140	msg_info("%s: %s fd=%d", myname, endp_label, fd);
141}
142
143/* scache_single_find_endp - look up cached session */
144
145static int scache_single_find_endp(SCACHE *scache, const char *endp_label,
146				           VSTRING *endp_prop)
147{
148    SCACHE_SINGLE *sp = (SCACHE_SINGLE *) scache;
149    const char *myname = "scache_single_find_endp";
150    int     fd;
151
152    if (!SCACHE_SINGLE_ENDP_BUSY(sp)) {
153	if (msg_verbose)
154	    msg_info("%s: no endpoint cache: %s", myname, endp_label);
155	return (-1);
156    }
157    if (strcmp(STR(sp->endp.endp_label), endp_label) == 0) {
158	vstring_strcpy(endp_prop, STR(sp->endp.endp_prop));
159	fd = sp->endp.fd;
160	sp->endp.fd = -1;
161	scache_single_free_endp(sp);
162	if (msg_verbose)
163	    msg_info("%s: found: %s fd=%d", myname, endp_label, fd);
164	return (fd);
165    }
166    if (msg_verbose)
167	msg_info("%s: not found: %s", myname, endp_label);
168    return (-1);
169}
170
171/* scache_single_free_dest - discard destination/endpoint association */
172
173static void scache_single_free_dest(SCACHE_SINGLE *sp)
174{
175    const char *myname = "scache_single_free_dest";
176
177    if (msg_verbose)
178	msg_info("%s: %s -> %s", myname, STR(sp->dest.dest_label),
179		 STR(sp->dest.endp_label));
180
181    event_cancel_timer(scache_single_expire_dest, (char *) sp);
182    VSTRING_RESET(sp->dest.dest_label);
183    VSTRING_TERMINATE(sp->dest.dest_label);
184    VSTRING_RESET(sp->dest.dest_prop);
185    VSTRING_TERMINATE(sp->dest.dest_prop);
186    VSTRING_RESET(sp->dest.endp_label);
187    VSTRING_TERMINATE(sp->dest.endp_label);
188}
189
190/* scache_single_expire_dest - discard expired destination/endpoint binding */
191
192static void scache_single_expire_dest(int unused_event, char *context)
193{
194    SCACHE_SINGLE *sp = (SCACHE_SINGLE *) context;
195
196    scache_single_free_dest(sp);
197}
198
199/* scache_single_save_dest - create destination/endpoint association */
200
201static void scache_single_save_dest(SCACHE *scache, int dest_ttl,
202				            const char *dest_label,
203				            const char *dest_prop,
204				            const char *endp_label)
205{
206    SCACHE_SINGLE *sp = (SCACHE_SINGLE *) scache;
207    const char *myname = "scache_single_save_dest";
208    int     refresh;
209
210    if (dest_ttl <= 0)
211	msg_panic("%s: bad dest_ttl: %d", myname, dest_ttl);
212
213    /*
214     * Optimize: reset timer only, if nothing has changed.
215     */
216    refresh =
217	(SCACHE_SINGLE_DEST_BUSY(sp)
218	 && strcmp(STR(sp->dest.dest_label), dest_label) == 0
219	 && strcmp(STR(sp->dest.dest_prop), dest_prop) == 0
220	 && strcmp(STR(sp->dest.endp_label), endp_label) == 0);
221
222    if (refresh == 0) {
223	vstring_strcpy(sp->dest.dest_label, dest_label);
224	vstring_strcpy(sp->dest.dest_prop, dest_prop);
225	vstring_strcpy(sp->dest.endp_label, endp_label);
226    }
227    event_request_timer(scache_single_expire_dest, (char *) sp, dest_ttl);
228
229    if (msg_verbose)
230	msg_info("%s: %s -> %s%s", myname, dest_label, endp_label,
231		 refresh ? " (refreshed)" : "");
232}
233
234/* scache_single_find_dest - look up cached session */
235
236static int scache_single_find_dest(SCACHE *scache, const char *dest_label,
237			             VSTRING *dest_prop, VSTRING *endp_prop)
238{
239    SCACHE_SINGLE *sp = (SCACHE_SINGLE *) scache;
240    const char *myname = "scache_single_find_dest";
241    int     fd;
242
243    if (!SCACHE_SINGLE_DEST_BUSY(sp)) {
244	if (msg_verbose)
245	    msg_info("%s: no destination cache: %s", myname, dest_label);
246	return (-1);
247    }
248    if (strcmp(STR(sp->dest.dest_label), dest_label) == 0) {
249	if (msg_verbose)
250	    msg_info("%s: found: %s", myname, dest_label);
251	if ((fd = scache_single_find_endp(scache, STR(sp->dest.endp_label), endp_prop)) >= 0) {
252	    vstring_strcpy(dest_prop, STR(sp->dest.dest_prop));
253	    return (fd);
254	}
255    }
256    if (msg_verbose)
257	msg_info("%s: not found: %s", myname, dest_label);
258    return (-1);
259}
260
261/* scache_single_size - size of single-element cache :-) */
262
263static void scache_single_size(SCACHE *scache, SCACHE_SIZE *size)
264{
265    SCACHE_SINGLE *sp = (SCACHE_SINGLE *) scache;
266
267    size->dest_count = (!SCACHE_SINGLE_DEST_BUSY(sp) ? 0 : 1);
268    size->endp_count = (!SCACHE_SINGLE_ENDP_BUSY(sp) ? 0 : 1);
269    size->sess_count = (sp->endp.fd < 0 ? 0 : 1);
270}
271
272/* scache_single_free - destroy single-element cache object */
273
274static void scache_single_free(SCACHE *scache)
275{
276    SCACHE_SINGLE *sp = (SCACHE_SINGLE *) scache;
277
278    vstring_free(sp->endp.endp_label);
279    vstring_free(sp->endp.endp_prop);
280    if (sp->endp.fd >= 0)
281	close(sp->endp.fd);
282
283    vstring_free(sp->dest.dest_label);
284    vstring_free(sp->dest.dest_prop);
285    vstring_free(sp->dest.endp_label);
286
287    myfree((char *) sp);
288}
289
290/* scache_single_create - initialize */
291
292SCACHE *scache_single_create(void)
293{
294    SCACHE_SINGLE *sp = (SCACHE_SINGLE *) mymalloc(sizeof(*sp));
295
296    sp->scache->save_endp = scache_single_save_endp;
297    sp->scache->find_endp = scache_single_find_endp;
298    sp->scache->save_dest = scache_single_save_dest;
299    sp->scache->find_dest = scache_single_find_dest;
300    sp->scache->size = scache_single_size;
301    sp->scache->free = scache_single_free;
302
303    sp->endp.endp_label = vstring_alloc(10);
304    sp->endp.endp_prop = vstring_alloc(10);
305    sp->endp.fd = -1;
306
307    sp->dest.dest_label = vstring_alloc(10);
308    sp->dest.dest_prop = vstring_alloc(10);
309    sp->dest.endp_label = vstring_alloc(10);
310
311    return (sp->scache);
312}
313