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