summaryrefslogtreecommitdiffstats
path: root/src/global/abounce.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/global/abounce.c')
-rw-r--r--src/global/abounce.c466
1 files changed, 466 insertions, 0 deletions
diff --git a/src/global/abounce.c b/src/global/abounce.c
new file mode 100644
index 0000000..5522f60
--- /dev/null
+++ b/src/global/abounce.c
@@ -0,0 +1,466 @@
+/*++
+/* NAME
+/* abounce 3
+/* SUMMARY
+/* asynchronous bounce/defer/trace service client
+/* SYNOPSIS
+/* #include <abounce.h>
+/*
+/* void abounce_flush(flags, queue, id, encoding, smtputf8, sender,
+/* dsn_envid, dsn_ret, callback, context)
+/* int flags;
+/* const char *queue;
+/* const char *id;
+/* const char *encoding;
+/* int smtputf8;
+/* const char *sender;
+/* const char *dsn_envid;
+/* int dsn_ret;
+/* void (*callback)(int status, void *context);
+/* void *context;
+/*
+/* void abounce_flush_verp(flags, queue, id, encoding, smtputf8, sender,
+/* dsn_envid, dsn_ret, verp, callback, context)
+/* int flags;
+/* const char *queue;
+/* const char *id;
+/* const char *encoding;
+/* int smtputf8;
+/* const char *sender;
+/* const char *dsn_envid;
+/* int dsn_ret;
+/* const char *verp;
+/* void (*callback)(int status, void *context);
+/* void *context;
+/*
+/* void adefer_flush(flags, queue, id, encoding, smtputf8, sender,
+/* dsn_envid, dsn_ret, callback, context)
+/* int flags;
+/* const char *queue;
+/* const char *id;
+/* const char *encoding;
+/* int smtputf8;
+/* const char *sender;
+/* const char *dsn_envid;
+/* int dsn_ret;
+/* void (*callback)(int status, void *context);
+/* void *context;
+/*
+/* void adefer_flush_verp(flags, queue, id, encoding, smtputf8, sender,
+/* dsn_envid, dsn_ret, verp, callback, context)
+/* int flags;
+/* const char *queue;
+/* const char *id;
+/* const char *encoding;
+/* int smtputf8;
+/* const char *sender;
+/* const char *dsn_envid;
+/* int dsn_ret;
+/* const char *verp;
+/* void (*callback)(int status, void *context);
+/* void *context;
+/*
+/* void adefer_warn(flags, queue, id, encoding, smtputf8, sender,
+/* dsn_envid, dsn_ret, callback, context)
+/* int flags;
+/* const char *queue;
+/* const char *id;
+/* const char *encoding;
+/* int smtputf8;
+/* const char *sender;
+/* const char *dsn_envid;
+/* int dsn_ret;
+/* void (*callback)(int status, void *context);
+/* void *context;
+/*
+/* void atrace_flush(flags, queue, id, encoding, smtputf8, sender,
+/* dsn_envid, dsn_ret, callback, context)
+/* int flags;
+/* const char *queue;
+/* const char *id;
+/* const char *encoding;
+/* int smtputf8;
+/* const char *sender;
+/* const char *dsn_envid;
+/* int dsn_ret;
+/* void (*callback)(int status, void *context);
+/* void *context;
+/* DESCRIPTION
+/* This module implements an asynchronous interface to the
+/* bounce/defer/trace service for submitting sender notifications
+/* without waiting for completion of the request.
+/*
+/* abounce_flush() bounces the specified message to
+/* the specified sender, including the bounce log that was
+/* built with bounce_append().
+/*
+/* abounce_flush_verp() is like abounce_flush() but sends
+/* one VERP style notification per undeliverable recipient.
+/*
+/* adefer_flush() bounces the specified message to
+/* the specified sender, including the defer log that was
+/* built with defer_append().
+/* adefer_flush() requests that the deferred recipients are deleted
+/* from the original queue file.
+/*
+/* adefer_flush_verp() is like adefer_flush() but sends
+/* one VERP style notification per undeliverable recipient.
+/*
+/* adefer_warn() sends a "mail is delayed" notification to
+/* the specified sender, including the defer log that was
+/* built with defer_append().
+/*
+/* atrace_flush() returns the specified message to the specified
+/* sender, including the message delivery record log that was
+/* built with vtrace_append().
+/*
+/* Arguments:
+/* .IP flags
+/* The bitwise OR of zero or more of the following (specify
+/* BOUNCE_FLAG_NONE to request no special processing):
+/* .RS
+/* .IP BOUNCE_FLAG_CLEAN
+/* Delete the bounce log in case of an error (as in: pretend
+/* that we never even tried to bounce this message).
+/* .IP BOUNCE_FLAG_DELRCPT
+/* When specified with a flush operation, request that
+/* recipients be deleted from the queue file.
+/*
+/* Note: the bounce daemon ignores this request when the
+/* recipient queue file offset is <= 0.
+/* .IP BOUNCE_FLAG_COPY
+/* Request that a postmaster copy is sent.
+/* .RE
+/* .IP queue
+/* The message queue name of the original message file.
+/* .IP id
+/* The message queue id if the original message file. The bounce log
+/* file has the same name as the original message file.
+/* .IP encoding
+/* The body content encoding: MAIL_ATTR_ENC_{7BIT,8BIT,NONE}.
+/* .IP smtputf8
+/* The level of SMTPUTF8 support (to be defined).
+/* .IP sender
+/* The sender envelope address.
+/* .IP dsn_envid
+/* Optional DSN envelope ID.
+/* .IP ret
+/* Optional DSN return full/headers option.
+/* .IP verp
+/* VERP delimiter characters.
+/* .IP callback
+/* Name of a routine that receives the notification status as
+/* documented for bounce_flush() or defer_flush().
+/* .IP context
+/* Application-specific context that is passed through to the
+/* callback routine. Use proper casts or the world will come
+/* to an end.
+/* DIAGNOSTICS
+/* In case of success, these functions log the action, and return a
+/* zero result via the callback routine. Otherwise, the functions
+/* return a non-zero result via the callback routine, and when
+/* BOUNCE_FLAG_CLEAN is disabled, log that message delivery is deferred.
+/* 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>
+
+/* Utility library. */
+
+#include <msg.h>
+#include <mymalloc.h>
+#include <events.h>
+#include <vstream.h>
+
+/* Global library. */
+
+#include <mail_params.h>
+#include <mail_proto.h>
+#include <abounce.h>
+
+/* Application-specific. */
+
+ /*
+ * Each bounce/defer flush/warn request is implemented by sending the
+ * request to the bounce/defer server, and by creating a pseudo thread that
+ * suspends itself until the server replies (or dies). Upon wakeup, the
+ * pseudo thread delivers the request completion status to the application
+ * and destroys itself. The structure below maintains all the necessary
+ * request state while the pseudo thread is suspended.
+ */
+typedef struct {
+ int command; /* bounce request type */
+ int flags; /* bounce options */
+ char *id; /* queue ID for logging */
+ VSTRING *request; /* serialized request */
+ ABOUNCE_FN callback; /* application callback */
+ void *context; /* application context */
+ VSTREAM *fp; /* server I/O handle */
+} ABOUNCE_STATE;
+
+ /*
+ * Encapsulate common code.
+ */
+#define ABOUNCE_EVENT_ENABLE(fd, callback, context, timeout) do { \
+ event_enable_read((fd), (callback), (context)); \
+ event_request_timer((callback), (context), (timeout)); \
+ } while (0)
+
+ /*
+ * If we set the reply timeout too short, then we make the problem worse by
+ * increasing overload. With 1000s timeout mail will keep flowing, but there
+ * will be a large number of blocked bounce processes, and some resource is
+ * likely to run out.
+ */
+#define ABOUNCE_TIMEOUT 1000
+
+ /*
+ * The initial buffer size for a serialized request.
+ */
+#define ABOUNCE_BUFSIZE VSTREAM_BUFSIZE
+
+ /*
+ * We share most of the verp and non-verp code paths.
+ */
+#define ABOUNCE_NO_VERP ((char *) 0)
+
+ /*
+ * SLMs.
+ */
+#define STR(x) vstring_str(x)
+#define LEN(x) VSTRING_LEN(x)
+
+/* abounce_done - deliver status to application and clean up pseudo thread */
+
+static void abounce_done(ABOUNCE_STATE *ap, int status)
+{
+ if (ap->fp) {
+ event_disable_readwrite(vstream_fileno(ap->fp));
+ (void) vstream_fclose(ap->fp);
+ }
+ if (status != 0 && (ap->flags & BOUNCE_FLAG_CLEAN) == 0)
+ msg_info("%s: status=deferred (%s failed)", ap->id,
+ ap->command == BOUNCE_CMD_FLUSH ? "bounce" :
+ ap->command == BOUNCE_CMD_WARN ? "delay warning" :
+ ap->command == BOUNCE_CMD_VERP ? "verp" :
+ ap->command == BOUNCE_CMD_TRACE ? "trace" :
+ "whatever");
+ ap->callback(status, ap->context);
+ myfree(ap->id);
+ vstring_free(ap->request);
+ myfree((void *) ap);
+}
+
+/* abounce_receive - receive server reply */
+
+static void abounce_receive(int event, void *context)
+{
+ ABOUNCE_STATE *ap = (ABOUNCE_STATE *) context;
+ int status;
+
+ if (event != EVENT_TIME)
+ event_cancel_timer(abounce_receive, context);
+
+ if (event == EVENT_READ
+ && attr_scan(ap->fp, ATTR_FLAG_STRICT,
+ RECV_ATTR_INT(MAIL_ATTR_STATUS, &status),
+ ATTR_TYPE_END) == 1) {
+ abounce_done(ap, status);
+ } else {
+ abounce_done(ap, -1);
+ }
+}
+
+/* abounce_send - send the request and suspend until the server replies */
+
+static void abounce_send(int event, void *context)
+{
+ ABOUNCE_STATE *ap = (ABOUNCE_STATE *) context;
+
+ /*
+ * Receive the server's protocol name announcement. At this point the
+ * server is ready to receive a request without blocking the sender. Send
+ * the request and suspend until the server replies (or dies).
+ */
+ if (event != EVENT_TIME)
+ event_cancel_timer(abounce_send, context);
+
+ non_blocking(vstream_fileno(ap->fp), BLOCKING);
+ if (event == EVENT_READ
+ && attr_scan(ap->fp, ATTR_FLAG_STRICT,
+ RECV_ATTR_STREQ(MAIL_ATTR_PROTO, MAIL_ATTR_PROTO_BOUNCE),
+ ATTR_TYPE_END) == 0
+ && vstream_fwrite(ap->fp, STR(ap->request),
+ LEN(ap->request)) == LEN(ap->request)
+ && vstream_fflush(ap->fp) == 0) {
+ ABOUNCE_EVENT_ENABLE(vstream_fileno(ap->fp), abounce_receive,
+ (void *) ap, ABOUNCE_TIMEOUT);
+ } else {
+ abounce_done(ap, -1);
+ }
+}
+
+/* abounce_connect - connect and suspend until the server replies */
+
+static void abounce_connect(const char *class, const char *service,
+ int command, int flags,
+ const char *queue, const char *id,
+ const char *encoding, int smtputf8,
+ const char *sender,
+ const char *dsn_envid, int dsn_ret,
+ const char *verp, ABOUNCE_FN callback,
+ void *context)
+{
+ ABOUNCE_STATE *ap;
+
+ /*
+ * Save pseudo thread state. Connect to the server. Prior to Postfix 3.6
+ * the asynchronous bounce flush/warn client called mail_connect_wait()
+ * which sleeps and retries several times before terminating with a fatal
+ * error. This block-and-sleep behavior was not consistent with a) the
+ * rest of the code in this module, and with b) the synchronous bounce
+ * client which gives up immediately. It should be safe to give up
+ * immediately because that leaves the bounce/defer/trace logs in the
+ * queue. In particular, this should not increase the simultaneous number
+ * of asynchronous bounce/defer/trace flush/warn requests that are in
+ * flight.
+ */
+ ap = (ABOUNCE_STATE *) mymalloc(sizeof(*ap));
+ ap->command = command;
+ ap->flags = flags;
+ ap->id = mystrdup(id);
+ ap->request = vstring_alloc(ABOUNCE_BUFSIZE);
+ ap->callback = callback;
+ ap->context = context;
+ ap->fp = mail_connect(class, service, NON_BLOCKING);
+
+ /*
+ * Format the request now, so that we don't have to save a lot of
+ * arguments now and format the request later.
+ */
+ if (ap->fp != 0) {
+ /* Note: all code paths must terminate or enable I/O events. */
+ VSTREAM *mp = vstream_memopen(ap->request, O_WRONLY);
+
+ if (attr_print(mp, ATTR_FLAG_MORE,
+ SEND_ATTR_INT(MAIL_ATTR_NREQ, command),
+ SEND_ATTR_INT(MAIL_ATTR_FLAGS, flags),
+ SEND_ATTR_STR(MAIL_ATTR_QUEUE, queue),
+ SEND_ATTR_STR(MAIL_ATTR_QUEUEID, id),
+ SEND_ATTR_STR(MAIL_ATTR_ENCODING, encoding),
+ SEND_ATTR_INT(MAIL_ATTR_SMTPUTF8, smtputf8),
+ SEND_ATTR_STR(MAIL_ATTR_SENDER, sender),
+ SEND_ATTR_STR(MAIL_ATTR_DSN_ENVID, dsn_envid),
+ SEND_ATTR_INT(MAIL_ATTR_DSN_RET, dsn_ret),
+ ATTR_TYPE_END) != 0
+ || (verp != 0
+ && attr_print(mp, ATTR_FLAG_MORE,
+ SEND_ATTR_STR(MAIL_ATTR_VERPDL, verp),
+ ATTR_TYPE_END) != 0)
+ || attr_print(mp, ATTR_FLAG_NONE,
+ ATTR_TYPE_END) != 0
+ || vstream_fclose(mp) != 0)
+ msg_panic("abounce_connect: write request to memory stream: %m");
+
+ /*
+ * Suspend until the server replies (or dies).
+ */
+ ABOUNCE_EVENT_ENABLE(vstream_fileno(ap->fp), abounce_send,
+ (void *) ap, ABOUNCE_TIMEOUT);
+ } else {
+ abounce_done(ap, -1);
+ }
+}
+
+/* abounce_flush_verp - asynchronous bounce flush */
+
+void abounce_flush_verp(int flags, const char *queue, const char *id,
+ const char *encoding, int smtputf8,
+ const char *sender, const char *dsn_envid,
+ int dsn_ret, const char *verp,
+ ABOUNCE_FN callback,
+ void *context)
+{
+ abounce_connect(MAIL_CLASS_PRIVATE, var_bounce_service,
+ BOUNCE_CMD_VERP, flags, queue, id, encoding, smtputf8,
+ sender, dsn_envid, dsn_ret, verp, callback, context);
+}
+
+/* adefer_flush_verp - asynchronous defer flush */
+
+void adefer_flush_verp(int flags, const char *queue, const char *id,
+ const char *encoding, int smtputf8,
+ const char *sender, const char *dsn_envid,
+ int dsn_ret, const char *verp,
+ ABOUNCE_FN callback, void *context)
+{
+ flags |= BOUNCE_FLAG_DELRCPT;
+ abounce_connect(MAIL_CLASS_PRIVATE, var_defer_service,
+ BOUNCE_CMD_VERP, flags, queue, id, encoding, smtputf8,
+ sender, dsn_envid, dsn_ret, verp, callback, context);
+}
+
+/* abounce_flush - asynchronous bounce flush */
+
+void abounce_flush(int flags, const char *queue, const char *id,
+ const char *encoding, int smtputf8,
+ const char *sender, const char *dsn_envid,
+ int dsn_ret, ABOUNCE_FN callback,
+ void *context)
+{
+ abounce_connect(MAIL_CLASS_PRIVATE, var_bounce_service, BOUNCE_CMD_FLUSH,
+ flags, queue, id, encoding, smtputf8, sender, dsn_envid,
+ dsn_ret, ABOUNCE_NO_VERP, callback, context);
+}
+
+/* adefer_flush - asynchronous defer flush */
+
+void adefer_flush(int flags, const char *queue, const char *id,
+ const char *encoding, int smtputf8,
+ const char *sender, const char *dsn_envid,
+ int dsn_ret, ABOUNCE_FN callback, void *context)
+{
+ flags |= BOUNCE_FLAG_DELRCPT;
+ abounce_connect(MAIL_CLASS_PRIVATE, var_defer_service, BOUNCE_CMD_FLUSH,
+ flags, queue, id, encoding, smtputf8, sender, dsn_envid,
+ dsn_ret, ABOUNCE_NO_VERP, callback, context);
+}
+
+/* adefer_warn - send copy of defer log to sender as warning bounce */
+
+void adefer_warn(int flags, const char *queue, const char *id,
+ const char *encoding, int smtputf8,
+ const char *sender, const char *dsn_envid,
+ int dsn_ret, ABOUNCE_FN callback, void *context)
+{
+ abounce_connect(MAIL_CLASS_PRIVATE, var_defer_service, BOUNCE_CMD_WARN,
+ flags, queue, id, encoding, smtputf8, sender, dsn_envid,
+ dsn_ret, ABOUNCE_NO_VERP, callback, context);
+}
+
+/* atrace_flush - asynchronous trace flush */
+
+void atrace_flush(int flags, const char *queue, const char *id,
+ const char *encoding, int smtputf8,
+ const char *sender, const char *dsn_envid,
+ int dsn_ret, ABOUNCE_FN callback, void *context)
+{
+ abounce_connect(MAIL_CLASS_PRIVATE, var_trace_service, BOUNCE_CMD_TRACE,
+ flags, queue, id, encoding, smtputf8, sender, dsn_envid,
+ dsn_ret, ABOUNCE_NO_VERP, callback, context);
+}