7 /* generic session cache API
19 /* unsigned scache_size(scache, size)
23 /* void scache_free(scache)
26 /* void scache_save_endp(scache, endp_ttl, endp_label, endp_prop, fd)
29 /* const char *endp_label;
30 /* const char *endp_prop;
33 /* int scache_find_endp(scache, endp_label, endp_prop)
35 /* const char *endp_label;
36 /* VSTRING *endp_prop;
38 /* void scache_save_dest(scache, dest_ttl, dest_label,
39 /* dest_prop, endp_label)
42 /* const char *dest_label;
43 /* const char *dest_prop;
44 /* const char *endp_label;
46 /* int scache_find_dest(dest_label, dest_prop, endp_prop)
48 /* const char *dest_label;
49 /* VSTRING *dest_prop;
50 /* VSTRING *endp_prop;
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.
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
68 /* scache_size() returns the number of logical destination
69 /* names, physical endpoint addresses, and cached sessions.
71 /* scache_free() destroys the specified session cache.
73 /* scache_save_endp() stores an open session under the specified
74 /* physical endpoint name.
76 /* scache_find_endp() looks up a saved session under the
77 /* specified physical endpoint name.
79 /* scache_save_dest() associates the specified physical endpoint
80 /* with the specified logical endpoint name.
82 /* scache_find_dest() looks up a saved session under the
83 /* specified physical endpoint name.
87 /* How long the session should be cached. When information
88 /* expires it is purged automatically.
90 /* The transport name and the physical endpoint name under
91 /* which the session is stored and looked up.
93 /* In the case of SMTP, the physical endpoint includes the numerical
94 /* IP address, address family information, and the numerical TCP port.
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.
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.
106 /* How long the destination-to-endpoint binding should be
107 /* cached. When information expires it is purged automatically.
109 /* The transport name and the logical destination under which the
110 /* destination-to-endpoint binding is stored and looked up.
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.
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
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.
126 /* File descriptor with session to be cached.
128 /* scache_find_endp() and scache_find_dest() return -1 when
129 /* the lookup fails, and a file descriptor upon success.
131 /* Other diagnostics: fatal error: memory allocation problem;
132 /* panic: internal consistency failure.
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
140 /* The Secure Mailer license must be distributed with this software.
143 /* IBM T.J. Watson Research
145 /* Yorktown Heights, NY 10598, USA
148 /* System library. */
150 #include <sys_defs.h>
155 /* Utility library. */
160 #include <vstring_vstream.h>
164 /* Global library. */
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.
177 static SCACHE
*scache
;
178 static VSTRING
*endp_prop
;
179 static VSTRING
*dest_prop
;
180 static int verbose_level
= 3;
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.
189 SCACHE
*(*create
) (void);
192 static struct cache_type cache_types
[] = {
193 "single", scache_single_create
,
194 "multi", scache_multi_create
,
198 #define STR(x) vstring_str(x)
200 /* cache_type - select session cache type */
202 static void cache_type(ARGV
*argv
)
204 struct cache_type
*cp
;
206 if (argv
->argc
!= 2) {
207 msg_error("usage: %s mode", argv
->argv
[0]);
212 for (cp
= cache_types
; cp
->mode
!= 0; cp
++) {
213 if (strcmp(cp
->mode
, argv
->argv
[1]) == 0) {
214 scache
= cp
->create();
218 msg_error("unknown cache type: %s", argv
->argv
[1]);
221 /* handle_events - handle events while time advances */
223 static void handle_events(ARGV
*argv
)
229 if (argv
->argc
!= 2 || (delay
= atoi(argv
->argv
[1])) <= 0) {
230 msg_error("usage: %s time", argv
->argv
[0]);
233 before
= event_time();
235 after
= event_time();
236 if (after
< before
+ delay
)
237 sleep(before
+ delay
- after
);
240 /* save_endp - save endpoint->session binding */
242 static void save_endp(ARGV
*argv
)
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");
254 msg_fatal("dup2(0, %d): %m", fd
);
255 scache_save_endp(scache
, ttl
, argv
->argv
[2], argv
->argv
[3], fd
);
258 /* find_endp - find endpoint->session binding */
260 static void find_endp(ARGV
*argv
)
264 if (argv
->argc
!= 2) {
265 msg_error("usage: find_endp endpoint");
268 if ((fd
= scache_find_endp(scache
, argv
->argv
[1], endp_prop
)) >= 0)
272 /* save_dest - save destination->endpoint binding */
274 static void save_dest(ARGV
*argv
)
278 if (argv
->argc
!= 5 || (ttl
= atoi(argv
->argv
[1])) <= 0) {
279 msg_error("usage: save_dest ttl destination dest_props endpoint");
282 scache_save_dest(scache
, ttl
, argv
->argv
[2], argv
->argv
[3], argv
->argv
[4]);
285 /* find_dest - find destination->endpoint->session binding */
287 static void find_dest(ARGV
*argv
)
291 if (argv
->argc
!= 2) {
292 msg_error("usage: find_dest destination");
295 if ((fd
= scache_find_dest(scache
, argv
->argv
[1], dest_prop
, endp_prop
)) >= 0)
299 /* verbose - adjust noise level during cache manipulation */
301 static void verbose(ARGV
*argv
)
305 if (argv
->argc
!= 2 || (level
= atoi(argv
->argv
[1])) < 0) {
306 msg_error("usage: verbose level");
309 verbose_level
= level
;
313 * The command lookup table.
317 void (*action
) (ARGV
*);
321 #define FLAG_NEED_CACHE (1<<0)
323 static void help(ARGV
*);
325 static 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,
337 /* help - list commands */
339 static void help(ARGV
*argv
)
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
);
350 /* get_buffer - prompt for input or log input */
352 static int get_buffer(VSTRING
*buf
, VSTREAM
*fp
, int interactive
)
357 vstream_printf("> ");
358 vstream_fflush(VSTREAM_OUT
);
360 if ((status
= vstring_get_nonl(buf
, fp
)) != VSTREAM_EOF
) {
362 vstream_printf(">>> %s\n", STR(buf
));
363 vstream_fflush(VSTREAM_OUT
);
369 /* at last, the main program */
371 int main(int unused_argc
, char **unused_argv
)
373 VSTRING
*buf
= vstring_alloc(1);
376 int interactive
= isatty(0);
378 endp_prop
= vstring_alloc(1);
379 dest_prop
= vstring_alloc(1);
381 vstream_fileno(VSTREAM_ERR
) = 1;
383 while (get_buffer(buf
, VSTREAM_IN
, interactive
) != VSTREAM_EOF
) {
384 argv
= argv_split(STR(buf
), " \t\r\n");
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");
397 if (ap
->command
== 0)
398 msg_error("bad command: %s", argv
->argv
[0]);
403 vstring_free(endp_prop
);
404 vstring_free(dest_prop
);