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 */ 78 typedef 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 92 static 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 101 static 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 175 static 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 252 static 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 317 static 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 396 static 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 406 static 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 420 SCACHE *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