summaryrefslogtreecommitdiffstats
path: root/src/global/scache_clnt.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/global/scache_clnt.c')
-rw-r--r--src/global/scache_clnt.c443
1 files changed, 443 insertions, 0 deletions
diff --git a/src/global/scache_clnt.c b/src/global/scache_clnt.c
new file mode 100644
index 0000000..97fe8aa
--- /dev/null
+++ b/src/global/scache_clnt.c
@@ -0,0 +1,443 @@
+/*++
+/* NAME
+/* scache_clnt 3
+/* SUMMARY
+/* session cache manager client
+/* SYNOPSIS
+/* #include <scache.h>
+/* 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 <sys_defs.h>
+#include <errno.h>
+
+/* Utility library. */
+
+#include <msg.h>
+#include <mymalloc.h>
+#include <auto_clnt.h>
+#include <stringops.h>
+
+/*#define msg_verbose 1*/
+
+/* Global library. */
+
+#include <mail_proto.h>
+#include <mail_params.h>
+#include <scache.h>
+
+/* 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);
+}