1/*++ 2/* NAME 3/* scache_clnt 3 4/* SUMMARY 5/* session cache manager client 6/* SYNOPSIS 7/* #include <scache.h> 8/* DESCRIPTION 9/* SCACHE *scache_clnt_create(server, timeout, idle_limit, ttl_limit) 10/* const char *server; 11/* int timeout; 12/* int idle_limit; 13/* int ttl_limit; 14/* DESCRIPTION 15/* This module implements the client-side protocol of the 16/* session cache service. 17/* 18/* scache_clnt_create() creates a session cache service client. 19/* 20/* Arguments: 21/* .IP server 22/* The session cache service name. 23/* .IP timeout 24/* Time limit for connect, send or receive operations. 25/* .IP idle_limit 26/* Idle time after which the client disconnects. 27/* .IP ttl_limit 28/* Upper bound on the time that a connection is allowed to persist. 29/* DIAGNOSTICS 30/* Fatal error: memory allocation problem; 31/* warning: communication error; 32/* panic: internal consistency failure. 33/* SEE ALSO 34/* scache(3), generic session cache API 35/* LICENSE 36/* .ad 37/* .fi 38/* The Secure Mailer license must be distributed with this software. 39/* AUTHOR(S) 40/* Wietse Venema 41/* IBM T.J. Watson Research 42/* P.O. Box 704 43/* Yorktown Heights, NY 10598, USA 44/*--*/ 45 46/* System library. */ 47 48#include <sys_defs.h> 49#include <errno.h> 50 51/* Utility library. */ 52 53#include <msg.h> 54#include <mymalloc.h> 55#include <auto_clnt.h> 56#include <stringops.h> 57 58/*#define msg_verbose 1*/ 59 60/* Global library. */ 61 62#include <mail_proto.h> 63#include <mail_params.h> 64#include <scache.h> 65 66/* Application-specific. */ 67 68 /* 69 * SCACHE_CLNT is a derived type from the SCACHE super-class. 70 */ 71typedef struct { 72 SCACHE scache[1]; /* super-class */ 73 AUTO_CLNT *auto_clnt; /* client endpoint */ 74#ifdef CANT_WRITE_BEFORE_SENDING_FD 75 VSTRING *dummy; /* dummy buffer */ 76#endif 77} SCACHE_CLNT; 78 79#define STR(x) vstring_str(x) 80 81#define SCACHE_MAX_TRIES 2 82 83/* scache_clnt_save_endp - save endpoint */ 84 85static void scache_clnt_save_endp(SCACHE *scache, int endp_ttl, 86 const char *endp_label, 87 const char *endp_prop, int fd) 88{ 89 SCACHE_CLNT *sp = (SCACHE_CLNT *) scache; 90 const char *myname = "scache_clnt_save_endp"; 91 VSTREAM *stream; 92 int status; 93 int tries; 94 int count = 0; 95 96 if (msg_verbose) 97 msg_info("%s: endp=%s prop=%s fd=%d", 98 myname, endp_label, endp_prop, fd); 99 100 /* 101 * Sanity check. 102 */ 103 if (endp_ttl <= 0) 104 msg_panic("%s: bad endp_ttl: %d", myname, endp_ttl); 105 106 /* 107 * Try a few times before disabling the cache. We use synchronous calls; 108 * the session cache service is CPU bound and making the client 109 * asynchronous would just complicate the code. 110 */ 111 for (tries = 0; sp->auto_clnt != 0; tries++) { 112 if ((stream = auto_clnt_access(sp->auto_clnt)) != 0) { 113 errno = 0; 114 count += 1; 115 if (attr_print(stream, ATTR_FLAG_NONE, 116 ATTR_TYPE_STR, MAIL_ATTR_REQ, SCACHE_REQ_SAVE_ENDP, 117 ATTR_TYPE_INT, MAIL_ATTR_TTL, endp_ttl, 118 ATTR_TYPE_STR, MAIL_ATTR_LABEL, endp_label, 119 ATTR_TYPE_STR, MAIL_ATTR_PROP, endp_prop, 120 ATTR_TYPE_END) != 0 121 || vstream_fflush(stream) 122#ifdef CANT_WRITE_BEFORE_SENDING_FD 123 || attr_scan(stream, ATTR_FLAG_STRICT, 124 ATTR_TYPE_STR, MAIL_ATTR_DUMMY, sp->dummy, 125 ATTR_TYPE_END) != 1 126#endif 127 || LOCAL_SEND_FD(vstream_fileno(stream), fd) < 0 128 || attr_scan(stream, ATTR_FLAG_STRICT, 129 ATTR_TYPE_INT, MAIL_ATTR_STATUS, &status, 130 ATTR_TYPE_END) != 1) { 131 if (msg_verbose || count > 1 || (errno && errno != EPIPE && errno != ENOENT)) 132 msg_warn("problem talking to service %s: %m", 133 VSTREAM_PATH(stream)); 134 /* Give up or recover. */ 135 } else { 136 if (msg_verbose && status != 0) 137 msg_warn("%s: descriptor save failed with status %d", 138 myname, status); 139 break; 140 } 141 } 142 /* Give up or recover. */ 143 if (tries >= SCACHE_MAX_TRIES - 1) { 144 msg_warn("disabling connection caching"); 145 auto_clnt_free(sp->auto_clnt); 146 sp->auto_clnt = 0; 147 break; 148 } 149 sleep(1); /* XXX make configurable */ 150 auto_clnt_recover(sp->auto_clnt); 151 } 152 /* Always close the descriptor before returning. */ 153 if (close(fd) < 0) 154 msg_warn("%s: close(%d): %m", myname, fd); 155} 156 157/* scache_clnt_find_endp - look up cached session */ 158 159static int scache_clnt_find_endp(SCACHE *scache, const char *endp_label, 160 VSTRING *endp_prop) 161{ 162 SCACHE_CLNT *sp = (SCACHE_CLNT *) scache; 163 const char *myname = "scache_clnt_find_endp"; 164 VSTREAM *stream; 165 int status; 166 int tries; 167 int fd; 168 169 /* 170 * Try a few times before disabling the cache. We use synchronous calls; 171 * the session cache service is CPU bound and making the client 172 * asynchronous would just complicate the code. 173 */ 174 for (tries = 0; sp->auto_clnt != 0; tries++) { 175 if ((stream = auto_clnt_access(sp->auto_clnt)) != 0) { 176 errno = 0; 177 if (attr_print(stream, ATTR_FLAG_NONE, 178 ATTR_TYPE_STR, MAIL_ATTR_REQ, SCACHE_REQ_FIND_ENDP, 179 ATTR_TYPE_STR, MAIL_ATTR_LABEL, endp_label, 180 ATTR_TYPE_END) != 0 181 || vstream_fflush(stream) 182 || attr_scan(stream, ATTR_FLAG_STRICT, 183 ATTR_TYPE_INT, MAIL_ATTR_STATUS, &status, 184 ATTR_TYPE_STR, MAIL_ATTR_PROP, endp_prop, 185 ATTR_TYPE_END) != 2) { 186 if (msg_verbose || (errno != EPIPE && errno != ENOENT)) 187 msg_warn("problem talking to service %s: %m", 188 VSTREAM_PATH(stream)); 189 /* Give up or recover. */ 190 } else if (status != 0) { 191 if (msg_verbose) 192 msg_info("%s: not found: %s", myname, endp_label); 193 return (-1); 194 } else if ( 195#ifdef CANT_WRITE_BEFORE_SENDING_FD 196 attr_print(stream, ATTR_FLAG_NONE, 197 ATTR_TYPE_STR, MAIL_ATTR_DUMMY, "", 198 ATTR_TYPE_END) != 0 199 || vstream_fflush(stream) != 0 200 || read_wait(vstream_fileno(stream), 201 stream->timeout) < 0 || /* XXX */ 202#endif 203 (fd = LOCAL_RECV_FD(vstream_fileno(stream))) < 0) { 204 if (msg_verbose || (errno != EPIPE && errno != ENOENT)) 205 msg_warn("problem talking to service %s: %m", 206 VSTREAM_PATH(stream)); 207 /* Give up or recover. */ 208 } else { 209#ifdef MUST_READ_AFTER_SENDING_FD 210 (void) attr_print(stream, ATTR_FLAG_NONE, 211 ATTR_TYPE_STR, MAIL_ATTR_DUMMY, "", 212 ATTR_TYPE_END); 213 (void) vstream_fflush(stream); 214#endif 215 if (msg_verbose) 216 msg_info("%s: endp=%s prop=%s fd=%d", 217 myname, endp_label, STR(endp_prop), fd); 218 return (fd); 219 } 220 } 221 /* Give up or recover. */ 222 if (tries >= SCACHE_MAX_TRIES - 1) { 223 msg_warn("disabling connection caching"); 224 auto_clnt_free(sp->auto_clnt); 225 sp->auto_clnt = 0; 226 return (-1); 227 } 228 sleep(1); /* XXX make configurable */ 229 auto_clnt_recover(sp->auto_clnt); 230 } 231 return (-1); 232} 233 234/* scache_clnt_save_dest - create destination/endpoint association */ 235 236static void scache_clnt_save_dest(SCACHE *scache, int dest_ttl, 237 const char *dest_label, 238 const char *dest_prop, 239 const char *endp_label) 240{ 241 SCACHE_CLNT *sp = (SCACHE_CLNT *) scache; 242 const char *myname = "scache_clnt_save_dest"; 243 VSTREAM *stream; 244 int status; 245 int tries; 246 247 if (msg_verbose) 248 msg_info("%s: dest_label=%s dest_prop=%s endp_label=%s", 249 myname, dest_label, dest_prop, endp_label); 250 251 /* 252 * Sanity check. 253 */ 254 if (dest_ttl <= 0) 255 msg_panic("%s: bad dest_ttl: %d", myname, dest_ttl); 256 257 /* 258 * Try a few times before disabling the cache. We use synchronous calls; 259 * the session cache service is CPU bound and making the client 260 * asynchronous would just complicate the code. 261 */ 262 for (tries = 0; sp->auto_clnt != 0; tries++) { 263 if ((stream = auto_clnt_access(sp->auto_clnt)) != 0) { 264 errno = 0; 265 if (attr_print(stream, ATTR_FLAG_NONE, 266 ATTR_TYPE_STR, MAIL_ATTR_REQ, SCACHE_REQ_SAVE_DEST, 267 ATTR_TYPE_INT, MAIL_ATTR_TTL, dest_ttl, 268 ATTR_TYPE_STR, MAIL_ATTR_LABEL, dest_label, 269 ATTR_TYPE_STR, MAIL_ATTR_PROP, dest_prop, 270 ATTR_TYPE_STR, MAIL_ATTR_LABEL, endp_label, 271 ATTR_TYPE_END) != 0 272 || vstream_fflush(stream) 273 || attr_scan(stream, ATTR_FLAG_STRICT, 274 ATTR_TYPE_INT, MAIL_ATTR_STATUS, &status, 275 ATTR_TYPE_END) != 1) { 276 if (msg_verbose || (errno != EPIPE && errno != ENOENT)) 277 msg_warn("problem talking to service %s: %m", 278 VSTREAM_PATH(stream)); 279 /* Give up or recover. */ 280 } else { 281 if (msg_verbose && status != 0) 282 msg_warn("%s: destination save failed with status %d", 283 myname, status); 284 break; 285 } 286 } 287 /* Give up or recover. */ 288 if (tries >= SCACHE_MAX_TRIES - 1) { 289 msg_warn("disabling connection caching"); 290 auto_clnt_free(sp->auto_clnt); 291 sp->auto_clnt = 0; 292 break; 293 } 294 sleep(1); /* XXX make configurable */ 295 auto_clnt_recover(sp->auto_clnt); 296 } 297} 298 299/* scache_clnt_find_dest - look up cached session */ 300 301static int scache_clnt_find_dest(SCACHE *scache, const char *dest_label, 302 VSTRING *dest_prop, 303 VSTRING *endp_prop) 304{ 305 SCACHE_CLNT *sp = (SCACHE_CLNT *) scache; 306 const char *myname = "scache_clnt_find_dest"; 307 VSTREAM *stream; 308 int status; 309 int tries; 310 int fd; 311 312 /* 313 * Try a few times before disabling the cache. We use synchronous calls; 314 * the session cache service is CPU bound and making the client 315 * asynchronous would just complicate the code. 316 */ 317 for (tries = 0; sp->auto_clnt != 0; tries++) { 318 if ((stream = auto_clnt_access(sp->auto_clnt)) != 0) { 319 errno = 0; 320 if (attr_print(stream, ATTR_FLAG_NONE, 321 ATTR_TYPE_STR, MAIL_ATTR_REQ, SCACHE_REQ_FIND_DEST, 322 ATTR_TYPE_STR, MAIL_ATTR_LABEL, dest_label, 323 ATTR_TYPE_END) != 0 324 || vstream_fflush(stream) 325 || attr_scan(stream, ATTR_FLAG_STRICT, 326 ATTR_TYPE_INT, MAIL_ATTR_STATUS, &status, 327 ATTR_TYPE_STR, MAIL_ATTR_PROP, dest_prop, 328 ATTR_TYPE_STR, MAIL_ATTR_PROP, endp_prop, 329 ATTR_TYPE_END) != 3) { 330 if (msg_verbose || (errno != EPIPE && errno != ENOENT)) 331 msg_warn("problem talking to service %s: %m", 332 VSTREAM_PATH(stream)); 333 /* Give up or recover. */ 334 } else if (status != 0) { 335 if (msg_verbose) 336 msg_info("%s: not found: %s", myname, dest_label); 337 return (-1); 338 } else if ( 339#ifdef CANT_WRITE_BEFORE_SENDING_FD 340 attr_print(stream, ATTR_FLAG_NONE, 341 ATTR_TYPE_STR, MAIL_ATTR_DUMMY, "", 342 ATTR_TYPE_END) != 0 343 || vstream_fflush(stream) != 0 344 || read_wait(vstream_fileno(stream), 345 stream->timeout) < 0 || /* XXX */ 346#endif 347 (fd = LOCAL_RECV_FD(vstream_fileno(stream))) < 0) { 348 if (msg_verbose || (errno != EPIPE && errno != ENOENT)) 349 msg_warn("problem talking to service %s: %m", 350 VSTREAM_PATH(stream)); 351 /* Give up or recover. */ 352 } else { 353#ifdef MUST_READ_AFTER_SENDING_FD 354 (void) attr_print(stream, ATTR_FLAG_NONE, 355 ATTR_TYPE_STR, MAIL_ATTR_DUMMY, "", 356 ATTR_TYPE_END); 357 (void) vstream_fflush(stream); 358#endif 359 if (msg_verbose) 360 msg_info("%s: dest=%s dest_prop=%s endp_prop=%s fd=%d", 361 myname, dest_label, STR(dest_prop), STR(endp_prop), fd); 362 return (fd); 363 } 364 } 365 /* Give up or recover. */ 366 if (tries >= SCACHE_MAX_TRIES - 1) { 367 msg_warn("disabling connection caching"); 368 auto_clnt_free(sp->auto_clnt); 369 sp->auto_clnt = 0; 370 return (-1); 371 } 372 sleep(1); /* XXX make configurable */ 373 auto_clnt_recover(sp->auto_clnt); 374 } 375 return (-1); 376} 377 378/* scache_clnt_size - dummy */ 379 380static void scache_clnt_size(SCACHE *unused_scache, SCACHE_SIZE *size) 381{ 382 /* XXX Crap in a hurry. */ 383 size->dest_count = 0; 384 size->endp_count = 0; 385 size->sess_count = 0; 386} 387 388/* scache_clnt_free - destroy cache */ 389 390static void scache_clnt_free(SCACHE *scache) 391{ 392 SCACHE_CLNT *sp = (SCACHE_CLNT *) scache; 393 394 if (sp->auto_clnt) 395 auto_clnt_free(sp->auto_clnt); 396#ifdef CANT_WRITE_BEFORE_SENDING_FD 397 vstring_free(sp->dummy); 398#endif 399 myfree((char *) sp); 400} 401 402/* scache_clnt_create - initialize */ 403 404SCACHE *scache_clnt_create(const char *server, int timeout, 405 int idle_limit, int ttl_limit) 406{ 407 SCACHE_CLNT *sp = (SCACHE_CLNT *) mymalloc(sizeof(*sp)); 408 char *service; 409 410 sp->scache->save_endp = scache_clnt_save_endp; 411 sp->scache->find_endp = scache_clnt_find_endp; 412 sp->scache->save_dest = scache_clnt_save_dest; 413 sp->scache->find_dest = scache_clnt_find_dest; 414 sp->scache->size = scache_clnt_size; 415 sp->scache->free = scache_clnt_free; 416 417 service = concatenate("local:private/", server, (char *) 0); 418 sp->auto_clnt = auto_clnt_create(service, timeout, idle_limit, ttl_limit); 419 myfree(service); 420 421#ifdef CANT_WRITE_BEFORE_SENDING_FD 422 sp->dummy = vstring_alloc(1); 423#endif 424 425 return (sp->scache); 426} 427