/*++ /* NAME /* scache_clnt 3 /* SUMMARY /* session cache manager client /* SYNOPSIS /* #include /* DESCRIPTION /* SCACHE *scache_clnt_create(server, timeout, idle_limit, ttl_limit) /* const char *server; /* int timeout; /* int idle_limit; /* int ttl_limit; /* DESCRIPTION /* This module implements the client-side protocol of the /* session cache service. /* /* scache_clnt_create() creates a session cache service client. /* /* Arguments: /* .IP server /* The session cache service name. /* .IP timeout /* Time limit for connect, send or receive operations. /* .IP idle_limit /* Idle time after which the client disconnects. /* .IP ttl_limit /* Upper bound on the time that a connection is allowed to persist. /* DIAGNOSTICS /* Fatal error: memory allocation problem; /* warning: communication error; /* panic: internal consistency failure. /* SEE ALSO /* scache(3), generic session cache API /* LICENSE /* .ad /* .fi /* The Secure Mailer license must be distributed with this software. /* AUTHOR(S) /* Wietse Venema /* IBM T.J. Watson Research /* P.O. Box 704 /* Yorktown Heights, NY 10598, USA /* /* Wietse Venema /* Google, Inc. /* 111 8th Avenue /* New York, NY 10011, USA /*--*/ /* System library. */ #include #include /* Utility library. */ #include #include #include #include /*#define msg_verbose 1*/ /* Global library. */ #include #include #include /* Application-specific. */ /* * SCACHE_CLNT is a derived type from the SCACHE super-class. */ typedef struct { SCACHE scache[1]; /* super-class */ AUTO_CLNT *auto_clnt; /* client endpoint */ #ifdef CANT_WRITE_BEFORE_SENDING_FD VSTRING *dummy; /* dummy buffer */ #endif } SCACHE_CLNT; #define STR(x) vstring_str(x) #define SCACHE_MAX_TRIES 2 /* scache_clnt_handshake - receive server protocol announcement */ static int scache_clnt_handshake(VSTREAM *stream) { return (attr_scan(stream, ATTR_FLAG_STRICT, RECV_ATTR_STREQ(MAIL_ATTR_PROTO, MAIL_ATTR_PROTO_SCACHE), ATTR_TYPE_END)); } /* scache_clnt_save_endp - save endpoint */ static void scache_clnt_save_endp(SCACHE *scache, int endp_ttl, const char *endp_label, const char *endp_prop, int fd) { SCACHE_CLNT *sp = (SCACHE_CLNT *) scache; const char *myname = "scache_clnt_save_endp"; VSTREAM *stream; int status; int tries; int count = 0; if (msg_verbose) msg_info("%s: endp=%s prop=%s fd=%d", myname, endp_label, endp_prop, fd); /* * Sanity check. */ if (endp_ttl <= 0) msg_panic("%s: bad endp_ttl: %d", myname, endp_ttl); /* * Try a few times before disabling the cache. We use synchronous calls; * the session cache service is CPU bound and making the client * asynchronous would just complicate the code. */ for (tries = 0; sp->auto_clnt != 0; tries++) { if ((stream = auto_clnt_access(sp->auto_clnt)) != 0) { errno = 0; count += 1; if (attr_print(stream, ATTR_FLAG_NONE, SEND_ATTR_STR(MAIL_ATTR_REQ, SCACHE_REQ_SAVE_ENDP), SEND_ATTR_INT(MAIL_ATTR_TTL, endp_ttl), SEND_ATTR_STR(MAIL_ATTR_LABEL, endp_label), SEND_ATTR_STR(MAIL_ATTR_PROP, endp_prop), ATTR_TYPE_END) != 0 || vstream_fflush(stream) #ifdef CANT_WRITE_BEFORE_SENDING_FD || attr_scan(stream, ATTR_FLAG_STRICT, RECV_ATTR_STR(MAIL_ATTR_DUMMY, sp->dummy), ATTR_TYPE_END) != 1 #endif || LOCAL_SEND_FD(vstream_fileno(stream), fd) < 0 || attr_scan(stream, ATTR_FLAG_STRICT, RECV_ATTR_INT(MAIL_ATTR_STATUS, &status), ATTR_TYPE_END) != 1) { if (msg_verbose || count > 1 || (errno && errno != EPIPE && errno != ENOENT)) msg_warn("problem talking to service %s: %m", VSTREAM_PATH(stream)); /* Give up or recover. */ } else { if (msg_verbose && status != 0) msg_warn("%s: descriptor save failed with status %d", myname, status); break; } } /* Give up or recover. */ if (tries >= SCACHE_MAX_TRIES - 1) { msg_warn("disabling connection caching"); auto_clnt_free(sp->auto_clnt); sp->auto_clnt = 0; break; } sleep(1); /* XXX make configurable */ auto_clnt_recover(sp->auto_clnt); } /* Always close the descriptor before returning. */ if (close(fd) < 0) msg_warn("%s: close(%d): %m", myname, fd); } /* scache_clnt_find_endp - look up cached session */ static int scache_clnt_find_endp(SCACHE *scache, const char *endp_label, VSTRING *endp_prop) { SCACHE_CLNT *sp = (SCACHE_CLNT *) scache; const char *myname = "scache_clnt_find_endp"; VSTREAM *stream; int status; int tries; int fd; /* * Try a few times before disabling the cache. We use synchronous calls; * the session cache service is CPU bound and making the client * asynchronous would just complicate the code. */ for (tries = 0; sp->auto_clnt != 0; tries++) { if ((stream = auto_clnt_access(sp->auto_clnt)) != 0) { errno = 0; if (attr_print(stream, ATTR_FLAG_NONE, SEND_ATTR_STR(MAIL_ATTR_REQ, SCACHE_REQ_FIND_ENDP), SEND_ATTR_STR(MAIL_ATTR_LABEL, endp_label), ATTR_TYPE_END) != 0 || vstream_fflush(stream) || attr_scan(stream, ATTR_FLAG_STRICT, RECV_ATTR_INT(MAIL_ATTR_STATUS, &status), RECV_ATTR_STR(MAIL_ATTR_PROP, endp_prop), ATTR_TYPE_END) != 2) { if (msg_verbose || (errno != EPIPE && errno != ENOENT)) msg_warn("problem talking to service %s: %m", VSTREAM_PATH(stream)); /* Give up or recover. */ } else if (status != 0) { if (msg_verbose) msg_info("%s: not found: %s", myname, endp_label); return (-1); } else if ( #ifdef CANT_WRITE_BEFORE_SENDING_FD attr_print(stream, ATTR_FLAG_NONE, SEND_ATTR_STR(MAIL_ATTR_DUMMY, ""), ATTR_TYPE_END) != 0 || vstream_fflush(stream) != 0 || read_wait(vstream_fileno(stream), stream->timeout) < 0 || /* XXX */ #endif (fd = LOCAL_RECV_FD(vstream_fileno(stream))) < 0) { if (msg_verbose || (errno != EPIPE && errno != ENOENT)) msg_warn("problem talking to service %s: %m", VSTREAM_PATH(stream)); /* Give up or recover. */ } else { #ifdef MUST_READ_AFTER_SENDING_FD (void) attr_print(stream, ATTR_FLAG_NONE, SEND_ATTR_STR(MAIL_ATTR_DUMMY, ""), ATTR_TYPE_END); (void) vstream_fflush(stream); #endif if (msg_verbose) msg_info("%s: endp=%s prop=%s fd=%d", myname, endp_label, STR(endp_prop), fd); return (fd); } } /* Give up or recover. */ if (tries >= SCACHE_MAX_TRIES - 1) { msg_warn("disabling connection caching"); auto_clnt_free(sp->auto_clnt); sp->auto_clnt = 0; return (-1); } sleep(1); /* XXX make configurable */ auto_clnt_recover(sp->auto_clnt); } return (-1); } /* scache_clnt_save_dest - create destination/endpoint association */ static void scache_clnt_save_dest(SCACHE *scache, int dest_ttl, const char *dest_label, const char *dest_prop, const char *endp_label) { SCACHE_CLNT *sp = (SCACHE_CLNT *) scache; const char *myname = "scache_clnt_save_dest"; VSTREAM *stream; int status; int tries; if (msg_verbose) msg_info("%s: dest_label=%s dest_prop=%s endp_label=%s", myname, dest_label, dest_prop, endp_label); /* * Sanity check. */ if (dest_ttl <= 0) msg_panic("%s: bad dest_ttl: %d", myname, dest_ttl); /* * Try a few times before disabling the cache. We use synchronous calls; * the session cache service is CPU bound and making the client * asynchronous would just complicate the code. */ for (tries = 0; sp->auto_clnt != 0; tries++) { if ((stream = auto_clnt_access(sp->auto_clnt)) != 0) { errno = 0; if (attr_print(stream, ATTR_FLAG_NONE, SEND_ATTR_STR(MAIL_ATTR_REQ, SCACHE_REQ_SAVE_DEST), SEND_ATTR_INT(MAIL_ATTR_TTL, dest_ttl), SEND_ATTR_STR(MAIL_ATTR_LABEL, dest_label), SEND_ATTR_STR(MAIL_ATTR_PROP, dest_prop), SEND_ATTR_STR(MAIL_ATTR_LABEL, endp_label), ATTR_TYPE_END) != 0 || vstream_fflush(stream) || attr_scan(stream, ATTR_FLAG_STRICT, RECV_ATTR_INT(MAIL_ATTR_STATUS, &status), ATTR_TYPE_END) != 1) { if (msg_verbose || (errno != EPIPE && errno != ENOENT)) msg_warn("problem talking to service %s: %m", VSTREAM_PATH(stream)); /* Give up or recover. */ } else { if (msg_verbose && status != 0) msg_warn("%s: destination save failed with status %d", myname, status); break; } } /* Give up or recover. */ if (tries >= SCACHE_MAX_TRIES - 1) { msg_warn("disabling connection caching"); auto_clnt_free(sp->auto_clnt); sp->auto_clnt = 0; break; } sleep(1); /* XXX make configurable */ auto_clnt_recover(sp->auto_clnt); } } /* scache_clnt_find_dest - look up cached session */ static int scache_clnt_find_dest(SCACHE *scache, const char *dest_label, VSTRING *dest_prop, VSTRING *endp_prop) { SCACHE_CLNT *sp = (SCACHE_CLNT *) scache; const char *myname = "scache_clnt_find_dest"; VSTREAM *stream; int status; int tries; int fd; /* * Try a few times before disabling the cache. We use synchronous calls; * the session cache service is CPU bound and making the client * asynchronous would just complicate the code. */ for (tries = 0; sp->auto_clnt != 0; tries++) { if ((stream = auto_clnt_access(sp->auto_clnt)) != 0) { errno = 0; if (attr_print(stream, ATTR_FLAG_NONE, SEND_ATTR_STR(MAIL_ATTR_REQ, SCACHE_REQ_FIND_DEST), SEND_ATTR_STR(MAIL_ATTR_LABEL, dest_label), ATTR_TYPE_END) != 0 || vstream_fflush(stream) || attr_scan(stream, ATTR_FLAG_STRICT, RECV_ATTR_INT(MAIL_ATTR_STATUS, &status), RECV_ATTR_STR(MAIL_ATTR_PROP, dest_prop), RECV_ATTR_STR(MAIL_ATTR_PROP, endp_prop), ATTR_TYPE_END) != 3) { if (msg_verbose || (errno != EPIPE && errno != ENOENT)) msg_warn("problem talking to service %s: %m", VSTREAM_PATH(stream)); /* Give up or recover. */ } else if (status != 0) { if (msg_verbose) msg_info("%s: not found: %s", myname, dest_label); return (-1); } else if ( #ifdef CANT_WRITE_BEFORE_SENDING_FD attr_print(stream, ATTR_FLAG_NONE, SEND_ATTR_STR(MAIL_ATTR_DUMMY, ""), ATTR_TYPE_END) != 0 || vstream_fflush(stream) != 0 || read_wait(vstream_fileno(stream), stream->timeout) < 0 || /* XXX */ #endif (fd = LOCAL_RECV_FD(vstream_fileno(stream))) < 0) { if (msg_verbose || (errno != EPIPE && errno != ENOENT)) msg_warn("problem talking to service %s: %m", VSTREAM_PATH(stream)); /* Give up or recover. */ } else { #ifdef MUST_READ_AFTER_SENDING_FD (void) attr_print(stream, ATTR_FLAG_NONE, SEND_ATTR_STR(MAIL_ATTR_DUMMY, ""), ATTR_TYPE_END); (void) vstream_fflush(stream); #endif if (msg_verbose) msg_info("%s: dest=%s dest_prop=%s endp_prop=%s fd=%d", myname, dest_label, STR(dest_prop), STR(endp_prop), fd); return (fd); } } /* Give up or recover. */ if (tries >= SCACHE_MAX_TRIES - 1) { msg_warn("disabling connection caching"); auto_clnt_free(sp->auto_clnt); sp->auto_clnt = 0; return (-1); } sleep(1); /* XXX make configurable */ auto_clnt_recover(sp->auto_clnt); } return (-1); } /* scache_clnt_size - dummy */ static void scache_clnt_size(SCACHE *unused_scache, SCACHE_SIZE *size) { /* XXX Crap in a hurry. */ size->dest_count = 0; size->endp_count = 0; size->sess_count = 0; } /* scache_clnt_free - destroy cache */ static void scache_clnt_free(SCACHE *scache) { SCACHE_CLNT *sp = (SCACHE_CLNT *) scache; if (sp->auto_clnt) auto_clnt_free(sp->auto_clnt); #ifdef CANT_WRITE_BEFORE_SENDING_FD vstring_free(sp->dummy); #endif myfree((void *) sp); } /* scache_clnt_create - initialize */ SCACHE *scache_clnt_create(const char *server, int timeout, int idle_limit, int ttl_limit) { SCACHE_CLNT *sp = (SCACHE_CLNT *) mymalloc(sizeof(*sp)); char *service; sp->scache->save_endp = scache_clnt_save_endp; sp->scache->find_endp = scache_clnt_find_endp; sp->scache->save_dest = scache_clnt_save_dest; sp->scache->find_dest = scache_clnt_find_dest; sp->scache->size = scache_clnt_size; sp->scache->free = scache_clnt_free; service = concatenate("local:" MAIL_CLASS_PRIVATE "/", server, (char *) 0); sp->auto_clnt = auto_clnt_create(service, timeout, idle_limit, ttl_limit); auto_clnt_control(sp->auto_clnt, AUTO_CLNT_CTL_HANDSHAKE, scache_clnt_handshake, AUTO_CLNT_CTL_END); myfree(service); #ifdef CANT_WRITE_BEFORE_SENDING_FD sp->dummy = vstring_alloc(1); #endif return (sp->scache); }