Portability: Siemens Pyramid (dcosx) patch by Thomas D.
Knox @ vushta.com.
+
+ Performance: FreeBSD has bidirectional pipes that are faster
+ than socketpairs. Anticpiating on more platform-specific
+ optimizations, all duplex pipe plumbing is now isolated in
+ a duplex_pipe.c module that provides a system-independent
+ interface.
+
+20000105
+
+ Cleanup: the INSTALL.sh script now updates the sample files
+ in /etc/postfix even when main.cf exists.
+
+20000106
+
+ Bugfix: the SMTP server should consult the relocated map
+ for virtual destinations (Denis Shaposhnikov). Files:
+ smtpd/smtpd.c smtpd/smtpd_check.c.
+
+20000108
+
+ Workaround: rename() over NFS can fail with ENOENT even
+ when the operation succeeds (Graham Orndorff @ WebTV). This
+ is not news. Any non-idempotent operation can fail over
+ NFS when the NFS server's acknowledgement is lost and the
+ NFS client code retries the operation (other examples are:
+ create, symlink, link, unlink, mkdir, rmdir). Postfix has
+ workarounds for the cases where this is most likely to
+ cause trouble. Files: util/sane_{rename,link}.[hc]. If
+ you want reliable mail system, do not use NFS.
+
+20000115
+
+ Workaround: better detection of bad hardware. Added SIGBUS
+ to the list of signals that the master will log before
+ exiting.
+
+20000122
+
+ Portability: preliminary SCO5 port Christopher Wong @
+ csports.com. This still needs to a workaround for "find"
+ not supporting "-type s" (actually, UNIX-domain sockets
+ have no unique representation in the file system and show
+ up as FIFOs).
+
+20000115-22
+
+ Bugfix: in case of a too long message header, don't extract
+ recipients from message headers. With the previous behavior,
+ Bcc information could be left in the message body, as one
+ person found out the hard way. Files: cleanup/cleanup.c,
+ cleanup/cleanup_extracted.c, global/cleanup_user.h.
+
+20000124
+
+ Whatever: RFC 1869 amends RFC 821 and specifies that code
+ 555 is to be used when a MAIL FROM or RCPT TO parameter is
+ not implemented or not recognized. Russ Allbery @stanford.edu.
+ This reply code is added to the list of reply codes that
+ cause the Postfix SMTP client to mail a transcript to the
+ postmaster. File: smtp/smtp_trouble.c.
+
+20000126
+
+ Emergency feature: qmgr_site_hog_factor (default: 90 percent)
+ limits the amount of resources that Postfix will devote to
+ a single site. With less than 100, Postfix will defer the
+ excess mail so that one site with a large backlog does not
+ block other deliveries.
+
compare_or_symlink $SENDMAIL_PATH $MAILQ_PATH
}
-compare_or_replace a+r,go-w conf/LICENSE $CONFIG_DIRECTORY/LICENSE || exit 1
-
-test -f $CONFIG_DIRECTORY/main.cf || {
+if [ -f $CONFIG_DIRECTORY/main.cf ]
+then
+ for file in LICENSE `cd conf; echo sample*` main.cf.default
+ do
+ compare_or_replace a+r,go-w conf/$file $CONFIG_DIRECTORY/$file || exit 1
+ done
+else
cp conf/* $CONFIG_DIRECTORY || exit 1
chmod a+r,go-w $CONFIG_DIRECTORY/* || exit 1
echo "BTW: Edit your alias database and be sure to set up aliases" 1>&2
echo "for root and postmaster, then run $NEWALIASES_PATH." 1>&2
}
-}
+fi
# Save settings.
domains that are defined by Postfix virtual maps.
- The SMTP server can reject mail for unknown local users. Specify
-"local_recipient_maps = $relocated_maps, $alias_maps, unix:passwd.byname"
-if your local mail is delivered by a UNIX-style local delivery
-agent. See example in conf/main.cf.
+"local_recipient_maps = $alias_maps, unix:passwd.byname" if your
+local mail is delivered by a UNIX-style local delivery agent. See
+example in conf/main.cf.
- Use "disable_vrfy_command = yes" to disable the SMTP VRFY command.
This prevents some forms of address harvesting.
SHELL = /bin/sh
SRCS = bounce.c bounce_append_service.c bounce_notify_service.c \
- bounce_cleanup.c
+ bounce_recip_service.c bounce_cleanup.c
OBJS = bounce.o bounce_append_service.o bounce_notify_service.o \
- bounce_cleanup.o
+ bounce_recip_service.o bounce_cleanup.o
HDRS =
TESTSRC =
WARN = -W -Wformat -Wimplicit -Wmissing-prototypes \
bounce_notify_service.o: ../include/mark_corrupt.h
bounce_notify_service.o: ../include/mail_error.h
bounce_notify_service.o: bounce_service.h
+bounce_recip_service.o: bounce_recip_service.c
+bounce_recip_service.o: ../include/sys_defs.h
+bounce_recip_service.o: ../include/msg.h
+bounce_recip_service.o: ../include/vstring.h
+bounce_recip_service.o: ../include/vbuf.h
+bounce_recip_service.o: ../include/vstream.h
+bounce_recip_service.o: ../include/vstring_vstream.h
+bounce_recip_service.o: ../include/mymalloc.h
+bounce_recip_service.o: ../include/stringops.h
+bounce_recip_service.o: ../include/events.h
+bounce_recip_service.o: ../include/line_wrap.h
+bounce_recip_service.o: ../include/name_mask.h
+bounce_recip_service.o: ../include/mail_queue.h
+bounce_recip_service.o: ../include/mail_proto.h
+bounce_recip_service.o: ../include/iostuff.h
+bounce_recip_service.o: ../include/quote_822_local.h
+bounce_recip_service.o: ../include/mail_params.h
+bounce_recip_service.o: ../include/canon_addr.h
+bounce_recip_service.o: ../include/is_header.h
+bounce_recip_service.o: ../include/record.h
+bounce_recip_service.o: ../include/rec_type.h
+bounce_recip_service.o: ../include/mail_conf.h
+bounce_recip_service.o: ../include/post_mail.h
+bounce_recip_service.o: ../include/cleanup_user.h
+bounce_recip_service.o: ../include/mail_addr.h
+bounce_recip_service.o: ../include/mark_corrupt.h
+bounce_recip_service.o: ../include/mail_error.h
+bounce_recip_service.o: bounce_service.h
/* Post a bounce message, with a copy of a log file and of the
/* corresponding message. When the bounce is posted successfully,
/* the log file is deleted.
+/* .IP \(bu
+/* Post a bounce message without accessing a per-message log file.
/* .PP
/* The software does a best effort to notify the sender that there
/* was a problem. A notification is sent even when the log file
* Execute the request.
*/
return (bounce_notify_service(service_name, STR(queue_name),
- STR(queue_id), STR(sender), flush));
+ STR(queue_id), STR(sender), flush));
+}
+
+/* bounce_recip_proto - bounce_recip server protocol */
+
+static int bounce_recip_proto(char *service_name, VSTREAM *client, int flush)
+{
+ int flags;
+
+ /*
+ * Read and validate the client request.
+ */
+ if (mail_command_read(client, "%d %s %s %s %s %s",
+ &flags, queue_name, queue_id,
+ sender, recipient, why) != 6) {
+ msg_warn("malformed request");
+ return (-1);
+ }
+ if (mail_queue_name_ok(STR(queue_name)) == 0) {
+ msg_warn("malformed queue name: %s", printable(STR(queue_name), '?'));
+ return (-1);
+ }
+ if (mail_queue_id_ok(STR(queue_id)) == 0) {
+ msg_warn("malformed queue id: %s", printable(STR(queue_id), '?'));
+ return (-1);
+ }
+ if (msg_verbose)
+ msg_info("bounce_recip_proto: service=%s queue=%s id=%s sender=%s recip=%s why=%s",
+ service_name, STR(queue_name), STR(queue_id),
+ STR(sender), STR(recipient), STR(why));
+
+ /*
+ * Execute the request.
+ */
+ return (bounce_recip_service(service_name, STR(queue_name), STR(queue_id),
+ STR(sender), STR(recipient), STR(why), flush));
}
/* bounce_service - parse bounce command type and delegate */
status = bounce_notify_proto(service_name, client, JUST_WARN);
} else if (command == BOUNCE_CMD_APPEND) {
status = bounce_append_proto(service_name, client);
+ } else if (command == BOUNCE_CMD_RECIP) {
+ status = bounce_recip_proto(service_name, client, REALLY_BOUNCE);
} else {
msg_warn("unknown command: %d", command);
status = -1;
#define BOUNCE_ALL 0
/*
- * The choice of sender address depends on recipient the address. For a
+ * The choice of sender address depends on the recipient address. For a
* single bounce (a non-delivery notification to the message originator),
* the sender address is the empty string. For a double bounce (typically
* a failed single bounce, or a postmaster notification that was produced
--- /dev/null
+/*++
+/* NAME
+/* bounce_recip_service 3
+/* SUMMARY
+/* send non-delivery report to sender, server side
+/* SYNOPSIS
+/* #include "bounce_service.h"
+/*
+/* int bounce_recip_service(queue_name, queue_id, sender,
+/* bounced_addr, why, flush)
+/* char *queue_name;
+/* char *queue_id;
+/* char *sender;
+/* char *recipient;
+/* char *why;
+/* int flush;
+/* DESCRIPTION
+/* This module implements the server side of the bounce_recip()
+/* (send bounce message) request. If flush is zero, the logfile
+/* is not removed, and a warning is sent instead of a bounce.
+/*
+/* When a message bounces, a full copy is sent to the originator,
+/* and an optional copy of the diagnostics with message headers is
+/* sent to the postmaster. The result is non-zero when the operation
+/* should be tried again.
+/*
+/* When a bounce is sent, the sender address is the empty
+/* address. When a bounce bounces, an optional double bounce
+/* with the entire undeliverable mail is sent to the postmaster,
+/* with as sender address the double bounce address.
+/* DIAGNOSTICS
+/* Fatal error: error opening existing file. Warnings: corrupt
+/* message file. A corrupt message is saved to the "corrupt"
+/* queue for further inspection.
+/* BUGS
+/* SEE ALSO
+/* bounce(3) basic bounce service client interface
+/* 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
+/*--*/
+
+/* System library. */
+
+#include <sys_defs.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <string.h>
+#include <ctype.h>
+
+#ifdef STRCASECMP_IN_STRINGS_H
+#include <strings.h>
+#endif
+
+/* Utility library. */
+
+#include <msg.h>
+#include <vstring.h>
+#include <vstream.h>
+#include <vstring_vstream.h>
+#include <mymalloc.h>
+#include <stringops.h>
+#include <events.h>
+#include <line_wrap.h>
+#include <name_mask.h>
+
+/* Global library. */
+
+#include <mail_queue.h>
+#include <mail_proto.h>
+#include <quote_822_local.h>
+#include <mail_params.h>
+#include <canon_addr.h>
+#include <is_header.h>
+#include <record.h>
+#include <rec_type.h>
+#include <mail_conf.h>
+#include <post_mail.h>
+#include <mail_addr.h>
+#include <mark_corrupt.h>
+#include <mail_error.h>
+
+/* Application-specific. */
+
+#include "bounce_service.h"
+
+#define STR vstring_str
+
+/* bounce_header - generate bounce message header */
+
+static int bounce_header(VSTREAM *bounce, VSTRING *buf, const char *dest,
+ const char *boundary, int flush)
+{
+
+ /*
+ * Print a minimal bounce header. The cleanup service will add other
+ * headers and will make all addresses fully qualified.
+ */
+#define STREQ(a, b) (strcasecmp((a), (b)) == 0)
+
+ post_mail_fprintf(bounce, "From: %s (Mail Delivery System)",
+ MAIL_ADDR_MAIL_DAEMON);
+
+ if (flush) {
+ post_mail_fputs(bounce, dest == var_bounce_rcpt
+ || dest == var_2bounce_rcpt || dest == var_delay_rcpt ?
+ "Subject: Postmaster Copy: Undelivered Mail" :
+ "Subject: Undelivered Mail Returned to Sender");
+ } else {
+ post_mail_fputs(bounce, dest == var_bounce_rcpt
+ || dest == var_2bounce_rcpt || dest == var_delay_rcpt ?
+ "Subject: Postmaster Warning: Delayed Mail" :
+ "Subject: Delayed Mail (still being retried)");
+ }
+ post_mail_fprintf(bounce, "To: %s", STR(quote_822_local(buf, dest)));
+
+ /*
+ * MIME header.
+ */
+ post_mail_fprintf(bounce, "MIME-Version: 1.0");
+#ifdef DSN
+ post_mail_fprintf(bounce, "Content-Type: %s; report-type=%s;",
+ "multipart/report", "delivery-status");
+#else
+ post_mail_fprintf(bounce, "Content-Type: multipart/mixed;");
+#endif
+ post_mail_fprintf(bounce, "\tboundary=\"%s\"", boundary);
+ post_mail_fputs(bounce, "");
+ post_mail_fputs(bounce, "This is a MIME-encapsulated message.");
+
+ /*
+ * More MIME header.
+ */
+ post_mail_fputs(bounce, "");
+ post_mail_fprintf(bounce, "--%s", boundary);
+ post_mail_fprintf(bounce, "Content-Description: %s", "Notification");
+ post_mail_fprintf(bounce, "Content-Type: %s", "text/plain");
+ post_mail_fputs(bounce, "");
+
+ return (vstream_ferror(bounce));
+}
+
+/* bounce_boilerplate - generate boiler-plate text */
+
+static int bounce_boilerplate(VSTREAM *bounce, VSTRING *buf,
+ const char *boundary, int flush)
+{
+
+ /*
+ * Print the message body with the problem report. XXX For now, we use a
+ * fixed bounce template. We could use a site-specific parametrized
+ * template with ${name} macros and we could do wonderful things such as
+ * word wrapping to make the text look nicer. No matter how hard we would
+ * try, receiving bounced mail will always suck.
+ */
+ post_mail_fprintf(bounce, "This is the %s program at host %s.",
+ var_mail_name, var_myhostname);
+ post_mail_fputs(bounce, "");
+ if (flush) {
+ post_mail_fputs(bounce,
+ "I'm sorry to have to inform you that the message returned");
+ post_mail_fputs(bounce,
+ "below could not be delivered to one or more destinations.");
+ } else {
+ post_mail_fputs(bounce,
+ "####################################################################");
+ post_mail_fputs(bounce,
+ "# THIS IS A WARNING ONLY. YOU DO NOT NEED TO RESEND YOUR MESSAGE. #");
+ post_mail_fputs(bounce,
+ "####################################################################");
+ post_mail_fputs(bounce, "");
+ post_mail_fprintf(bounce,
+ "Your message could not be delivered for %d hours.",
+ var_delay_warn_time);
+ post_mail_fprintf(bounce,
+ "It will be retried until it is %d days old.",
+ var_max_queue_time);
+ }
+
+ post_mail_fputs(bounce, "");
+ post_mail_fprintf(bounce,
+ "For further assistance, please contact <%s>",
+ STR(canon_addr_external(buf, MAIL_ADDR_POSTMASTER)));
+ if (flush) {
+ post_mail_fputs(bounce, "");
+ post_mail_fprintf(bounce,
+ "If you do so, please include this problem report. You can");
+ post_mail_fprintf(bounce,
+ "delete your own text from the message returned below.");
+ }
+ post_mail_fputs(bounce, "");
+ post_mail_fprintf(bounce, "\t\t\tThe %s program", var_mail_name);
+ post_mail_fputs(bounce, "");
+ return (vstream_ferror(bounce));
+}
+
+/* bounce_print - line_wrap callback */
+
+static void bounce_print(const char *str, int len, int indent, char *context)
+{
+ VSTREAM *bounce = (VSTREAM *) context;
+
+ post_mail_fprintf(bounce, "%*s%.*s", indent, "", len, str);
+}
+
+/* bounce_diagnostics - send bounce log report */
+
+static int bounce_diagnostics(VSTREAM *bounce, const char *boundary,
+ const char *unused_recipient, char *why)
+{
+
+ /*
+ * MIME header.
+ */
+#ifdef DSN
+ post_mail_fputs(bounce, "");
+ post_mail_fprintf(bounce, "--%s", boundary);
+ post_mail_fprintf(bounce, "Content-Description: %s", "Delivery error report");
+ post_mail_fprintf(bounce, "Content-Type: %s", "message/delivery-status");
+ post_mail_fputs(bounce, "");
+#endif
+
+ /*
+ * Append a copy of the delivery error log. Again, we're doing a best
+ * effort, so there is no point raising a fatal run-time error in case of
+ * a logfile read error. Wrap long lines, filter non-printable
+ * characters, and prepend one blank, so this data can safely be piped
+ * into other programs.
+ *
+ * XXX recipient may be empty.
+ */
+
+#define LENGTH 79
+#define INDENT 4
+ printable(why, '_');
+ line_wrap(why, LENGTH, INDENT, bounce_print, (char *) bounce);
+ return (vstream_ferror(bounce));
+}
+
+/* bounce_original - send a copy of the original to the victim */
+
+static int bounce_original(char *service, VSTREAM *bounce, VSTRING *buf,
+ char *queue_name, char *queue_id,
+ const char *boundary, int headers_only)
+{
+ int status = 0;
+ VSTREAM *src;
+ int rec_type;
+ int bounce_length;
+
+ /*
+ * MIME headers.
+ */
+ post_mail_fputs(bounce, "");
+ post_mail_fprintf(bounce, "--%s", boundary);
+ post_mail_fprintf(bounce, "Content-Description: %s", "Undelivered Message");
+ post_mail_fprintf(bounce, "Content-Type: %s", headers_only ?
+ "text/rfc822-headers" : "message/rfc822");
+ post_mail_fputs(bounce, "");
+
+ /*
+ * If the original message cannot be found, do not raise a run-time
+ * error. There is nothing we can do about the error, and all we are
+ * doing is to inform the sender of a delivery problem. Bouncing a
+ * message does not have to be a perfect job. But if the system IS
+ * running out of resources, raise a fatal run-time error and force a
+ * backoff.
+ */
+ if ((src = mail_queue_open(queue_name, queue_id, O_RDONLY, 0)) == 0) {
+ if (errno != ENOENT)
+ msg_fatal("open %s %s: %m", service, queue_id);
+ post_mail_fputs(bounce, "\t--- Undelivered message unavailable ---");
+ post_mail_fputs(bounce, "");
+ return (vstream_ferror(bounce));
+ }
+
+ /*
+ * Skip over the original message envelope records. If the envelope is
+ * corrupted just send whatever we can (remember this is a best effort,
+ * it does not have to be perfect).
+ */
+ while ((rec_type = rec_get(src, buf, 0)) > 0)
+ if (rec_type == REC_TYPE_MESG)
+ break;
+
+ /*
+ * Copy the original message contents. Limit the amount of bounced text
+ * so there is a better chance of the bounce making it back. We're doing
+ * raw record output here so that we don't throw away binary transparency
+ * yet.
+ */
+#define IS_HEADER(s) (ISSPACE(*(s)) || is_header(s))
+
+ bounce_length = 0;
+ while (status == 0 && (rec_type = rec_get(src, buf, 0)) > 0) {
+ if (rec_type != REC_TYPE_NORM && rec_type != REC_TYPE_CONT)
+ break;
+ if (headers_only && !IS_HEADER(vstring_str(buf)))
+ break;
+ if (var_bounce_limit == 0 || bounce_length < var_bounce_limit) {
+ bounce_length += VSTRING_LEN(buf);
+ status = (REC_PUT_BUF(bounce, rec_type, buf) != rec_type);
+ }
+ }
+ post_mail_fputs(bounce, "");
+ post_mail_fprintf(bounce, "--%s--", boundary);
+ if (headers_only == 0 && rec_type != REC_TYPE_XTRA)
+ status |= mark_corrupt(src);
+ if (vstream_fclose(src))
+ msg_warn("read message file %s %s: %m", queue_name, queue_id);
+ return (status);
+}
+
+/* bounce_recip_service - send a bounce */
+
+int bounce_recip_service(char *service, char *queue_name, char *queue_id,
+ char *recipient, char *bounced_addr,
+ char *why, int flush)
+{
+ VSTRING *buf = vstring_alloc(100);
+ int bounce_status = 1;
+ int postmaster_status = 1;
+ VSTREAM *bounce;
+ int notify_mask = name_mask(mail_error_masks, var_notify_classes);
+ VSTRING *boundary = vstring_alloc(100);
+ char *postmaster;
+
+ /*
+ * Unique string for multi-part message boundaries.
+ */
+ vstring_sprintf(boundary, "%s.%ld/%s",
+ queue_id, event_time(), var_myhostname);
+
+#define NULL_SENDER MAIL_ADDR_EMPTY /* special address */
+#define NULL_CLEANUP_FLAGS 0
+#define BOUNCE_HEADERS 1
+#define BOUNCE_ALL 0
+
+ /*
+ * The choice of sender address depends on the recipient address. For a
+ * single bounce (a non-delivery notification to the message originator),
+ * the sender address is the empty string. For a double bounce (typically
+ * a failed single bounce, or a postmaster notification that was produced
+ * by any of the mail processes) the sender address is defined by the
+ * var_double_bounce_sender configuration variable. When a double bounce
+ * cannot be delivered, the queue manager blackholes the resulting triple
+ * bounce message.
+ */
+
+ /*
+ * Double bounce failed. Never send a triple bounce.
+ *
+ * However, this does not prevent double bounces from bouncing on other
+ * systems. In order to cope with this, either the queue manager must
+ * recognize the double-bounce recipient address and discard mail, or
+ * every delivery agent must recognize the double-bounce sender address
+ * and substitute something else so mail does not come back at us.
+ */
+ if (strcasecmp(recipient, mail_addr_double_bounce()) == 0) {
+ msg_warn("%s: undeliverable postmaster notification discarded",
+ queue_id);
+ bounce_status = 0;
+ }
+
+ /*
+ * Single bounce failed. Optionally send a double bounce to postmaster.
+ */
+#define ANY_BOUNCE (MAIL_ERROR_2BOUNCE | MAIL_ERROR_BOUNCE)
+#define SKIP_IF_BOUNCE (flush == 1 && (notify_mask & ANY_BOUNCE) == 0)
+#define SKIP_IF_DELAY (flush == 0 && (notify_mask & MAIL_ERROR_DELAY) == 0)
+
+ else if (*recipient == 0) {
+ if (SKIP_IF_BOUNCE || SKIP_IF_DELAY) {
+ bounce_status = 0;
+ } else {
+ postmaster = flush ? var_2bounce_rcpt : var_delay_rcpt;
+ if ((bounce = post_mail_fopen_nowait(mail_addr_double_bounce(),
+ postmaster,
+ NULL_CLEANUP_FLAGS,
+ "BOUNCE")) != 0) {
+
+ /*
+ * Double bounce to Postmaster. This is the last opportunity
+ * for this message to be delivered. Send the text with
+ * reason for the bounce, and the headers of the original
+ * message. Don't bother sending the boiler-plate text.
+ */
+ if (!bounce_header(bounce, buf, postmaster,
+ STR(boundary), flush)
+ && bounce_diagnostics(bounce, STR(boundary),
+ bounced_addr, why) == 0)
+ bounce_original(service, bounce, buf, queue_name, queue_id,
+ STR(boundary),
+ flush ? BOUNCE_ALL : BOUNCE_HEADERS);
+ bounce_status = post_mail_fclose(bounce);
+ }
+ }
+ }
+
+ /*
+ * Non-bounce failed. Send a single bounce.
+ */
+ else {
+ if ((bounce = post_mail_fopen_nowait(NULL_SENDER, recipient,
+ NULL_CLEANUP_FLAGS,
+ "BOUNCE")) != 0) {
+
+ /*
+ * Send the bounce message header, some boilerplate text that
+ * pretends that we are a polite mail system, the text with
+ * reason for the bounce, and a copy of the original message.
+ */
+ if (bounce_header(bounce, buf, recipient, STR(boundary), flush) == 0
+ && bounce_boilerplate(bounce, buf, STR(boundary), flush) == 0
+ && bounce_diagnostics(bounce, STR(boundary),
+ bounced_addr, why) == 0)
+ bounce_original(service, bounce, buf, queue_name, queue_id,
+ STR(boundary),
+ flush ? BOUNCE_ALL : BOUNCE_HEADERS);
+ bounce_status = post_mail_fclose(bounce);
+ }
+
+ /*
+ * Optionally, send a postmaster notice.
+ *
+ * This postmaster notice is not critical, so if it fails don't
+ * retransmit the bounce that we just generated, just log a warning.
+ */
+#define WANT_IF_BOUNCE (flush == 1 && (notify_mask & MAIL_ERROR_BOUNCE))
+#define WANT_IF_DELAY (flush == 0 && (notify_mask & MAIL_ERROR_DELAY))
+
+ if (bounce_status == 0 && (WANT_IF_BOUNCE || WANT_IF_DELAY)
+ && strcasecmp(recipient, mail_addr_double_bounce()) != 0) {
+
+ /*
+ * Send the text with reason for the bounce, and the headers of
+ * the original message. Don't bother sending the boiler-plate
+ * text. This postmaster notice is not critical, so if it fails
+ * don't retransmit the bounce that we just generated, just log a
+ * warning.
+ */
+ postmaster = flush ? var_bounce_rcpt : var_delay_rcpt;
+ if ((bounce = post_mail_fopen_nowait(mail_addr_double_bounce(),
+ postmaster,
+ NULL_CLEANUP_FLAGS,
+ "BOUNCE")) != 0) {
+ if (!bounce_header(bounce, buf, postmaster,
+ STR(boundary), flush)
+ && bounce_diagnostics(bounce, STR(boundary),
+ bounced_addr, why) == 0)
+ bounce_original(service, bounce, buf, queue_name, queue_id,
+ STR(boundary), BOUNCE_HEADERS);
+ postmaster_status = post_mail_fclose(bounce);
+ }
+ if (postmaster_status)
+ msg_warn("postmaster notice failed while bouncing to %s",
+ recipient);
+ }
+ }
+
+ /*
+ * Cleanup.
+ */
+ vstring_free(buf);
+ vstring_free(boundary);
+
+ return (bounce_status);
+}
*/
extern int bounce_notify_service(char *, char *, char *, char *, int);
+ /*
+ * bounce_recip_service.c
+ */
+extern int bounce_recip_service(char *, char *, char *, char *, char *, char *, int);
+
/*
* bounce_cleanup.c
*/
SHELL = /bin/sh
SRCS = cleanup.c cleanup_out.c cleanup_envelope.c cleanup_message.c \
- cleanup_extracted.c cleanup_state.c cleanup_skip.c cleanup_rewrite.c \
+ cleanup_extracted.c cleanup_state.c cleanup_api.c cleanup_rewrite.c \
cleanup_map11.c cleanup_map1n.c cleanup_masquerade.c \
cleanup_out_recipient.c
OBJS = cleanup.o cleanup_out.o cleanup_envelope.o cleanup_message.o \
- cleanup_extracted.o cleanup_state.o cleanup_skip.o cleanup_rewrite.o \
+ cleanup_extracted.o cleanup_state.o cleanup_api.o cleanup_rewrite.o \
cleanup_map11.o cleanup_map1n.o cleanup_masquerade.o \
cleanup_out_recipient.o
HDRS =
cleanup.o: ../include/mail_stream.h
cleanup.o: ../include/mail_addr.h
cleanup.o: ../include/ext_prop.h
+cleanup.o: ../include/record.h
+cleanup.o: ../include/rec_type.h
cleanup.o: ../include/mail_server.h
cleanup.o: cleanup.h
cleanup.o: ../include/maps.h
cleanup.o: ../include/tok822.h
cleanup.o: ../include/resolve_clnt.h
cleanup.o: ../include/been_here.h
+cleanup_api.o: cleanup_api.c
+cleanup_api.o: ../include/sys_defs.h
+cleanup_api.o: ../include/msg.h
+cleanup_api.o: ../include/vstring.h
+cleanup_api.o: ../include/vbuf.h
+cleanup_api.o: ../include/mymalloc.h
+cleanup_api.o: ../include/cleanup_user.h
+cleanup_api.o: ../include/mail_queue.h
+cleanup_api.o: ../include/vstream.h
+cleanup_api.o: ../include/mail_proto.h
+cleanup_api.o: ../include/iostuff.h
+cleanup_api.o: ../include/opened.h
+cleanup_api.o: ../include/bounce.h
+cleanup_api.o: ../include/mail_params.h
+cleanup_api.o: ../include/mail_stream.h
+cleanup_api.o: ../include/mail_addr.h
+cleanup_api.o: cleanup.h
+cleanup_api.o: ../include/argv.h
+cleanup_api.o: ../include/maps.h
+cleanup_api.o: ../include/dict.h
+cleanup_api.o: ../include/tok822.h
+cleanup_api.o: ../include/resolve_clnt.h
+cleanup_api.o: ../include/been_here.h
cleanup_envelope.o: cleanup_envelope.c
cleanup_envelope.o: ../include/sys_defs.h
cleanup_envelope.o: ../include/msg.h
cleanup_rewrite.o: ../include/dict.h
cleanup_rewrite.o: ../include/been_here.h
cleanup_rewrite.o: ../include/mail_stream.h
-cleanup_skip.o: cleanup_skip.c
-cleanup_skip.o: ../include/sys_defs.h
-cleanup_skip.o: ../include/msg.h
-cleanup_skip.o: ../include/vstream.h
-cleanup_skip.o: ../include/vbuf.h
-cleanup_skip.o: ../include/cleanup_user.h
-cleanup_skip.o: ../include/record.h
-cleanup_skip.o: ../include/vstring.h
-cleanup_skip.o: ../include/rec_type.h
-cleanup_skip.o: cleanup.h
-cleanup_skip.o: ../include/argv.h
-cleanup_skip.o: ../include/maps.h
-cleanup_skip.o: ../include/dict.h
-cleanup_skip.o: ../include/tok822.h
-cleanup_skip.o: ../include/resolve_clnt.h
-cleanup_skip.o: ../include/been_here.h
-cleanup_skip.o: ../include/mail_stream.h
cleanup_state.o: cleanup_state.c
cleanup_state.o: ../include/sys_defs.h
cleanup_state.o: ../include/mymalloc.h
cleanup_state.o: ../include/vstring.h
cleanup_state.o: ../include/vbuf.h
-cleanup_state.o: ../include/vstream.h
+cleanup_state.o: ../include/argv.h
cleanup_state.o: ../include/been_here.h
cleanup_state.o: ../include/mail_params.h
cleanup_state.o: cleanup.h
-cleanup_state.o: ../include/argv.h
+cleanup_state.o: ../include/vstream.h
cleanup_state.o: ../include/maps.h
cleanup_state.o: ../include/dict.h
cleanup_state.o: ../include/tok822.h
#include <mail_stream.h>
#include <mail_addr.h>
#include <ext_prop.h>
+#include <record.h>
+#include <rec_type.h>
/* Single-threaded server skeleton. */
#include "cleanup.h"
- /*
- * Global state: any queue files that we have open, so that the error
- * handler can clean up in case of trouble.
- */
-char *cleanup_path; /* queue file name */
-
/*
* Tunable parameters.
*/
static void cleanup_service(VSTREAM *src, char *unused_service, char **argv)
{
- char *junk;
- static char *log_queues[] = {
- MAIL_QUEUE_DEFER,
- MAIL_QUEUE_BOUNCE,
- 0,
- };
- char **cpp;
+ VSTRING *buf = vstring_alloc(100);
+ CLEANUP_STATE *state;
+ int flags;
+ int type;
/*
* Sanity check. This service takes no command-line arguments.
msg_fatal("unexpected command-line argument: %s", argv[0]);
/*
- * Initialize.
- */
- cleanup_state_alloc();
- cleanup_src = src;
-
- /*
- * Open the queue file. Send the queue ID to the client so they can use
- * it for logging purposes. For example, the SMTP server sends the queue
- * id to the SMTP client after completion of the DATA command; and when
- * the local delivery agent forwards a message, it logs the new queue id
- * together with the old one. All this is done to make it easier for mail
- * admins to follow a message while it hops from machine to machine.
- *
- * Save the queue file name, so that the runtime error handler can clean up
- * in case of problems.
+ * Open a queue file and initialize state.
*/
- cleanup_handle = mail_stream_file(MAIL_QUEUE_INCOMING,
- MAIL_CLASS_PUBLIC, MAIL_SERVICE_QUEUE);
- cleanup_dst = cleanup_handle->stream;
- cleanup_path = mystrdup(VSTREAM_PATH(cleanup_dst));
- cleanup_queue_id = mystrdup(cleanup_handle->id);
- if (msg_verbose)
- msg_info("cleanup_service: open %s", cleanup_path);
-
- /*
- * If there is a time to get rid of spurious bounce/defer log files, this
- * is it. The down side is that this costs performance for every message,
- * while the probability of spurious bounce/defer log files is quite low.
- * Perhaps we should put the queue file ID inside the defer and bounce
- * files, so that the bounce and defer daemons can figure out if a file
- * is a left-over from a previous message instance. For now, we play safe
- * and check each time a new queue file is created.
- */
- for (cpp = log_queues; *cpp; cpp++) {
- if (mail_queue_remove(*cpp, cleanup_queue_id) == 0)
- msg_warn("%s: removed spurious %s log", *cpp, cleanup_queue_id);
- else if (errno != ENOENT)
- msg_fatal("%s: remove %s log: %m", *cpp, cleanup_queue_id);
- }
+ state = cleanup_open();
/*
* Send the queue id to the client. Read client processing options. If we
* can't read the client processing options we can pretty much forget
* about the whole operation.
*/
- mail_print(cleanup_src, "%s", cleanup_queue_id);
- if (mail_scan(src, "%d", &cleanup_flags) != 1) {
- cleanup_errs |= CLEANUP_STAT_BAD;
- cleanup_flags = 0;
- }
-
- /*
- * If the client requests us to do the bouncing in case of problems,
- * throw away the input only in case of real show-stopper errors, such as
- * unrecognizable data (which should never happen) or insufficient space
- * for the queue file (which will happen occasionally). Otherwise, throw
- * away the input after any error. See the CLEANUP_OUT_OK() definition.
- */
- if (cleanup_flags & CLEANUP_FLAG_BOUNCE) {
- cleanup_err_mask =
- (CLEANUP_STAT_BAD | CLEANUP_STAT_WRITE | CLEANUP_STAT_SIZE);
- } else {
- cleanup_err_mask = ~0;
+ mail_print(src, "%s", state->queue_id);
+ if (mail_scan(src, "%d", &flags) != 1) {
+ state->errs |= CLEANUP_STAT_BAD;
+ flags = 0;
}
+ cleanup_control(state, flags);
/*
+ * XXX Rely on the front-end programs to enforce record size limits.
+ *
* First, copy the envelope records to the queue file. Then, copy the
* message content (headers and body). Finally, attach any information
* extracted from message headers.
*/
- cleanup_envelope();
- if (CLEANUP_OUT_OK())
- cleanup_message();
- if (CLEANUP_OUT_OK())
- cleanup_extracted();
-
- /*
- * Now that we have captured the entire message, see if there are any
- * other errors. For example, if the message needs to be bounced for lack
- * of recipients. We want to turn on the execute bits on a file only when
- * we want the queue manager to process it.
- */
- if (cleanup_recip == 0)
- cleanup_errs |= CLEANUP_STAT_RCPT;
-
- /*
- * If there are no errors, be very picky about queue file write errors
- * because we are about to tell the sender that it can throw away its
- * copy of the message.
- */
- if (cleanup_errs == 0)
- cleanup_errs |= mail_stream_finish(cleanup_handle);
- else
- mail_stream_cleanup(cleanup_handle);
- cleanup_handle = 0;
- cleanup_dst = 0;
+ while (CLEANUP_OUT_OK(state)) {
+ if ((type = rec_get(src, buf, 0)) < 0) {
+ state->errs |= CLEANUP_STAT_BAD;
+ break;
+ }
+ CLEANUP_RECORD(state, type, vstring_str(buf), VSTRING_LEN(buf));
+ if (type == REC_TYPE_END)
+ break;
+ }
/*
- * If there was an error, remove the queue file, after optionally
- * bouncing it. An incomplete message should never be bounced: it was
- * canceled by the client, and may not even have an address to bounce to.
- * That last test is redundant but we keep it just for robustness.
- *
- * If we are responsible for bouncing a message, we must must report success
- * to the client unless the bounce message file could not be written
- * (which is just as bad as not being able to write the message queue
- * file in the first place).
- *
- * Do not log the arrival of a message that will be bounced by the client.
+ * Keep reading in case of problems, so that the sender is ready to
+ * receive our status report.
*/
-#define CAN_BOUNCE() \
- ((cleanup_errs & (CLEANUP_STAT_BAD | CLEANUP_STAT_WRITE)) == 0 \
- && cleanup_sender != 0 \
- && (cleanup_flags & CLEANUP_FLAG_BOUNCE) != 0)
-
- if (cleanup_errs) {
- if (CAN_BOUNCE()) {
- if (bounce_append(BOUNCE_FLAG_CLEAN,
- cleanup_queue_id, cleanup_recip ?
- cleanup_recip : "", "cleanup", cleanup_time,
- "%s", cleanup_strerror(cleanup_errs)) == 0
- && bounce_flush(BOUNCE_FLAG_CLEAN,
- MAIL_QUEUE_INCOMING,
- cleanup_queue_id, cleanup_sender) == 0) {
- cleanup_errs = 0;
- } else {
- msg_warn("%s: bounce message failure", cleanup_queue_id);
- cleanup_errs = CLEANUP_STAT_WRITE;
- }
- }
- if (REMOVE(cleanup_path))
- msg_warn("remove %s: %m", cleanup_path);
+ if (CLEANUP_OUT_OK(state) == 0) {
+ if ((state->errs & CLEANUP_STAT_CONT) == 0)
+ msg_warn("%s: skipping further client input", state->queue_id);
+ while ((type = rec_get(src, buf, 0)) > 0
+ && type != REC_TYPE_END)
+ /* void */ ;
}
/*
- * Report the completion status back to the client. Order of operations
- * matters very much: make sure that our queue file will not be deleted
- * by the error handler AFTER we have taken responsibility for delivery.
- * Better to deliver twice than to lose mail.
+ * Finish this message, and report the result status to the client.
*/
- junk = cleanup_path;
- cleanup_path = 0; /* don't delete upon error */
- mail_print(cleanup_src, "%d", cleanup_errs);/* we're committed now */
- if (msg_verbose)
- msg_info("cleanup_service: status %d", cleanup_errs);
- myfree(junk);
+ mail_print(src, "%d", cleanup_close(state));/* we're committed now */
/*
- * Cleanup internal state. This is simply complementary to the
- * initializations at the beginning of this routine.
+ * Cleanup.
*/
- cleanup_state_free();
-}
-
-/* cleanup_all - callback for the runtime error handler */
-
-static void cleanup_all(void)
-{
- if (cleanup_path && REMOVE(cleanup_path))
- msg_warn("cleanup_all: remove %s: %m", cleanup_path);
+ vstring_free(buf);
}
/* cleanup_sig - cleanup after signal */
/*
* These state variables are accessed by many functions, and there is only
- * one instance of each. Rather than passing around lots and lots of
- * parameters, or passing them around as part of a huge structure, we just
- * make the variables global, because that is what they are.
- */
-extern VSTRING *cleanup_inbuf; /* read buffer */
-extern VSTRING *cleanup_temp1; /* scratch buffer, local use only */
-extern VSTRING *cleanup_temp2; /* scratch buffer, local use only */
-extern VSTREAM *cleanup_src; /* current input stream */
-extern VSTREAM *cleanup_dst; /* current output stream */
-extern MAIL_STREAM *cleanup_handle; /* mail stream handle */
-extern char *cleanup_queue_id; /* queue file basename */
-extern time_t cleanup_time; /* posting time */
-extern char *cleanup_fullname; /* envelope sender full name */
-extern char *cleanup_sender; /* envelope sender address */
-extern char *cleanup_from; /* From: address */
-extern char *cleanup_resent_from; /* Resent-From: address */
-extern char *cleanup_recip; /* envelope recipient address */
-extern char *cleanup_return_receipt; /* return-receipt address */
-extern char *cleanup_errors_to; /* errors-to address */
-extern int cleanup_flags; /* processing options */
-extern int cleanup_errs; /* any badness experienced */
-extern int cleanup_err_mask; /* allowed badness */
-extern VSTRING *cleanup_header_buf; /* multi-record header */
-extern int cleanup_headers_seen; /* which headers were seen */
-extern int cleanup_hop_count; /* count of received: headers */
-extern ARGV *cleanup_recipients; /* recipients from regular headers */
-extern ARGV *cleanup_resent_recip; /* recipients from resent headers */
-extern char *cleanup_resent; /* any resent- header seen */
-extern BH_TABLE *cleanup_dups; /* recipient dup filter */
+ * one instance of each per message.
+ */
+typedef struct CLEANUP_STATE {
+ VSTRING *temp1; /* scratch buffer, local use only */
+ VSTRING *temp2; /* scratch buffer, local use only */
+ VSTREAM *src; /* current input stream */
+ VSTREAM *dst; /* current output stream */
+ MAIL_STREAM *handle; /* mail stream handle */
+ char *queue_id; /* queue file basename */
+ time_t time; /* posting time */
+ char *fullname; /* envelope sender full name */
+ char *sender; /* envelope sender address */
+ char *from; /* From: address */
+ char *resent_from; /* Resent-From: address */
+ char *recip; /* envelope recipient address */
+ char *return_receipt; /* return-receipt address */
+ char *errors_to; /* errors-to address */
+ int flags; /* processing options */
+ int errs; /* any badness experienced */
+ int err_mask; /* allowed badness */
+ VSTRING *header_buf; /* multi-record header */
+ int headers_seen; /* which headers were seen */
+ int hop_count; /* count of received: headers */
+ ARGV *recipients; /* recipients from regular headers */
+ ARGV *resent_recip; /* recipients from resent headers */
+ char *resent; /* any resent- header seen */
+ BH_TABLE *dups; /* recipient dup filter */
+ long warn_time; /* cleanup_envelope.c */
+ void (*action) (struct CLEANUP_STATE *, int, char *, int);
+ long mesg_offset; /* start of message segment */
+ long data_offset; /* start of message content */
+} CLEANUP_STATE;
/*
* Mappings.
/*
* cleanup_state.c
*/
-extern void cleanup_state_alloc(void);
-extern void cleanup_state_free(void);
+extern CLEANUP_STATE *cleanup_state_alloc(void);
+extern void cleanup_state_free(CLEANUP_STATE *);
+
+ /*
+ * cleanup_api.c
+ */
+extern CLEANUP_STATE *cleanup_open(void);
+extern void cleanup_control(CLEANUP_STATE *, int);
+extern int cleanup_close(CLEANUP_STATE *);
+extern void cleanup_all(void);
+
+#define CLEANUP_RECORD(s, t, b, l) ((s)->action((s), (t), (b), (l)))
/*
* cleanup_out.c
*/
-extern void cleanup_out(int, char *, int);
-extern void cleanup_out_string(int, char *);
-extern void cleanup_out_format(int, char *,...);
+extern void cleanup_out(CLEANUP_STATE *, int, char *, int);
+extern void cleanup_out_string(CLEANUP_STATE *, int, char *);
+extern void cleanup_out_format(CLEANUP_STATE *, int, char *,...);
-#define CLEANUP_OUT_BUF(t, b) \
- cleanup_out((t), vstring_str((b)), VSTRING_LEN((b)))
+#define CLEANUP_OUT_BUF(s, t, b) \
+ cleanup_out((s), (t), vstring_str((b)), VSTRING_LEN((b)))
-#define CLEANUP_OUT_OK() \
- ((cleanup_errs & cleanup_err_mask) == 0)
+#define CLEANUP_OUT_OK(s) (((s)->errs & (s)->err_mask) == 0)
/*
* cleanup_envelope.c
*/
-extern void cleanup_envelope(void);
+extern void cleanup_envelope_init(CLEANUP_STATE *, int, char *, int);
+extern void cleanup_envelope_process(CLEANUP_STATE *, int, char *, int);
/*
* cleanup_message.c
*/
-extern void cleanup_message(void);
+extern void cleanup_message_init(CLEANUP_STATE *, int, char *, int);
+extern void cleanup_message_header(CLEANUP_STATE *, int, char *, int);
+extern void cleanup_message_body(CLEANUP_STATE *, int, char *, int);
/*
* cleanup_extracted.c
*/
-extern void cleanup_extracted(void);
-
- /*
- * cleanup_skip.o
- */
-extern void cleanup_skip(void);
+extern void cleanup_extracted_init(CLEANUP_STATE *, int, char *, int);
+extern void cleanup_extracted_process(CLEANUP_STATE *, int, char *, int);
/*
* cleanup_rewrite.c
/*
* cleanup_map11.c
*/
-extern void cleanup_map11_external(VSTRING *, MAPS *, int);
-extern void cleanup_map11_internal(VSTRING *, MAPS *, int);
-extern void cleanup_map11_tree(TOK822 *, MAPS *, int);
+extern void cleanup_map11_external(CLEANUP_STATE *, VSTRING *, MAPS *, int);
+extern void cleanup_map11_internal(CLEANUP_STATE *, VSTRING *, MAPS *, int);
+extern void cleanup_map11_tree(CLEANUP_STATE *, TOK822 *, MAPS *, int);
/*
* cleanup_map1n.c
*/
-ARGV *cleanup_map1n_internal(char *, MAPS *, int);
+ARGV *cleanup_map1n_internal(CLEANUP_STATE *, char *, MAPS *, int);
/*
* cleanup_masquerade.c
/*
* Cleanup_recipient.c
*/
-extern void cleanup_out_recipient(char *);
+extern void cleanup_out_recipient(CLEANUP_STATE *, char *);
/* LICENSE
/* .ad
--- /dev/null
+/*++
+/* NAME
+/* cleanup_api 3
+/* SUMMARY
+/* callable interface
+/* SYNOPSIS
+/* #include "cleanup.h"
+/*
+/* char *cleanup_path;
+/*
+/* void cleanup_all()
+/*
+/* CLEANUP_STATE *cleanup_open()
+/*
+/* void cleanup_control(state, flags)
+/* CLEANUP_STATE *state;
+/* int flags;
+/*
+/* void CLEANUP_RECORD(state, type, buf, len)
+/* CLEANUP_STATE *state;
+/* int type;
+/* char *buf;
+/* int len;
+/*
+/* int cleanup_close(state)
+/* CLEANUP_STATE *state;
+/* DESCRIPTION
+/* This module implements a callable interface to the cleanup service.
+/* For a description of the cleanup service, see cleanup(8).
+/*
+/* cleanup_path is a null pointer or it is the name of the queue
+/* file that currently is being written. This information is used
+/* by cleanup_all() to clean up in case of fatal errors.
+/*
+/* cleanup_open() creates a new queue file and performs other
+/* initialization. The result is a handle that should be given
+/* to the cleanup_control(), cleanup_record() and cleanup_close()
+/* routines. The name of the queue file is in the queue_id result
+/* structure member.
+/*
+/* cleanup_control() processes flags specified by the caller.
+/* These flags control what happens in case of data errors.
+/*
+/* CLEANUP_RECORD() processes one queue file record and maintains
+/* a little state machine. CLEANUP_RECORD() is a macro that calls
+/* the appropriate routine depending on what section of a queue file
+/* is being processed. In order to find out if a file is corrupted,
+/* the caller can test the CLEANUP_OUT_OK(state) macro. The result is
+/* false when further message processing is futile.
+/*
+/* cleanup_close() finishes a queue file. In case of any errors,
+/* the file is removed. The result status is non-zero in case of
+/* problems. use cleanup_strerror() to translate the result into
+/* human_readable text.
+/*
+/* cleanup_all() should be called in case of fatal error, in order
+/* to remove an incomplete queue file. Typically one registers a
+/* msg_cleanup() handler and a signal() handler that call
+/* cleanup_all() before terminating the process.
+/* STANDARDS
+/* RFC 822 (ARPA Internet Text Messages)
+/* DIAGNOSTICS
+/* Problems and transactions are logged to \fBsyslogd\fR(8).
+/* SEE ALSO
+/* cleanup(8) cleanup service description.
+/* FILES
+/* /etc/postfix/canonical*, canonical mapping table
+/* /etc/postfix/virtual*, virtual mapping table
+/* 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
+/*--*/
+
+/* System library. */
+
+#include <sys_defs.h>
+#include <errno.h>
+
+/* Utility library. */
+
+#include <msg.h>
+#include <vstring.h>
+#include <mymalloc.h>
+
+/* Global library. */
+
+#include <cleanup_user.h>
+#include <mail_queue.h>
+#include <mail_proto.h>
+#include <opened.h>
+#include <bounce.h>
+#include <mail_params.h>
+#include <mail_stream.h>
+#include <mail_addr.h>
+
+/* Application-specific. */
+
+#include "cleanup.h"
+
+ /*
+ * Global state: any queue files that we have open, so that the error
+ * handler can clean up in case of trouble.
+ */
+char *cleanup_path; /* queue file name */
+
+/* cleanup_open - open queue file and initialize */
+
+CLEANUP_STATE * cleanup_open(void)
+{
+ CLEANUP_STATE *state;
+ static char *log_queues[] = {
+ MAIL_QUEUE_DEFER,
+ MAIL_QUEUE_BOUNCE,
+ 0,
+ };
+ char **cpp;
+
+ /*
+ * Initialize.
+ */
+ state = cleanup_state_alloc();
+
+ /*
+ * Open the queue file. Send the queue ID to the client so they can use
+ * it for logging purposes. For example, the SMTP server sends the queue
+ * id to the SMTP client after completion of the DATA command; and when
+ * the local delivery agent forwards a message, it logs the new queue id
+ * together with the old one. All this is done to make it easier for mail
+ * admins to follow a message while it hops from machine to machine.
+ *
+ * Save the queue file name, so that the runtime error handler can clean up
+ * in case of problems.
+ */
+ state->handle = mail_stream_file(MAIL_QUEUE_INCOMING,
+ MAIL_CLASS_PUBLIC, MAIL_SERVICE_QUEUE);
+ state->dst = state->handle->stream;
+ cleanup_path = mystrdup(VSTREAM_PATH(state->dst));
+ state->queue_id = mystrdup(state->handle->id);
+ if (msg_verbose)
+ msg_info("cleanup_open: open %s", cleanup_path);
+
+ /*
+ * If there is a time to get rid of spurious bounce/defer log files, this
+ * is it. The down side is that this costs performance for every message,
+ * while the probability of spurious bounce/defer log files is quite low.
+ * Perhaps we should put the queue file ID inside the defer and bounce
+ * files, so that the bounce and defer daemons can figure out if a file
+ * is a left-over from a previous message instance. For now, we play safe
+ * and check each time a new queue file is created.
+ */
+ for (cpp = log_queues; *cpp; cpp++) {
+ if (mail_queue_remove(*cpp, state->queue_id) == 0)
+ msg_warn("%s: removed spurious %s log", *cpp, state->queue_id);
+ else if (errno != ENOENT)
+ msg_fatal("%s: remove %s log: %m", *cpp, state->queue_id);
+ }
+ return (state);
+}
+
+/* cleanup_control - process client options */
+
+void cleanup_control(CLEANUP_STATE *state, int flags)
+{
+
+ /*
+ * If the client requests us to do the bouncing in case of problems,
+ * throw away the input only in case of real show-stopper errors, such as
+ * unrecognizable data (which should never happen) or insufficient space
+ * for the queue file (which will happen occasionally). Otherwise,
+ * discard input after any lethal error. See the CLEANUP_OUT_OK()
+ * definition.
+ */
+ if ((state->flags = flags) & CLEANUP_FLAG_BOUNCE) {
+ state->err_mask =
+ (CLEANUP_STAT_BAD | CLEANUP_STAT_WRITE | CLEANUP_STAT_SIZE);
+ } else {
+ state->err_mask = CLEANUP_STAT_LETHAL;
+ }
+}
+
+/* cleanup_close - finish queue file */
+
+int cleanup_close(CLEANUP_STATE *state)
+{
+ char *junk;
+ int status;
+
+ /*
+ * Now that we have captured the entire message, see if there are any
+ * other errors. For example, if the message needs to be bounced for lack
+ * of recipients. We want to turn on the execute bits on a file only when
+ * we want the queue manager to process it.
+ */
+ if (state->recip == 0)
+ state->errs |= CLEANUP_STAT_RCPT;
+
+ /*
+ * If there are no errors, be very picky about queue file write errors
+ * because we are about to tell the sender that it can throw away its
+ * copy of the message.
+ */
+ if (state->errs == 0)
+ state->errs |= mail_stream_finish(state->handle);
+ else
+ mail_stream_cleanup(state->handle);
+ state->handle = 0;
+ state->dst = 0;
+
+ /*
+ * If there was an error, remove the queue file, after optionally
+ * bouncing it. An incomplete message should never be bounced: it was
+ * canceled by the client, and may not even have an address to bounce to.
+ * That last test is redundant but we keep it just for robustness.
+ *
+ * If we are responsible for bouncing a message, we must must report success
+ * to the client unless the bounce message file could not be written
+ * (which is just as bad as not being able to write the message queue
+ * file in the first place).
+ *
+ * Do not log the arrival of a message that will be bounced by the client.
+ *
+ * XXX CLEANUP_STAT_LETHAL masks errors that are not directly fatal (e.g.,
+ * header buffer overflow is normally allowed to happen), but that can
+ * indirectly become a problem (e.g., no recipients were extracted from
+ * message headers because we could not process all the message headers).
+ * However, cleanup_strerror() prioritizes errors so that it can report
+ * the cause (e.g., header buffer overflow), which is more useful.
+ * Amazing.
+ */
+#define CAN_BOUNCE() \
+ ((state->errs & (CLEANUP_STAT_BAD | CLEANUP_STAT_WRITE)) == 0 \
+ && state->sender != 0 \
+ && (state->flags & CLEANUP_FLAG_BOUNCE) != 0)
+
+ if (state->errs & CLEANUP_STAT_LETHAL) {
+ if (CAN_BOUNCE()) {
+ if (bounce_recip(BOUNCE_FLAG_CLEAN,
+ MAIL_QUEUE_INCOMING, state->queue_id,
+ state->sender, state->recip ?
+ state->recip : "", "cleanup", state->time,
+ "Message rejected: %s",
+ cleanup_strerror(state->errs)) == 0) {
+ state->errs = 0;
+ } else {
+ msg_warn("%s: bounce message failure", state->queue_id);
+ state->errs = CLEANUP_STAT_WRITE;
+ }
+ }
+ if (REMOVE(cleanup_path))
+ msg_warn("remove %s: %m", cleanup_path);
+ }
+
+ /*
+ * Make sure that our queue file will not be deleted by the error handler
+ * AFTER we have taken responsibility for delivery. Better to deliver
+ * twice than to lose mail.
+ */
+ junk = cleanup_path;
+ cleanup_path = 0; /* don't delete upon error */
+ myfree(junk);
+
+ /*
+ * Cleanup internal state. This is simply complementary to the
+ * initializations at the beginning of cleanup_open().
+ */
+ if (msg_verbose)
+ msg_info("cleanup_close: status %d", state->errs);
+ status = state->errs & CLEANUP_STAT_LETHAL;
+ cleanup_state_free(state);
+ return (status);
+}
+
+/* cleanup_all - callback for the runtime error handler */
+
+void cleanup_all(void)
+{
+ if (cleanup_path && REMOVE(cleanup_path))
+ msg_warn("cleanup_all: remove %s: %m", cleanup_path);
+}
/* SYNOPSIS
/* #include <cleanup.h>
/*
-/* void cleanup_envelope()
+/* void cleanup_envelope_init(state, type, buf, len)
+/* CLEANUP_STATE *state;
+/* int type;
+/* char *buf;
+/* int len;
+/*
+/* void cleanup_envelope_process(state, type, buf, len)
+/* CLEANUP_STATE *state;
+/* int type;
+/* char *buf;
+/* int len;
/* DESCRIPTION
/* This module processes the envelope segment of a mail message.
/* While copying records from input to output it validates the
/* message structure, rewrites sender/recipient addresses
-/* to canonical form, and expands recipients according to
-/* entries in the virtual table.
+/* to canonical form, expands recipients according to
+/* entries in the virtual table, and updates the state structure.
/* LICENSE
/* .ad
/* .fi
#define STR vstring_str
-/* cleanup_envelope - process envelope segment */
+/* cleanup_envelope_init - initialization */
-void cleanup_envelope(void)
+void cleanup_envelope_init(CLEANUP_STATE *state, int type, char *str, int len)
{
- VSTRING *clean_addr = vstring_alloc(100);
- int type = 0;
- long warn_time = 0;
/*
* The message content size record goes first, so it can easily be
* estimate provided by the client. Size goes first so that it it easy to
* produce queue file reports.
*/
- cleanup_out_format(REC_TYPE_SIZE, REC_TYPE_SIZE_FORMAT, 0L);
+ cleanup_out_format(state, REC_TYPE_SIZE, REC_TYPE_SIZE_FORMAT, 0L);
+ state->action = cleanup_envelope_process;
+ cleanup_envelope_process(state, type, str, len);
+}
- /*
- * XXX Rely on the front-end programs to enforce record size limits.
- */
- while (CLEANUP_OUT_OK()) {
- if ((type = rec_get(cleanup_src, cleanup_inbuf, 0)) < 0) {
- cleanup_errs |= CLEANUP_STAT_BAD;
- break;
- }
- if (type == REC_TYPE_MESG) {
- if (cleanup_sender == 0 || cleanup_time == 0) {
- msg_warn("%s: missing sender or time envelope record",
- cleanup_queue_id);
- cleanup_errs |= CLEANUP_STAT_BAD;
- } else {
- if (warn_time == 0 && var_delay_warn_time > 0)
- warn_time = cleanup_time + var_delay_warn_time * 3600L;
- if (warn_time)
- cleanup_out_format(REC_TYPE_WARN, REC_TYPE_WARN_FORMAT,
- warn_time);
- }
- break;
- }
- if (strchr(REC_TYPE_ENVELOPE, type) == 0) {
- msg_warn("%s: unexpected record type %d in envelope",
- cleanup_queue_id, type);
- cleanup_errs |= CLEANUP_STAT_BAD;
- break;
+/* cleanup_envelope_process - process one envelope record */
+
+void cleanup_envelope_process(CLEANUP_STATE *state, int type, char *buf, int len)
+{
+ if (type == REC_TYPE_MESG) {
+ if (state->sender == 0 || state->time == 0) {
+ msg_warn("%s: missing sender or time envelope record",
+ state->queue_id);
+ state->errs |= CLEANUP_STAT_BAD;
+ } else {
+ if (state->warn_time == 0 && var_delay_warn_time > 0)
+ state->warn_time = state->time + var_delay_warn_time * 3600L;
+ if (state->warn_time)
+ cleanup_out_format(state, REC_TYPE_WARN, REC_TYPE_WARN_FORMAT,
+ state->warn_time);
+ state->action = cleanup_message_init;
}
- if (msg_verbose)
- msg_info("envelope %c %s", type, STR(cleanup_inbuf));
-
- if (type == REC_TYPE_TIME) {
- cleanup_time = atol(STR(cleanup_inbuf));
- CLEANUP_OUT_BUF(type, cleanup_inbuf);
- } else if (type == REC_TYPE_FULL) {
- cleanup_fullname = mystrdup(STR(cleanup_inbuf));
- } else if (type == REC_TYPE_FROM) {
- cleanup_rewrite_internal(clean_addr, STR(cleanup_inbuf));
- if (cleanup_send_canon_maps)
- cleanup_map11_internal(clean_addr, cleanup_send_canon_maps,
+ return;
+ }
+ if (strchr(REC_TYPE_ENVELOPE, type) == 0) {
+ msg_warn("%s: unexpected record type %d in envelope",
+ state->queue_id, type);
+ state->errs |= CLEANUP_STAT_BAD;
+ return;
+ }
+ if (msg_verbose)
+ msg_info("envelope %c %.*s", type, len, buf);
+
+ if (type == REC_TYPE_TIME) {
+ state->time = atol(buf);
+ cleanup_out(state, type, buf, len);
+ } else if (type == REC_TYPE_FULL) {
+ state->fullname = mystrdup(buf);
+ } else if (type == REC_TYPE_FROM) {
+ VSTRING *clean_addr = vstring_alloc(100);
+
+ cleanup_rewrite_internal(clean_addr, buf);
+ if (cleanup_send_canon_maps)
+ cleanup_map11_internal(state, clean_addr, cleanup_send_canon_maps,
cleanup_ext_prop_mask & EXT_PROP_CANONICAL);
- if (cleanup_comm_canon_maps)
- cleanup_map11_internal(clean_addr, cleanup_comm_canon_maps,
+ if (cleanup_comm_canon_maps)
+ cleanup_map11_internal(state, clean_addr, cleanup_comm_canon_maps,
cleanup_ext_prop_mask & EXT_PROP_CANONICAL);
- if (cleanup_masq_domains)
- cleanup_masquerade_internal(clean_addr, cleanup_masq_domains);
- CLEANUP_OUT_BUF(type, clean_addr);
- if (cleanup_sender == 0)
- cleanup_sender = mystrdup(STR(clean_addr));
- } else if (type == REC_TYPE_RCPT) {
- if (cleanup_sender == 0) { /* protect showq */
- msg_warn("%s: envelope recipient precedes sender",
- cleanup_queue_id);
- cleanup_errs |= CLEANUP_STAT_BAD;
- break;
- }
- cleanup_rewrite_internal(clean_addr, *STR(cleanup_inbuf) ?
- STR(cleanup_inbuf) : var_empty_addr);
- if (cleanup_rcpt_canon_maps)
- cleanup_map11_internal(clean_addr, cleanup_rcpt_canon_maps,
+ if (cleanup_masq_domains)
+ cleanup_masquerade_internal(clean_addr, cleanup_masq_domains);
+ CLEANUP_OUT_BUF(state, type, clean_addr);
+ if (state->sender == 0)
+ state->sender = mystrdup(STR(clean_addr));
+ vstring_free(clean_addr);
+ } else if (type == REC_TYPE_RCPT) {
+ VSTRING *clean_addr = vstring_alloc(100);
+
+ if (state->sender == 0) { /* protect showq */
+ msg_warn("%s: envelope recipient precedes sender",
+ state->queue_id);
+ state->errs |= CLEANUP_STAT_BAD;
+ return;
+ }
+ cleanup_rewrite_internal(clean_addr, *buf ?
+ buf : var_empty_addr);
+ if (cleanup_rcpt_canon_maps)
+ cleanup_map11_internal(state, clean_addr, cleanup_rcpt_canon_maps,
cleanup_ext_prop_mask & EXT_PROP_CANONICAL);
- if (cleanup_comm_canon_maps)
- cleanup_map11_internal(clean_addr, cleanup_comm_canon_maps,
+ if (cleanup_comm_canon_maps)
+ cleanup_map11_internal(state, clean_addr, cleanup_comm_canon_maps,
cleanup_ext_prop_mask & EXT_PROP_CANONICAL);
- cleanup_out_recipient(STR(clean_addr));
- if (cleanup_recip == 0)
- cleanup_recip = mystrdup(STR(clean_addr));
- } else if (type == REC_TYPE_WARN) {
- if ((warn_time = atol(STR(cleanup_inbuf))) < 0) {
- cleanup_errs |= CLEANUP_STAT_BAD;
- break;
- }
- } else {
- CLEANUP_OUT_BUF(type, cleanup_inbuf);
+ cleanup_out_recipient(state, STR(clean_addr));
+ if (state->recip == 0)
+ state->recip = mystrdup(STR(clean_addr));
+ vstring_free(clean_addr);
+ } else if (type == REC_TYPE_WARN) {
+ if ((state->warn_time = atol(buf)) < 0) {
+ state->errs |= CLEANUP_STAT_BAD;
+ return;
}
+ } else {
+ cleanup_out(state, type, buf, len);
}
-
- /*
- * XXX Keep reading in case of trouble, so that the sender is ready to
- * receive our status report.
- */
- if (!CLEANUP_OUT_OK())
- if (type >= 0)
- cleanup_skip();
-
- vstring_free(clean_addr);
}
#define STR(x) vstring_str(x)
-/* cleanup_extracted - generate segment with header-extracted information */
+/* cleanup_extracted_init - initialize extracted segment */
-void cleanup_extracted(void)
+void cleanup_extracted_init(CLEANUP_STATE *state, int type, char *buf, int len)
+{
+
+ /*
+ * Start the extracted segment.
+ */
+ cleanup_out_string(state, REC_TYPE_XTRA, "");
+ state->action = cleanup_extracted_process;
+ cleanup_extracted_process(state, type, buf, len);
+}
+
+/* cleanup_extracted_process - process extracted segment */
+
+void cleanup_extracted_process(CLEANUP_STATE *state, int type, char *buf, int unused_len)
{
VSTRING *clean_addr;
ARGV *rcpt;
char **cpp;
- int type;
- /*
- * Do not complain in case of premature EOF - most likely the client
- * aborted the operation.
- *
- * XXX Rely on the front-end programs to enforce record size limits.
- */
- while (CLEANUP_OUT_OK()) {
- if ((type = rec_get(cleanup_src, cleanup_inbuf, 0)) < 0) {
- cleanup_errs |= CLEANUP_STAT_BAD;
- return;
- }
- if (type == REC_TYPE_RRTO) {
- /* XXX Use extracted information instead. */ ;
- } else if (type == REC_TYPE_ERTO) {
- /* XXX Use extracted information instead. */ ;
- } else if (type == REC_TYPE_RCPT) {
- clean_addr = vstring_alloc(100);
- cleanup_rewrite_internal(clean_addr, *STR(cleanup_inbuf) ?
- STR(cleanup_inbuf) : var_empty_addr);
- if (cleanup_rcpt_canon_maps)
- cleanup_map11_internal(clean_addr, cleanup_rcpt_canon_maps,
+ if (type == REC_TYPE_RRTO) {
+ /* XXX Use extracted information instead. */
+ return;
+ }
+ if (type == REC_TYPE_ERTO) {
+ /* XXX Use extracted information instead. */
+ return;
+ }
+ if (type == REC_TYPE_RCPT) {
+ clean_addr = vstring_alloc(100);
+ cleanup_rewrite_internal(clean_addr, *buf ? buf : var_empty_addr);
+ if (cleanup_rcpt_canon_maps)
+ cleanup_map11_internal(state, clean_addr, cleanup_rcpt_canon_maps,
cleanup_ext_prop_mask & EXT_PROP_CANONICAL);
- if (cleanup_comm_canon_maps)
- cleanup_map11_internal(clean_addr, cleanup_comm_canon_maps,
+ if (cleanup_comm_canon_maps)
+ cleanup_map11_internal(state, clean_addr, cleanup_comm_canon_maps,
cleanup_ext_prop_mask & EXT_PROP_CANONICAL);
- cleanup_out_recipient(STR(clean_addr));
- if (cleanup_recip == 0)
- cleanup_recip = mystrdup(STR(clean_addr));
- vstring_free(clean_addr);
- } else if (type == REC_TYPE_END) {
- break;
- } else {
- msg_warn("%s: unexpected record type %d in extracted segment",
- cleanup_queue_id, type);
- cleanup_errs |= CLEANUP_STAT_BAD;
- if (type >= 0)
- cleanup_skip();
- return;
- }
+ cleanup_out_recipient(state, STR(clean_addr));
+ if (state->recip == 0)
+ state->recip = mystrdup(STR(clean_addr));
+ vstring_free(clean_addr);
+ return;
+ }
+ if (type != REC_TYPE_END) {
+ msg_warn("%s: unexpected record type %d in extracted segment",
+ state->queue_id, type);
+ state->errs |= CLEANUP_STAT_BAD;
+ return;
}
/*
- * Start the extracted segment.
- */
- cleanup_out_string(REC_TYPE_XTRA, "");
-
- /*
- * Always emit Return-Receipt-To and Errors-To records, and always emit
- * them ahead of extracted recipients, so that the queue manager does not
- * waste lots of time searching through large numbers of recipient
- * addresses.
+ * Always emit Return-Receipt-To and Errors-To records, and always try to
+ * emit them ahead of extracted recipients, so that the queue manager
+ * does not waste lots of time searching through large numbers of
+ * recipient addresses.
*/
- cleanup_out_string(REC_TYPE_RRTO, cleanup_return_receipt ?
- cleanup_return_receipt : "");
+ cleanup_out_string(state, REC_TYPE_RRTO, state->return_receipt ?
+ state->return_receipt : "");
- cleanup_out_string(REC_TYPE_ERTO, cleanup_errors_to ?
- cleanup_errors_to : cleanup_sender);
+ cleanup_out_string(state, REC_TYPE_ERTO, state->errors_to ?
+ state->errors_to : state->sender);
/*
* Optionally account for missing recipient envelope records.
*
+ * Don't extract recipients when some header was too long. We have
+ * incomplete information.
+ *
* XXX Code duplication from cleanup_envelope.c. This should be in one
* place.
*/
- if (cleanup_recip == 0) {
- rcpt = (cleanup_resent[0] ? cleanup_resent_recip : cleanup_recipients);
+ if (state->recip == 0 && (state->errs & CLEANUP_STAT_HOVFL) == 0) {
+ rcpt = (state->resent[0] ? state->resent_recip : state->recipients);
if (*var_always_bcc && rcpt->argv[0]) {
clean_addr = vstring_alloc(100);
cleanup_rewrite_internal(clean_addr, var_always_bcc);
if (cleanup_rcpt_canon_maps)
- cleanup_map11_internal(clean_addr, cleanup_rcpt_canon_maps,
+ cleanup_map11_internal(state, clean_addr, cleanup_rcpt_canon_maps,
cleanup_ext_prop_mask & EXT_PROP_CANONICAL);
if (cleanup_comm_canon_maps)
- cleanup_map11_internal(clean_addr, cleanup_comm_canon_maps,
+ cleanup_map11_internal(state, clean_addr, cleanup_comm_canon_maps,
cleanup_ext_prop_mask & EXT_PROP_CANONICAL);
argv_add(rcpt, STR(clean_addr), (char *) 0);
vstring_free(clean_addr);
}
argv_terminate(rcpt);
- for (cpp = rcpt->argv; CLEANUP_OUT_OK() && *cpp; cpp++)
- cleanup_out_recipient(*cpp);
+ for (cpp = rcpt->argv; CLEANUP_OUT_OK(state) && *cpp; cpp++)
+ cleanup_out_recipient(state, *cpp);
if (rcpt->argv[0])
- cleanup_recip = mystrdup(rcpt->argv[0]);
+ state->recip = mystrdup(rcpt->argv[0]);
}
/*
* Terminate the extracted segment.
*/
- cleanup_out_string(REC_TYPE_END, "");
+ cleanup_out_string(state, REC_TYPE_END, "");
}
/* SYNOPSIS
/* #include <cleanup.h>
/*
-/* void cleanup_map11_external(addr, maps, propagate)
+/* void cleanup_map11_external(state, addr, maps, propagate)
+/* CLEANUP_STATE *state;
/* VSTRING *addr;
/* MAPS *maps;
/* int propagate;
/*
-/* void cleanup_map11_internal(addr, maps, propagate)
+/* void cleanup_map11_internal(state, addr, maps, propagate)
+/* CLEANUP_STATE *state;
/* VSTRING *addr;
/* MAPS *maps;
/* int propagate;
/*
-/* void cleanup_map11_tree(tree, maps, propagate)
+/* void cleanup_map11_tree(state, tree, maps, propagate)
+/* CLEANUP_STATE *state;
/* TOK822 *tree;
/* MAPS *maps;
/* int propagate;
/* subjected to another iteration of rewriting and mapping.
/* Recursion continues until an address maps onto itself,
/* or until an unreasonable recursion level is reached.
-/* An unmatched address extension is propagated when
+/* An unmatched address extension is propagated when
/* \fIpropagate\fR is non-zero.
/*
/* cleanup_map11_external() looks up the external (quoted) string
/* cleanup_map11_external - one-to-one table lookups */
-void cleanup_map11_external(VSTRING *addr, MAPS *maps, int propagate)
+void cleanup_map11_external(CLEANUP_STATE *state, VSTRING *addr,
+ MAPS *maps, int propagate)
{
int count;
int expand_to_self;
if ((new_addr = mail_addr_map(maps, STR(addr), propagate)) != 0) {
if (new_addr->argc > 1)
msg_warn("%s: multi-valued %s entry for %s",
- cleanup_queue_id, maps->title, STR(addr));
+ state->queue_id, maps->title, STR(addr));
saved_addr = mystrdup(STR(addr));
vstring_strcpy(addr, new_addr->argv[0]);
expand_to_self = !strcasecmp(saved_addr, STR(addr));
return;
} else if (dict_errno != 0) {
msg_warn("%s: %s map lookup problem for %s",
- cleanup_queue_id, maps->title, STR(addr));
- cleanup_errs |= CLEANUP_STAT_WRITE;
+ state->queue_id, maps->title, STR(addr));
+ state->errs |= CLEANUP_STAT_WRITE;
return;
} else {
return;
}
}
msg_warn("%s: unreasonable %s map nesting for %s",
- cleanup_queue_id, maps->title, STR(addr));
+ state->queue_id, maps->title, STR(addr));
}
/* cleanup_map11_tree - rewrite address node */
-void cleanup_map11_tree(TOK822 *tree, MAPS *maps, int propagate)
+void cleanup_map11_tree(CLEANUP_STATE *state, TOK822 *tree,
+ MAPS *maps, int propagate)
{
VSTRING *temp = vstring_alloc(100);
* the place.
*/
tok822_externalize(temp, tree->head, TOK822_STR_DEFL);
- cleanup_map11_external(temp, maps, propagate);
+ cleanup_map11_external(state, temp, maps, propagate);
tok822_free_tree(tree->head);
tree->head = tok822_scan(STR(temp), &tree->tail);
vstring_free(temp);
/* cleanup_map11_internal - rewrite address internal form */
-void cleanup_map11_internal(VSTRING *addr, MAPS *maps, int propagate)
+void cleanup_map11_internal(CLEANUP_STATE *state, VSTRING *addr,
+ MAPS *maps, int propagate)
{
VSTRING *temp = vstring_alloc(100);
* the place.
*/
quote_822_local(temp, STR(addr));
- cleanup_map11_external(temp, maps, propagate);
+ cleanup_map11_external(state, temp, maps, propagate);
unquote_822_local(addr, STR(temp));
vstring_free(temp);
}
/* cleanup_map1n_internal - one-to-many table lookups */
-ARGV *cleanup_map1n_internal(char *addr, MAPS *maps, int propagate)
+ARGV *cleanup_map1n_internal(CLEANUP_STATE *state, char *addr,
+ MAPS *maps, int propagate)
{
ARGV *argv;
ARGV *lookup;
for (expand_to_self = 0, arg = 0; arg < argv->argc; arg++) {
if (argv->argc > MAX_EXPANSION) {
msg_warn("%s: unreasonable %s map expansion size for %s",
- cleanup_queue_id, maps->title, addr);
+ state->queue_id, maps->title, addr);
break;
}
for (count = 0; /* void */ ; count++) {
if (count >= MAX_RECURSION) {
msg_warn("%s: unreasonable %s map nesting for %s",
- cleanup_queue_id, maps->title, addr);
+ state->queue_id, maps->title, addr);
break;
}
if ((lookup = mail_addr_map(maps, argv->argv[arg], propagate)) != 0) {
saved_lhs = mystrdup(argv->argv[arg]);
for (i = 0; i < lookup->argc; i++) {
- unquote_822_local(cleanup_temp1, lookup->argv[i]);
- if (strcasecmp(saved_lhs, STR(cleanup_temp1)) == 0)
+ unquote_822_local(state->temp1, lookup->argv[i]);
+ if (strcasecmp(saved_lhs, STR(state->temp1)) == 0)
expand_to_self = 1;
if (i == 0) {
- UPDATE(argv->argv[arg], STR(cleanup_temp1));
+ UPDATE(argv->argv[arg], STR(state->temp1));
} else {
- argv_add(argv, STR(cleanup_temp1), ARGV_END);
+ argv_add(argv, STR(state->temp1), ARGV_END);
argv_terminate(argv);
}
}
return (argv);
} else if (dict_errno != 0) {
msg_warn("%s: %s map lookup problem for %s",
- cleanup_queue_id, maps->title, addr);
- cleanup_errs |= CLEANUP_STAT_WRITE;
+ state->queue_id, maps->title, addr);
+ state->errs |= CLEANUP_STAT_WRITE;
return (argv);
} else {
break;
/* SYNOPSIS
/* #include "cleanup.h"
/*
-/* void cleanup_message(void)
+/* void cleanup_message_init(state, type, buf, len)
+/* CLEANUP_STATE *state;
+/* int type;
+/* char *buf;
+/*
+/* void cleanup_message_process(state, type, buf, len)
+/* CLEANUP_STATE *state;
+/* int type;
+/* char *buf;
+/* int len;
/* DESCRIPTION
/* This module processes message content segments.
/* While copying records from input to output, it validates
/* cleanup_out_header - output one header as a bunch of records */
-static void cleanup_out_header(void)
+static void cleanup_out_header(CLEANUP_STATE *state)
{
- char *start = vstring_str(cleanup_header_buf);
+ char *start = vstring_str(state->header_buf);
char *line;
char *next_line;
/*
* Prepend a tab to continued header lines that went through the address
- * rewriting machinery. See cleanup_fold_header() below for the form of
- * such header lines. NB: This code destroys the header. We could try to
- * avoid clobbering it, but we're not going to use the data any further.
+ * rewriting machinery. See cleanup_fold_header(state) below for the form
+ * of such header lines. NB: This code destroys the header. We could try
+ * to avoid clobbering it, but we're not going to use the data any
+ * further.
*/
for (line = start; line; line = next_line) {
next_line = split_at(line, '\n');
if (line == start || ISSPACE(*line)) {
- cleanup_out_string(REC_TYPE_NORM, line);
+ cleanup_out_string(state, REC_TYPE_NORM, line);
} else {
- cleanup_out_format(REC_TYPE_NORM, "\t%s", line);
+ cleanup_out_format(state, REC_TYPE_NORM, "\t%s", line);
}
}
}
/* cleanup_fold_header - wrap address list header */
-static void cleanup_fold_header(void)
+static void cleanup_fold_header(CLEANUP_STATE *state)
{
- char *start_line = vstring_str(cleanup_header_buf);
+ char *start_line = vstring_str(state->header_buf);
char *end_line;
char *next_line;
char *line;
}
next_line = *end_line ? end_line + 1 : 0;
}
- cleanup_out_header();
+ cleanup_out_header(state);
}
/* cleanup_extract_internal - save unquoted copy of extracted address */
/* cleanup_rewrite_sender - sender address rewriting */
-static void cleanup_rewrite_sender(HEADER_OPTS *hdr_opts)
+static void cleanup_rewrite_sender(CLEANUP_STATE *state, HEADER_OPTS *hdr_opts)
{
TOK822 *tree;
TOK822 **addr_list;
* sender addresses, and regenerate the header line. Finally, pipe the
* result through the header line folding routine.
*/
- tree = tok822_parse(vstring_str(cleanup_header_buf)
+ tree = tok822_parse(vstring_str(state->header_buf)
+ strlen(hdr_opts->name) + 1);
addr_list = tok822_grep(tree, TOK822_ADDR);
for (tpp = addr_list; *tpp; tpp++) {
cleanup_rewrite_tree(*tpp);
if (cleanup_send_canon_maps)
- cleanup_map11_tree(*tpp, cleanup_send_canon_maps,
+ cleanup_map11_tree(state, *tpp, cleanup_send_canon_maps,
cleanup_ext_prop_mask & EXT_PROP_CANONICAL);
if (cleanup_comm_canon_maps)
- cleanup_map11_tree(*tpp, cleanup_comm_canon_maps,
+ cleanup_map11_tree(state, *tpp, cleanup_comm_canon_maps,
cleanup_ext_prop_mask & EXT_PROP_CANONICAL);
if (cleanup_masq_domains)
cleanup_masquerade_tree(*tpp, cleanup_masq_domains);
- if (hdr_opts->type == HDR_FROM && cleanup_from == 0)
- cleanup_from = cleanup_extract_internal(cleanup_header_buf, *tpp);
- if (hdr_opts->type == HDR_RESENT_FROM && cleanup_resent_from == 0)
- cleanup_resent_from =
- cleanup_extract_internal(cleanup_header_buf, *tpp);
+ if (hdr_opts->type == HDR_FROM && state->from == 0)
+ state->from = cleanup_extract_internal(state->header_buf, *tpp);
+ if (hdr_opts->type == HDR_RESENT_FROM && state->resent_from == 0)
+ state->resent_from =
+ cleanup_extract_internal(state->header_buf, *tpp);
}
- vstring_sprintf(cleanup_header_buf, "%s: ", hdr_opts->name);
- tok822_externalize(cleanup_header_buf, tree, TOK822_STR_HEAD);
+ vstring_sprintf(state->header_buf, "%s: ", hdr_opts->name);
+ tok822_externalize(state->header_buf, tree, TOK822_STR_HEAD);
myfree((char *) addr_list);
tok822_free_tree(tree);
if ((hdr_opts->flags & HDR_OPT_DROP) == 0)
- cleanup_fold_header();
+ cleanup_fold_header(state);
}
/* cleanup_rewrite_recip - recipient address rewriting */
-static void cleanup_rewrite_recip(HEADER_OPTS *hdr_opts)
+static void cleanup_rewrite_recip(CLEANUP_STATE *state, HEADER_OPTS *hdr_opts)
{
TOK822 *tree;
TOK822 **addr_list;
* recipient addresses, and regenerate the header line. Finally, pipe the
* result through the header line folding routine.
*/
- tree = tok822_parse(vstring_str(cleanup_header_buf)
+ tree = tok822_parse(vstring_str(state->header_buf)
+ strlen(hdr_opts->name) + 1);
addr_list = tok822_grep(tree, TOK822_ADDR);
for (tpp = addr_list; *tpp; tpp++) {
cleanup_rewrite_tree(*tpp);
if (cleanup_rcpt_canon_maps)
- cleanup_map11_tree(*tpp, cleanup_rcpt_canon_maps,
+ cleanup_map11_tree(state, *tpp, cleanup_rcpt_canon_maps,
cleanup_ext_prop_mask & EXT_PROP_CANONICAL);
if (cleanup_comm_canon_maps)
- cleanup_map11_tree(*tpp, cleanup_comm_canon_maps,
+ cleanup_map11_tree(state, *tpp, cleanup_comm_canon_maps,
cleanup_ext_prop_mask & EXT_PROP_CANONICAL);
- tok822_internalize(cleanup_temp1, tpp[0]->head, TOK822_STR_DEFL);
- if (cleanup_recip == 0 && (hdr_opts->flags & HDR_OPT_EXTRACT) != 0)
+ tok822_internalize(state->temp1, tpp[0]->head, TOK822_STR_DEFL);
+ if (state->recip == 0 && (hdr_opts->flags & HDR_OPT_EXTRACT) != 0)
argv_add((hdr_opts->flags & HDR_OPT_RR) ?
- cleanup_resent_recip : cleanup_recipients,
- vstring_str(cleanup_temp1), (char *) 0);
+ state->resent_recip : state->recipients,
+ vstring_str(state->temp1), (char *) 0);
if (cleanup_masq_domains)
cleanup_masquerade_tree(*tpp, cleanup_masq_domains);
- if (hdr_opts->type == HDR_RETURN_RECEIPT_TO && !cleanup_return_receipt)
- cleanup_return_receipt =
- cleanup_extract_internal(cleanup_header_buf, *tpp);
- if (hdr_opts->type == HDR_ERRORS_TO && !cleanup_errors_to)
- cleanup_errors_to =
- cleanup_extract_internal(cleanup_header_buf, *tpp);
+ if (hdr_opts->type == HDR_RETURN_RECEIPT_TO && !state->return_receipt)
+ state->return_receipt =
+ cleanup_extract_internal(state->header_buf, *tpp);
+ if (hdr_opts->type == HDR_ERRORS_TO && !state->errors_to)
+ state->errors_to =
+ cleanup_extract_internal(state->header_buf, *tpp);
}
- vstring_sprintf(cleanup_header_buf, "%s: ", hdr_opts->name);
- tok822_externalize(cleanup_header_buf, tree, TOK822_STR_HEAD);
+ vstring_sprintf(state->header_buf, "%s: ", hdr_opts->name);
+ tok822_externalize(state->header_buf, tree, TOK822_STR_HEAD);
myfree((char *) addr_list);
tok822_free_tree(tree);
if ((hdr_opts->flags & HDR_OPT_DROP) == 0)
- cleanup_fold_header();
+ cleanup_fold_header(state);
}
/* cleanup_header - process one complete header line */
-static void cleanup_header(void)
+static void cleanup_header(CLEANUP_STATE *state)
{
char *myname = "cleanup_header";
HEADER_OPTS *hdr_opts;
if (msg_verbose)
- msg_info("%s: '%s'", myname, vstring_str(cleanup_header_buf));
+ msg_info("%s: '%s'", myname, vstring_str(state->header_buf));
- if ((cleanup_flags & CLEANUP_FLAG_FILTER) && cleanup_header_checks) {
- char *header = vstring_str(cleanup_header_buf);
+ if ((state->flags & CLEANUP_FLAG_FILTER) && cleanup_header_checks) {
+ char *header = vstring_str(state->header_buf);
const char *value;
if ((value = maps_find(cleanup_header_checks, header, 0)) != 0) {
if (strcasecmp(value, "REJECT") == 0) {
- msg_warn("%s: reject: header %.100s", cleanup_queue_id, header);
- cleanup_errs |= CLEANUP_STAT_CONT;
+ msg_warn("%s: reject: header %.100s", state->queue_id, header);
+ state->errs |= CLEANUP_STAT_CONT;
}
}
}
* even bothering to fold long lines. XXX Should split header lines that
* do not fit a REC_TYPE_NORM record.
*/
- if ((hdr_opts = header_opts_find(vstring_str(cleanup_header_buf))) == 0) {
- cleanup_out_header();
+ if ((hdr_opts = header_opts_find(vstring_str(state->header_buf))) == 0) {
+ cleanup_out_header(state);
}
/*
* because the addresses in those headers might be needed elsewhere.
*/
else {
- cleanup_headers_seen |= (1 << hdr_opts->type);
+ state->headers_seen |= (1 << hdr_opts->type);
if (hdr_opts->type == HDR_MESSAGE_ID)
- msg_info("%s: message-id=%s", cleanup_queue_id,
- vstring_str(cleanup_header_buf) + strlen(hdr_opts->name) + 2);
+ msg_info("%s: message-id=%s", state->queue_id,
+ vstring_str(state->header_buf) + strlen(hdr_opts->name) + 2);
if (hdr_opts->type == HDR_RESENT_MESSAGE_ID)
- msg_info("%s: resent-message-id=%s", cleanup_queue_id,
- vstring_str(cleanup_header_buf) + strlen(hdr_opts->name) + 2);
+ msg_info("%s: resent-message-id=%s", state->queue_id,
+ vstring_str(state->header_buf) + strlen(hdr_opts->name) + 2);
if (hdr_opts->type == HDR_RECEIVED)
- if (++cleanup_hop_count >= var_hopcount_limit)
- cleanup_errs |= CLEANUP_STAT_HOPS;
- if (CLEANUP_OUT_OK()) {
+ if (++state->hop_count >= var_hopcount_limit)
+ state->errs |= CLEANUP_STAT_HOPS;
+ if (CLEANUP_OUT_OK(state)) {
if (hdr_opts->flags & HDR_OPT_RR)
- cleanup_resent = "Resent-";
+ state->resent = "Resent-";
if (hdr_opts->flags & HDR_OPT_SENDER) {
- cleanup_rewrite_sender(hdr_opts);
+ cleanup_rewrite_sender(state, hdr_opts);
} else if (hdr_opts->flags & HDR_OPT_RECIP) {
- cleanup_rewrite_recip(hdr_opts);
+ cleanup_rewrite_recip(state, hdr_opts);
} else if ((hdr_opts->flags & HDR_OPT_DROP) == 0) {
- cleanup_out_header();
+ cleanup_out_header(state);
}
}
}
/* cleanup_missing_headers - insert missing message headers */
-static void cleanup_missing_headers(void)
+static void cleanup_missing_headers(CLEANUP_STATE *state)
{
char time_stamp[1024]; /* XXX locale dependent? */
struct tm *tp;
* Add a missing (Resent-)Message-Id: header. The message ID gives the
* time in GMT units, plus the local queue ID.
*/
- if ((cleanup_headers_seen & (1 << (cleanup_resent[0] ?
+ if ((state->headers_seen & (1 << (state->resent[0] ?
HDR_RESENT_MESSAGE_ID : HDR_MESSAGE_ID))) == 0) {
- tp = gmtime(&cleanup_time);
+ tp = gmtime(&state->time);
strftime(time_stamp, sizeof(time_stamp), "%Y%m%d%H%M%S", tp);
- cleanup_out_format(REC_TYPE_NORM, "%sMessage-Id: <%s.%s@%s>",
- cleanup_resent, time_stamp, cleanup_queue_id, var_myhostname);
+ cleanup_out_format(state, REC_TYPE_NORM, "%sMessage-Id: <%s.%s@%s>",
+ state->resent, time_stamp, state->queue_id, var_myhostname);
msg_info("%s: %smessage-id=<%s.%s@%s>",
- cleanup_queue_id, *cleanup_resent ? "resent-" : "",
- time_stamp, cleanup_queue_id, var_myhostname);
+ state->queue_id, *state->resent ? "resent-" : "",
+ time_stamp, state->queue_id, var_myhostname);
}
/*
* Add a missing (Resent-)Date: header. The date is in local time units,
* with the GMT offset at the end.
*/
- if ((cleanup_headers_seen & (1 << (cleanup_resent[0] ?
- HDR_RESENT_DATE : HDR_DATE))) == 0) {
- cleanup_out_format(REC_TYPE_NORM, "%sDate: %s",
- cleanup_resent, mail_date(cleanup_time));
+ if ((state->headers_seen & (1 << (state->resent[0] ?
+ HDR_RESENT_DATE : HDR_DATE))) == 0) {
+ cleanup_out_format(state, REC_TYPE_NORM, "%sDate: %s",
+ state->resent, mail_date(state->time));
}
/*
#define NOT_SPECIAL_SENDER(addr) (*(addr) != 0 \
&& strcasecmp(addr, mail_addr_double_bounce()) != 0)
- if ((cleanup_headers_seen & (1 << (cleanup_resent[0] ?
- HDR_RESENT_FROM : HDR_FROM))) == 0) {
- quote_822_local(cleanup_temp1, cleanup_sender);
- vstring_sprintf(cleanup_temp2, "%sFrom: %s",
- cleanup_resent, vstring_str(cleanup_temp1));
- if (cleanup_fullname && *cleanup_fullname) {
- vstring_strcat(cleanup_temp2, " (");
- token = tok822_alloc(TOK822_COMMENT, cleanup_fullname);
- tok822_externalize(cleanup_temp2, token, TOK822_STR_NONE);
+ if ((state->headers_seen & (1 << (state->resent[0] ?
+ HDR_RESENT_FROM : HDR_FROM))) == 0) {
+ quote_822_local(state->temp1, state->sender);
+ vstring_sprintf(state->temp2, "%sFrom: %s",
+ state->resent, vstring_str(state->temp1));
+ if (state->fullname && *state->fullname) {
+ vstring_strcat(state->temp2, " (");
+ token = tok822_alloc(TOK822_COMMENT, state->fullname);
+ tok822_externalize(state->temp2, token, TOK822_STR_NONE);
tok822_free(token);
- vstring_strcat(cleanup_temp2, ")");
+ vstring_strcat(state->temp2, ")");
}
- CLEANUP_OUT_BUF(REC_TYPE_NORM, cleanup_temp2);
- } else if ((cleanup_headers_seen & (1 << (cleanup_resent[0] ?
+ CLEANUP_OUT_BUF(state, REC_TYPE_NORM, state->temp2);
+ } else if ((state->headers_seen & (1 << (state->resent[0] ?
HDR_RESENT_SENDER : HDR_SENDER))) == 0
- && NOT_SPECIAL_SENDER(cleanup_sender)) {
- from = (cleanup_resent[0] ? cleanup_resent_from : cleanup_from);
- if (from == 0 || strcasecmp(cleanup_sender, from) != 0) {
- quote_822_local(cleanup_temp1, cleanup_sender);
- cleanup_out_format(REC_TYPE_NORM, "%sSender: %s",
- cleanup_resent, vstring_str(cleanup_temp1));
+ && NOT_SPECIAL_SENDER(state->sender)) {
+ from = (state->resent[0] ? state->resent_from : state->from);
+ if (from == 0 || strcasecmp(state->sender, from) != 0) {
+ quote_822_local(state->temp1, state->sender);
+ cleanup_out_format(state, REC_TYPE_NORM, "%sSender: %s",
+ state->resent, vstring_str(state->temp1));
}
}
}
-/* cleanup_message - process message content segment */
+/* cleanup_message - initialize message content segment */
-void cleanup_message(void)
+void cleanup_message_init(CLEANUP_STATE *state, int type, char *buf, int len)
{
- char *myname = "cleanup_message";
- long mesg_offset;
- long data_offset;
- long xtra_offset;
- int in_header;
- char *start;
- int type = 0;
+ char *myname = "cleanup_message_init";
/*
* Write a dummy start-of-content segment marker. We'll update it with
* real file offset information after reaching the end of the message
* content.
*/
- if ((mesg_offset = vstream_ftell(cleanup_dst)) < 0)
+ if ((state->mesg_offset = vstream_ftell(state->dst)) < 0)
msg_fatal("%s: vstream_ftell %s: %m", myname, cleanup_path);
- cleanup_out_format(REC_TYPE_MESG, REC_TYPE_MESG_FORMAT, 0L);
- if ((data_offset = vstream_ftell(cleanup_dst)) < 0)
+ cleanup_out_format(state, REC_TYPE_MESG, REC_TYPE_MESG_FORMAT, 0L);
+ if ((state->data_offset = vstream_ftell(state->dst)) < 0)
msg_fatal("%s: vstream_ftell %s: %m", myname, cleanup_path);
+ state->action = cleanup_message_header;
+ cleanup_message_header(state, type, buf, len);
+}
+
+/* cleanup_message_header - process message content, header */
+
+void cleanup_message_header(CLEANUP_STATE *state, int type, char *buf, int len)
+{
+ char *myname = "cleanup_message_header";
+
+ if (strchr(REC_TYPE_CONTENT, type) == 0) {
+ msg_warn("%s: %s: unexpected record type %d",
+ state->queue_id, myname, type);
+ state->errs |= CLEANUP_STAT_BAD;
+ return;
+ }
/*
- * An unannounced end-of-input condition most likely means that the
- * client did not want to send this message after all. Don't complain,
- * just stop generating any further output.
+ * First, deal with header information that we have accumulated from
+ * previous input records. A whole record that starts with whitespace is
+ * a continuation of previous data.
*
- * XXX Rely on the front-end programs to enforce record size limits.
+ * XXX Silently switch to body processing when some message header requires
+ * an unreasonable amount of storage, or when a message header record
+ * does not fit in a REC_TYPE_NORM type record.
*/
- in_header = 1;
-
- while (CLEANUP_OUT_OK()) {
-
- if ((type = rec_get(cleanup_src, cleanup_inbuf, 0)) < 0) {
- cleanup_errs |= CLEANUP_STAT_BAD;
- break;
- }
- if (strchr(REC_TYPE_CONTENT, type) == 0) {
- msg_warn("%s: %s: unexpected record type %d",
- cleanup_queue_id, myname, type);
- cleanup_errs |= CLEANUP_STAT_BAD;
- break;
+ if (VSTRING_LEN(state->header_buf) > 0) {
+ if ((VSTRING_LEN(state->header_buf) >= var_header_limit
+ || type != REC_TYPE_NORM)) {
+ state->errs |= CLEANUP_STAT_HOVFL;
+ } else if (ISSPACE(*buf)) {
+ VSTRING_ADDCH(state->header_buf, '\n');
+ vstring_strcat(state->header_buf, buf);
+ return;
}
- start = vstring_str(cleanup_inbuf);
/*
- * First, deal with header information that we have accumulated from
- * previous input records. A whole record that starts with whitespace
- * is a continuation of previous data.
- *
- * XXX Silently switch to body processing when some message header
- * requires an unreasonable amount of storage, or when a message
- * header record does not fit in a REC_TYPE_NORM type record.
+ * No more input to append to this saved header. Do output processing
+ * and reset the saved header buffer.
*/
- if (VSTRING_LEN(cleanup_header_buf) > 0) {
- if (VSTRING_LEN(cleanup_header_buf) < var_header_limit
- && type == REC_TYPE_NORM && ISSPACE(*start)) {
- VSTRING_ADDCH(cleanup_header_buf, '\n');
- vstring_strcat(cleanup_header_buf, start);
- continue;
- }
+ VSTRING_TERMINATE(state->header_buf);
+ cleanup_header(state);
+ VSTRING_RESET(state->header_buf);
+ }
- /*
- * No more input to append to this saved header. Do output
- * processing and reset the saved header buffer.
- */
- VSTRING_TERMINATE(cleanup_header_buf);
- cleanup_header();
- VSTRING_RESET(cleanup_header_buf);
- }
+ /*
+ * Switch to body processing if this is not a header or if the saved
+ * header would require an unreasonable amount of storage. Generate
+ * missing headers. Add one blank line when the message headers are
+ * immediately followed by a non-empty message body.
+ */
+ if (((state->errs & CLEANUP_STAT_HOVFL) || !is_header(buf))) {
+ cleanup_missing_headers(state);
+ if (type != REC_TYPE_XTRA && *buf) /* output blank line */
+ cleanup_out_string(state, REC_TYPE_NORM, "");
+ state->action = cleanup_message_body;
+ cleanup_message_body(state, type, buf, len);
+ }
- /*
- * Switch to body processing if we didn't read a header or if the
- * saved header requires an unreasonable amount of storage. Generate
- * missing headers. Add one blank line when the message headers are
- * immediately followed by a non-empty message body.
- */
- if (in_header
- && (VSTRING_LEN(cleanup_header_buf) >= var_header_limit
- || type != REC_TYPE_NORM
- || !is_header(start))) {
- in_header = 0;
- cleanup_missing_headers();
- if (type != REC_TYPE_XTRA && *start)/* output blank line */
- cleanup_out_string(REC_TYPE_NORM, "");
- }
+ /*
+ * Save this header record until we know that the header is complete.
+ */
+ else {
+ vstring_strcpy(state->header_buf, buf);
+ }
+}
- /*
- * If this is a header record, save it until we know that the header
- * is complete. If this is a body record, copy it to the output
- * immediately.
- */
- if (type == REC_TYPE_NORM || type == REC_TYPE_CONT) {
- if (in_header) {
- vstring_strcpy(cleanup_header_buf, start);
- } else {
- CLEANUP_OUT_BUF(type, cleanup_inbuf);
- }
- }
+/* cleanup_message_body - process message segment, body */
- /*
- * If we have reached the end of the message content segment, update
- * the start-of-content marker, now that we know how large the
- * message content segment is, and update the content size indicator
- * at the beginning of the message envelope segment. vstream_fseek()
- * implicitly flushes the stream, which may fail for various reasons.
- */
- else if (type == REC_TYPE_XTRA) {
- if ((xtra_offset = vstream_ftell(cleanup_dst)) < 0)
- msg_fatal("%s: vstream_ftell %s: %m", myname, cleanup_path);
- if (vstream_fseek(cleanup_dst, mesg_offset, SEEK_SET) < 0) {
- msg_warn("%s: write queue file: %m", cleanup_queue_id);
- if (errno == EFBIG)
- cleanup_errs |= CLEANUP_STAT_SIZE;
- else
- cleanup_errs |= CLEANUP_STAT_WRITE;
- break;
- }
- cleanup_out_format(REC_TYPE_MESG, REC_TYPE_MESG_FORMAT, xtra_offset);
- if (vstream_fseek(cleanup_dst, 0L, SEEK_SET) < 0)
- msg_fatal("%s: vstream_fseek %s: %m", myname, cleanup_path);
- cleanup_out_format(REC_TYPE_SIZE, REC_TYPE_SIZE_FORMAT,
- xtra_offset - data_offset);
- if (vstream_fseek(cleanup_dst, xtra_offset, SEEK_SET) < 0)
- msg_fatal("%s: vstream_fseek %s: %m", myname, cleanup_path);
- break;
- }
+void cleanup_message_body(CLEANUP_STATE *state, int type, char *buf, int len)
+{
+ char *myname = "cleanup_message_body";
+ long xtra_offset;
- /*
- * This should never happen.
- */
- else {
- msg_panic("%s: unexpected record type: %d", myname, type);
+ /*
+ * Copy body record to the output.
+ */
+ if (type == REC_TYPE_NORM || type == REC_TYPE_CONT) {
+ cleanup_out(state, type, buf, len);
+ }
+
+ /*
+ * If we have reached the end of the message content segment, update the
+ * start-of-content marker, now that we know how large the message
+ * content segment is, and update the content size indicator at the
+ * beginning of the message envelope segment. vstream_fseek() implicitly
+ * flushes the stream, which may fail for various reasons.
+ */
+ else if (type == REC_TYPE_XTRA) {
+ if ((xtra_offset = vstream_ftell(state->dst)) < 0)
+ msg_fatal("%s: vstream_ftell %s: %m", myname, cleanup_path);
+ if (vstream_fseek(state->dst, state->mesg_offset, SEEK_SET) < 0) {
+ msg_warn("%s: write queue file: %m", state->queue_id);
+ if (errno == EFBIG)
+ state->errs |= CLEANUP_STAT_SIZE;
+ else
+ state->errs |= CLEANUP_STAT_WRITE;
+ return;
}
+ cleanup_out_format(state, REC_TYPE_MESG, REC_TYPE_MESG_FORMAT, xtra_offset);
+ if (vstream_fseek(state->dst, 0L, SEEK_SET) < 0)
+ msg_fatal("%s: vstream_fseek %s: %m", myname, cleanup_path);
+ cleanup_out_format(state, REC_TYPE_SIZE, REC_TYPE_SIZE_FORMAT,
+ xtra_offset - state->data_offset);
+ if (vstream_fseek(state->dst, xtra_offset, SEEK_SET) < 0)
+ msg_fatal("%s: vstream_fseek %s: %m", myname, cleanup_path);
+ state->action = cleanup_extracted_init;
}
/*
- * Keep reading in case of problems, so that the sender is ready to
- * receive our status report.
+ * This should never happen.
*/
- if (CLEANUP_OUT_OK() == 0)
- if (type >= 0)
- cleanup_skip();
+ else {
+ msg_warn("%s: unexpected record type: %d", myname, type);
+ state->errs |= CLEANUP_STAT_BAD;
+ }
}
/* SYNOPSIS
/* #include "cleanup.h"
/*
-/* int CLEANUP_OUT_OK()
+/* int CLEANUP_OUT_OK(state)
+/* CLEANUP_STATE *state;
/*
-/* void cleanup_out(type, data, len)
+/* void cleanup_out(state, type, data, len)
+/* CLEANUP_STATE *state;
/* int type;
/* char *data;
/* int len;
/*
-/* void cleanup_out_string(type, str)
+/* void cleanup_out_string(state, type, str)
+/* CLEANUP_STATE *state;
/* int type;
/* char *str;
/*
-/* void CLEANUP_OUT_BUF(type, buf)
+/* void CLEANUP_OUT_BUF(state, type, buf)
+/* CLEANUP_STATE *state;
/* int type;
/* VSTRING *buf;
/*
-/* void cleanup_out_format(type, format, ...)
+/* void cleanup_out_format(state, type, format, ...)
+/* CLEANUP_STATE *state;
/* int type;
/* char *format;
/* DESCRIPTION
#include <sys_defs.h>
#include <errno.h>
-#include <stdlib.h> /* 44BSD stdarg.h uses abort() */
+#include <stdlib.h> /* 44BSD stdarg.h uses abort() */
#include <stdarg.h>
#include <string.h>
/* cleanup_out - output one single record */
-void cleanup_out(int type, char *string, int len)
+void cleanup_out(CLEANUP_STATE *state, int type, char *string, int len)
{
- if (CLEANUP_OUT_OK()) {
- if (rec_put(cleanup_dst, type, string, len) < 0) {
+ if (CLEANUP_OUT_OK(state)) {
+ if (rec_put(state->dst, type, string, len) < 0) {
if (errno == EFBIG) {
msg_warn("%s: queue file size limit exceeded",
- cleanup_queue_id);
- cleanup_errs |= CLEANUP_STAT_SIZE;
+ state->queue_id);
+ state->errs |= CLEANUP_STAT_SIZE;
} else {
- msg_warn("%s: write queue file: %m", cleanup_queue_id);
- cleanup_errs |= CLEANUP_STAT_WRITE;
+ msg_warn("%s: write queue file: %m", state->queue_id);
+ state->errs |= CLEANUP_STAT_WRITE;
}
}
}
/* cleanup_out_string - output string to one single record */
-void cleanup_out_string(int type, char *string)
+void cleanup_out_string(CLEANUP_STATE *state, int type, char *string)
{
- cleanup_out(type, string, strlen(string));
+ cleanup_out(state, type, string, strlen(string));
}
/* cleanup_out_format - output one formatted record */
-void cleanup_out_format(int type, char *fmt,...)
+void cleanup_out_format(CLEANUP_STATE *state, int type, char *fmt,...)
{
static VSTRING *vp;
va_list ap;
va_start(ap, fmt);
vstring_vsprintf(vp, fmt, ap);
va_end(ap);
- CLEANUP_OUT_BUF(type, vp);
+ CLEANUP_OUT_BUF(state, type, vp);
}
/* SYNOPSIS
/* #include "cleanup.h"
/*
-/* void cleanup_out_recipient(recipient)
+/* void cleanup_out_recipient(state, recipient)
+/* CLEANUP_STATE *state;
/* char *recipient;
/* DESCRIPTION
/* This module implements an envelope recipient output filter.
/* cleanup_out_recipient - envelope recipient output filter */
-void cleanup_out_recipient(char *recip)
+void cleanup_out_recipient(CLEANUP_STATE *state, char *recip)
{
ARGV *argv;
char **cpp;
if (cleanup_virtual_maps == 0) {
- if (been_here_fixed(cleanup_dups, recip) == 0)
- cleanup_out_string(REC_TYPE_RCPT, recip);
+ if (been_here_fixed(state->dups, recip) == 0)
+ cleanup_out_string(state, REC_TYPE_RCPT, recip);
} else {
- argv = cleanup_map1n_internal(recip, cleanup_virtual_maps,
+ argv = cleanup_map1n_internal(state, recip, cleanup_virtual_maps,
cleanup_ext_prop_mask & EXT_PROP_VIRTUAL);
for (cpp = argv->argv; *cpp; cpp++)
- if (been_here_fixed(cleanup_dups, *cpp) == 0)
- cleanup_out_string(REC_TYPE_RCPT, *cpp);
+ if (been_here_fixed(state->dups, *cpp) == 0)
+ cleanup_out_string(state, REC_TYPE_RCPT, *cpp);
argv_free(argv);
}
}
+++ /dev/null
-/*++
-/* NAME
-/* cleanup_skip 3
-/* SUMMARY
-/* skip further input
-/* SYNOPSIS
-/* #include "cleanup.h"
-/*
-/* void cleanup_skip(void)
-/* DESCRIPTION
-/* cleanup_skip() skips client input. This function is used after
-/* detecting an error. The idea is to drain input from the client
-/* so the client is ready to read the cleanup service status report.
-/* 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
-/*--*/
-
-/* System library. */
-
-#include <sys_defs.h>
-
-/* Utility library. */
-
-#include <msg.h>
-#include <vstream.h>
-
-/* Global library. */
-
-#include <cleanup_user.h>
-#include <record.h>
-#include <rec_type.h>
-
-/* Application-specific. */
-
-#include "cleanup.h"
-
-/* cleanup_skip - skip further client input */
-
-void cleanup_skip(void)
-{
- int type;
-
- if ((cleanup_errs & CLEANUP_STAT_CONT) == 0)
- msg_warn("%s: skipping further client input", cleanup_queue_id);
-
- /*
- * XXX Rely on the front-end programs to enforce record size limits.
- */
- while ((type = rec_get(cleanup_src, cleanup_inbuf, 0)) > 0
- && type != REC_TYPE_END)
- /* void */ ;
-}
/* SYNOPSIS
/* #include "cleanup.h"
/*
-/* void cleanup_state_alloc(void)
+/* CLEANUP_STATE *cleanup_state_alloc(void)
/*
-/* void cleanup_state_free(void)
+/* void cleanup_state_free(state)
+/* CLEANUP_STATE *state;
/* DESCRIPTION
-/* This module maintains about two dozen global (ugh) state variables
+/* This module maintains about two dozen state variables
/* that are used by many routines in the course of processing one
-/* message. Using globals seems to make more sense than passing around
-/* large parameter lists, or passing around a structure with a large
-/* number of components. We can get away with this because we need only
-/* one instance at a time.
+/* message.
/*
/* cleanup_state_alloc() initializes the per-message state variables.
/*
#include <mymalloc.h>
#include <vstring.h>
-#include <vstream.h>
+#include <argv.h>
/* Global library. */
#include "cleanup.h"
- /*
- * These variables are accessed by many functions, and there is only one
- * instance of each. Rather than passing around lots and lots of parameters,
- * or passing them around in a structure, we just make the variables global,
- * because that is what they are.
- */
-VSTRING *cleanup_inbuf; /* read buffer */
-VSTRING *cleanup_temp1; /* scratch buffer, local use only */
-VSTRING *cleanup_temp2; /* scratch buffer, local use only */
-VSTREAM *cleanup_src; /* current input stream */
-VSTREAM *cleanup_dst; /* current output stream */
-MAIL_STREAM *cleanup_handle; /* mail stream handle */
-char *cleanup_queue_id; /* queue file basename */
-time_t cleanup_time; /* posting time */
-char *cleanup_fullname; /* envelope sender full name */
-char *cleanup_sender; /* envelope sender address */
-char *cleanup_from; /* From: address */
-char *cleanup_resent_from; /* Resent-From: address */
-char *cleanup_recip; /* envelope recipient address */
-char *cleanup_return_receipt; /* return-receipt address */
-char *cleanup_errors_to; /* errors-to address */
-int cleanup_flags; /* processing options */
-int cleanup_errs; /* any badness experienced */
-int cleanup_err_mask; /* allowed badness */
-VSTRING *cleanup_header_buf; /* multi-record header */
-int cleanup_headers_seen; /* which headers were seen */
-int cleanup_hop_count; /* count of received: headers */
-ARGV *cleanup_recipients; /* recipients from regular headers */
-ARGV *cleanup_resent_recip; /* recipients from resent headers */
-char *cleanup_resent; /* any resent- header seen */
-BH_TABLE *cleanup_dups; /* recipient dup filter */
-
/* cleanup_state_alloc - initialize global state */
-void cleanup_state_alloc(void)
+CLEANUP_STATE *cleanup_state_alloc(void)
{
- cleanup_hop_count = 0;
- cleanup_headers_seen = 0;
- cleanup_time = 0;
- cleanup_errs = 0;
- cleanup_from = 0;
- cleanup_fullname = 0;
- cleanup_header_buf = vstring_alloc(100);
- cleanup_queue_id = 0;
- cleanup_recip = 0;
- cleanup_return_receipt = 0;
- cleanup_errors_to = 0;
- cleanup_recipients = argv_alloc(2);
- cleanup_resent = "";
- cleanup_resent_from = 0;
- cleanup_resent_recip = argv_alloc(2);
- cleanup_sender = 0;
- cleanup_temp1 = vstring_alloc(10);
- cleanup_temp2 = vstring_alloc(10);
- cleanup_inbuf = vstring_alloc(100);
- cleanup_dups = been_here_init(var_dup_filter_limit, BH_FLAG_FOLD);
+ CLEANUP_STATE *state = (CLEANUP_STATE *) mymalloc(sizeof(*state));
+
+ state->temp1 = vstring_alloc(10);
+ state->temp2 = vstring_alloc(10);
+ state->hop_count = 0;
+ state->headers_seen = 0;
+ state->time = 0;
+ state->errs = 0;
+ state->from = 0;
+ state->fullname = 0;
+ state->header_buf = vstring_alloc(100);
+ state->queue_id = 0;
+ state->recip = 0;
+ state->return_receipt = 0;
+ state->errors_to = 0;
+ state->recipients = argv_alloc(2);
+ state->resent = "";
+ state->resent_from = 0;
+ state->resent_recip = argv_alloc(2);
+ state->sender = 0;
+ state->dups = been_here_init(var_dup_filter_limit, BH_FLAG_FOLD);
+ state->warn_time = 0;
+ state->action = cleanup_envelope_init;
+
+ return (state);
}
/* cleanup_state_free - destroy global state */
-void cleanup_state_free(void)
+void cleanup_state_free(CLEANUP_STATE *state)
{
- if (cleanup_fullname)
- myfree(cleanup_fullname);
- if (cleanup_sender)
- myfree(cleanup_sender);
- if (cleanup_from)
- myfree(cleanup_from);
- if (cleanup_resent_from)
- myfree(cleanup_resent_from);
- if (cleanup_recip)
- myfree(cleanup_recip);
- if (cleanup_return_receipt)
- myfree(cleanup_return_receipt);
- if (cleanup_errors_to)
- myfree(cleanup_errors_to);
- vstring_free(cleanup_header_buf);
- argv_free(cleanup_recipients);
- argv_free(cleanup_resent_recip);
- vstring_free(cleanup_temp1);
- vstring_free(cleanup_temp2);
- vstring_free(cleanup_inbuf);
- if (cleanup_queue_id)
- myfree(cleanup_queue_id);
- been_here_free(cleanup_dups);
+ vstring_free(state->temp1);
+ vstring_free(state->temp2);
+ if (state->fullname)
+ myfree(state->fullname);
+ if (state->sender)
+ myfree(state->sender);
+ if (state->from)
+ myfree(state->from);
+ if (state->resent_from)
+ myfree(state->resent_from);
+ if (state->recip)
+ myfree(state->recip);
+ if (state->return_receipt)
+ myfree(state->return_receipt);
+ if (state->errors_to)
+ myfree(state->errors_to);
+ vstring_free(state->header_buf);
+ argv_free(state->recipients);
+ argv_free(state->resent_recip);
+ if (state->queue_id)
+ myfree(state->queue_id);
+ been_here_free(state->dups);
+ myfree((char *) state);
}
# FOR THIS TO WORK, DO NOT SPECIFY VIRTUAL DOMAINS IN MYDESTINATION.
# MYDESTINATION MUST LIST NON-VIRTUAL DOMAINS ONLY.
#
-#local_recipient_maps = $relocated_maps $alias_maps unix:passwd.byname
+#local_recipient_maps = $alias_maps unix:passwd.byname
# ADDRESS REWRITING
#
# SHOW SOFTWARE VERSION OR NOT
#
# The smtpd_banner parameter specifies the text that follows the 220
-# status code in the SMTP greeting banner. Some people like to see
+# code in the SMTP server's greeting banner. Some people like to see
# the mail version advertised. By default, Postfix shows no version.
#
-# You MUST specify the $myhostname at the start of the text. When
-# the SMTP client sees its own hostname at the start of an SMTP
-# greeting banner it will report a mailer loop. That's better than
-# having a machine meltdown.
+# You MUST specify $myhostname at the start of the text. That is an
+# RFC requirement. Postfix itself does not care.
#
#smtpd_banner = $myhostname ESMTP $mail_name
#smtpd_banner = $myhostname ESMTP $mail_name ($mail_version)
--- /dev/null
+THIS IS TODO
--- /dev/null
+THIS IS TODO
# ${name:value} to expand value only when $name does (does not) exist.
#
#forward_path = /var/forward/$user
+#forward_path = /var/forward/$user/.forward$recipient_delimiter$extension,
+# /var/forward/$user/.forward
forward_path = $home/.forward$recipient_delimiter$extension,$home/.forward
# The allow_mail_to_commands parameter restricts mail delivery to
/* const char *queue;
/* const char *id;
/* const char *sender;
+/*
+/* int bounce_recip(flags, queue, id, sender, recipient, relay,
+/* entry, format, ...)
+/* int flags;
+/* const char *queue;
+/* const char *id;
+/* const char *sender;
+/* const char *recipient;
+/* const char *relay;
+/* time_t entry;
+/* const char *format;
+/*
+/* int vbounce_recip(flags, queue, id, sender, recipient, relay,
+/* entry, format, ap)
+/* int flags;
+/* const char *queue;
+/* const char *id;
+/* const char *sender;
+/* const char *recipient;
+/* const char *relay;
+/* time_t entry;
+/* const char *format;
+/* va_list ap;
/* DESCRIPTION
/* This module implements the client interface to the message
/* bounce service, which maintains a per-message log of status
/* the specified sender, including the bounce log that was
/* built with bounce_append().
/*
+/* bounce_recip() and vbounce_recipient() send one bounce
+/* message immediately, without accessing a per-message bounce file.
+/*
/* Arguments:
/* .IP flags
-/* The bitwise OR of zero or mor of the following (specify
+/* 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_COPY
-/* Request that a postmaster copy is sent (bounce_flush() only).
/* .RE
/* .IP queue
/* The message queue name of the original message file.
return (-1);
}
}
+
+/* bounce_recip - send bounce for one recipient */
+
+int bounce_recip(int flags, const char *queue, const char *id,
+ const char *sender, const char *recipient,
+ const char *relay, time_t entry,
+ const char *fmt,...)
+{
+ va_list ap;
+ int status;
+
+ va_start(ap, fmt);
+ status = vbounce_recip(flags, queue, id, sender, recipient, relay,
+ entry, fmt, ap);
+ va_end(ap);
+ return (status);
+}
+
+/* vbounce_recip - send bounce for one recipient */
+
+int vbounce_recip(int flags, const char *queue, const char *id,
+ const char *sender, const char *recipient,
+ const char *relay, time_t entry,
+ const char *fmt, va_list ap)
+{
+ VSTRING *why;
+ int status;
+ int delay;
+
+ /*
+ * When we're pretending that we can't bounce, don't create a defer log
+ * file when we wouldn't keep the bounce log file. That's a lot of
+ * negatives in one sentence.
+ */
+ if (var_soft_bounce && (flags & BOUNCE_FLAG_CLEAN))
+ return (-1);
+
+ delay = time((time_t *) 0) - entry;
+ why = vstring_alloc(100);
+ vstring_vsprintf(why, fmt, ap);
+ if (mail_command_write(MAIL_CLASS_PRIVATE, var_soft_bounce ?
+ MAIL_SERVICE_DEFER : MAIL_SERVICE_BOUNCE,
+ "%d %d %s %s %s %s %s", BOUNCE_CMD_RECIP,
+ flags, queue, id, sender, recipient,
+ vstring_str(why)) == 0) {
+ msg_info("%s: to=<%s>, relay=%s, delay=%d, status=%s (%s)",
+ id, recipient, relay, delay, var_soft_bounce ? "deferred" :
+ "bounced", vstring_str(why));
+ status = (var_soft_bounce ? -1 : 0);
+ } else if ((flags & BOUNCE_FLAG_CLEAN) == 0) {
+ status = defer_append(flags, id, recipient, "bounce", delay,
+ "bounce failed");
+ } else {
+ status = -1;
+ }
+ vstring_free(why);
+ return (status);
+}
time_t, const char *, va_list);
extern int bounce_flush(int, const char *, const char *, const char *);
+extern int bounce_recip(int, const char *, const char *, const char *,
+ const char *, const char *, time_t,
+ const char *,...);
+extern int vbounce_recip(int, const char *, const char *, const char *,
+ const char *, const char *, time_t,
+ const char *, va_list);
+
/*
* Bounce/defer protocol commands.
*/
#define BOUNCE_CMD_APPEND 0 /* append log */
#define BOUNCE_CMD_FLUSH 1 /* send log */
-#define BOUNCE_CMD_WARN 2 /* send warning bounce, don't delete log */
+#define BOUNCE_CMD_WARN 2 /* send warning bounce, don't delete
+ * log */
+#define BOUNCE_CMD_RECIP 3 /* immediate bounce, no logfile */
/*
* Flags.
*/
#define BOUNCE_FLAG_NONE 0 /* no flags up */
#define BOUNCE_FLAG_CLEAN (1<<0) /* remove log on error */
-#define BOUNCE_FLAG_COPY (1<<1) /* postmaster notice */
-#define BOUNCE_FLAG_VERP (1<<2) /* personalized bounce */
/*
* Backwards compatibility.
/*
* Mapping from status code to printable string. One message may suffer from
* multiple errors, to it is important to list the most severe errors first,
- * because the result of lookup can be only one string.
+ * or to list the cause (header overflow) before the effect (no recipients),
+ * because cleanup_strerror() can report only one error.
*/
struct cleanup_stat_map {
unsigned status;
static struct cleanup_stat_map cleanup_stat_map[] = {
CLEANUP_STAT_BAD, "Internal protocol error",
+ CLEANUP_STAT_HOVFL, "Message header too long",
CLEANUP_STAT_RCPT, "No recipients specified",
CLEANUP_STAT_HOPS, "Too many hops",
CLEANUP_STAT_SIZE, "Message file too big",
#define CLEANUP_STAT_HOPS (1<<4) /* Too many hops */
#define CLEANUP_STAT_SYN (1<<5) /* Bad address syntax */
#define CLEANUP_STAT_RCPT (1<<6) /* No recipients found */
+#define CLEANUP_STAT_HOVFL (1<<7) /* Header overflow */
+
+#define CLEANUP_STAT_LETHAL (~CLEANUP_STAT_HOVFL) /* lethal errors */
extern const char *cleanup_strerror(unsigned);
/* .IP BOUNCE_FLAG_CLEAN
/* Delete the defer log in case of an error (as in: pretend
/* that we never even tried to defer this message).
-/* .IP BOUNCE_FLAG_COPY
-/* Request that postmaster a copy is sent (defer_flush() only).
/* .RE
/* .IP queue
/* The message queue name of the original message file.
#define VAR_QMGR_ACT_LIMIT "qmgr_message_active_limit"
#define DEF_QMGR_ACT_LIMIT 1000
+extern int var_qmgr_active_limit;
#define VAR_QMGR_RCPT_LIMIT "qmgr_message_recipient_limit"
#define DEF_QMGR_RCPT_LIMIT 10000
#define DEF_QMGR_FUDGE 100
extern int var_qmgr_fudge;
+#define VAR_QMGR_HOG "qmgr_site_hog_factor"
+#define DEF_QMGR_HOG 90
+extern int var_qmgr_hog;
+
/*
* Queue manager: default destination concurrency levels.
*/
extern int var_init_dest_concurrency;
#define VAR_DEST_CON_LIMIT "default_destination_concurrency_limit"
+#define _DEST_CON_LIMIT "_destination_concurrency_limit"
#define DEF_DEST_CON_LIMIT 10
extern int var_dest_con_limit;
* Queue manager: default number of recipients per transaction.
*/
#define VAR_DEST_RCPT_LIMIT "default_destination_recipient_limit"
+#define _DEST_RCPT_LIMIT "_destination_recipient_limit"
#define DEF_DEST_RCPT_LIMIT 50
extern int var_dest_rcpt_limit;
+#define VAR_LOCAL_RCPT_LIMIT "local" _DEST_RCPT_LIMIT /* XXX */
+#define DEF_LOCAL_RCPT_LIMIT 1 /* XXX */
+
/*
* Queue manager: default delay before retrying a dead transport.
*/
int fd;
const char *file_id;
VSTREAM *stream;
+ int count;
/*
* Initialize.
file_id = get_file_id(fd);
GETTIMEOFDAY(&tv);
- for (;;) {
+ for (count = 0;; count++) {
vstring_sprintf(id_buf, "%05X%s", (int) tv.tv_usec, file_id);
mail_queue_path(path_buf, queue_name, STR(id_buf));
- if (rename(STR(temp_path), STR(path_buf)) == 0) /* success */
+ if (sane_rename(STR(temp_path), STR(path_buf)) == 0) /* success */
break;
if (errno == EPERM || errno == EISDIR) {/* collision. weird. */
if ((int) ++tv.tv_usec < 0)
if (errno != ENOENT || mail_queue_mkdirs(STR(path_buf)) < 0) {
msg_warn("%s: rename %s to %s: %m", myname,
STR(temp_path), STR(path_buf));
- sleep(10);
}
+ if (count > 1000) /* XXX whatever */
+ msg_fatal("%s: rename %s to %s: giving up", myname,
+ STR(temp_path), STR(path_buf));
}
stream = vstream_fdopen(fd, O_RDWR);
* Version of this program.
*/
#define VAR_MAIL_VERSION "mail_version"
-#define DEF_MAIL_VERSION "Snapshot-20000104"
+#define DEF_MAIL_VERSION "Snapshot-20000129"
extern char *var_mail_version;
/* LICENSE
postlog.1.html postdrop.1.html postmap.1.html sendmail.1.html \
postsuper.1.html
CONFIG = access.5.html aliases.5.html canonical.5.html relocated.5.html \
- transport.5.html virtual.5.html
+ transport.5.html virtual.5.html pcre_table.5.html regexp_table.5.html
update: $(DAEMONS) $(COMMANDS) $(CONFIG)
canonical.5.html: ../conf/canonical
srctoman - $? | nroff -man | man2html | postlink >$@
+pcre_table.5.html: ../conf/pcre_table
+ srctoman - $? | nroff -man | man2html | postlink >$@
+
+regexp_table.5.html: ../conf/regexp_table
+ srctoman - $? | nroff -man | man2html | postlink >$@
+
relocated.5.html: ../conf/relocated
srctoman - $? | nroff -man | man2html | postlink >$@
hosts, domains, networks, host addresses or mail
addresses.
- The table serves as input to the <a href="postmap.1.html"><b>postmap</b>(1)</a> command. The
- result, an indexed file in <b>dbm</b> or <b>db</b> format, is used for
- fast searching by the mail system. After an update it may
- take a minute or so before the change becomes visible.
- Issue a <b>postfix</b> <b>reload</b> command to eliminate the delay.
-
+ Normally, the table serves as input to the <a href="postmap.1.html"><b>postmap</b>(1)</a> com-
+ mand. The result, an indexed file in <b>dbm</b> or <b>db</b> format, is
+ used for fast searching by the mail system. After an
+ update it may take a minute or so before the change
+ becomes visible. Issue a <b>postfix</b> <b>reload</b> command to elimi-
+ nate the delay.
+
+ When the table is provided via other means such as NIS,
+ LDAP or SQL, the same lookups are done as for ordinary
+ indexed files.
+
+ Alternatively, the table can be provided as a regular-
+ expression map where patterns are given as regular expres-
+ sions. In that case, the lookups are done in a slightly
+ different way as described below.
+
+<b>TABLE</b> <b>FORMAT</b>
The format of the access table is as follows:
blanks and comments
address, perform the corresponding <i>action</i>.
<b>PATTERNS</b>
- Patterns are tried in the order as listed below:
+ With lookups from indexed files, patterns are tried in the
+ order as listed below:
<i>user</i>@<i>domain</i>
Matches the specified mail address.
<i>domain.name</i>
- Matches the <i>domain.name</i> itself and any subdomain
- thereof, either in hostnames or in mail addresses.
+ Matches the <i>domain.name</i> itself and any subdomain
+ thereof, either in hostnames or in mail addresses.
Top-level domains will never be matched.
- <i>user</i>@ Matches all mail addresses with the specified user
+ <i>user</i>@ Matches all mail addresses with the specified user
part.
- <i>net.work.addr.ess</i>
- <i>net.work.addr</i>
- <i>net.work</i>
- <i>net</i> Matches any host address in the specified network.
- A network address is a sequence of one or more
- octets separated by ".".
-
-<b>ACTIONS</b>
+ 1
- 1
+ACCESS(5) ACCESS(5)
+ <i>net.work.addr.ess</i>
+ <i>net.work.addr</i>
-ACCESS(5) ACCESS(5)
+ <i>net.work</i>
+ <i>net</i> Matches any host address in the specified network.
+ A network address is a sequence of one or more
+ octets separated by ".".
+<b>ACTIONS</b>
[<b>45</b>]<i>XX</i> <i>text</i>
- Reject the address etc. that matches the pattern,
+ Reject the address etc. that matches the pattern,
and respond with the numerical code and text.
<b>REJECT</b> Reject the address etc. that matches the pattern. A
<i>Any</i> <i>other</i> <i>text</i>
Accept the address etc. that matches the pattern.
+<b>REGULAR</b> <b>EXPRESSION</b> <b>TABLES</b>
+ This section describes how the table lookups change when
+ the table is given in the form of regular expressions. For
+ a description of regular expression lookup table syntax,
+ see <b>regexp</b><i>_</i><b>table</b>(5) or <b>pcre</b><i>_</i><b>table</b>(5).
+
+ Patterns become regular expressions that are applied to
+ the entire string being looked up. Depending on the appli-
+ cation, that string is an entire client hostname, an
+ entire client IP address, or an entire mail address.
+
+ In contrast to the normal lookups from indexed files, no
+ parent domain or network search is done, and <i>user@domain</i>
+ mail addresses are not broken up into their <i>user@</i> and
+ <i>domain</i> constituent parts.
+
+ Actions are the same as with normal indexed file lookups,
+ with the additional feature that parenthesized substrings
+ from the pattern can be interpolated as <b>$1</b>, <b>$2</b> and so on.
+
<b>BUGS</b>
- The table format does not understand quoting conventions.
+ The table format does not understand quoting conventions.
<b>SEE</b> <b>ALSO</b>
<a href="postmap.1.html">postmap(1)</a> create mapping table
<a href="smtpd.8.html">smtpd(8)</a> smtp server
+ pcre_table(5) format of PCRE tables
+ regexp_table(5) format of POSIX regexp tables
<b>LICENSE</b>
- The Secure Mailer license must be distributed with
- this software.
+ The Secure Mailer license must be distributed with this
+
+
+
+ 2
+
+
+
+
+
+ACCESS(5) ACCESS(5)
+
+
+ software.
<b>AUTHOR(S)</b>
Wietse Venema
- 2
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 3
</pre> </body> </html>
<li><a href="#sendmail_incompatibility">Sendmail incompatibility</a>
+<li><a href="#receiving">Receiving mail via the network</a>
+
<li><a href="#relaying">Mail relaying</a>
<li><a href="#remote_delivery">Remote delivery</a>
<li><a href="#skip_greeting">Postfix does not try all the MX addresses</a>
+<li><a href="#worm">Postfix accepts MAIL FROM and RCPT TO "| command"</a>
+
+</ul>
+
+<a name="receiving"><h3>Receiving mail via the network</h3>
+
+<ul>
+
+<li><a href="#numerical_log">Postfix logs SMTP clients as IP
+addresses</a>
+
</ul>
<a name="relaying"><h3>Mail relaying</h3>
<pre>
/etc/postfix/main.cf:
- delay_warning_time = 4
+ delay_warning_time = 4
</pre>
<p>
<hr>
+<a name="numerical_log"><h3>Postfix logs SMTP clients as IP
+addresses</h3>
+
+<blockquote>
+
+The Postfix SMTP server logs client connections with numerical IP
+addresses instead of resolving the hostname. When I use <b>nslookup</b>
+the address does resolve to a name.
+
+</blockquote>
+
+<p>
+
+You run the Postfix SMTP server inside a <b>chroot</b> jail for
+extra security, but some configuration files are missing. In order
+to run inside a chroot jail, the Postfix SMTP client and server
+need copies of system configuration files inside the Postfix queue
+directory. The exact list of files is very system dependent, but
+you will probably need at the very least:
+
+<p>
+
+<pre>
+ /var/spool/postfix/etc/resolv.conf
+ /var/spool/postfix/etc/services
+</pre>
+
+<p>
+
+Of course, these directories and files must be owned by root, but
+they must be accessible by the postfix user, so directories need
+mode 0755 and files need mode 0644.
+
+<p>
+For more details, see the files in the <b>examples/chroot-setup</b>
+directory of the Postfix source code distribution.
+
+<hr>
+
<a name="open_relay"><h3>Help! Postfix is an open relay</h3>
According to some relay checking software, Postfix accepts
<pre>
/etc/postfix/main.cf:
smtpd_recipient_restrictions =
- regexp:/etc/postfix/regexp_access
- ...other restrictions...
+ regexp:/etc/postfix/regexp_access
+ ...other restrictions...
/etc/postfix/regexp_access:
/[%!@].*[%!@]/ 550 Sender specified routing is not supported here.
<pre>
/etc/postfix/main.cf:
- smtp_skip_4xx_greeting = yes
- smtp_skip_5xx_greeting = yes
+ smtp_skip_4xx_greeting = yes
+ smtp_skip_5xx_greeting = yes
</pre>
<p>
<pre>
/etc/postfix/main.cf:
- local_recipient_maps = $relocated_maps $alias_maps, unix:passwd.byname
+ local_recipient_maps = $relocated_maps $alias_maps, unix:passwd.byname
</pre>
<p>
<pre>
/etc/postfix/main.cf:
- smtpd_recipient_restrictions =
- ... regexp:/etc/postfix/access_regexp ...
- smtpd_recipient_restrictions =
- ... pcre:/etc/postfix/access_regexp ...
+ smtpd_recipient_restrictions =
+ ... regexp:/etc/postfix/access_regexp ...
+ smtpd_recipient_restrictions =
+ ... pcre:/etc/postfix/access_regexp ...
/etc/postfix/access_regexp:
- /^(.*)-outgoing@(.*)/ 554 Use $1@$2 instead
+ /^(.*)-outgoing@(.*)/ 554 Use $1@$2 instead
</pre>
<p>
<hr>
+<a name="worm"><h3>Postfix accepts MAIL FROM and RCPT TO "| command"</h3>
+
+With Postfix, | or / has special meaning only when it appears in
+aliases, .forward files or in :include: files. It has no special
+meaning in mail addresses.
+
+
+<p>
+
+If you must receive mail for systems with 10-year old vulnerabilities,
+it is prudent to set up a regexp filter that rejects potentially
+harmful MAIL FROM or RCPT TO commands.
+
+<p>
+
+<pre>
+ /etc/postfix/main.cf:
+ smtpd_sender_restrictions =
+ regexp:/etc/postfix/envelope-regexp
+ reject_unknown_sender_domain
+ smtpd_recipient_restrictions =
+ regexp:/etc/postfix/envelope-regexp
+ permit_mynetworks
+ check_relay_domains
+
+ /etc/postfix/envelope-regexp:
+ /[/|]/ REJECT
+</pre>
+
+<p>
+
+However, rejecting all envelope addresses with / causes trouble
+with simple-minded X.400 to Internet address mappings that leave
+the X.400 address structure exposed.
+
+<p>
+
+See also the documentation on <a href="uce.html#header_checks">header
+checks</a> restrictions for message header contents. These restrictions
+can be used to protect against attacks with command/file destinations
+in, for example, Errors-To: or Return-Receipt_To: message headers.
+
+<hr>
+
<a name="internal-list"><h3>Protecting internal email distribution lists</h3>
<blockquote>
<pre>
/etc/postfix/main.cf:
- recipient_delimiter = +
- virtual_maps =
- ...non-regexp virtual maps...
- regexp:/etc/postfix/virtual_regexp
+ recipient_delimiter = +
+ virtual_maps =
+ ...non-regexp virtual maps...
+ regexp:/etc/postfix/virtual_regexp
/etc/postfix/virtual_regexp:
- /^virtual\.domain$/ whatever
- /^(.*\)@virtual\.domain$/ joe+$1
+ /^virtual\.domain$/ whatever
+ /^(.*\)@virtual\.domain$/ joe+$1
</pre>
<p>
list receive that same burst of messages a whole
day later.
+ <b>qmgr</b><i>_</i><b>site</b><i>_</i><b>hog</b><i>_</i><b>factor</b> (valid range: 10..100)
+ The percentage of delivery resources that a busy
+ mail system will use up for delivery to a single
+ site. With 100%, mail is delivered in first-in,
+ first-out order, so that a burst of mail for one
+ site can block mail for other destinations. With
+ less than 100%, the excess mail is deferred. The
+ deferred mail is delivered in little bursts, the
+ remainder of the backlog being deferred again, with
+ a lot of I/O activity happening as Postfix searches
+ the deferred queue for deliverable mail.
+
<b>initial</b><i>_</i><b>destination</b><i>_</i><b>concurrency</b>
- Initial per-destination concurrency level for par-
+ Initial per-destination concurrency level for par-
allel delivery to the same destination.
<b>default</b><i>_</i><b>destination</b><i>_</i><b>concurrency</b><i>_</i><b>limit</b>
- Default limit on the number of parallel deliveries
+ Default limit on the number of parallel deliveries
to the same destination.
<i>transport_</i><b>destination</b><i>_</i><b>concurrency</b><i>_</i><b>limit</b>
- Limit on the number of parallel deliveries to the
- same destination, for delivery via the named mes-
+ Limit on the number of parallel deliveries to the
+ same destination, for delivery via the named mes-
sage <i>transport</i>.
<b>Recipient</b> <b>controls</b>
<b>default</b><i>_</i><b>destination</b><i>_</i><b>recipient</b><i>_</i><b>limit</b>
- Default limit on the number of recipients per mes-
+ Default limit on the number of recipients per mes-
sage transfer.
<i>transport_</i><b>destination</b><i>_</i><b>recipient</b><i>_</i><b>limit</b>
- Limit on the number of recipients per message
+ Limit on the number of recipients per message
transfer, for the named message <i>transport</i>.
-<b>SEE</b> <b>ALSO</b>
- <a href="master.8.html">master(8)</a>, process manager
- <a href="relocated.5.html">relocated(5)</a>, format of the "user has moved" table
- syslogd(8) system logging
- <a href="trivial-rewrite.8.html">trivial-rewrite(8)</a>, address routing
-
-<b>LICENSE</b>
- The Secure Mailer license must be distributed with this
- software.
-
-<b>AUTHOR(S)</b>
- Wietse Venema
QMGR(8) QMGR(8)
+<b>SEE</b> <b>ALSO</b>
+ <a href="master.8.html">master(8)</a>, process manager
+ <a href="relocated.5.html">relocated(5)</a>, format of the "user has moved" table
+ syslogd(8) system logging
+ <a href="trivial-rewrite.8.html">trivial-rewrite(8)</a>, address routing
+
+<b>LICENSE</b>
+ The Secure Mailer license must be distributed with this
+ software.
+
+<b>AUTHOR(S)</b>
+ Wietse Venema
IBM T.J. Watson Research
P.O. Box 704
Yorktown Heights, NY 10598, USA
-
-
-
-
-
-
-
-
-
-
-
-
<b>smtp</b><i>_</i><b>skip</b><i>_</i><b>4xx</b><i>_</i><b>greeting</b>
Skip servers that greet us with a 4xx status code.
- <b>smtp</b><i>_</i><b>skip</b><i>_</i><b>quit</b><i>_</i><b>response</b>
- Do not wait for the server response after sending
- QUIT.
+ <b>smtp</b><i>_</i><b>skip</b><i>_</i><b>5xx</b><i>_</i><b>greeting</b>
+ Skip servers that greet us with a 5xx status code.
+
SMTP(8) SMTP(8)
+ <b>smtp</b><i>_</i><b>skip</b><i>_</i><b>quit</b><i>_</i><b>response</b>
+ Do not wait for the server response after sending
+ QUIT.
+
<b>Resource</b> <b>controls</b>
<b>smtp</b><i>_</i><b>destination</b><i>_</i><b>concurrency</b><i>_</i><b>limit</b>
Limit the number of parallel deliveries to the same
- destination. The default limit is taken from the
+ destination. The default limit is taken from the
<b>default</b><i>_</i><b>destination</b><i>_</i><b>concurrency</b><i>_</i><b>limit</b> parameter.
<b>smtp</b><i>_</i><b>destination</b><i>_</i><b>recipient</b><i>_</i><b>limit</b>
- Limit the number of recipients per message deliv-
- ery. The default limit is taken from the
+ Limit the number of recipients per message deliv-
+ ery. The default limit is taken from the
<b>default</b><i>_</i><b>destination</b><i>_</i><b>recipient</b><i>_</i><b>limit</b> parameter.
<b>Timeout</b> <b>controls</b>
<b>smtp</b><i>_</i><b>connect</b><i>_</i><b>timeout</b>
Timeout in seconds for completing a TCP connection.
When no connection can be made within the deadline,
- the SMTP client tries the next address on the mail
+ the SMTP client tries the next address on the mail
exchanger list.
<b>smtp</b><i>_</i><b>helo</b><i>_</i><b>timeout</b>
- Timeout in seconds for receiving the SMTP greeting
+ Timeout in seconds for receiving the SMTP greeting
banner. When the server drops the connection with-
- out sending a greeting banner, or when it sends no
+ out sending a greeting banner, or when it sends no
greeting banner within the deadline, the SMTP
client tries the next address on the mail exchanger
list.
<b>smtp</b><i>_</i><b>helo</b><i>_</i><b>timeout</b>
- Timeout in seconds for sending the <b>HELO</b> command,
+ Timeout in seconds for sending the <b>HELO</b> command,
and for receiving the server response.
<b>smtp</b><i>_</i><b>mail</b><i>_</i><b>timeout</b>
- Timeout in seconds for sending the <b>MAIL</b> <b>FROM</b> com-
+ Timeout in seconds for sending the <b>MAIL</b> <b>FROM</b> com-
mand, and for receiving the server response.
<b>smtp</b><i>_</i><b>rcpt</b><i>_</i><b>timeout</b>
and for receiving the server response.
<b>smtp</b><i>_</i><b>data</b><i>_</i><b>init</b><i>_</i><b>timeout</b>
- Timeout in seconds for sending the <b>DATA</b> command,
+ Timeout in seconds for sending the <b>DATA</b> command,
and for receiving the server response.
<b>smtp</b><i>_</i><b>data</b><i>_</i><b>xfer</b><i>_</i><b>timeout</b>
<b>smtp</b><i>_</i><b>data</b><i>_</i><b>done</b><i>_</i><b>timeout</b>
Timeout in seconds for sending the "<b>.</b>" command, and
for receiving the server response. When no response
- is received, a warning is logged that the mail may
+ is received, a warning is logged that the mail may
be delivered multiple times.
- <b>smtp</b><i>_</i><b>quit</b><i>_</i><b>timeout</b>
- Timeout in seconds for sending the <b>QUIT</b> command,
- and for receiving the server response.
-
3
SMTP(8) SMTP(8)
+ <b>smtp</b><i>_</i><b>quit</b><i>_</i><b>timeout</b>
+ Timeout in seconds for sending the <b>QUIT</b> command,
+ and for receiving the server response.
+
<b>SEE</b> <b>ALSO</b>
<a href="bounce.8.html">bounce(8)</a> non-delivery status reports
<a href="master.8.html">master(8)</a> process manager
syslogd(8) system logging
<b>LICENSE</b>
- The Secure Mailer license must be distributed with this
+ The Secure Mailer license must be distributed with this
software.
<b>AUTHOR(S)</b>
-
-
-
-
#include <make_dirs.h>
#include <set_eugid.h>
#include <get_hostname.h>
+#include <sane_fsops.h>
/* Global library. */
vstring_sprintf(why, "create %s: %m", tmpfile);
} else {
if (mail_copy(COPY_ATTR(state.msg_attr), dst, copy_flags, why) == 0) {
- if (link(tmpfile, newfile) < 0
+ if (sane_link(tmpfile, newfile) < 0
&& (errno != ENOENT
|| (make_dirs(curdir, 0700), make_dirs(newdir, 0700)) < 0
- || link(tmpfile, newfile) < 0)) {
+ || sane_link(tmpfile, newfile) < 0)) {
vstring_sprintf(why, "link to %s: %m", newfile);
} else {
if (unlink(tmpfile) < 0)
esac
case "$SYSTEM.$RELEASE" in
+ SCO_SV.3.2) SYSTYPE=SCO5
+ # Use the native compiler by default
+ : ${CC="/usr/bin/cc -b elf"}
+ : ${DEBUG=}
+ SYSLIBS="-lsocket -ldbm"
+ RANLIB=echo
+ ;;
UnixWare.5*) SYSTYPE=UW7
- CC=cc
- DEBUG=
+ # Use the native compiler by default
+ : ${CC=/usr/bin/cc}
+ : ${DEBUG=}
RANLIB=echo
SYSLIBS="-lresolv -lsocket -lnsl"
;;
UNIX_SV.4.2*) case "`uname -v`" in
2.1*) SYSTYPE=UW21
- CC=/usr/bin/cc
+ # Use the native compiler by default
+ : ${CC=/usr/bin/cc}
RANLIB=echo
SYSLIBS="-lresolv -lsocket -lnsl -lc -L/usr/ucblib -lucb"
;;
man1/postmap.1 man1/sendmail.1 man1/mailq.1 man1/newaliases.1 \
man1/postsuper.1
CONFIG = man5/access.5 man5/aliases.5 man5/canonical.5 man5/relocated.5 \
- man5/transport.5 man5/virtual.5
+ man5/transport.5 man5/virtual.5 man5/pcre_table.5 man5/regexp_table.5
update: $(DAEMONS) $(COMMANDS) $(CONFIG)
man5/canonical.5: ../conf/canonical
srctoman - $? >$@
+man5/pcre_table.5: ../conf/pcre_table
+ srctoman - $? >$@
+
+man5/regexp_table.5: ../conf/regexp_table
+ srctoman - $? >$@
+
man5/relocated.5: ../conf/relocated
srctoman - $? >$@
to selectively reject or accept mail from or to specific hosts,
domains, networks, host addresses or mail addresses.
-The table serves as input to the \fBpostmap\fR(1) command. The
-result, an indexed file in \fBdbm\fR or \fBdb\fR format,
+Normally, the table serves as input to the \fBpostmap\fR(1) command.
+The result, an indexed file in \fBdbm\fR or \fBdb\fR format,
is used for fast searching by the mail system. After an update
it may take a minute or so before the change becomes visible.
Issue a \fBpostfix reload\fR command to eliminate the delay.
+When the table is provided via other means such as NIS, LDAP
+or SQL, the same lookups are done as for ordinary indexed files.
+
+Alternatively, the table can be provided as a regular-expression
+map where patterns are given as regular expressions. In that case,
+the lookups are done in a slightly different way as described below.
+.SH TABLE FORMAT
+.na
+.nf
+.ad
+.fi
The format of the access table is as follows:
.IP "blanks and comments"
Blank lines are ignored, as are lines beginning with `#'.
.SH PATTERNS
.na
.nf
-Patterns are tried in the order as listed below:
.ad
.fi
+With lookups from indexed files, patterns are tried in the order as
+listed below:
.IP \fIuser\fR@\fIdomain\fR
Matches the specified mail address.
.IP \fIdomain.name\fR
.IP \fBOK\fR
.IP "\fIAny other text\fR"
Accept the address etc. that matches the pattern.
+.SH REGULAR EXPRESSION TABLES
+.na
+.nf
+.ad
+.fi
+This section describes how the table lookups change when the table
+is given in the form of regular expressions. For a description of
+regular expression lookup table syntax, see \fBregexp_table\fR(5)
+or \fBpcre_table\fR(5).
+
+Patterns become regular expressions that are applied to the entire
+string being looked up. Depending on the application, that string
+is an entire client hostname, an entire client IP address, or an
+entire mail address.
+
+In contrast to the normal lookups from indexed files, no parent
+domain or network search is done, and \fIuser@domain\fR mail
+addresses are not broken up into their \fIuser@\fR and \fIdomain\fR
+constituent parts.
+
+Actions are the same as with normal indexed file lookups, with
+the additional feature that parenthesized substrings from the
+pattern can be interpolated as \fB$1\fR, \fB$2\fR and so on.
.SH BUGS
.ad
.fi
.nf
postmap(1) create mapping table
smtpd(8) smtp server
+pcre_table(5) format of PCRE tables
+regexp_table(5) format of POSIX regexp tables
.SH LICENSE
.na
.nf
case, recipients near the beginning of a large list receive a burst
of messages immediately, while recipients near the end of that list
receive that same burst of messages a whole day later.
+.IP "\fBqmgr_site_hog_factor\fR (valid range: 10..100)"
+The percentage of delivery resources that a busy mail system will
+use up for delivery to a single site.
+With 100%, mail is delivered in first-in, first-out order, so that
+a burst of mail for one site can block mail for other destinations.
+With less than 100%, the excess mail is deferred. The deferred mail
+is delivered in little bursts, the remainder of the backlog being
+deferred again, with a lot of I/O activity happening as Postfix
+searches the deferred queue for deliverable mail.
.IP \fBinitial_destination_concurrency\fR
Initial per-destination concurrency level for parallel delivery
to the same destination.
postmaster with transcripts of SMTP sessions with protocol errors.
.IP \fBsmtp_skip_4xx_greeting\fR
Skip servers that greet us with a 4xx status code.
+.IP \fBsmtp_skip_5xx_greeting\fR
+Skip servers that greet us with a 5xx status code.
.IP \fBsmtp_skip_quit_response\fR
Do not wait for the server response after sending QUIT.
.SH "Resource controls"
char *myname = "master_sigsetup";
struct sigaction action;
static int sigs[] = {
- SIGINT, SIGQUIT, SIGSEGV, SIGILL, SIGTERM,
+ SIGINT, SIGQUIT, SIGILL, SIGBUS, SIGSEGV, SIGTERM,
};
unsigned i;
"local_destination_concurrency_limit", "$default_destination_concurrency_limit", &var_local_destination_concurrency_limit, 0, 0,
- "local_destination_recipient_limit", "$default_destination_recipient_limit", &var_local_destination_recipient_limit, 0, 0,
char *var_local_destination_concurrency_limit;
-char *var_local_destination_recipient_limit;
/* use up for delivery of a large mailing list message.
/* With 100%, delivery of one message does not begin before the previous
/* message has been delivered. This results in good performance for large
-/* mailing lists, but results in poor response time for one-to-one mail.
+/* mailing lists, but results in poor response time for one-to-one mail.
/* With less than 100%, response time for one-to-one mail improves,
/* but large mailing list delivery performance suffers. In the worst
/* case, recipients near the beginning of a large list receive a burst
/* of messages immediately, while recipients near the end of that list
/* receive that same burst of messages a whole day later.
+/* .IP "\fBqmgr_site_hog_factor\fR (valid range: 10..100)"
+/* The percentage of delivery resources that a busy mail system will
+/* use up for delivery to a single site.
+/* With 100%, mail is delivered in first-in, first-out order, so that
+/* a burst of mail for one site can block mail for other destinations.
+/* With less than 100%, the excess mail is deferred. The deferred mail
+/* is delivered in little bursts, the remainder of the backlog being
+/* deferred again, with a lot of I/O activity happening as Postfix
+/* searches the deferred queue for deliverable mail.
/* .IP \fBinitial_destination_concurrency\fR
/* Initial per-destination concurrency level for parallel delivery
/* to the same destination.
char *var_virtual_maps;
char *var_defer_xports;
bool var_allow_min_user;
-bool var_qmgr_fudge;
+int var_qmgr_fudge;
+int var_qmgr_hog;
+int var_local_rcpt_lim; /* XXX */
static QMGR_SCAN *qmgr_incoming;
static QMGR_SCAN *qmgr_deferred;
VAR_DEST_CON_LIMIT, DEF_DEST_CON_LIMIT, &var_dest_con_limit, 0, 0,
VAR_DEST_RCPT_LIMIT, DEF_DEST_RCPT_LIMIT, &var_dest_rcpt_limit, 0, 0,
VAR_QMGR_FUDGE, DEF_QMGR_FUDGE, &var_qmgr_fudge, 10, 100,
+ VAR_QMGR_HOG, DEF_QMGR_HOG, &var_qmgr_hog, 10, 100,
+ VAR_LOCAL_RCPT_LIMIT, DEF_LOCAL_RCPT_LIMIT, &var_local_rcpt_lim, 0, 0,
0,
};
static CONFIG_BOOL_TABLE bool_table[] = {
*/
if ((at = strrchr(STR(reply.recipient), '@')) == 0
|| resolve_local(at + 1)) {
+#if 0
vstring_strcpy(reply.nexthop, STR(reply.recipient));
(void) split_at_right(STR(reply.nexthop), '@');
+#endif
#if 0
if (*var_rcpt_delim)
(void) split_addr(STR(reply.nexthop), *var_rcpt_delim);
* have to configure something for mail directed to the local
* postmaster, though, but that is an RFC requirement anyway.
*/
- if (strcasecmp(STR(reply.nexthop), var_double_bounce_sender) == 0) {
+ if (strncasecmp(STR(reply.recipient), var_double_bounce_sender,
+ at - STR(reply.recipient)) == 0
+ && !var_double_bounce_sender[at - STR(reply.recipient)]) {
sent(message->queue_id, recipient->address,
"none", message->arrival_time, "discarded");
deliver_completed(message->fp, recipient->offset);
continue;
}
+ /*
+ * This queue is a hog. Defer this recipient until the queue drains.
+ * When a site accumulates a large backlog, Postfix will deliver a
+ * little chunk and hammer the disk as it defers the remainder of the
+ * backlog and searches the deferred queue for deliverable mail.
+ */
+ if (var_qmgr_hog < 100) {
+ if (queue->todo_refcount + queue->busy_refcount
+ > (var_qmgr_hog / 100.0)
+ * (qmgr_recipient_count > 0.8 * var_qmgr_rcpt_limit ?
+ qmgr_message_count : var_qmgr_active_limit)) {
+ qmgr_defer_recipient(message, recipient->address,
+ "site destination queue overflow");
+ continue;
+ }
+ }
+
/*
* This queue is alive. Bind this recipient to this queue instance.
*/
* anti-UCE systems, by people who aren't aware of RFC details.
*/
if ((!SMTP_SOFT(code) && !SMTP_HARD(code))
+ || code == 555 /* RFC 1869, section 6.1. */
|| (code >= 500 && code < 510))
state->error_mask |= MAIL_ERROR_PROTOCOL;
}
/* Restrict what domain names can be used in \fBETRN\fR commands,
/* and what clients may issue \fBETRN\fR commands.
/* .IP \fBallow_untrusted_routing\fR
-/* Allow untrusted clients to specify addresses with sender-specified
-/* routing. Enabling this opens up nasty relay loopholes involving
+/* Allow untrusted clients to specify addresses with sender-specified
+/* routing. Enabling this opens up nasty relay loopholes involving
/* trusted backup MX hosts.
/* .IP \fBrestriction_classes\fR
/* Declares the name of zero or more parameters that contain a
char *var_canonical_maps;
char *var_rcpt_canon_maps;
char *var_virtual_maps;
+char *var_relocated_maps;
char *var_alias_maps;
char *var_local_rcpt_maps;
bool var_allow_untrust_route;
rec_fprintf(state->cleanup, REC_TYPE_NORM,
"\tid %s; %s", state->queue_id, mail_date(state->time));
}
+#ifdef RECEIVED_ENVELOPE_FROM
+ rec_fprintf(state->cleanup, REC_TYPE_NORM,
+ "\t(envelope-from %s)", state->sender);
+#endif
smtpd_chat_reply(state, "354 End data with <CR><LF>.<CR><LF>");
/*
VAR_CANONICAL_MAPS, DEF_CANONICAL_MAPS, &var_canonical_maps, 0, 0,
VAR_RCPT_CANON_MAPS, DEF_RCPT_CANON_MAPS, &var_rcpt_canon_maps, 0, 0,
VAR_VIRTUAL_MAPS, DEF_VIRTUAL_MAPS, &var_virtual_maps, 0, 0,
+ VAR_RELOCATED_MAPS, DEF_RELOCATED_MAPS, &var_relocated_maps, 0, 0,
VAR_ALIAS_MAPS, DEF_ALIAS_MAPS, &var_alias_maps, 0, 0,
VAR_LOCAL_RCPT_MAPS, DEF_LOCAL_RCPT_MAPS, &var_local_rcpt_maps, 0, 0,
0,
static MAPS *rcpt_canon_maps;
static MAPS *canonical_maps;
static MAPS *virtual_maps;
+static MAPS *relocated_maps;
/*
* Pre-opened access control lists.
DICT_FLAG_LOCK);
virtual_maps = maps_create(VAR_VIRTUAL_MAPS, var_virtual_maps,
DICT_FLAG_LOCK);
+ relocated_maps = maps_create(VAR_RELOCATED_MAPS, var_relocated_maps,
+ DICT_FLAG_LOCK);
/*
* Reply is used as a cache for resolved addresses, and error_text is
if (*var_local_rcpt_maps
&& !mail_addr_find(rcpt_canon_maps, STR(reply.recipient), NOP)
&& !mail_addr_find(canonical_maps, STR(reply.recipient), NOP)
+ && !mail_addr_find(relocated_maps, STR(reply.recipient), NOP)
&& !mail_addr_find(local_rcpt_maps, STR(reply.recipient), NOP)) {
(void) smtpd_check_reject(state, MAIL_ERROR_BOUNCE,
"550 <%s>: User unknown", recipient);
if (*var_virtual_maps
&& !mail_addr_find(rcpt_canon_maps, STR(reply.recipient), NOP)
&& !mail_addr_find(canonical_maps, STR(reply.recipient), NOP)
+ && !mail_addr_find(relocated_maps, STR(reply.recipient), NOP)
&& !mail_addr_find(virtual_maps, STR(reply.recipient), NOP)
&& maps_find(virtual_maps, domain, 0)) {
(void) smtpd_check_reject(state, MAIL_ERROR_BOUNCE,
char *var_rcpt_canon_maps;
char *var_canonical_maps;
char *var_virtual_maps;
+char *var_relocated_maps;
char *var_local_rcpt_maps;
typedef struct {
VAR_RCPT_CANON_MAPS, DEF_RCPT_CANON_MAPS, &var_rcpt_canon_maps,
VAR_CANONICAL_MAPS, DEF_CANONICAL_MAPS, &var_canonical_maps,
VAR_VIRTUAL_MAPS, DEF_VIRTUAL_MAPS, &var_virtual_maps,
+ VAR_RELOCATED_MAPS, DEF_RELOCATED_MAPS, &var_relocated_maps,
VAR_LOCAL_RCPT_MAPS, DEF_LOCAL_RCPT_MAPS, &var_local_rcpt_maps,
0,
};
vstream.c vstream_popen.c vstring.c vstring_vstream.c writable.c \
write_buf.c write_wait.c dict_unix.c dict_pcre.c stream_listen.c \
stream_connect.c stream_trigger.c dict_regexp.c mac_expand.c \
- clean_env.c watchdog.c spawn_command.c duplex_pipe.c
+ clean_env.c watchdog.c spawn_command.c duplex_pipe.c sane_rename.c \
+ sane_link.c
OBJS = argv.o argv_split.o attr.o basename.o binhash.o chroot_uid.o \
close_on_exec.o concatenate.o dict.o dict_db.o dict_dbm.o \
dict_env.o dict_ht.o dict_ldap.o dict_mysql.o dict_ni.o dict_nis.o \
vstream.o vstream_popen.o vstring.o vstring_vstream.o writable.o \
write_buf.o write_wait.o dict_unix.o dict_pcre.o stream_listen.o \
stream_connect.o stream_trigger.o dict_regexp.o mac_expand.o \
- clean_env.o watchdog.o spawn_command.o duplex_pipe.o
+ clean_env.o watchdog.o spawn_command.o duplex_pipe.o sane_rename.o \
+ sane_link.o
HDRS = argv.h attr.h binhash.h chroot_uid.h connect.h dict.h dict_db.h \
dict_dbm.h dict_env.h dict_ht.h dict_ldap.h dict_mysql.h \
dict_ni.h dict_nis.h dict_nisplus.h dir_forest.h events.h \
timed_connect.h timed_wait.h trigger.h username.h valid_hostname.h \
vbuf.h vbuf_print.h vstream.h vstring.h vstring_vstream.h \
dict_unix.h dict_pcre.h dict_regexp.h mac_expand.h clean_env.h \
- watchdog.h spawn_command.h
+ watchdog.h spawn_command.h sane_fsops.h
TESTSRC = fifo_open.c fifo_rdwr_bug.c fifo_rdonly_bug.c select_bug.c \
stream_test.c dup2_pass_on_exec.c
WARN = -W -Wformat -Wimplicit -Wmissing-prototypes \
sane_accept.o: sane_accept.c
sane_accept.o: sys_defs.h
sane_accept.o: sane_accept.h
+sane_link.o: sane_link.c
+sane_link.o: sys_defs.h
+sane_link.o: msg.h
+sane_link.o: sane_fsops.h
+sane_rename.o: sane_rename.c
+sane_rename.o: sys_defs.h
+sane_rename.o: msg.h
+sane_rename.o: sane_fsops.h
scan_dir.o: scan_dir.c
scan_dir.o: sys_defs.h
scan_dir.o: msg.h
SKIP_WHILE(*cp != '/', cp);
if ((saved_ch = *cp) != 0)
*cp = 0;
- if ((ret = stat(saved_path, &st)) < 0)
- if (errno != ENOENT || (ret = mkdir(saved_path, perms)) < 0)
+ if ((ret = stat(saved_path, &st)) >= 0) {
+ if (!S_ISDIR(st.st_mode)) {
+ errno = ENOTDIR;
+ ret = -1;
break;
+ }
+ } else if (errno != ENOENT || (ret = mkdir(saved_path, perms)) < 0)
+ break;
if (saved_ch != 0)
*cp = saved_ch;
SKIP_WHILE(*cp == '/', cp);
/* <2rdb0s$568@mail.fwi.uva.nl>, posted to comp.security.unix
/* (May 18, 1994).
/*
-/* Olaf Kirch discusses how the lstat()/open()+stat() test can
+/* Olaf Kirch discusses how the lstat()/open()+fstat() test can
/* be fooled by delaying the open() until the inode found with
/* lstat() has been re-used for a sensitive file (article
/* <20000103212443.A5807@monad.swb.de> posted to bugtraq on
-/* Jan 3, 2000). This can be a concern for set-uid processes
-/* that run under the control of a user and this can be
+/* Jan 3, 2000). This can be a concern for a set-uid process
+/* that runs under the control of a user and that can be
/* manipulated with start/stop signals.
/* LICENSE
/* .ad
--- /dev/null
+#ifndef _SANE_FSOPS_H_
+#define _SANE_FSOPS_H_
+
+/*++
+/* NAME
+/* sane_rename 3h
+/* SUMMARY
+/* sanitize rename() error returns
+/* SYNOPSIS
+/* #include <sane_rename.h>
+/* DESCRIPTION
+/* .nf
+
+ /* External interface. */
+
+extern int sane_rename(const char *, const char *);
+extern int sane_link(const char *, const char *);
+
+/* 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
+/*--*/
+
+#endif
--- /dev/null
+/*++
+/* NAME
+/* sane_link 3
+/* SUMMARY
+/* sanitize link() error returns
+/* SYNOPSIS
+/* #include <sane_fsops.h>
+/*
+/* int sane_link(old, new)
+/* const char *from;
+/* const char *to;
+/* DESCRIPTION
+/* sane_link() implements the link(2) system call, and works
+/* around some errors that are possible with NFS file systems.
+/* 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
+/*--*/
+
+/* System library. */
+
+#include "sys_defs.h"
+#include <sys/stat.h>
+#include <errno.h>
+#include <unistd.h>
+
+/* Utility library. */
+
+#include "msg.h"
+#include "sane_fsops.h"
+
+/* sane_link - sanitize link() error returns */
+
+int sane_link(const char *from, const char *to)
+{
+ char *myname = "sane_link";
+ int saved_errno;
+ struct stat from_st;
+ struct stat to_st;
+
+ /*
+ * Normal case: link() succeeds.
+ */
+ if (link(from, to) >= 0)
+ return (0);
+
+ /*
+ * Woops. Save errno, and see if the error is an NFS artefact. If it is,
+ * pretend the error never happened.
+ */
+ saved_errno = errno;
+ if (stat(from, &from_st) >= 0 && stat(to, &to_st) >= 0
+ && from_st.st_dev == to_st.st_dev
+ && from_st.st_ino == to_st.st_ino) {
+ msg_info("%s(%s,%s): worked around spurious NFS error",
+ myname, from, to);
+ return (0);
+ }
+
+ /*
+ * Nope, it didn't. Restore errno and report the error.
+ */
+ errno = saved_errno;
+ return (-1);
+}
--- /dev/null
+/*++
+/* NAME
+/* sane_rename 3
+/* SUMMARY
+/* sanitize rename() error returns
+/* SYNOPSIS
+/* #include <sane_fsops.h>
+/*
+/* int sane_rename(old, new)
+/* const char *from;
+/* const char *to;
+/* DESCRIPTION
+/* sane_rename() implements the rename(2) system call, and works
+/* around some errors that are possible with NFS file systems.
+/* 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
+/*--*/
+
+/* System library. */
+
+#include "sys_defs.h"
+#include <sys/stat.h>
+#include <errno.h>
+#include <stdio.h> /* rename(2) syscall in stdio.h? */
+
+/* Utility library. */
+
+#include "msg.h"
+#include "sane_fsops.h"
+
+/* sane_rename - sanitize rename() error returns */
+
+int sane_rename(const char *from, const char *to)
+{
+ char *myname = "sane_rename";
+ int saved_errno;
+ struct stat st;
+
+ /*
+ * Normal case: rename() succeeds.
+ */
+ if (rename(from, to) >= 0)
+ return (0);
+
+ /*
+ * Woops. Save errno, and see if the error is an NFS artefact. If it is,
+ * pretend the error never happened.
+ */
+ saved_errno = errno;
+ if (stat(from, &st) < 0 && stat(to, &st) >= 0) {
+ msg_info("%s(%s,%s): worked around spurious NFS error",
+ myname, from, to);
+ return (0);
+ }
+
+ /*
+ * Nope, it didn't. Restore errno and report the error.
+ */
+ errno = saved_errno;
+ return (-1);
+}
#define FD_SETSIZE 96
#endif
#include <sys/types.h>
-#define UNSAFE_CTYPE /* XXX verify */
#define _PATH_MAILDIR "/var/spool/mail"
#define _PATH_BSHELL "/bin/sh"
#define _PATH_DEFPATH "/bin:/usr/bin:/usr/ucb"
#ifndef S_ISSOCK
#define S_ISSOCK(mode) ((mode&0xF000) == 0xC000)
#endif
+#endif
+
+#ifdef SCO5
+#define SUPPORTED
+#include <sys/types.h>
+#include <sys/socket.h>
+extern int h_errno;
+
+#define _PATH_MAILDIR "/usr/spool/mail"
+#define _PATH_BSHELL "/bin/sh"
+#define _PATH_DEFPATH "/bin:/usr/bin"
+#define USE_PATHS_H
+#define USE_FCNTL_LOCK
+#define USE_DOT_LOCK
+#define HAS_FSYNC
+#define HAS_DBM
+#define DEF_DB_TYPE "dbm"
+#define ALIAS_DB_MAP "dbm:/etc/mail/aliases"
+#define DBM_NO_TRAILING_NULL
+#define HAS_NIS
+#define GETTIMEOFDAY(t) gettimeofday(t,(struct timezone *) 0)
+#define ROOT_PATH "/bin:/etc:/usr/bin:/tcb/bin"
+#define USE_STATVFS
+#define STATVFS_IN_SYS_STATVFS_H
+#define UNIX_DOMAIN_CONNECT_BLOCKS_FOR_ACCEPT
+#define MISSING_SETENV
+/* SCO5 misses just S_ISSOCK, the others are there
+ * Use C_ISSOCK definition from cpio.h.
+ */
+#include <cpio.h>
+#define S_ISSOCK(mode) (((mode) & (S_IFMT)) == (C_ISSOCK))
#endif
/*