From: Wietse Venema Date: Sat, 29 Jan 2000 00:00:00 +0000 (+0000) Subject: snapshot-20000129 X-Git-Tag: v20010228~72 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=131764ca61648d31a8170bd4c2a1e3851ca92a37;p=thirdparty%2Fpostfix.git snapshot-20000129 --- diff --git a/postfix/HISTORY b/postfix/HISTORY index b2ede1bab..9fae0c9d4 100644 --- a/postfix/HISTORY +++ b/postfix/HISTORY @@ -3543,3 +3543,72 @@ Apologies for any names omitted. 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. + diff --git a/postfix/INSTALL.sh b/postfix/INSTALL.sh index d6a82f21f..a2678419a 100644 --- a/postfix/INSTALL.sh +++ b/postfix/INSTALL.sh @@ -255,9 +255,13 @@ test -f bin/sendmail && { 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 @@ -269,7 +273,7 @@ test -f $CONFIG_DIRECTORY/main.cf || { 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. diff --git a/postfix/RELEASE_NOTES b/postfix/RELEASE_NOTES index 29ee8f20b..506e100dc 100644 --- a/postfix/RELEASE_NOTES +++ b/postfix/RELEASE_NOTES @@ -88,9 +88,9 @@ installation can be done without write access to the build tree. 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. diff --git a/postfix/bounce/Makefile.in b/postfix/bounce/Makefile.in index f2070a7ce..44d5b7ed2 100644 --- a/postfix/bounce/Makefile.in +++ b/postfix/bounce/Makefile.in @@ -1,8 +1,8 @@ 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 \ @@ -119,3 +119,31 @@ bounce_notify_service.o: ../include/mail_addr.h 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 diff --git a/postfix/bounce/bounce.c b/postfix/bounce/bounce.c index 63a46e3fc..616527fbd 100644 --- a/postfix/bounce/bounce.c +++ b/postfix/bounce/bounce.c @@ -21,6 +21,8 @@ /* 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 @@ -198,7 +200,42 @@ static int bounce_notify_proto(char *service_name, VSTREAM *client, int flush) * 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 */ @@ -233,6 +270,8 @@ static void bounce_service(VSTREAM *client, char *service_name, char **argv) 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; diff --git a/postfix/bounce/bounce_notify_service.c b/postfix/bounce/bounce_notify_service.c index 53be2e8a6..5ff5475b7 100644 --- a/postfix/bounce/bounce_notify_service.c +++ b/postfix/bounce/bounce_notify_service.c @@ -361,7 +361,7 @@ int bounce_notify_service(char *service, char *queue_name, #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 diff --git a/postfix/bounce/bounce_recip_service.c b/postfix/bounce/bounce_recip_service.c new file mode 100644 index 000000000..80af89aeb --- /dev/null +++ b/postfix/bounce/bounce_recip_service.c @@ -0,0 +1,474 @@ +/*++ +/* 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 +#include +#include +#include +#include + +#ifdef STRCASECMP_IN_STRINGS_H +#include +#endif + +/* Utility library. */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Global library. */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* 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); +} diff --git a/postfix/bounce/bounce_service.h b/postfix/bounce/bounce_service.h index 5385e550f..c29889707 100644 --- a/postfix/bounce/bounce_service.h +++ b/postfix/bounce/bounce_service.h @@ -23,6 +23,11 @@ extern int bounce_append_service(char *, char *, char *, char *); */ 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 */ diff --git a/postfix/cleanup/Makefile.in b/postfix/cleanup/Makefile.in index 47bcbe6fe..7185795f9 100644 --- a/postfix/cleanup/Makefile.in +++ b/postfix/cleanup/Makefile.in @@ -1,10 +1,10 @@ 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 = @@ -80,12 +80,37 @@ cleanup.o: ../include/mail_params.h 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 @@ -256,33 +281,16 @@ cleanup_rewrite.o: ../include/maps.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 diff --git a/postfix/cleanup/cleanup.c b/postfix/cleanup/cleanup.c index f40e27751..a1d89e9ab 100644 --- a/postfix/cleanup/cleanup.c +++ b/postfix/cleanup/cleanup.c @@ -145,6 +145,8 @@ #include #include #include +#include +#include /* Single-threaded server skeleton. */ @@ -154,12 +156,6 @@ #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. */ @@ -197,13 +193,10 @@ int cleanup_ext_prop_mask; 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. @@ -212,166 +205,60 @@ static void cleanup_service(VSTREAM *src, char *unused_service, char **argv) 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 */ diff --git a/postfix/cleanup/cleanup.h b/postfix/cleanup/cleanup.h index 69fc01429..15d46981e 100644 --- a/postfix/cleanup/cleanup.h +++ b/postfix/cleanup/cleanup.h @@ -25,35 +25,38 @@ /* * 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. @@ -79,41 +82,49 @@ extern char *cleanup_path; /* * 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 @@ -125,14 +136,14 @@ extern void cleanup_rewrite_tree(TOK822 *); /* * 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 @@ -144,7 +155,7 @@ extern void cleanup_masquerade_tree(TOK822 *, ARGV *); /* * Cleanup_recipient.c */ -extern void cleanup_out_recipient(char *); +extern void cleanup_out_recipient(CLEANUP_STATE *, char *); /* LICENSE /* .ad diff --git a/postfix/cleanup/cleanup_api.c b/postfix/cleanup/cleanup_api.c new file mode 100644 index 000000000..7d795b003 --- /dev/null +++ b/postfix/cleanup/cleanup_api.c @@ -0,0 +1,285 @@ +/*++ +/* 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 +#include + +/* Utility library. */ + +#include +#include +#include + +/* Global library. */ + +#include +#include +#include +#include +#include +#include +#include +#include + +/* 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); +} diff --git a/postfix/cleanup/cleanup_envelope.c b/postfix/cleanup/cleanup_envelope.c index dab3525e0..1ca238e2b 100644 --- a/postfix/cleanup/cleanup_envelope.c +++ b/postfix/cleanup/cleanup_envelope.c @@ -6,13 +6,23 @@ /* SYNOPSIS /* #include /* -/* 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 @@ -52,13 +62,10 @@ #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 @@ -66,92 +73,87 @@ void cleanup_envelope(void) * 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); } diff --git a/postfix/cleanup/cleanup_extracted.c b/postfix/cleanup/cleanup_extracted.c index f7e2b94b9..dd5a7151d 100644 --- a/postfix/cleanup/cleanup_extracted.c +++ b/postfix/cleanup/cleanup_extracted.c @@ -49,102 +49,101 @@ #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, ""); } diff --git a/postfix/cleanup/cleanup_map11.c b/postfix/cleanup/cleanup_map11.c index 96069324a..6d699d348 100644 --- a/postfix/cleanup/cleanup_map11.c +++ b/postfix/cleanup/cleanup_map11.c @@ -6,17 +6,20 @@ /* SYNOPSIS /* #include /* -/* 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; @@ -27,7 +30,7 @@ /* 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 @@ -87,7 +90,8 @@ /* 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; @@ -104,7 +108,7 @@ void cleanup_map11_external(VSTRING *addr, MAPS *maps, int propagate) 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)); @@ -114,20 +118,21 @@ void cleanup_map11_external(VSTRING *addr, MAPS *maps, int propagate) 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); @@ -138,7 +143,7 @@ void cleanup_map11_tree(TOK822 *tree, MAPS *maps, int propagate) * 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); @@ -146,7 +151,8 @@ void cleanup_map11_tree(TOK822 *tree, MAPS *maps, int propagate) /* 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); @@ -157,7 +163,7 @@ void cleanup_map11_internal(VSTRING *addr, MAPS *maps, int propagate) * 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); } diff --git a/postfix/cleanup/cleanup_map1n.c b/postfix/cleanup/cleanup_map1n.c index 2bab163df..bd7303ddf 100644 --- a/postfix/cleanup/cleanup_map1n.c +++ b/postfix/cleanup/cleanup_map1n.c @@ -61,7 +61,8 @@ /* 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; @@ -93,25 +94,25 @@ ARGV *cleanup_map1n_internal(char *addr, MAPS *maps, int propagate) 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); } } @@ -121,8 +122,8 @@ ARGV *cleanup_map1n_internal(char *addr, MAPS *maps, int propagate) 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; diff --git a/postfix/cleanup/cleanup_message.c b/postfix/cleanup/cleanup_message.c index 25c55bcf9..e35a3b4da 100644 --- a/postfix/cleanup/cleanup_message.c +++ b/postfix/cleanup/cleanup_message.c @@ -6,7 +6,16 @@ /* 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 @@ -67,33 +76,34 @@ /* 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; @@ -116,7 +126,7 @@ static void cleanup_fold_header(void) } next_line = *end_line ? end_line + 1 : 0; } - cleanup_out_header(); + cleanup_out_header(state); } /* cleanup_extract_internal - save unquoted copy of extracted address */ @@ -134,7 +144,7 @@ static char *cleanup_extract_internal(VSTRING *buffer, TOK822 *addr) /* 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; @@ -148,36 +158,36 @@ static void cleanup_rewrite_sender(HEADER_OPTS *hdr_opts) * 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; @@ -191,57 +201,57 @@ static void cleanup_rewrite_recip(HEADER_OPTS *hdr_opts) * 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; } } } @@ -251,8 +261,8 @@ static void cleanup_header(void) * 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); } /* @@ -262,25 +272,25 @@ static void cleanup_header(void) * 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); } } } @@ -288,7 +298,7 @@ static void cleanup_header(void) /* 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; @@ -299,25 +309,25 @@ static void cleanup_missing_headers(void) * 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)); } /* @@ -327,173 +337,161 @@ static void cleanup_missing_headers(void) #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; + } } diff --git a/postfix/cleanup/cleanup_out.c b/postfix/cleanup/cleanup_out.c index 7be237040..aa9618dcc 100644 --- a/postfix/cleanup/cleanup_out.c +++ b/postfix/cleanup/cleanup_out.c @@ -6,22 +6,27 @@ /* 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 @@ -57,7 +62,7 @@ #include #include -#include /* 44BSD stdarg.h uses abort() */ +#include /* 44BSD stdarg.h uses abort() */ #include #include @@ -79,17 +84,17 @@ /* 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; } } } @@ -97,14 +102,14 @@ void cleanup_out(int type, char *string, int len) /* 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; @@ -114,5 +119,5 @@ void cleanup_out_format(int type, char *fmt,...) va_start(ap, fmt); vstring_vsprintf(vp, fmt, ap); va_end(ap); - CLEANUP_OUT_BUF(type, vp); + CLEANUP_OUT_BUF(state, type, vp); } diff --git a/postfix/cleanup/cleanup_out_recipient.c b/postfix/cleanup/cleanup_out_recipient.c index 596e17258..ff4450ed1 100644 --- a/postfix/cleanup/cleanup_out_recipient.c +++ b/postfix/cleanup/cleanup_out_recipient.c @@ -6,7 +6,8 @@ /* 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. @@ -55,20 +56,20 @@ /* 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); } } diff --git a/postfix/cleanup/cleanup_skip.c b/postfix/cleanup/cleanup_skip.c deleted file mode 100644 index a5ace4964..000000000 --- a/postfix/cleanup/cleanup_skip.c +++ /dev/null @@ -1,59 +0,0 @@ -/*++ -/* 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 - -/* Utility library. */ - -#include -#include - -/* Global library. */ - -#include -#include -#include - -/* 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 */ ; -} diff --git a/postfix/cleanup/cleanup_state.c b/postfix/cleanup/cleanup_state.c index fe73981e1..97e41d9b3 100644 --- a/postfix/cleanup/cleanup_state.c +++ b/postfix/cleanup/cleanup_state.c @@ -6,16 +6,14 @@ /* 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. /* @@ -39,7 +37,7 @@ #include #include -#include +#include /* Global library. */ @@ -50,89 +48,62 @@ #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); } diff --git a/postfix/conf/main.cf b/postfix/conf/main.cf index 0eeb4e4b0..6763727fc 100644 --- a/postfix/conf/main.cf +++ b/postfix/conf/main.cf @@ -128,7 +128,7 @@ mail_owner = postfix # 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 # @@ -325,13 +325,11 @@ mail_owner = postfix # 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) diff --git a/postfix/conf/pcre_table b/postfix/conf/pcre_table new file mode 100644 index 000000000..cdecf7822 --- /dev/null +++ b/postfix/conf/pcre_table @@ -0,0 +1 @@ +THIS IS TODO diff --git a/postfix/conf/regexp_table b/postfix/conf/regexp_table new file mode 100644 index 000000000..cdecf7822 --- /dev/null +++ b/postfix/conf/regexp_table @@ -0,0 +1 @@ +THIS IS TODO diff --git a/postfix/conf/sample-local.cf b/postfix/conf/sample-local.cf index 5bc62a6bd..940ef40b8 100644 --- a/postfix/conf/sample-local.cf +++ b/postfix/conf/sample-local.cf @@ -51,6 +51,8 @@ local_transport = local # ${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 diff --git a/postfix/global/bounce.c b/postfix/global/bounce.c index e5e6def48..122652d69 100644 --- a/postfix/global/bounce.c +++ b/postfix/global/bounce.c @@ -28,6 +28,29 @@ /* 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 @@ -42,16 +65,17 @@ /* 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. @@ -185,3 +209,61 @@ int bounce_flush(int flags, const char *queue, const char *id, 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); +} diff --git a/postfix/global/bounce.h b/postfix/global/bounce.h index c1316b26c..632d62fb0 100644 --- a/postfix/global/bounce.h +++ b/postfix/global/bounce.h @@ -26,20 +26,27 @@ extern int vbounce_append(int, const char *, const char *, const char *, 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. diff --git a/postfix/global/cleanup_strerror.c b/postfix/global/cleanup_strerror.c index cd23b4d7a..088f31aaa 100644 --- a/postfix/global/cleanup_strerror.c +++ b/postfix/global/cleanup_strerror.c @@ -39,7 +39,8 @@ /* * 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; @@ -48,6 +49,7 @@ struct cleanup_stat_map { 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", diff --git a/postfix/global/cleanup_user.h b/postfix/global/cleanup_user.h index ed28005e9..cc7d768e4 100644 --- a/postfix/global/cleanup_user.h +++ b/postfix/global/cleanup_user.h @@ -31,6 +31,9 @@ #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); diff --git a/postfix/global/defer.c b/postfix/global/defer.c index f59e41ccb..70735497a 100644 --- a/postfix/global/defer.c +++ b/postfix/global/defer.c @@ -60,8 +60,6 @@ /* .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. diff --git a/postfix/global/mail_params.h b/postfix/global/mail_params.h index 508acbf02..d7f309d68 100644 --- a/postfix/global/mail_params.h +++ b/postfix/global/mail_params.h @@ -420,6 +420,7 @@ extern int var_delay_warn_time; #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 @@ -429,6 +430,10 @@ extern int var_qmgr_rcpt_limit; #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. */ @@ -437,6 +442,7 @@ extern int var_qmgr_fudge; 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; @@ -444,9 +450,13 @@ 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. */ diff --git a/postfix/global/mail_queue.c b/postfix/global/mail_queue.c index 4cde268e7..310798d71 100644 --- a/postfix/global/mail_queue.c +++ b/postfix/global/mail_queue.c @@ -299,6 +299,7 @@ VSTREAM *mail_queue_enter(const char *queue_name, int mode) int fd; const char *file_id; VSTREAM *stream; + int count; /* * Initialize. @@ -349,10 +350,10 @@ VSTREAM *mail_queue_enter(const char *queue_name, int mode) 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) @@ -362,8 +363,10 @@ VSTREAM *mail_queue_enter(const char *queue_name, int mode) 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); diff --git a/postfix/global/mail_version.h b/postfix/global/mail_version.h index dc50b79d4..5cccad7e6 100644 --- a/postfix/global/mail_version.h +++ b/postfix/global/mail_version.h @@ -15,7 +15,7 @@ * 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 diff --git a/postfix/html/Makefile.in b/postfix/html/Makefile.in index 70cbbba4a..5354ad333 100644 --- a/postfix/html/Makefile.in +++ b/postfix/html/Makefile.in @@ -8,7 +8,7 @@ COMMANDS= mailq.1.html newaliases.1.html postalias.1.html postcat.1.html \ 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) @@ -113,6 +113,12 @@ aliases.5.html: ../conf/aliases 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 >$@ diff --git a/postfix/html/access.5.html b/postfix/html/access.5.html index 78284a568..3c0de66f9 100644 --- a/postfix/html/access.5.html +++ b/postfix/html/access.5.html @@ -17,12 +17,23 @@ ACCESS(5) ACCESS(5) hosts, domains, networks, host addresses or mail addresses. - The table serves as input to the postmap(1) command. The - result, an indexed file in dbm or db 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 postfix reload command to eliminate the delay. - + Normally, the table serves as input to the postmap(1) com- + mand. The result, an indexed file in dbm or db 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 postfix reload 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. + +TABLE FORMAT The format of the access table is as follows: blanks and comments @@ -34,45 +45,45 @@ ACCESS(5) ACCESS(5) address, perform the corresponding action. PATTERNS - Patterns are tried in the order as listed below: + With lookups from indexed files, patterns are tried in the + order as listed below: user@domain Matches the specified mail address. domain.name - Matches the domain.name itself and any subdomain - thereof, either in hostnames or in mail addresses. + Matches the domain.name itself and any subdomain + thereof, either in hostnames or in mail addresses. Top-level domains will never be matched. - user@ Matches all mail addresses with the specified user + user@ Matches all mail addresses with the specified user part. - net.work.addr.ess - net.work.addr - net.work - net Matches any host address in the specified network. - A network address is a sequence of one or more - octets separated by ".". - -ACTIONS + 1 - 1 +ACCESS(5) ACCESS(5) + net.work.addr.ess + net.work.addr -ACCESS(5) ACCESS(5) + net.work + net Matches any host address in the specified network. + A network address is a sequence of one or more + octets separated by ".". +ACTIONS [45]XX text - Reject the address etc. that matches the pattern, + Reject the address etc. that matches the pattern, and respond with the numerical code and text. REJECT Reject the address etc. that matches the pattern. A @@ -83,16 +94,50 @@ ACCESS(5) ACCESS(5) Any other text Accept the address etc. that matches the pattern. +REGULAR EXPRESSION TABLES + 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 regexp_table(5) or pcre_table(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 user@domain + mail addresses are not broken up into their user@ and + domain 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 $1, $2 and so on. + BUGS - The table format does not understand quoting conventions. + The table format does not understand quoting conventions. SEE ALSO postmap(1) create mapping table smtpd(8) smtp server + pcre_table(5) format of PCRE tables + regexp_table(5) format of POSIX regexp tables LICENSE - 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. AUTHOR(S) Wietse Venema @@ -128,7 +173,28 @@ ACCESS(5) ACCESS(5) - 2 + + + + + + + + + + + + + + + + + + + + + + 3 diff --git a/postfix/html/faq.html b/postfix/html/faq.html index 50bfd4f29..f26e92fae 100644 --- a/postfix/html/faq.html +++ b/postfix/html/faq.html @@ -26,6 +26,8 @@
  • Sendmail incompatibility +
  • Receiving mail via the network +
  • Mail relaying
  • Remote delivery @@ -89,6 +91,17 @@ distribution list
  • Postfix does not try all the MX addresses +
  • Postfix accepts MAIL FROM and RCPT TO "| command" + + + +

    Receiving mail via the network

    + +

    Mail relaying

    @@ -663,7 +676,7 @@ four hours, specify:
         /etc/postfix/main.cf:
    -	delay_warning_time = 4
    +        delay_warning_time = 4
     

    @@ -728,6 +741,45 @@ aliasing loops.


    +

    Postfix logs SMTP clients as IP +addresses

    + +
    + +The Postfix SMTP server logs client connections with numerical IP +addresses instead of resolving the hostname. When I use nslookup +the address does resolve to a name. + +
    + +

    + +You run the Postfix SMTP server inside a chroot 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: + +

    + +

    +    /var/spool/postfix/etc/resolv.conf
    +    /var/spool/postfix/etc/services
    +
    + +

    + +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. + +

    +For more details, see the files in the examples/chroot-setup +directory of the Postfix source code distribution. + +


    +

    Help! Postfix is an open relay

    According to some relay checking software, Postfix accepts @@ -838,8 +890,8 @@ ahead of the other SMTPD recipient restrictions:
         /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.
    @@ -1142,8 +1194,8 @@ that work around them.
     
     
         /etc/postfix/main.cf:
    -	smtp_skip_4xx_greeting = yes
    -	smtp_skip_5xx_greeting = yes
    +        smtp_skip_4xx_greeting = yes
    +        smtp_skip_5xx_greeting = yes
     

    @@ -1237,7 +1289,7 @@ in /etc/postfix/master.cf, specify:

         /etc/postfix/main.cf:
    -	local_recipient_maps = $relocated_maps $alias_maps, unix:passwd.byname
    +        local_recipient_maps = $relocated_maps $alias_maps, unix:passwd.byname
     

    @@ -1445,13 +1497,13 @@ expression-based filter at the SMTP port:

         /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
     

    @@ -1515,6 +1567,50 @@ header".


    +

    Postfix accepts MAIL FROM and RCPT TO "| command"

    + +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. + + +

    + +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. + +

    + +

    +    /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
    +
    + +

    + +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. + +

    + +See also the documentation on header +checks 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. + +


    +

    Protecting internal email distribution lists

    @@ -1766,14 +1862,14 @@ headers is sufficient to reliably implement a domain in a mailbox.
         /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
     

    diff --git a/postfix/html/qmgr.8.html b/postfix/html/qmgr.8.html index bcbc2623d..213a35b04 100644 --- a/postfix/html/qmgr.8.html +++ b/postfix/html/qmgr.8.html @@ -289,40 +289,40 @@ QMGR(8) QMGR(8) list receive that same burst of messages a whole day later. + qmgr_site_hog_factor (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. + initial_destination_concurrency - Initial per-destination concurrency level for par- + Initial per-destination concurrency level for par- allel delivery to the same destination. default_destination_concurrency_limit - Default limit on the number of parallel deliveries + Default limit on the number of parallel deliveries to the same destination. transport_destination_concurrency_limit - 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 transport. Recipient controls default_destination_recipient_limit - Default limit on the number of recipients per mes- + Default limit on the number of recipients per mes- sage transfer. transport_destination_recipient_limit - Limit on the number of recipients per message + Limit on the number of recipients per message transfer, for the named message transport. -SEE ALSO - master(8), process manager - relocated(5), format of the "user has moved" table - syslogd(8) system logging - trivial-rewrite(8), address routing - -LICENSE - The Secure Mailer license must be distributed with this - software. - -AUTHOR(S) - Wietse Venema @@ -335,6 +335,18 @@ QMGR(8) QMGR(8) QMGR(8) QMGR(8) +SEE ALSO + master(8), process manager + relocated(5), format of the "user has moved" table + syslogd(8) system logging + trivial-rewrite(8), address routing + +LICENSE + 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 @@ -367,18 +379,6 @@ QMGR(8) QMGR(8) - - - - - - - - - - - - diff --git a/postfix/html/smtp.8.html b/postfix/html/smtp.8.html index 4dfcdfd0e..5c6a4dd39 100644 --- a/postfix/html/smtp.8.html +++ b/postfix/html/smtp.8.html @@ -122,9 +122,9 @@ SMTP(8) SMTP(8) smtp_skip_4xx_greeting Skip servers that greet us with a 4xx status code. - smtp_skip_quit_response - Do not wait for the server response after sending - QUIT. + smtp_skip_5xx_greeting + Skip servers that greet us with a 5xx status code. + @@ -137,38 +137,42 @@ SMTP(8) SMTP(8) SMTP(8) SMTP(8) + smtp_skip_quit_response + Do not wait for the server response after sending + QUIT. + Resource controls smtp_destination_concurrency_limit 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 default_destination_concurrency_limit parameter. smtp_destination_recipient_limit - 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 default_destination_recipient_limit parameter. Timeout controls smtp_connect_timeout 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. smtp_helo_timeout - 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. smtp_helo_timeout - Timeout in seconds for sending the HELO command, + Timeout in seconds for sending the HELO command, and for receiving the server response. smtp_mail_timeout - Timeout in seconds for sending the MAIL FROM com- + Timeout in seconds for sending the MAIL FROM com- mand, and for receiving the server response. smtp_rcpt_timeout @@ -176,7 +180,7 @@ SMTP(8) SMTP(8) and for receiving the server response. smtp_data_init_timeout - Timeout in seconds for sending the DATA command, + Timeout in seconds for sending the DATA command, and for receiving the server response. smtp_data_xfer_timeout @@ -185,13 +189,9 @@ SMTP(8) SMTP(8) smtp_data_done_timeout Timeout in seconds for sending the "." 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. - smtp_quit_timeout - Timeout in seconds for sending the QUIT command, - and for receiving the server response. - 3 @@ -203,6 +203,10 @@ SMTP(8) SMTP(8) SMTP(8) SMTP(8) + smtp_quit_timeout + Timeout in seconds for sending the QUIT command, + and for receiving the server response. + SEE ALSO bounce(8) non-delivery status reports master(8) process manager @@ -210,7 +214,7 @@ SMTP(8) SMTP(8) syslogd(8) system logging LICENSE - The Secure Mailer license must be distributed with this + The Secure Mailer license must be distributed with this software. AUTHOR(S) @@ -251,10 +255,6 @@ SMTP(8) SMTP(8) - - - - diff --git a/postfix/local/maildir.c b/postfix/local/maildir.c index ec8eca5e9..40fe847d5 100644 --- a/postfix/local/maildir.c +++ b/postfix/local/maildir.c @@ -53,6 +53,7 @@ #include #include #include +#include /* Global library. */ @@ -136,10 +137,10 @@ int deliver_maildir(LOCAL_STATE state, USER_ATTR usr_attr, char *path) 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) diff --git a/postfix/makedefs b/postfix/makedefs index 1627dcb0b..c92a1b216 100644 --- a/postfix/makedefs +++ b/postfix/makedefs @@ -58,15 +58,24 @@ case "$VERSION" in 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" ;; diff --git a/postfix/man/Makefile.in b/postfix/man/Makefile.in index c776af6da..08b328e09 100644 --- a/postfix/man/Makefile.in +++ b/postfix/man/Makefile.in @@ -8,7 +8,7 @@ COMMANDS= man1/postalias.1 man1/postcat.1 man1/postconf.1 man1/postfix.1 \ 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) @@ -110,6 +110,12 @@ man5/aliases.5: ../conf/aliases 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 - $? >$@ diff --git a/postfix/man/man5/access.5 b/postfix/man/man5/access.5 index f07281ae8..f923d7353 100644 --- a/postfix/man/man5/access.5 +++ b/postfix/man/man5/access.5 @@ -16,12 +16,23 @@ The optional \fBaccess\fR table directs the Postfix SMTP server 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 `#'. @@ -31,9 +42,10 @@ perform the corresponding \fIaction\fR. .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 @@ -62,6 +74,29 @@ error response message is generated. .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 @@ -71,6 +106,8 @@ The table format does not understand quoting conventions. .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 diff --git a/postfix/man/man8/qmgr.8 b/postfix/man/man8/qmgr.8 index 444970acb..c0cb2ad96 100644 --- a/postfix/man/man8/qmgr.8 +++ b/postfix/man/man8/qmgr.8 @@ -223,6 +223,15 @@ 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. diff --git a/postfix/man/man8/smtp.8 b/postfix/man/man8/smtp.8 index 18db2df16..a4fc99e3c 100644 --- a/postfix/man/man8/smtp.8 +++ b/postfix/man/man8/smtp.8 @@ -103,6 +103,8 @@ When this parameter includes the \fBprotocol\fR class, send mail to the 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" diff --git a/postfix/master/master_sig.c b/postfix/master/master_sig.c index edab09101..0ad0fa7b1 100644 --- a/postfix/master/master_sig.c +++ b/postfix/master/master_sig.c @@ -159,7 +159,7 @@ void master_sigsetup(void) char *myname = "master_sigsetup"; struct sigaction action; static int sigs[] = { - SIGINT, SIGQUIT, SIGSEGV, SIGILL, SIGTERM, + SIGINT, SIGQUIT, SIGILL, SIGBUS, SIGSEGV, SIGTERM, }; unsigned i; diff --git a/postfix/postconf/local_table.h b/postfix/postconf/local_table.h index fd9969748..c612ab7d1 100644 --- a/postfix/postconf/local_table.h +++ b/postfix/postconf/local_table.h @@ -1,2 +1 @@ "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, diff --git a/postfix/postconf/local_vars.h b/postfix/postconf/local_vars.h index aa4ee57e1..c09dc1473 100644 --- a/postfix/postconf/local_vars.h +++ b/postfix/postconf/local_vars.h @@ -1,2 +1 @@ char *var_local_destination_concurrency_limit; -char *var_local_destination_recipient_limit; diff --git a/postfix/qmgr/qmgr.c b/postfix/qmgr/qmgr.c index 401c38600..3731ff8fa 100644 --- a/postfix/qmgr/qmgr.c +++ b/postfix/qmgr/qmgr.c @@ -193,12 +193,21 @@ /* 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. @@ -280,7 +289,9 @@ char *var_relocated_maps; 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; @@ -474,6 +485,8 @@ int main(int argc, char **argv) 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[] = { diff --git a/postfix/qmgr/qmgr_message.c b/postfix/qmgr/qmgr_message.c index 526566e30..c5c28a300 100644 --- a/postfix/qmgr/qmgr_message.c +++ b/postfix/qmgr/qmgr_message.c @@ -528,8 +528,10 @@ static void qmgr_message_resolve(QMGR_MESSAGE *message) */ 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); @@ -541,7 +543,9 @@ static void qmgr_message_resolve(QMGR_MESSAGE *message) * 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); @@ -611,6 +615,23 @@ static void qmgr_message_resolve(QMGR_MESSAGE *message) 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. */ diff --git a/postfix/smtp/smtp_trouble.c b/postfix/smtp/smtp_trouble.c index 2bf2c64b7..04f5d4eb4 100644 --- a/postfix/smtp/smtp_trouble.c +++ b/postfix/smtp/smtp_trouble.c @@ -134,6 +134,7 @@ static void smtp_check_code(SMTP_STATE *state, int code) * 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; } diff --git a/postfix/smtpd/smtpd.c b/postfix/smtpd/smtpd.c index 17e7cdd9a..c511dde45 100644 --- a/postfix/smtpd/smtpd.c +++ b/postfix/smtpd/smtpd.c @@ -142,8 +142,8 @@ /* 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 @@ -302,6 +302,7 @@ bool var_disable_vrfy_cmd; 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; @@ -780,6 +781,10 @@ static int data_cmd(SMTPD_STATE *state, int argc, SMTPD_TOKEN *unused_argv) 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 ."); /* @@ -1347,6 +1352,7 @@ int main(int argc, char **argv) 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, diff --git a/postfix/smtpd/smtpd_check.c b/postfix/smtpd/smtpd_check.c index 2c5101997..93a6ff438 100644 --- a/postfix/smtpd/smtpd_check.c +++ b/postfix/smtpd/smtpd_check.c @@ -312,6 +312,7 @@ static MAPS *local_rcpt_maps; static MAPS *rcpt_canon_maps; static MAPS *canonical_maps; static MAPS *virtual_maps; +static MAPS *relocated_maps; /* * Pre-opened access control lists. @@ -455,6 +456,8 @@ void smtpd_check_init(void) 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 @@ -1969,6 +1972,7 @@ char *smtpd_check_rcptmap(SMTPD_STATE *state, char *recipient) 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); @@ -1978,6 +1982,7 @@ char *smtpd_check_rcptmap(SMTPD_STATE *state, char *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, @@ -2064,6 +2069,7 @@ char *var_alias_maps; char *var_rcpt_canon_maps; char *var_canonical_maps; char *var_virtual_maps; +char *var_relocated_maps; char *var_local_rcpt_maps; typedef struct { @@ -2081,6 +2087,7 @@ static STRING_TABLE string_table[] = { 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, }; diff --git a/postfix/util/Makefile.in b/postfix/util/Makefile.in index 7fe7b2e5b..c1634b0aa 100644 --- a/postfix/util/Makefile.in +++ b/postfix/util/Makefile.in @@ -20,7 +20,8 @@ SRCS = argv.c argv_split.c attr.c basename.c binhash.c chroot_uid.c \ 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 \ @@ -42,7 +43,8 @@ OBJS = argv.o argv_split.o attr.o basename.o binhash.o chroot_uid.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 \ @@ -57,7 +59,7 @@ HDRS = argv.h attr.h binhash.h chroot_uid.h connect.h dict.h dict_db.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 \ @@ -719,6 +721,14 @@ safe_open.o: safe_open.h 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 diff --git a/postfix/util/make_dirs.c b/postfix/util/make_dirs.c index ebf8fd019..5fe5e9457 100644 --- a/postfix/util/make_dirs.c +++ b/postfix/util/make_dirs.c @@ -72,9 +72,14 @@ int make_dirs(const char *path, int perms) 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); diff --git a/postfix/util/safe_open.c b/postfix/util/safe_open.c index fae3e5341..07dce44e3 100644 --- a/postfix/util/safe_open.c +++ b/postfix/util/safe_open.c @@ -46,12 +46,12 @@ /* <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 diff --git a/postfix/util/sane_fsops.h b/postfix/util/sane_fsops.h new file mode 100644 index 000000000..0c9b3ac2a --- /dev/null +++ b/postfix/util/sane_fsops.h @@ -0,0 +1,30 @@ +#ifndef _SANE_FSOPS_H_ +#define _SANE_FSOPS_H_ + +/*++ +/* NAME +/* sane_rename 3h +/* SUMMARY +/* sanitize rename() error returns +/* SYNOPSIS +/* #include +/* 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 diff --git a/postfix/util/sane_link.c b/postfix/util/sane_link.c new file mode 100644 index 000000000..13c071cc7 --- /dev/null +++ b/postfix/util/sane_link.c @@ -0,0 +1,71 @@ +/*++ +/* NAME +/* sane_link 3 +/* SUMMARY +/* sanitize link() error returns +/* SYNOPSIS +/* #include +/* +/* 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 +#include +#include + +/* 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); +} diff --git a/postfix/util/sane_rename.c b/postfix/util/sane_rename.c new file mode 100644 index 000000000..416f8976e --- /dev/null +++ b/postfix/util/sane_rename.c @@ -0,0 +1,68 @@ +/*++ +/* NAME +/* sane_rename 3 +/* SUMMARY +/* sanitize rename() error returns +/* SYNOPSIS +/* #include +/* +/* 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 +#include +#include /* 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); +} diff --git a/postfix/util/sys_defs.h b/postfix/util/sys_defs.h index 9ad93b870..a0a18b44e 100644 --- a/postfix/util/sys_defs.h +++ b/postfix/util/sys_defs.h @@ -79,7 +79,6 @@ #define FD_SETSIZE 96 #endif #include -#define UNSAFE_CTYPE /* XXX verify */ #define _PATH_MAILDIR "/var/spool/mail" #define _PATH_BSHELL "/bin/sh" #define _PATH_DEFPATH "/bin:/usr/bin:/usr/ucb" @@ -603,6 +602,37 @@ extern int opterr; /* XXX use */ #ifndef S_ISSOCK #define S_ISSOCK(mode) ((mode&0xF000) == 0xC000) #endif +#endif + +#ifdef SCO5 +#define SUPPORTED +#include +#include +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 +#define S_ISSOCK(mode) (((mode) & (S_IFMT)) == (C_ISSOCK)) #endif /*