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