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