diff options
Diffstat (limited to 'src/global/deliver_pass.c')
-rw-r--r-- | src/global/deliver_pass.c | 240 |
1 files changed, 240 insertions, 0 deletions
diff --git a/src/global/deliver_pass.c b/src/global/deliver_pass.c new file mode 100644 index 0000000..e2112ba --- /dev/null +++ b/src/global/deliver_pass.c @@ -0,0 +1,240 @@ +/*++ +/* NAME +/* deliver_pass 3 +/* SUMMARY +/* deliver request pass_through +/* SYNOPSIS +/* #include <deliver_request.h> +/* +/* int deliver_pass(class, service, request, recipient) +/* const char *class; +/* const char *service; +/* DELIVER_REQUEST *request; +/* RECIPIENT *recipient; +/* +/* int deliver_pass_all(class, service, request) +/* const char *class; +/* const char *service; +/* DELIVER_REQUEST *request; +/* DESCRIPTION +/* This module implements the client side of the `queue manager +/* to delivery agent' protocol, passing one recipient on from +/* one delivery agent to another. +/* +/* deliver_pass() delegates delivery of the named recipient. +/* +/* deliver_pass_all() delegates an entire delivery request. +/* +/* Arguments: +/* .IP class +/* Destination delivery agent service class +/* .IP service +/* String of the form \fItransport\fR:\fInexthop\fR. Either transport +/* or nexthop are optional. For details see the transport map manual page. +/* .IP request +/* Delivery request with queue file information. +/* .IP recipient +/* Recipient information. See recipient_list(3). +/* DIAGNOSTICS +/* LICENSE +/* .ad +/* .fi +/* The Secure Mailer license must be distributed with this software. +/* BUGS +/* One recipient at a time; this is OK for mailbox deliveries. +/* +/* Hop status information cannot be passed back. +/* AUTHOR(S) +/* Wietse Venema +/* IBM T.J. Watson Research +/* P.O. Box 704 +/* Yorktown Heights, NY 10598, USA +/*--*/ + +/* System library. */ + +#include <sys_defs.h> + +/* Utility library. */ + +#include <msg.h> +#include <vstring.h> +#include <vstream.h> +#include <split_at.h> +#include <mymalloc.h> + +/* Global library. */ + +#include <mail_params.h> +#include <deliver_pass.h> +#include <dsb_scan.h> +#include <defer.h> +#include <rcpt_print.h> + +#define DELIVER_PASS_DEFER 1 +#define DELIVER_PASS_UNKNOWN 2 + +/* deliver_pass_initial_reply - retrieve initial delivery process response */ + +static int deliver_pass_initial_reply(VSTREAM *stream) +{ + int stat; + + if (attr_scan(stream, ATTR_FLAG_STRICT, + RECV_ATTR_INT(MAIL_ATTR_STATUS, &stat), + ATTR_TYPE_END) != 1) { + msg_warn("%s: malformed response", VSTREAM_PATH(stream)); + stat = -1; + } + return (stat); +} + +/* deliver_pass_send_request - send delivery request to delivery process */ + +static int deliver_pass_send_request(VSTREAM *stream, DELIVER_REQUEST *request, + const char *nexthop, + RECIPIENT *rcpt) +{ + int stat; + + attr_print(stream, ATTR_FLAG_NONE, + SEND_ATTR_INT(MAIL_ATTR_FLAGS, request->flags), + SEND_ATTR_STR(MAIL_ATTR_QUEUE, request->queue_name), + SEND_ATTR_STR(MAIL_ATTR_QUEUEID, request->queue_id), + SEND_ATTR_LONG(MAIL_ATTR_OFFSET, request->data_offset), + SEND_ATTR_LONG(MAIL_ATTR_SIZE, request->data_size), + SEND_ATTR_STR(MAIL_ATTR_NEXTHOP, nexthop), + SEND_ATTR_STR(MAIL_ATTR_ENCODING, request->encoding), + SEND_ATTR_INT(MAIL_ATTR_SMTPUTF8, request->smtputf8), + SEND_ATTR_STR(MAIL_ATTR_SENDER, request->sender), + SEND_ATTR_STR(MAIL_ATTR_DSN_ENVID, request->dsn_envid), + SEND_ATTR_INT(MAIL_ATTR_DSN_RET, request->dsn_ret), + SEND_ATTR_FUNC(msg_stats_print, (void *) &request->msg_stats), + /* XXX Should be encapsulated with ATTR_TYPE_FUNC. */ + SEND_ATTR_STR(MAIL_ATTR_LOG_CLIENT_NAME, request->client_name), + SEND_ATTR_STR(MAIL_ATTR_LOG_CLIENT_ADDR, request->client_addr), + SEND_ATTR_STR(MAIL_ATTR_LOG_CLIENT_PORT, request->client_port), + SEND_ATTR_STR(MAIL_ATTR_LOG_PROTO_NAME, request->client_proto), + SEND_ATTR_STR(MAIL_ATTR_LOG_HELO_NAME, request->client_helo), + /* XXX Should be encapsulated with ATTR_TYPE_FUNC. */ + SEND_ATTR_STR(MAIL_ATTR_SASL_METHOD, request->sasl_method), + SEND_ATTR_STR(MAIL_ATTR_SASL_USERNAME, request->sasl_username), + SEND_ATTR_STR(MAIL_ATTR_SASL_SENDER, request->sasl_sender), + /* XXX Ditto if we want to pass TLS certificate info. */ + SEND_ATTR_STR(MAIL_ATTR_LOG_IDENT, request->log_ident), + SEND_ATTR_STR(MAIL_ATTR_RWR_CONTEXT, request->rewrite_context), + SEND_ATTR_INT(MAIL_ATTR_RCPT_COUNT, 1), + ATTR_TYPE_END); + attr_print(stream, ATTR_FLAG_NONE, + SEND_ATTR_FUNC(rcpt_print, (void *) rcpt), + ATTR_TYPE_END); + + if (vstream_fflush(stream)) { + msg_warn("%s: bad write: %m", VSTREAM_PATH(stream)); + stat = -1; + } else { + stat = 0; + } + return (stat); +} + +/* deliver_pass_final_reply - retrieve final delivery status response */ + +static int deliver_pass_final_reply(VSTREAM *stream, DSN_BUF *dsb) +{ + int stat; + + if (attr_scan(stream, ATTR_FLAG_STRICT, + RECV_ATTR_FUNC(dsb_scan, (void *) dsb), + RECV_ATTR_INT(MAIL_ATTR_STATUS, &stat), + ATTR_TYPE_END) != 2) { + msg_warn("%s: malformed response", VSTREAM_PATH(stream)); + return (DELIVER_PASS_UNKNOWN); + } else { + return (stat ? DELIVER_PASS_DEFER : 0); + } +} + +/* deliver_pass - deliver one per-site queue entry */ + +int deliver_pass(const char *class, const char *service, + DELIVER_REQUEST *request, + RECIPIENT *rcpt) +{ + VSTREAM *stream; + DSN_BUF *dsb; + DSN dsn; + int status; + char *saved_service; + char *transport; + char *nexthop; + + /* + * Parse service into transport:nexthop form, and allow for omission of + * optional fields + */ + transport = saved_service = mystrdup(service); + if ((nexthop = split_at(saved_service, ':')) == 0 || *nexthop == 0) + nexthop = request->nexthop; + if (*transport == 0) + msg_fatal("missing transport name in \"%s\"", service); + + /* + * Initialize. + */ + stream = mail_connect_wait(class, transport); + dsb = dsb_create(); + + /* + * Get the delivery process initial response. Send the queue file info + * and recipient info to the delivery process. Retrieve the delivery + * agent status report. The numerical status code indicates if delivery + * should be tried again. The reason text is sent only when a destination + * should be avoided for a while, so that the queue manager can log why + * it does not even try to schedule delivery to the affected recipients. + * XXX Can't pass back hop status info because the problem is with a + * different transport. + */ + if (deliver_pass_initial_reply(stream) != 0 + || deliver_pass_send_request(stream, request, nexthop, rcpt) != 0) { + (void) DSN_SIMPLE(&dsn, "4.3.0", "mail transport unavailable"); + status = defer_append(DEL_REQ_TRACE_FLAGS(request->flags), + request->queue_id, &request->msg_stats, + rcpt, "none", &dsn); + } else if ((status = deliver_pass_final_reply(stream, dsb)) + == DELIVER_PASS_UNKNOWN) { + (void) DSN_SIMPLE(&dsn, "4.3.0", "unknown mail transport error"); + status = defer_append(DEL_REQ_TRACE_FLAGS(request->flags), + request->queue_id, &request->msg_stats, + rcpt, "none", &dsn); + } + + /* + * Clean up. + */ + vstream_fclose(stream); + dsb_free(dsb); + myfree(saved_service); + + return (status); +} + +/* deliver_pass_all - pass entire delivery request */ + +int deliver_pass_all(const char *class, const char *service, + DELIVER_REQUEST *request) +{ + RECIPIENT_LIST *list; + RECIPIENT *rcpt; + int status = 0; + + /* + * XXX We should find out if the target transport can handle + * multi-recipient requests. Unfortunately such code is hard to test, + * rarely used, and therefore will be buggy. + */ + list = &request->rcpt_list; + for (rcpt = list->info; rcpt < list->info + list->len; rcpt++) + status |= deliver_pass(class, service, request, rcpt); + return (status); +} |