示例#1
0
static int eval_command_status(int command_status, char *service,
			          DELIVER_REQUEST *request, PIPE_ATTR *attr,
			               DSN_BUF *why)
{
    RECIPIENT *rcpt;
    int     status;
    int     result = 0;
    int     n;

    /*
     * Depending on the result, bounce or defer the message, and mark the
     * recipient as done where appropriate.
     */
    switch (command_status) {
    case PIPE_STAT_OK:
	dsb_update(why, "2.0.0", (attr->flags & PIPE_OPT_FINAL_DELIVERY) ?
		   "delivered" : "relayed", DSB_SKIP_RMTA, DSB_SKIP_REPLY,
		   "delivered via %s service", service);
	(void) DSN_FROM_DSN_BUF(why);
	for (n = 0; n < request->rcpt_list.len; n++) {
	    rcpt = request->rcpt_list.info + n;
	    status = sent(DEL_REQ_TRACE_FLAGS(request->flags),
			  request->queue_id, &request->msg_stats, rcpt,
			  service, &why->dsn);
	    if (status == 0 && (request->flags & DEL_REQ_FLAG_SUCCESS))
		deliver_completed(request->fp, rcpt->offset);
	    result |= status;
	}
	break;
    case PIPE_STAT_BOUNCE:
    case PIPE_STAT_DEFER:
	(void) DSN_FROM_DSN_BUF(why);
	for (n = 0; n < request->rcpt_list.len; n++) {
	    rcpt = request->rcpt_list.info + n;
	    /* XXX Maybe encapsulate this with ndr_append(). */
	    status = (STR(why->status)[0] != '4' ?
		      bounce_append : defer_append)
		(DEL_REQ_TRACE_FLAGS(request->flags),
		 request->queue_id,
		 &request->msg_stats, rcpt,
		 service, &why->dsn);
	    if (status == 0)
		deliver_completed(request->fp, rcpt->offset);
	    result |= status;
	}
	break;
    case PIPE_STAT_CORRUPT:
	/* XXX DSN should we send something? */
	result |= DEL_STAT_DEFER;
	break;
    default:
	msg_panic("eval_command_status: bad status %d", command_status);
	/* NOTREACHED */
    }

    return (result);
}
示例#2
0
static int deliver_message(DELIVER_REQUEST *request)
{
    const char *myname = "deliver_message";
    VSTREAM *src;
    int     result = 0;
    int     status;
    RECIPIENT *rcpt;
    int     nrcpt;
    DSN_SPLIT dp;
    DSN     dsn;

    if (msg_verbose)
	msg_info("deliver_message: from %s", request->sender);

    /*
     * Sanity checks.
     */
    if (request->nexthop[0] == 0)
	msg_fatal("empty nexthop hostname");
    if (request->rcpt_list.len <= 0)
	msg_fatal("recipient count: %d", request->rcpt_list.len);

    /*
     * Open the queue file. Opening the file can fail for a variety of
     * reasons, such as the system running out of resources. Instead of
     * throwing away mail, we're raising a fatal error which forces the mail
     * system to back off, and retry later.
     */
    src = mail_queue_open(request->queue_name, request->queue_id,
			  O_RDWR, 0);
    if (src == 0)
	msg_fatal("%s: open %s %s: %m", myname,
		  request->queue_name, request->queue_id);
    if (msg_verbose)
	msg_info("%s: file %s", myname, VSTREAM_PATH(src));

    /*
     * Discard all recipients.
     */
#define BOUNCE_FLAGS(request) DEL_REQ_TRACE_FLAGS(request->flags)

    dsn_split(&dp, "2.0.0", request->nexthop);
    (void) DSN_SIMPLE(&dsn, DSN_STATUS(dp.dsn), dp.text);
    for (nrcpt = 0; nrcpt < request->rcpt_list.len; nrcpt++) {
	rcpt = request->rcpt_list.info + nrcpt;
	status = sent(BOUNCE_FLAGS(request), request->queue_id,
		      &request->msg_stats, rcpt, "none", &dsn);
	if (status == 0 && (request->flags & DEL_REQ_FLAG_SUCCESS))
	    deliver_completed(src, rcpt->offset);
	result |= status;
    }

    /*
     * Clean up.
     */
    if (vstream_fclose(src))
	msg_warn("close %s %s: %m", request->queue_name, request->queue_id);

    return (result);
}
示例#3
0
void    smtp_rcpt_fail(SMTP_STATE *state, RECIPIENT *rcpt, const char *mta_name,
		               SMTP_RESP *resp, const char *format,...)
{
    DELIVER_REQUEST *request = state->request;
    SMTP_SESSION *session = state->session;
    DSN_BUF *why = state->why;
    int     status;
    int     soft_error;
    int     soft_bounce_error;
    va_list ap;

    /*
     * Sanity check.
     */
    if (SMTP_RCPT_ISMARKED(rcpt))
	msg_panic("smtp_rcpt_fail: recipient <%s> is marked", rcpt->address);

    /*
     * Initialize.
     */
    va_start(ap, format);
    vsmtp_fill_dsn(state, mta_name, resp->dsn, resp->str, format, ap);
    va_end(ap);
    soft_error = STR(why->status)[0] == '4';
    soft_bounce_error = (STR(why->status)[0] == '5' && var_soft_bounce);

    if (state->session && mta_name)
	smtp_check_code(state->session, resp->code);

    /*
     * Don't defer this recipient record just yet when this error qualifies
     * for trying other mail servers. Just log something informative to show
     * why we're skipping this recipient now.
     */
    if ((soft_error || soft_bounce_error)
	&& (state->misc_flags & SMTP_MISC_FLAG_FINAL_SERVER) == 0) {
	msg_info("%s: %s", request->queue_id, STR(why->reason));
	SMTP_RCPT_KEEP(state, rcpt);
    }

    /*
     * Defer or bounce this recipient, and delete from the delivery request.
     * If the bounce fails, defer instead and do not qualify the recipient
     * for delivery to a backup server.
     * 
     * Note: we may still make an SMTP connection to deliver other recipients
     * that did qualify for delivery to a backup server.
     */
    else {
	(void) DSN_FROM_DSN_BUF(state->why);
	status = (soft_error ? defer_append : bounce_append)
	    (DEL_REQ_TRACE_FLAGS(request->flags), request->queue_id,
	     &request->msg_stats, rcpt,
	     session ? session->namaddrport : "none", &why->dsn);
	if (status == 0)
	    deliver_completed(state->src, rcpt->offset);
	SMTP_RCPT_DROP(state, rcpt);
	state->status |= status;
    }
}
示例#4
0
文件: error.c 项目: TonyChengTW/Rmail
static int deliver_message(DELIVER_REQUEST *request)
{
    char   *myname = "deliver_message";
    VSTREAM *src;
    int     result = 0;
    int     status;
    RECIPIENT *rcpt;
    int     nrcpt;

    if (msg_verbose)
	msg_info("deliver_message: from %s", request->sender);

    /*
     * Sanity checks.
     */
    if (request->nexthop[0] == 0)
	msg_fatal("empty nexthop hostname");
    if (request->rcpt_list.len <= 0)
	msg_fatal("recipient count: %d", request->rcpt_list.len);

    /*
     * Open the queue file. Opening the file can fail for a variety of
     * reasons, such as the system running out of resources. Instead of
     * throwing away mail, we're raising a fatal error which forces the mail
     * system to back off, and retry later.
     */
    src = mail_queue_open(request->queue_name, request->queue_id,
			  O_RDWR, 0);
    if (src == 0)
	msg_fatal("%s: open %s %s: %m", myname,
		  request->queue_name, request->queue_id);
    if (msg_verbose)
	msg_info("%s: file %s", myname, VSTREAM_PATH(src));

    /*
     * Bounce all recipients.
     */
#define BOUNCE_FLAGS(request) DEL_REQ_TRACE_FLAGS(request->flags)

    for (nrcpt = 0; nrcpt < request->rcpt_list.len; nrcpt++) {
	rcpt = request->rcpt_list.info + nrcpt;
	if (rcpt->offset >= 0) {
	    status = bounce_append(BOUNCE_FLAGS(request), request->queue_id,
				   rcpt->orig_addr, rcpt->address,
				rcpt->offset, "none", request->arrival_time,
				   "%s", request->nexthop);
	    if (status == 0)
		deliver_completed(src, rcpt->offset);
	    result |= status;
	}
    }

    /*
     * Clean up.
     */
    if (vstream_fclose(src))
	msg_warn("close %s %s: %m", request->queue_name, request->queue_id);

    return (result);
}
示例#5
0
static int local_deliver(DELIVER_REQUEST *rqst, char *service)
{
    const char *myname = "local_deliver";
    RECIPIENT *rcpt_end = rqst->rcpt_list.info + rqst->rcpt_list.len;
    RECIPIENT *rcpt;
    int     rcpt_stat;
    int     msg_stat;
    LOCAL_STATE state;
    USER_ATTR usr_attr;

    if (msg_verbose)
	msg_info("local_deliver: %s from %s", rqst->queue_id, rqst->sender);

    /*
     * Initialize the delivery attributes that are not recipient specific.
     */
    state.level = 0;
    deliver_attr_init(&state.msg_attr);
    state.msg_attr.queue_name = rqst->queue_name;
    state.msg_attr.queue_id = rqst->queue_id;
    state.msg_attr.fp = rqst->fp;
    state.msg_attr.offset = rqst->data_offset;
    state.msg_attr.sender = rqst->sender;
    state.msg_attr.dsn_envid = rqst->dsn_envid;
    state.msg_attr.dsn_ret = rqst->dsn_ret;
    state.msg_attr.relay = service;
    state.msg_attr.msg_stats = rqst->msg_stats;
    RESET_USER_ATTR(usr_attr, state.level);
    state.request = rqst;

    /*
     * Iterate over each recipient named in the delivery request. When the
     * mail delivery status for a given recipient is definite (i.e. bounced
     * or delivered), update the message queue file and cross off the
     * recipient. Update the per-message delivery status.
     */
    for (msg_stat = 0, rcpt = rqst->rcpt_list.info; rcpt < rcpt_end; rcpt++) {
	state.msg_attr.rcpt = *rcpt;
	rcpt_stat = deliver_recipient(state, usr_attr);
	if (rcpt_stat == 0 && (rqst->flags & DEL_REQ_FLAG_SUCCESS))
	    deliver_completed(state.msg_attr.fp, rcpt->offset);
	msg_stat |= rcpt_stat;
    }

    deliver_attr_free(&state.msg_attr);
    return (msg_stat);
}
示例#6
0
void    smtp_rcpt_done(SMTP_STATE *state, SMTP_RESP *resp, RECIPIENT *rcpt)
{
    DELIVER_REQUEST *request = state->request;
    SMTP_SESSION *session = state->session;
    SMTP_ITERATOR *iter = state->iterator;
    DSN_BUF *why = state->why;
    const char *dsn_action = "relayed";
    int     status;

    /*
     * Assume this was intermediate delivery when the server announced DSN
     * support, and don't send a DSN "SUCCESS" notification.
     */
    if (session->features & SMTP_FEATURE_DSN)
	rcpt->dsn_notify &= ~DSN_NOTIFY_SUCCESS;

    /*
     * Assume this was final delivery when the LMTP server announced no DSN
     * support. In backwards compatibility mode, send a "relayed" instead of
     * a "delivered" DSN "SUCCESS" notification. Do not attempt to "simplify"
     * the expression. The redundancy is for clarity. It is trivially
     * eliminated by the compiler. There is no need to sacrifice clarity for
     * the sake of "performance".
     */
    if ((session->features & SMTP_FEATURE_DSN) == 0
	&& !smtp_mode
	&& var_lmtp_assume_final != 0)
	dsn_action = "delivered";

    /*
     * Report success and delete the recipient from the delivery request.
     * Defer if the success can't be reported.
     * 
     * Note: the DSN action is ignored in case of address probes.
     */
    dsb_update(why, resp->dsn, dsn_action, DSB_MTYPE_DNS, STR(iter->host),
	       DSB_DTYPE_SMTP, resp->str, "%s", resp->str);

    status = sent(DEL_REQ_TRACE_FLAGS(request->flags),
		  request->queue_id, &request->msg_stats, rcpt,
		  session->namaddrport, DSN_FROM_DSN_BUF(why));
    if (status == 0)
	if (request->flags & DEL_REQ_FLAG_SUCCESS)
	    deliver_completed(state->src, rcpt->offset);
    SMTP_RCPT_DROP(state, rcpt);
    state->status |= status;
}
示例#7
0
static int local_deliver(DELIVER_REQUEST *rqst, char *service)
{
    const char *myname = "local_deliver";
    RECIPIENT *rcpt_end = rqst->rcpt_list.info + rqst->rcpt_list.len;
    RECIPIENT *rcpt;
    int     rcpt_stat;
    int     msg_stat;
    LOCAL_STATE state;
    USER_ATTR usr_attr;

    if (msg_verbose)
	msg_info("local_deliver: %s from %s", rqst->queue_id, rqst->sender);

    /*
     * Initialize the delivery attributes that are not recipient specific.
     * While messages are being delivered and while aliases or forward files
     * are being expanded, this attribute list is being changed constantly.
     * For this reason, the list is passed on by value (except when it is
     * being initialized :-), so that there is no need to undo attribute
     * changes made by lower-level routines. The alias/include/forward
     * expansion attribute list is part of a tree with self and parent
     * references (see the EXPAND_ATTR definitions). The user-specific
     * attributes are security sensitive, and are therefore kept separate.
     * All this results in a noticeable level of clumsiness, but passing
     * things around by value gives good protection against accidental change
     * by subroutines.
     */
    state.level = 0;
    deliver_attr_init(&state.msg_attr);
    state.msg_attr.queue_name = rqst->queue_name;
    state.msg_attr.queue_id = rqst->queue_id;
    state.msg_attr.fp = rqst->fp;
    state.msg_attr.offset = rqst->data_offset;
    state.msg_attr.encoding = rqst->encoding;
    state.msg_attr.sender = rqst->sender;
    state.msg_attr.dsn_envid = rqst->dsn_envid;
    state.msg_attr.dsn_ret = rqst->dsn_ret;
    state.msg_attr.relay = service;
    state.msg_attr.msg_stats = rqst->msg_stats;
    state.msg_attr.request = rqst;
    RESET_OWNER_ATTR(state.msg_attr, state.level);
    RESET_USER_ATTR(usr_attr, state.level);
    state.loop_info = delivered_hdr_init(rqst->fp, rqst->data_offset,
					 FOLD_ADDR_ALL);
    state.request = rqst;

    /*
     * Iterate over each recipient named in the delivery request. When the
     * mail delivery status for a given recipient is definite (i.e. bounced
     * or delivered), update the message queue file and cross off the
     * recipient. Update the per-message delivery status.
     */
    for (msg_stat = 0, rcpt = rqst->rcpt_list.info; rcpt < rcpt_end; rcpt++) {
	state.dup_filter = been_here_init(var_dup_filter_limit, BH_FLAG_FOLD);
	forward_init();
	state.msg_attr.rcpt = *rcpt;
	rcpt_stat = deliver_recipient(state, usr_attr);
	rcpt_stat |= forward_finish(rqst, state.msg_attr, rcpt_stat);
	if (rcpt_stat == 0 && (rqst->flags & DEL_REQ_FLAG_SUCCESS))
	    deliver_completed(state.msg_attr.fp, rcpt->offset);
	been_here_free(state.dup_filter);
	msg_stat |= rcpt_stat;
    }

    /*
     * Clean up.
     */
    delivered_hdr_free(state.loop_info);
    deliver_attr_free(&state.msg_attr);

    return (msg_stat);
}
示例#8
0
static void qmgr_message_resolve(QMGR_MESSAGE *message)
{
    static ARGV *defer_xport_argv;
    RECIPIENT_LIST list = message->rcpt_list;
    RECIPIENT *recipient;
    QMGR_TRANSPORT *transport = 0;
    QMGR_QUEUE *queue = 0;
    RESOLVE_REPLY reply;
    VSTRING *queue_name;
    char   *at;
    char  **cpp;
    char   *nexthop;
    ssize_t len;
    int     status;
    DSN     dsn;
    MSG_STATS stats;
    DSN    *saved_dsn;

#define STREQ(x,y)	(strcmp(x,y) == 0)
#define STR		vstring_str
#define LEN		VSTRING_LEN

    resolve_clnt_init(&reply);
    queue_name = vstring_alloc(1);
    for (recipient = list.info; recipient < list.info + list.len; recipient++) {

	/*
	 * Redirect overrides all else. But only once (per entire message).
	 * For consistency with the remainder of Postfix, rewrite the address
	 * to canonical form before resolving it.
	 */
	if (message->redirect_addr) {
	    if (recipient > list.info) {
		recipient->u.queue = 0;
		continue;
	    }
	    message->rcpt_offset = 0;
	    message->rcpt_unread = 0;

	    rewrite_clnt_internal(REWRITE_CANON, message->redirect_addr,
				  reply.recipient);
	    RECIPIENT_UPDATE(recipient->address, STR(reply.recipient));
	    if (qmgr_resolve_one(message, recipient,
				 recipient->address, &reply) < 0)
		continue;
	    if (!STREQ(recipient->address, STR(reply.recipient)))
		RECIPIENT_UPDATE(recipient->address, STR(reply.recipient));
	}

	/*
	 * Content filtering overrides the address resolver.
	 * 
	 * XXX Bypass content_filter inspection for user-generated probes
	 * (sendmail -bv). MTA-generated probes never have the "please filter
	 * me" bits turned on, but we handle them here anyway for the sake of
	 * future proofing.
	 */
#define FILTER_WITHOUT_NEXTHOP(filter, next) \
	(((next) = split_at((filter), ':')) == 0 || *(next) == 0)

#define RCPT_WITHOUT_DOMAIN(rcpt, next) \
	((next = strrchr(rcpt, '@')) == 0 || *++(next) == 0)

	else if (message->filter_xport
		 && (message->tflags & DEL_REQ_TRACE_ONLY_MASK) == 0) {
	    reply.flags = 0;
	    vstring_strcpy(reply.transport, message->filter_xport);
	    if (FILTER_WITHOUT_NEXTHOP(STR(reply.transport), nexthop)
		&& *(nexthop = var_def_filter_nexthop) == 0
		&& RCPT_WITHOUT_DOMAIN(recipient->address, nexthop))
		nexthop = var_myhostname;
	    vstring_strcpy(reply.nexthop, nexthop);
	    vstring_strcpy(reply.recipient, recipient->address);
	}

	/*
	 * Resolve the destination to (transport, nexthop, address). The
	 * result address may differ from the one specified by the sender.
	 */
	else {
	    if (qmgr_resolve_one(message, recipient,
				 recipient->address, &reply) < 0)
		continue;
	    if (!STREQ(recipient->address, STR(reply.recipient)))
		RECIPIENT_UPDATE(recipient->address, STR(reply.recipient));
	}

	/*
	 * Bounce null recipients. This should never happen, but is most
	 * likely the result of a fault in a different program, so aborting
	 * the queue manager process does not help.
	 */
	if (recipient->address[0] == 0) {
	    QMGR_REDIRECT(&reply, MAIL_SERVICE_ERROR,
			  "5.1.3 null recipient address");
	}

	/*
	 * Discard mail to the local double bounce address here, so this
	 * system can run without a local delivery agent. They'd still have
	 * to configure something for mail directed to the local postmaster,
	 * though, but that is an RFC requirement anyway.
	 * 
	 * XXX This lookup should be done in the resolver, and the mail should
	 * be directed to a general-purpose null delivery agent.
	 */
	if (reply.flags & RESOLVE_CLASS_LOCAL) {
	    at = strrchr(STR(reply.recipient), '@');
	    len = (at ? (at - STR(reply.recipient))
		   : strlen(STR(reply.recipient)));
	    if (strncasecmp(STR(reply.recipient), var_double_bounce_sender,
			    len) == 0
		&& !var_double_bounce_sender[len]) {
		status = sent(message->tflags, message->queue_id,
			      QMGR_MSG_STATS(&stats, message), recipient,
			      "none", DSN_SIMPLE(&dsn, "2.0.0",
			"undeliverable postmaster notification discarded"));
		if (status == 0) {
		    deliver_completed(message->fp, recipient->offset);
#if 0
		    /* It's the default verification probe sender address. */
		    msg_warn("%s: undeliverable postmaster notification discarded",
			     message->queue_id);
#endif
		} else
		    message->flags |= status;
		continue;
	    }
	}

	/*
	 * Optionally defer deliveries over specific transports, unless the
	 * restriction is lifted temporarily.
	 */
	if (*var_defer_xports && (message->qflags & QMGR_FLUSH_DFXP) == 0) {
	    if (defer_xport_argv == 0)
		defer_xport_argv = argv_split(var_defer_xports, CHARS_COMMA_SP);
	    for (cpp = defer_xport_argv->argv; *cpp; cpp++)
		if (strcmp(*cpp, STR(reply.transport)) == 0)
		    break;
	    if (*cpp) {
		QMGR_REDIRECT(&reply, MAIL_SERVICE_RETRY,
			      "4.3.2 deferred transport");
	    }
	}

	/*
	 * Look up or instantiate the proper transport.
	 */
	if (transport == 0 || !STREQ(transport->name, STR(reply.transport))) {
	    if ((transport = qmgr_transport_find(STR(reply.transport))) == 0)
		transport = qmgr_transport_create(STR(reply.transport));
	    queue = 0;
	}

	/*
	 * This message is being flushed. If need-be unthrottle the
	 * transport.
	 */
	if ((message->qflags & QMGR_FLUSH_EACH) != 0
	    && QMGR_TRANSPORT_THROTTLED(transport))
	    qmgr_transport_unthrottle(transport);

	/*
	 * This transport is dead. Defer delivery to this recipient.
	 */
	if (QMGR_TRANSPORT_THROTTLED(transport)) {
	    saved_dsn = transport->dsn;
	    if ((transport = qmgr_error_transport(MAIL_SERVICE_RETRY)) != 0) {
		nexthop = qmgr_error_nexthop(saved_dsn);
		vstring_strcpy(reply.nexthop, nexthop);
		myfree(nexthop);
		queue = 0;
	    } else {
		qmgr_defer_recipient(message, recipient, saved_dsn);
		continue;
	    }
	}

	/*
	 * The nexthop destination provides the default name for the
	 * per-destination queue. When the delivery agent accepts only one
	 * recipient per delivery, give each recipient its own queue, so that
	 * deliveries to different recipients of the same message can happen
	 * in parallel, and so that we can enforce per-recipient concurrency
	 * limits and prevent one recipient from tying up all the delivery
	 * agent resources. We use recipient@nexthop as queue name rather
	 * than the actual recipient domain name, so that one recipient in
	 * multiple equivalent domains cannot evade the per-recipient
	 * concurrency limit. Split the address on the recipient delimiter if
	 * one is defined, so that extended addresses don't get extra
	 * delivery slots.
	 * 
	 * Fold the result to lower case so that we don't have multiple queues
	 * for the same name.
	 * 
	 * Important! All recipients in a queue must have the same nexthop
	 * value. It is OK to have multiple queues with the same nexthop
	 * value, but only when those queues are named after recipients.
	 * 
	 * The single-recipient code below was written for local(8) like
	 * delivery agents, and assumes that all domains that deliver to the
	 * same (transport + nexthop) are aliases for $nexthop. Delivery
	 * concurrency is changed from per-domain into per-recipient, by
	 * changing the queue name from nexthop into localpart@nexthop.
	 * 
	 * XXX This assumption is incorrect when different destinations share
	 * the same (transport + nexthop). In reality, such transports are
	 * rarely configured to use single-recipient deliveries. The fix is
	 * to decouple the per-destination recipient limit from the
	 * per-destination concurrency.
	 */
	vstring_strcpy(queue_name, STR(reply.nexthop));
	if (strcmp(transport->name, MAIL_SERVICE_ERROR) != 0
	    && strcmp(transport->name, MAIL_SERVICE_RETRY) != 0
	    && transport->recipient_limit == 1) {
	    /* Copy the recipient localpart. */
	    at = strrchr(STR(reply.recipient), '@');
	    len = (at ? (at - STR(reply.recipient))
		   : strlen(STR(reply.recipient)));
	    vstring_strncpy(queue_name, STR(reply.recipient), len);
	    /* Remove the address extension from the recipient localpart. */
	    if (*var_rcpt_delim && split_addr(STR(queue_name), var_rcpt_delim))
		vstring_truncate(queue_name, strlen(STR(queue_name)));
	    /* Assume the recipient domain is equivalent to nexthop. */
	    vstring_sprintf_append(queue_name, "@%s", STR(reply.nexthop));
	}
	lowercase(STR(queue_name));

	/*
	 * This transport is alive. Find or instantiate a queue for this
	 * recipient.
	 */
	if (queue == 0 || !STREQ(queue->name, STR(queue_name))) {
	    if ((queue = qmgr_queue_find(transport, STR(queue_name))) == 0)
		queue = qmgr_queue_create(transport, STR(queue_name),
					  STR(reply.nexthop));
	}

	/*
	 * This message is being flushed. If need-be unthrottle the queue.
	 */
	if ((message->qflags & QMGR_FLUSH_EACH) != 0
	    && QMGR_QUEUE_THROTTLED(queue))
	    qmgr_queue_unthrottle(queue);

	/*
	 * This queue is dead. Defer delivery to this recipient.
	 */
	if (QMGR_QUEUE_THROTTLED(queue)) {
	    saved_dsn = queue->dsn;
	    if ((queue = qmgr_error_queue(MAIL_SERVICE_RETRY, saved_dsn)) == 0) {
		qmgr_defer_recipient(message, recipient, saved_dsn);
		continue;
	    }
	}

	/*
	 * This queue is alive. Bind this recipient to this queue instance.
	 */
	recipient->u.queue = queue;
    }
    resolve_clnt_free(&reply);
    vstring_free(queue_name);
}
示例#9
0
static int smtp_bulk_fail(SMTP_STATE *state, int throttle_queue)
{
    DELIVER_REQUEST *request = state->request;
    SMTP_SESSION *session = state->session;
    DSN_BUF *why = state->why;
    RECIPIENT *rcpt;
    int     status;
    int     soft_error = (STR(why->status)[0] == '4');
    int     soft_bounce_error = (STR(why->status)[0] == '5' && var_soft_bounce);
    int     nrcpt;

    /*
     * Don't defer the recipients just yet when this error qualifies them for
     * delivery to a backup server. Just log something informative to show
     * why we're skipping this host.
     */
    if ((soft_error || soft_bounce_error)
	&& (state->misc_flags & SMTP_MISC_FLAG_FINAL_SERVER) == 0) {
	msg_info("%s: %s", request->queue_id, STR(why->reason));
	for (nrcpt = 0; nrcpt < SMTP_RCPT_LEFT(state); nrcpt++) {
	    rcpt = request->rcpt_list.info + nrcpt;
	    if (SMTP_RCPT_ISMARKED(rcpt))
		continue;
	    SMTP_RCPT_KEEP(state, rcpt);
	}
    }

    /*
     * Defer or bounce all the remaining recipients, and delete them from the
     * delivery request. If a bounce fails, defer instead and do not qualify
     * the recipient for delivery to a backup server.
     */
    else {

	/*
	 * If we are still in the connection set-up phase, update the set-up
	 * completion time here, otherwise the time spent in set-up latency
	 * will be attributed as message transfer latency.
	 * 
	 * All remaining recipients have failed at this point, so we update the
	 * delivery completion time stamp so that multiple recipient status
	 * records show the same delay values.
	 */
	if (request->msg_stats.conn_setup_done.tv_sec == 0) {
	    GETTIMEOFDAY(&request->msg_stats.conn_setup_done);
	    request->msg_stats.deliver_done =
		request->msg_stats.conn_setup_done;
	} else
	    GETTIMEOFDAY(&request->msg_stats.deliver_done);

	(void) DSN_FROM_DSN_BUF(why);
	for (nrcpt = 0; nrcpt < SMTP_RCPT_LEFT(state); nrcpt++) {
	    rcpt = request->rcpt_list.info + nrcpt;
	    if (SMTP_RCPT_ISMARKED(rcpt))
		continue;
	    status = (soft_error ? defer_append : bounce_append)
		(DEL_REQ_TRACE_FLAGS(request->flags), request->queue_id,
		 &request->msg_stats, rcpt,
		 session ? session->namaddrport : "none", &why->dsn);
	    if (status == 0)
		deliver_completed(state->src, rcpt->offset);
	    SMTP_RCPT_DROP(state, rcpt);
	    state->status |= status;
	}
	if ((state->misc_flags & SMTP_MISC_FLAG_COMPLETE_SESSION) == 0
	    && throttle_queue && (soft_error || soft_bounce_error)
	    && request->hop_status == 0)
	    request->hop_status = DSN_COPY(&why->dsn);
    }

    /*
     * Don't cache this session. We can't talk to this server.
     */
    if (throttle_queue && session)
	DONT_CACHE_BAD_SESSION;

    return (-1);
}
示例#10
0
static int deliver_message(DELIVER_REQUEST *request, char *service, char **argv)
{
    const char *myname = "deliver_message";
    static PIPE_PARAMS conf;
    static PIPE_ATTR attr;
    RECIPIENT_LIST *rcpt_list = &request->rcpt_list;
    DSN_BUF *why = dsb_create();
    VSTRING *buf;
    ARGV   *expanded_argv = 0;
    int     deliver_status;
    int     command_status;
    ARGV   *export_env;
    const char *sender;

#define DELIVER_MSG_CLEANUP() { \
	dsb_free(why); \
	if (expanded_argv) argv_free(expanded_argv); \
    }

    if (msg_verbose)
	msg_info("%s: from <%s>", myname, request->sender);

    /*
     * Sanity checks. The get_service_params() and get_service_attr()
     * routines also do some sanity checks. Look up service attributes and
     * config information only once. This is safe since the information comes
     * from a trusted source, not from the delivery request.
     */
    if (request->nexthop[0] == 0)
	msg_fatal("empty nexthop hostname");
    if (rcpt_list->len <= 0)
	msg_fatal("recipient count: %d", rcpt_list->len);
    if (attr.command == 0) {
	get_service_params(&conf, service);
	get_service_attr(&attr, argv);
    }

    /*
     * The D flag cannot be specified for multi-recipient deliveries.
     */
    if ((attr.flags & MAIL_COPY_DELIVERED) && (rcpt_list->len > 1)) {
	dsb_simple(why, "4.3.5", "mail system configuration error");
	deliver_status = eval_command_status(PIPE_STAT_DEFER, service,
					     request, &attr, why);
	msg_warn("pipe flag `D' requires %s_destination_recipient_limit = 1",
		 service);
	DELIVER_MSG_CLEANUP();
	return (deliver_status);
    }

    /*
     * The O flag cannot be specified for multi-recipient deliveries.
     */
    if ((attr.flags & MAIL_COPY_ORIG_RCPT) && (rcpt_list->len > 1)) {
	dsb_simple(why, "4.3.5", "mail system configuration error");
	deliver_status = eval_command_status(PIPE_STAT_DEFER, service,
					     request, &attr, why);
	msg_warn("pipe flag `O' requires %s_destination_recipient_limit = 1",
		 service);
	DELIVER_MSG_CLEANUP();
	return (deliver_status);
    }

    /*
     * Check that this agent accepts messages this large.
     */
    if (attr.size_limit != 0 && request->data_size > attr.size_limit) {
	if (msg_verbose)
	    msg_info("%s: too big: size_limit = %ld, request->data_size = %ld",
		     myname, (long) attr.size_limit, request->data_size);
	dsb_simple(why, "5.2.3", "message too large");
	deliver_status = eval_command_status(PIPE_STAT_BOUNCE, service,
					     request, &attr, why);
	DELIVER_MSG_CLEANUP();
	return (deliver_status);
    }

    /*
     * Don't deliver a trace-only request.
     */
    if (DEL_REQ_TRACE_ONLY(request->flags)) {
	RECIPIENT *rcpt;
	int     status;
	int     n;

	deliver_status = 0;
	dsb_simple(why, "2.0.0", "delivers to command: %s", attr.command[0]);
	(void) DSN_FROM_DSN_BUF(why);
	for (n = 0; n < request->rcpt_list.len; n++) {
	    rcpt = request->rcpt_list.info + n;
	    status = sent(DEL_REQ_TRACE_FLAGS(request->flags),
			  request->queue_id, &request->msg_stats,
			  rcpt, service, &why->dsn);
	    if (status == 0 && (request->flags & DEL_REQ_FLAG_SUCCESS))
		deliver_completed(request->fp, rcpt->offset);
	    deliver_status |= status;
	}
	DELIVER_MSG_CLEANUP();
	return (deliver_status);
    }

    /*
     * Report mail delivery loops. By definition, this requires
     * single-recipient delivery. Don't silently lose recipients.
     */
    if (attr.flags & MAIL_COPY_DELIVERED) {
	DELIVERED_HDR_INFO *info;
	RECIPIENT *rcpt;
	int     loop_found;

	if (request->rcpt_list.len > 1)
	    msg_panic("%s: delivered-to enabled with multi-recipient request",
		      myname);
	info = delivered_hdr_init(request->fp, request->data_offset,
				  FOLD_ADDR_ALL);
	rcpt = request->rcpt_list.info;
	loop_found = delivered_hdr_find(info, rcpt->address);
	delivered_hdr_free(info);
	if (loop_found) {
	    dsb_simple(why, "5.4.6", "mail forwarding loop for %s",
		       rcpt->address);
	    deliver_status = eval_command_status(PIPE_STAT_BOUNCE, service,
						 request, &attr, why);
	    DELIVER_MSG_CLEANUP();
	    return (deliver_status);
	}
    }

    /*
     * Deliver. Set the nexthop and sender variables, and expand the command
     * argument vector. Recipients will be expanded on the fly. XXX Rewrite
     * envelope and header addresses according to transport-specific
     * rewriting rules.
     */
    if (vstream_fseek(request->fp, request->data_offset, SEEK_SET) < 0)
	msg_fatal("seek queue file %s: %m", VSTREAM_PATH(request->fp));

    /*
     * A non-empty null sender replacement is subject to the 'q' flag.
     */
    buf = vstring_alloc(10);
    sender = *request->sender ? request->sender : STR(attr.null_sender);
    if (*sender && (attr.flags & PIPE_OPT_QUOTE_LOCAL)) {
	quote_822_local(buf, sender);
	dict_update(PIPE_DICT_TABLE, PIPE_DICT_SENDER, STR(buf));
    } else
	dict_update(PIPE_DICT_TABLE, PIPE_DICT_SENDER, sender);
    if (attr.flags & PIPE_OPT_FOLD_HOST) {
	vstring_strcpy(buf, request->nexthop);
	lowercase(STR(buf));
	dict_update(PIPE_DICT_TABLE, PIPE_DICT_NEXTHOP, STR(buf));
    } else
	dict_update(PIPE_DICT_TABLE, PIPE_DICT_NEXTHOP, request->nexthop);
    vstring_sprintf(buf, "%ld", (long) request->data_size);
    dict_update(PIPE_DICT_TABLE, PIPE_DICT_SIZE, STR(buf));
    dict_update(PIPE_DICT_TABLE, PIPE_DICT_CLIENT_ADDR,
		request->client_addr);
    dict_update(PIPE_DICT_TABLE, PIPE_DICT_CLIENT_HELO,
		request->client_helo);
    dict_update(PIPE_DICT_TABLE, PIPE_DICT_CLIENT_NAME,
		request->client_name);
    dict_update(PIPE_DICT_TABLE, PIPE_DICT_CLIENT_PORT,
		request->client_port);
    dict_update(PIPE_DICT_TABLE, PIPE_DICT_CLIENT_PROTO,
		request->client_proto);
    dict_update(PIPE_DICT_TABLE, PIPE_DICT_SASL_METHOD,
		request->sasl_method);
    dict_update(PIPE_DICT_TABLE, PIPE_DICT_SASL_USERNAME,
		request->sasl_username);
    dict_update(PIPE_DICT_TABLE, PIPE_DICT_SASL_SENDER,
		request->sasl_sender);
    dict_update(PIPE_DICT_TABLE, PIPE_DICT_QUEUE_ID,
		request->queue_id);
    vstring_free(buf);

    if ((expanded_argv = expand_argv(service, attr.command,
				     rcpt_list, attr.flags)) == 0) {
	dsb_simple(why, "4.3.5", "mail system configuration error");
	deliver_status = eval_command_status(PIPE_STAT_DEFER, service,
					     request, &attr, why);
	DELIVER_MSG_CLEANUP();
	return (deliver_status);
    }
    export_env = argv_split(var_export_environ, ", \t\r\n");

    command_status = pipe_command(request->fp, why,
				  PIPE_CMD_UID, attr.uid,
				  PIPE_CMD_GID, attr.gid,
				  PIPE_CMD_SENDER, sender,
				  PIPE_CMD_COPY_FLAGS, attr.flags,
				  PIPE_CMD_ARGV, expanded_argv->argv,
				  PIPE_CMD_TIME_LIMIT, conf.time_limit,
				  PIPE_CMD_EOL, STR(attr.eol),
				  PIPE_CMD_EXPORT, export_env->argv,
				  PIPE_CMD_CWD, attr.exec_dir,
				  PIPE_CMD_CHROOT, attr.chroot_dir,
			   PIPE_CMD_ORIG_RCPT, rcpt_list->info[0].orig_addr,
			     PIPE_CMD_DELIVERED, rcpt_list->info[0].address,
				  PIPE_CMD_END);
    argv_free(export_env);

    deliver_status = eval_command_status(command_status, service, request,
					 &attr, why);

    /*
     * Clean up.
     */
    DELIVER_MSG_CLEANUP();

    return (deliver_status);
}
示例#11
0
static int eval_command_status(int command_status, char *service,
			          DELIVER_REQUEST *request, PIPE_ATTR *attr,
			               DSN_BUF *why)
{
    RECIPIENT *rcpt;
    int     status;
    int     result = 0;
    int     n;
    char   *saved_text;

    /*
     * Depending on the result, bounce or defer the message, and mark the
     * recipient as done where appropriate.
     */
    switch (command_status) {
    case PIPE_STAT_OK:
	/* Save the command output before dsb_update() clobbers it. */
	vstring_truncate(why->reason, trimblanks(STR(why->reason),
			      VSTRING_LEN(why->reason)) - STR(why->reason));
	if (VSTRING_LEN(why->reason) > 0) {
	    VSTRING_TERMINATE(why->reason);
	    saved_text =
		vstring_export(vstring_sprintf(
				    vstring_alloc(VSTRING_LEN(why->reason)),
					    " (%.100s)", STR(why->reason)));
	} else
	    saved_text = mystrdup("");		/* uses shared R/O storage */
	dsb_update(why, "2.0.0", (attr->flags & PIPE_OPT_FINAL_DELIVERY) ?
		   "delivered" : "relayed", DSB_SKIP_RMTA, DSB_SKIP_REPLY,
		   "delivered via %s service%s", service, saved_text);
	myfree(saved_text);
	(void) DSN_FROM_DSN_BUF(why);
	for (n = 0; n < request->rcpt_list.len; n++) {
	    rcpt = request->rcpt_list.info + n;
	    status = sent(DEL_REQ_TRACE_FLAGS(request->flags),
			  request->queue_id, &request->msg_stats, rcpt,
			  service, &why->dsn);
	    if (status == 0 && (request->flags & DEL_REQ_FLAG_SUCCESS))
		deliver_completed(request->fp, rcpt->offset);
	    result |= status;
	}
	break;
    case PIPE_STAT_BOUNCE:
    case PIPE_STAT_DEFER:
	(void) DSN_FROM_DSN_BUF(why);
	for (n = 0; n < request->rcpt_list.len; n++) {
	    rcpt = request->rcpt_list.info + n;
	    /* XXX Maybe encapsulate this with ndr_append(). */
	    status = (STR(why->status)[0] != '4' ?
		      bounce_append : defer_append)
		(DEL_REQ_TRACE_FLAGS(request->flags),
		 request->queue_id,
		 &request->msg_stats, rcpt,
		 service, &why->dsn);
	    if (status == 0)
		deliver_completed(request->fp, rcpt->offset);
	    result |= status;
	}
	break;
    case PIPE_STAT_CORRUPT:
	/* XXX DSN should we send something? */
	result |= DEL_STAT_DEFER;
	break;
    default:
	msg_panic("eval_command_status: bad status %d", command_status);
	/* NOTREACHED */
    }

    return (result);
}