/*++ /* NAME /* deliver_pass 3 /* SUMMARY /* deliver request pass_through /* SYNOPSIS /* #include /* /* 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 /* /* Wietse Venema /* Google, Inc. /* 111 8th Avenue /* New York, NY 10011, USA /*--*/ /* System library. */ #include /* Utility library. */ #include #include #include #include #include /* Global library. */ #include #include #include #include #include #include #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) { if (attr_scan(stream, ATTR_FLAG_STRICT, RECV_ATTR_STREQ(MAIL_ATTR_PROTO, MAIL_ATTR_PROTO_DELIVER), ATTR_TYPE_END) != 0) { msg_warn("%s: malformed response", VSTREAM_PATH(stream)); return (-1); } return (0); } /* 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, (const 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, (const 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. */ msg_info("%s: passing <%s> to transport=%s", request->queue_id, info_log_addr_form_recipient(rcpt->address), transport); 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); }