Пример #1
0
static void pickup_service(char *unused_buf, ssize_t unused_len,
			           char *unused_service, char **argv)
{
    SCAN_DIR *scan;
    char   *queue_name;
    PICKUP_INFO info;
    const char *path;
    char   *id;
    int     file_count;

    /*
     * Sanity check. This service takes no command-line arguments.
     */
    if (argv[0])
	msg_fatal("unexpected command-line argument: %s", argv[0]);

    /*
     * Skip over things that we don't want to open, such as files that are
     * still being written, or garbage. Leave it up to the sysadmin to remove
     * garbage. Keep scanning the queue directory until we stop removing
     * files from it.
     * 
     * When we find a file, stroke the watchdog so that it will not bark while
     * some application is keeping us busy by injecting lots of mail into the
     * maildrop directory.
     */
    queue_name = MAIL_QUEUE_MAILDROP;		/* XXX should be a list */
    do {
	file_count = 0;
	scan = scan_dir_open(queue_name);
	while ((id = scan_dir_next(scan)) != 0) {
	    if (mail_open_ok(queue_name, id, &info.st, &path) == MAIL_OPEN_YES) {
		pickup_init(&info);
		info.path = mystrdup(path);
		watchdog_pat();
		if (pickup_file(&info) == REMOVE_MESSAGE_FILE) {
		    if (REMOVE(info.path))
			msg_warn("remove %s: %m", info.path);
		    else
			file_count++;
		}
		pickup_free(&info);
	    }
	}
	scan_dir_close(scan);
    } while (file_count);
}
Пример #2
0
static void qmgr_active_done_2_generic(QMGR_MESSAGE *message)
{
    const char *path;
    struct stat st;

    /*
     * A delivery agent marks a queue file as corrupt by changing its
     * attributes, and by pretending that delivery was deferred.
     */
    if (message->flags
	&& mail_open_ok(MAIL_QUEUE_ACTIVE, message->queue_id, &st, &path) == MAIL_OPEN_NO) {
	qmgr_active_corrupt(message->queue_id);
	qmgr_message_free(message);
	return;
    }

    /*
     * If we did not read all recipients from this file, go read some more,
     * but remember whether some recipients have to be tried again.
     * 
     * Throwing away queue files seems bad, especially when they made it this
     * far into the mail system. Therefore we save bad files to a separate
     * directory for further inspection by a human being.
     */
    if (message->rcpt_offset > 0) {
	if (qmgr_message_realloc(message) == 0) {
	    qmgr_active_corrupt(message->queue_id);
	    qmgr_message_free(message);
	} else {
	    if (message->refcount == 0)
		qmgr_active_done(message);	/* recurse for consistency */
	}
	return;
    }

    /*
     * XXX With multi-recipient mail, some recipients may have NOTIFY=SUCCESS
     * and others not. Depending on what subset of recipients are delivered,
     * a trace file may or may not be created. Even when the last partial
     * delivery attempt had no NOTIFY=SUCCESS recipients, a trace file may
     * still exist from a previous partial delivery attempt. So as long as
     * any recipient has NOTIFY=SUCCESS we have to always look for the trace
     * file and be prepared for the file not to exist.
     * 
     * See also comments in bounce/bounce_notify_util.c.
     */
    if ((message->tflags & (DEL_REQ_FLAG_USR_VRFY | DEL_REQ_FLAG_RECORD))
	|| (message->rflags & QMGR_READ_FLAG_NOTIFY_SUCCESS)) {
	atrace_flush(message->tflags,
		     message->queue_name,
		     message->queue_id,
		     message->encoding,
		     message->sender,
		     message->dsn_envid,
		     message->dsn_ret,
		     qmgr_active_done_25_trace_flush,
		     (char *) message);
	return;
    }

    /*
     * Asynchronous processing does not reach this point.
     */
    qmgr_active_done_25_generic(message);
}
Пример #3
0
int     qmgr_active_feed(QMGR_SCAN *scan_info, const char *queue_id)
{
    const char *myname = "qmgr_active_feed";
    QMGR_MESSAGE *message;
    struct stat st;
    const char *path;

    if (strcmp(scan_info->queue, MAIL_QUEUE_ACTIVE) == 0)
	msg_panic("%s: bad queue %s", myname, scan_info->queue);
    if (msg_verbose)
	msg_info("%s: queue %s", myname, scan_info->queue);

    /*
     * Make sure this is something we are willing to open.
     */
    if (mail_open_ok(scan_info->queue, queue_id, &st, &path) == MAIL_OPEN_NO)
	return (0);

    if (msg_verbose)
	msg_info("%s: %s", myname, path);

    /*
     * Skip files that have time stamps into the future. They need to cool
     * down. Incoming and deferred files can have future time stamps.
     */
    if ((scan_info->flags & QMGR_SCAN_ALL) == 0
	&& st.st_mtime > time((time_t *) 0) + 1) {
	if (msg_verbose)
	    msg_info("%s: skip %s (%ld seconds)", myname, queue_id,
		     (long) (st.st_mtime - event_time()));
	return (0);
    }

    /*
     * Move the message to the active queue. File access errors are fatal.
     */
    if (mail_queue_rename(queue_id, scan_info->queue, MAIL_QUEUE_ACTIVE)) {
	if (errno != ENOENT)
	    msg_fatal("%s: %s: rename from %s to %s: %m", myname,
		      queue_id, scan_info->queue, MAIL_QUEUE_ACTIVE);
	msg_warn("%s: %s: rename from %s to %s: %m", myname,
		 queue_id, scan_info->queue, MAIL_QUEUE_ACTIVE);
	return (0);
    }

    /*
     * Extract envelope information: sender and recipients. At this point,
     * mail addresses have been processed by the cleanup service so they
     * should be in canonical form. Generate requests to deliver this
     * message.
     * 
     * Throwing away queue files seems bad, especially when they made it this
     * far into the mail system. Therefore we save bad files to a separate
     * directory for further inspection.
     * 
     * After queue manager restart it is possible that a queue file is still
     * being delivered. In that case (the file is locked), defer delivery by
     * a minimal amount of time.
     */
#define QMGR_FLUSH_AFTER	(QMGR_FLUSH_EACH | QMGR_FLUSH_DFXP)

    if ((message = qmgr_message_alloc(MAIL_QUEUE_ACTIVE, queue_id,
				 (st.st_mode & MAIL_QUEUE_STAT_UNTHROTTLE) ?
				      scan_info->flags | QMGR_FLUSH_AFTER :
				      scan_info->flags,
				 (st.st_mode & MAIL_QUEUE_STAT_UNTHROTTLE) ?
				  st.st_mode & ~MAIL_QUEUE_STAT_UNTHROTTLE :
				      0)) == 0) {
	qmgr_active_corrupt(queue_id);
	return (0);
    } else if (message == QMGR_MESSAGE_LOCKED) {
	qmgr_active_defer(MAIL_QUEUE_ACTIVE, queue_id, MAIL_QUEUE_INCOMING, 60);
	return (0);
    } else {

	/*
	 * Special case if all recipients were already delivered. Send any
	 * bounces and clean up.
	 */
	if (message->refcount == 0)
	    qmgr_active_done(message);
	return (1);
    }
}
Пример #4
0
static int deliver_request_get(VSTREAM *stream, DELIVER_REQUEST *request)
{
    const char *myname = "deliver_request_get";
    const char *path;
    struct stat st;
    static VSTRING *queue_name;
    static VSTRING *queue_id;
    static VSTRING *nexthop;
    static VSTRING *encoding;
    static VSTRING *address;
    static VSTRING *client_name;
    static VSTRING *client_addr;
    static VSTRING *client_port;
    static VSTRING *client_proto;
    static VSTRING *client_helo;
    static VSTRING *sasl_method;
    static VSTRING *sasl_username;
    static VSTRING *sasl_sender;
    static VSTRING *rewrite_context;
    static VSTRING *dsn_envid;
    static RCPT_BUF *rcpt_buf;
    int     rcpt_count;
    int     dsn_ret;

    /*
     * Initialize. For some reason I wanted to allow for multiple instances
     * of a deliver_request structure, thus the hoopla with string
     * initialization and copying.
     */
    if (queue_name == 0) {
	queue_name = vstring_alloc(10);
	queue_id = vstring_alloc(10);
	nexthop = vstring_alloc(10);
	encoding = vstring_alloc(10);
	address = vstring_alloc(10);
	client_name = vstring_alloc(10);
	client_addr = vstring_alloc(10);
	client_port = vstring_alloc(10);
	client_proto = vstring_alloc(10);
	client_helo = vstring_alloc(10);
	sasl_method = vstring_alloc(10);
	sasl_username = vstring_alloc(10);
	sasl_sender = vstring_alloc(10);
	rewrite_context = vstring_alloc(10);
	dsn_envid = vstring_alloc(10);
	rcpt_buf = rcpb_create();
    }

    /*
     * Extract the queue file name, data offset, and sender address. Abort
     * the conversation when they send bad information.
     */
    if (attr_scan(stream, ATTR_FLAG_STRICT,
		  ATTR_TYPE_INT, MAIL_ATTR_FLAGS, &request->flags,
		  ATTR_TYPE_STR, MAIL_ATTR_QUEUE, queue_name,
		  ATTR_TYPE_STR, MAIL_ATTR_QUEUEID, queue_id,
		  ATTR_TYPE_LONG, MAIL_ATTR_OFFSET, &request->data_offset,
		  ATTR_TYPE_LONG, MAIL_ATTR_SIZE, &request->data_size,
		  ATTR_TYPE_STR, MAIL_ATTR_NEXTHOP, nexthop,
		  ATTR_TYPE_STR, MAIL_ATTR_ENCODING, encoding,
		  ATTR_TYPE_STR, MAIL_ATTR_SENDER, address,
		  ATTR_TYPE_STR, MAIL_ATTR_DSN_ENVID, dsn_envid,
		  ATTR_TYPE_INT, MAIL_ATTR_DSN_RET, &dsn_ret,
	       ATTR_TYPE_FUNC, msg_stats_scan, (void *) &request->msg_stats,
    /* XXX Should be encapsulated with ATTR_TYPE_FUNC. */
		  ATTR_TYPE_STR, MAIL_ATTR_LOG_CLIENT_NAME, client_name,
		  ATTR_TYPE_STR, MAIL_ATTR_LOG_CLIENT_ADDR, client_addr,
		  ATTR_TYPE_STR, MAIL_ATTR_LOG_CLIENT_PORT, client_port,
		  ATTR_TYPE_STR, MAIL_ATTR_LOG_PROTO_NAME, client_proto,
		  ATTR_TYPE_STR, MAIL_ATTR_LOG_HELO_NAME, client_helo,
    /* XXX Should be encapsulated with ATTR_TYPE_FUNC. */
		  ATTR_TYPE_STR, MAIL_ATTR_SASL_METHOD, sasl_method,
		  ATTR_TYPE_STR, MAIL_ATTR_SASL_USERNAME, sasl_username,
		  ATTR_TYPE_STR, MAIL_ATTR_SASL_SENDER, sasl_sender,
    /* XXX Ditto if we want to pass TLS certificate info. */
		  ATTR_TYPE_STR, MAIL_ATTR_RWR_CONTEXT, rewrite_context,
		  ATTR_TYPE_INT, MAIL_ATTR_RCPT_COUNT, &rcpt_count,
		  ATTR_TYPE_END) != 21) {
	msg_warn("%s: error receiving common attributes", myname);
	return (-1);
    }
    if (mail_open_ok(vstring_str(queue_name),
		     vstring_str(queue_id), &st, &path) == 0)
	return (-1);

    /* Don't override hand-off time after deliver_pass() delegation. */
    if (request->msg_stats.agent_handoff.tv_sec == 0)
	GETTIMEOFDAY(&request->msg_stats.agent_handoff);

    request->queue_name = mystrdup(vstring_str(queue_name));
    request->queue_id = mystrdup(vstring_str(queue_id));
    request->nexthop = mystrdup(vstring_str(nexthop));
    request->encoding = mystrdup(vstring_str(encoding));
    request->sender = mystrdup(vstring_str(address));
    request->client_name = mystrdup(vstring_str(client_name));
    request->client_addr = mystrdup(vstring_str(client_addr));
    request->client_port = mystrdup(vstring_str(client_port));
    request->client_proto = mystrdup(vstring_str(client_proto));
    request->client_helo = mystrdup(vstring_str(client_helo));
    request->sasl_method = mystrdup(vstring_str(sasl_method));
    request->sasl_username = mystrdup(vstring_str(sasl_username));
    request->sasl_sender = mystrdup(vstring_str(sasl_sender));
    request->rewrite_context = mystrdup(vstring_str(rewrite_context));
    request->dsn_envid = mystrdup(vstring_str(dsn_envid));
    request->dsn_ret = dsn_ret;

    /*
     * Extract the recipient offset and address list. Skip over any
     * attributes from the sender that we do not understand.
     */
    while (rcpt_count-- > 0) {
	if (attr_scan(stream, ATTR_FLAG_STRICT,
		      ATTR_TYPE_FUNC, rcpb_scan, (void *) rcpt_buf,
		      ATTR_TYPE_END) != 1) {
	    msg_warn("%s: error receiving recipient attributes", myname);
	    return (-1);
	}
	recipient_list_add(&request->rcpt_list, rcpt_buf->offset,
			   vstring_str(rcpt_buf->dsn_orcpt),
			   rcpt_buf->dsn_notify,
			   vstring_str(rcpt_buf->orig_addr),
			   vstring_str(rcpt_buf->address));
    }
    if (request->rcpt_list.len <= 0) {
	msg_warn("%s: no recipients in delivery request for destination %s",
		 request->queue_id, request->nexthop);
	return (-1);
    }

    /*
     * Open the queue file and set a shared lock, in order to prevent
     * duplicate deliveries when the queue is flushed immediately after queue
     * manager restart.
     * 
     * The queue manager locks the file exclusively when it enters the active
     * queue, and releases the lock before starting deliveries from that
     * file. The queue manager does not lock the file again when reading more
     * recipients into memory. When the queue manager is restarted, the new
     * process moves files from the active queue to the incoming queue to cool
     * off for a while. Delivery agents should therefore never try to open a
     * file that is locked by a queue manager process.
     * 
     * Opening the queue 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.
     */
#define DELIVER_LOCK_MODE (MYFLOCK_OP_SHARED | MYFLOCK_OP_NOWAIT)

    request->fp =
	mail_queue_open(request->queue_name, request->queue_id, O_RDWR, 0);
    if (request->fp == 0) {
	if (errno != ENOENT)
	    msg_fatal("open %s %s: %m", request->queue_name, request->queue_id);
	msg_warn("open %s %s: %m", request->queue_name, request->queue_id);
	return (-1);
    }
    if (msg_verbose)
	msg_info("%s: file %s", myname, VSTREAM_PATH(request->fp));
    if (myflock(vstream_fileno(request->fp), INTERNAL_LOCK, DELIVER_LOCK_MODE) < 0)
	msg_fatal("shared lock %s: %m", VSTREAM_PATH(request->fp));
    close_on_exec(vstream_fileno(request->fp), CLOSE_ON_EXEC);

    return (0);
}
Пример #5
0
static void qmgr_active_done_2_generic(QMGR_MESSAGE *message)
{
    char   *myname = "qmgr_active_done_2_generic";
    const char *path;
    struct stat st;
    int     status;

    /*
     * A delivery agent marks a queue file as corrupt by changing its
     * attributes, and by pretending that delivery was deferred.
     */
    if (message->flags
	&& mail_open_ok(MAIL_QUEUE_ACTIVE, message->queue_id, &st, &path) == MAIL_OPEN_NO) {
	qmgr_active_corrupt(message->queue_id);
	qmgr_message_free(message);
	return;
    }

    /*
     * If we did not read all recipients from this file, go read some more,
     * but remember whether some recipients have to be tried again.
     * 
     * Throwing away queue files seems bad, especially when they made it this
     * far into the mail system. Therefore we save bad files to a separate
     * directory for further inspection by a human being.
     */
    if (message->rcpt_offset > 0) {
	if (qmgr_message_realloc(message) == 0) {
	    qmgr_active_corrupt(message->queue_id);
	    qmgr_message_free(message);
	} else {
	    if (message->refcount == 0)
		qmgr_active_done(message);	/* recurse for consistency */
	}
	return;
    }

    /*
     * As a temporary implementation, synchronously inform the sender of
     * trace information. This will block for 10 seconds when the qmgr FIFO
     * is full.
     */
    if (message->tflags & (DEL_REQ_FLAG_EXPAND | DEL_REQ_FLAG_RECORD)) {
	status = trace_flush(message->tflags,
			     message->queue_name,
			     message->queue_id,
			     message->encoding,
			     message->sender);
	if (status == 0 && message->tflags_offset)
	    qmgr_message_kill_record(message, message->tflags_offset);
	message->flags |= status;
    }

    /*
     * If we get to this point we have tried all recipients for this message.
     * If the message is too old, try to bounce it.
     * 
     * Bounces are sent asynchronously to avoid stalling while the cleanup
     * daemon waits for the qmgr to accept the "new mail" trigger.
     */
    if (message->flags) {
	if (event_time() >= message->arrival_time +
	    (*message->sender ? var_max_queue_time : var_dsn_queue_time)) {
	    msg_info("%s: from=<%s>, status=expired, returned to sender",
		     message->queue_id, message->sender);
	    if (message->verp_delims == 0 || var_verp_bounce_off)
		adefer_flush(BOUNCE_FLAG_KEEP,
			     message->queue_name,
			     message->queue_id,
			     message->encoding,
			     message->errors_to,
			     qmgr_active_done_3_defer_flush,
			     (char *) message);
	    else
		adefer_flush_verp(BOUNCE_FLAG_KEEP,
				  message->queue_name,
				  message->queue_id,
				  message->encoding,
				  message->errors_to,
				  message->verp_delims,
				  qmgr_active_done_3_defer_flush,
				  (char *) message);
	    return;
	} else if (message->warn_time > 0
		   && event_time() > message->warn_time) {
	    if (msg_verbose)
		msg_info("%s: sending defer warning for %s", myname, message->queue_id);
	    adefer_warn(BOUNCE_FLAG_KEEP,
			message->queue_name,
			message->queue_id,
			message->encoding,
			message->errors_to,
			qmgr_active_done_3_defer_warn,
			(char *) message);
	    return;
	}
    }

    /*
     * Asynchronous processing does not reach this point.
     */
    qmgr_active_done_3_generic(message);
}