1/* $NetBSD: scache_clnt.c,v 1.3 2022/10/08 16:12:45 christos 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/* Wietse Venema 48/* Google, Inc. 49/* 111 8th Avenue 50/* New York, NY 10011, USA 51/*--*/ 52 53/* System library. */ 54 55#include <sys_defs.h> 56#include <errno.h> 57 58/* Utility library. */ 59 60#include <msg.h> 61#include <mymalloc.h> 62#include <auto_clnt.h> 63#include <stringops.h> 64 65/*#define msg_verbose 1*/ 66 67/* Global library. */ 68 69#include <mail_proto.h> 70#include <mail_params.h> 71#include <scache.h> 72 73/* Application-specific. */ 74 75 /* 76 * SCACHE_CLNT is a derived type from the SCACHE super-class. 77 */ 78typedef struct { 79 SCACHE scache[1]; /* super-class */ 80 AUTO_CLNT *auto_clnt; /* client endpoint */ 81#ifdef CANT_WRITE_BEFORE_SENDING_FD 82 VSTRING *dummy; /* dummy buffer */ 83#endif 84} SCACHE_CLNT; 85 86#define STR(x) vstring_str(x) 87 88#define SCACHE_MAX_TRIES 2 89 90/* scache_clnt_handshake - receive server protocol announcement */ 91 92static int scache_clnt_handshake(VSTREAM *stream) 93{ 94 return (attr_scan(stream, ATTR_FLAG_STRICT, 95 RECV_ATTR_STREQ(MAIL_ATTR_PROTO, MAIL_ATTR_PROTO_SCACHE), 96 ATTR_TYPE_END)); 97} 98 99/* scache_clnt_save_endp - save endpoint */ 100 101static void scache_clnt_save_endp(SCACHE *scache, int endp_ttl, 102 const char *endp_label, 103 const char *endp_prop, int fd) 104{ 105 SCACHE_CLNT *sp = (SCACHE_CLNT *) scache; 106 const char *myname = "scache_clnt_save_endp"; 107 VSTREAM *stream; 108 int status; 109 int tries; 110 int count = 0; 111 112 if (msg_verbose) 113 msg_info("%s: endp=%s prop=%s fd=%d", 114 myname, endp_label, endp_prop, fd); 115 116 /* 117 * Sanity check. 118 */ 119 if (endp_ttl <= 0) 120 msg_panic("%s: bad endp_ttl: %d", myname, endp_ttl); 121 122 /* 123 * Try a few times before disabling the cache. We use synchronous calls; 124 * the session cache service is CPU bound and making the client 125 * asynchronous would just complicate the code. 126 */ 127 for (tries = 0; sp->auto_clnt != 0; tries++) { 128 if ((stream = auto_clnt_access(sp->auto_clnt)) != 0) { 129 errno = 0; 130 count += 1; 131 if (attr_print(stream, ATTR_FLAG_NONE, 132 SEND_ATTR_STR(MAIL_ATTR_REQ, SCACHE_REQ_SAVE_ENDP), 133 SEND_ATTR_INT(MAIL_ATTR_TTL, endp_ttl), 134 SEND_ATTR_STR(MAIL_ATTR_LABEL, endp_label), 135 SEND_ATTR_STR(MAIL_ATTR_PROP, endp_prop), 136 ATTR_TYPE_END) != 0 137 || vstream_fflush(stream) 138#ifdef CANT_WRITE_BEFORE_SENDING_FD 139 || attr_scan(stream, ATTR_FLAG_STRICT, 140 RECV_ATTR_STR(MAIL_ATTR_DUMMY, sp->dummy), 141 ATTR_TYPE_END) != 1 142#endif 143 || LOCAL_SEND_FD(vstream_fileno(stream), fd) < 0 144 || attr_scan(stream, ATTR_FLAG_STRICT, 145 RECV_ATTR_INT(MAIL_ATTR_STATUS, &status), 146 ATTR_TYPE_END) != 1) { 147 if (msg_verbose || count > 1 || (errno && errno != EPIPE && errno != ENOENT)) 148 msg_warn("problem talking to service %s: %m", 149 VSTREAM_PATH(stream)); 150 /* Give up or recover. */ 151 } else { 152 if (msg_verbose && status != 0) 153 msg_warn("%s: descriptor save failed with status %d", 154 myname, status); 155 break; 156 } 157 } 158 /* Give up or recover. */ 159 if (tries >= SCACHE_MAX_TRIES - 1) { 160 msg_warn("disabling connection caching"); 161 auto_clnt_free(sp->auto_clnt); 162 sp->auto_clnt = 0; 163 break; 164 } 165 sleep(1); /* XXX make configurable */ 166 auto_clnt_recover(sp->auto_clnt); 167 } 168 /* Always close the descriptor before returning. */ 169 if (close(fd) < 0) 170 msg_warn("%s: close(%d): %m", myname, fd); 171} 172 173/* scache_clnt_find_endp - look up cached session */ 174 175static int scache_clnt_find_endp(SCACHE *scache, const char *endp_label, 176 VSTRING *endp_prop) 177{ 178 SCACHE_CLNT *sp = (SCACHE_CLNT *) scache; 179 const char *myname = "scache_clnt_find_endp"; 180 VSTREAM *stream; 181 int status; 182 int tries; 183 int fd; 184 185 /* 186 * Try a few times before disabling the cache. We use synchronous calls; 187 * the session cache service is CPU bound and making the client 188 * asynchronous would just complicate the code. 189 */ 190 for (tries = 0; sp->auto_clnt != 0; tries++) { 191 if ((stream = auto_clnt_access(sp->auto_clnt)) != 0) { 192 errno = 0; 193 if (attr_print(stream, ATTR_FLAG_NONE, 194 SEND_ATTR_STR(MAIL_ATTR_REQ, SCACHE_REQ_FIND_ENDP), 195 SEND_ATTR_STR(MAIL_ATTR_LABEL, endp_label), 196 ATTR_TYPE_END) != 0 197 || vstream_fflush(stream) 198 || attr_scan(stream, ATTR_FLAG_STRICT, 199 RECV_ATTR_INT(MAIL_ATTR_STATUS, &status), 200 RECV_ATTR_STR(MAIL_ATTR_PROP, endp_prop), 201 ATTR_TYPE_END) != 2) { 202 if (msg_verbose || (errno != EPIPE && errno != ENOENT)) 203 msg_warn("problem talking to service %s: %m", 204 VSTREAM_PATH(stream)); 205 /* Give up or recover. */ 206 } else if (status != 0) { 207 if (msg_verbose) 208 msg_info("%s: not found: %s", myname, endp_label); 209 return (-1); 210 } else if ( 211#ifdef CANT_WRITE_BEFORE_SENDING_FD 212 attr_print(stream, ATTR_FLAG_NONE, 213 SEND_ATTR_STR(MAIL_ATTR_DUMMY, ""), 214 ATTR_TYPE_END) != 0 215 || vstream_fflush(stream) != 0 216 || read_wait(vstream_fileno(stream), 217 stream->timeout) < 0 || /* XXX */ 218#endif 219 (fd = LOCAL_RECV_FD(vstream_fileno(stream))) < 0) { 220 if (msg_verbose || (errno != EPIPE && errno != ENOENT)) 221 msg_warn("problem talking to service %s: %m", 222 VSTREAM_PATH(stream)); 223 /* Give up or recover. */ 224 } else { 225#ifdef MUST_READ_AFTER_SENDING_FD 226 (void) attr_print(stream, ATTR_FLAG_NONE, 227 SEND_ATTR_STR(MAIL_ATTR_DUMMY, ""), 228 ATTR_TYPE_END); 229 (void) vstream_fflush(stream); 230#endif 231 if (msg_verbose) 232 msg_info("%s: endp=%s prop=%s fd=%d", 233 myname, endp_label, STR(endp_prop), fd); 234 return (fd); 235 } 236 } 237 /* Give up or recover. */ 238 if (tries >= SCACHE_MAX_TRIES - 1) { 239 msg_warn("disabling connection caching"); 240 auto_clnt_free(sp->auto_clnt); 241 sp->auto_clnt = 0; 242 return (-1); 243 } 244 sleep(1); /* XXX make configurable */ 245 auto_clnt_recover(sp->auto_clnt); 246 } 247 return (-1); 248} 249 250/* scache_clnt_save_dest - create destination/endpoint association */ 251 252static void scache_clnt_save_dest(SCACHE *scache, int dest_ttl, 253 const char *dest_label, 254 const char *dest_prop, 255 const char *endp_label) 256{ 257 SCACHE_CLNT *sp = (SCACHE_CLNT *) scache; 258 const char *myname = "scache_clnt_save_dest"; 259 VSTREAM *stream; 260 int status; 261 int tries; 262 263 if (msg_verbose) 264 msg_info("%s: dest_label=%s dest_prop=%s endp_label=%s", 265 myname, dest_label, dest_prop, endp_label); 266 267 /* 268 * Sanity check. 269 */ 270 if (dest_ttl <= 0) 271 msg_panic("%s: bad dest_ttl: %d", myname, dest_ttl); 272 273 /* 274 * Try a few times before disabling the cache. We use synchronous calls; 275 * the session cache service is CPU bound and making the client 276 * asynchronous would just complicate the code. 277 */ 278 for (tries = 0; sp->auto_clnt != 0; tries++) { 279 if ((stream = auto_clnt_access(sp->auto_clnt)) != 0) { 280 errno = 0; 281 if (attr_print(stream, ATTR_FLAG_NONE, 282 SEND_ATTR_STR(MAIL_ATTR_REQ, SCACHE_REQ_SAVE_DEST), 283 SEND_ATTR_INT(MAIL_ATTR_TTL, dest_ttl), 284 SEND_ATTR_STR(MAIL_ATTR_LABEL, dest_label), 285 SEND_ATTR_STR(MAIL_ATTR_PROP, dest_prop), 286 SEND_ATTR_STR(MAIL_ATTR_LABEL, endp_label), 287 ATTR_TYPE_END) != 0 288 || vstream_fflush(stream) 289 || attr_scan(stream, ATTR_FLAG_STRICT, 290 RECV_ATTR_INT(MAIL_ATTR_STATUS, &status), 291 ATTR_TYPE_END) != 1) { 292 if (msg_verbose || (errno != EPIPE && errno != ENOENT)) 293 msg_warn("problem talking to service %s: %m", 294 VSTREAM_PATH(stream)); 295 /* Give up or recover. */ 296 } else { 297 if (msg_verbose && status != 0) 298 msg_warn("%s: destination save failed with status %d", 299 myname, status); 300 break; 301 } 302 } 303 /* Give up or recover. */ 304 if (tries >= SCACHE_MAX_TRIES - 1) { 305 msg_warn("disabling connection caching"); 306 auto_clnt_free(sp->auto_clnt); 307 sp->auto_clnt = 0; 308 break; 309 } 310 sleep(1); /* XXX make configurable */ 311 auto_clnt_recover(sp->auto_clnt); 312 } 313} 314 315/* scache_clnt_find_dest - look up cached session */ 316 317static int scache_clnt_find_dest(SCACHE *scache, const char *dest_label, 318 VSTRING *dest_prop, 319 VSTRING *endp_prop) 320{ 321 SCACHE_CLNT *sp = (SCACHE_CLNT *) scache; 322 const char *myname = "scache_clnt_find_dest"; 323 VSTREAM *stream; 324 int status; 325 int tries; 326 int fd; 327 328 /* 329 * Try a few times before disabling the cache. We use synchronous calls; 330 * the session cache service is CPU bound and making the client 331 * asynchronous would just complicate the code. 332 */ 333 for (tries = 0; sp->auto_clnt != 0; tries++) { 334 if ((stream = auto_clnt_access(sp->auto_clnt)) != 0) { 335 errno = 0; 336 if (attr_print(stream, ATTR_FLAG_NONE, 337 SEND_ATTR_STR(MAIL_ATTR_REQ, SCACHE_REQ_FIND_DEST), 338 SEND_ATTR_STR(MAIL_ATTR_LABEL, dest_label), 339 ATTR_TYPE_END) != 0 340 || vstream_fflush(stream) 341 || attr_scan(stream, ATTR_FLAG_STRICT, 342 RECV_ATTR_INT(MAIL_ATTR_STATUS, &status), 343 RECV_ATTR_STR(MAIL_ATTR_PROP, dest_prop), 344 RECV_ATTR_STR(MAIL_ATTR_PROP, endp_prop), 345 ATTR_TYPE_END) != 3) { 346 if (msg_verbose || (errno != EPIPE && errno != ENOENT)) 347 msg_warn("problem talking to service %s: %m", 348 VSTREAM_PATH(stream)); 349 /* Give up or recover. */ 350 } else if (status != 0) { 351 if (msg_verbose) 352 msg_info("%s: not found: %s", myname, dest_label); 353 return (-1); 354 } else if ( 355#ifdef CANT_WRITE_BEFORE_SENDING_FD 356 attr_print(stream, ATTR_FLAG_NONE, 357 SEND_ATTR_STR(MAIL_ATTR_DUMMY, ""), 358 ATTR_TYPE_END) != 0 359 || vstream_fflush(stream) != 0 360 || read_wait(vstream_fileno(stream), 361 stream->timeout) < 0 || /* XXX */ 362#endif 363 (fd = LOCAL_RECV_FD(vstream_fileno(stream))) < 0) { 364 if (msg_verbose || (errno != EPIPE && errno != ENOENT)) 365 msg_warn("problem talking to service %s: %m", 366 VSTREAM_PATH(stream)); 367 /* Give up or recover. */ 368 } else { 369#ifdef MUST_READ_AFTER_SENDING_FD 370 (void) attr_print(stream, ATTR_FLAG_NONE, 371 SEND_ATTR_STR(MAIL_ATTR_DUMMY, ""), 372 ATTR_TYPE_END); 373 (void) vstream_fflush(stream); 374#endif 375 if (msg_verbose) 376 msg_info("%s: dest=%s dest_prop=%s endp_prop=%s fd=%d", 377 myname, dest_label, STR(dest_prop), STR(endp_prop), fd); 378 return (fd); 379 } 380 } 381 /* Give up or recover. */ 382 if (tries >= SCACHE_MAX_TRIES - 1) { 383 msg_warn("disabling connection caching"); 384 auto_clnt_free(sp->auto_clnt); 385 sp->auto_clnt = 0; 386 return (-1); 387 } 388 sleep(1); /* XXX make configurable */ 389 auto_clnt_recover(sp->auto_clnt); 390 } 391 return (-1); 392} 393 394/* scache_clnt_size - dummy */ 395 396static void scache_clnt_size(SCACHE *unused_scache, SCACHE_SIZE *size) 397{ 398 /* XXX Crap in a hurry. */ 399 size->dest_count = 0; 400 size->endp_count = 0; 401 size->sess_count = 0; 402} 403 404/* scache_clnt_free - destroy cache */ 405 406static void scache_clnt_free(SCACHE *scache) 407{ 408 SCACHE_CLNT *sp = (SCACHE_CLNT *) scache; 409 410 if (sp->auto_clnt) 411 auto_clnt_free(sp->auto_clnt); 412#ifdef CANT_WRITE_BEFORE_SENDING_FD 413 vstring_free(sp->dummy); 414#endif 415 myfree((void *) sp); 416} 417 418/* scache_clnt_create - initialize */ 419 420SCACHE *scache_clnt_create(const char *server, int timeout, 421 int idle_limit, int ttl_limit) 422{ 423 SCACHE_CLNT *sp = (SCACHE_CLNT *) mymalloc(sizeof(*sp)); 424 char *service; 425 426 sp->scache->save_endp = scache_clnt_save_endp; 427 sp->scache->find_endp = scache_clnt_find_endp; 428 sp->scache->save_dest = scache_clnt_save_dest; 429 sp->scache->find_dest = scache_clnt_find_dest; 430 sp->scache->size = scache_clnt_size; 431 sp->scache->free = scache_clnt_free; 432 433 service = concatenate("local:" MAIL_CLASS_PRIVATE "/", server, (char *) 0); 434 sp->auto_clnt = auto_clnt_create(service, timeout, idle_limit, ttl_limit); 435 auto_clnt_control(sp->auto_clnt, 436 AUTO_CLNT_CTL_HANDSHAKE, scache_clnt_handshake, 437 AUTO_CLNT_CTL_END); 438 myfree(service); 439 440#ifdef CANT_WRITE_BEFORE_SENDING_FD 441 sp->dummy = vstring_alloc(1); 442#endif 443 444 return (sp->scache); 445} 446