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