From: Wietse Venema Date: Wed, 17 Mar 1999 05:00:00 +0000 (-0500) Subject: postfix-19990317 X-Git-Tag: v20010228~133 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=bc5e358ef8db36675165b46d040d22d4a01ebad1;p=thirdparty%2Fpostfix.git postfix-19990317 --- diff --git a/postfix/.indent.pro b/postfix/.indent.pro index e59d396ad..b38eded21 100644 --- a/postfix/.indent.pro +++ b/postfix/.indent.pro @@ -6,6 +6,7 @@ -TBOUNCE_STAT -TCLEANUP_STATE -TCLIENT_LIST +-TCLNT_STREAM -TCONFIG_BOOL_FN_TABLE -TCONFIG_BOOL_TABLE -TCONFIG_INT_FN_TABLE @@ -25,6 +26,8 @@ -TDICT_NISPLUS -TDICT_NODE -TDICT_OPEN_INFO +-TDICT_PCRE +-TDICT_UNIX -TDNS_FIXED -TDNS_REPLY -TDNS_RR @@ -68,8 +71,14 @@ -TRECIPIENT_LIST -TREC_TYPE_NAME -TRESOLVE_REPLY +-TRESPONSE -TSCAN_DIR +-TSCAN_INFO +-TSCAN_OBJ +-TSESSION -TSINGLE_SERVER +-TSINK_COMMAND +-TSINK_STATE -TSMTPD_STATE -TSMTPD_TOKEN -TSMTP_ADDR diff --git a/postfix/HISTORY b/postfix/HISTORY index 33722da0f..befee96dd 100644 --- a/postfix/HISTORY +++ b/postfix/HISTORY @@ -817,7 +817,7 @@ Apologies for any names omitted. are gone and have become db (dbm, nis, etc) maps. Files: smtpd/smtpd_check.c, config/main.cf. -1998029-31 +19980729-31 Feature: hashed queues. Rewrote parts of the mail queue API. Configuration parameters: "hash_queue_names" specifies @@ -1981,7 +1981,7 @@ Apologies for any names omitted. Portability: doze() function for systems without usleep(). - Cleanup: clients are logged as host[address]. + Cleanup: clients are now consistently logged as host[address]. 19990122 @@ -2063,3 +2063,314 @@ Apologies for any names omitted. Requested by Matthew Green and others. Files: local/alias.c, global/split_addr.c. This affects canonical, virtual and alias lookups. + +19990204 + + Portability: signal handling for HP-UX 9 by Lamont Jones + of Hewlett Packard. File: master/master_sig.c. + + Robustness: disable random walk inside a per-site queue to + avoid message starvation under heavy load. File: qmgr_entry.c. + + Robustness: under some conditions the queue manager could + declare a host dead after just one delivery failure. File: + qmgr_queue.c. + +19990212 + + Feature: skip SMTP servers that greet us with a 4XX status + code. Example: "smtp_skip_4xx_greeting = yes". By default, + the Postfix SMTP client defers delivery when a server + declines talking to us. File: smtp/smtp_connect.c. + + Robustness: upon startup the queue manager now moves active + queue files to the incoming queue instead of the deferred + queue, to avoid anomalous delivery delays on systems that + have a huge incoming queue. Files: qmgr/qmgr.c, + qmgr/qmgr_active.c, global/mail_flush.c, conf/postfix-script* + +19990213 + + Robustness: added watchdog timers to avoid getting stuck + on systems with broken select() socket implementations. + File: qmgr_transport.c, qmgr_deliver.c. + +19990218 + + Feature: NFS-friendly delivery to mailbox by avoiding the + use of root privileges as much as possible. With input by + Mike Muus, Army Research Lab, USA. + + Feature: the smtp-sink test server now supports SMTP command + pipelining. To this end we had to generalize the timer and + vstream support. Poor performance is fixed 19990222. + + Cleanup: timer event routines now have the same interface + as read/write event routines (event type + context). File: + util/events.c. + + Feature: new vstream_peek() routine to tell how much unread + data is left in a VSTREAM buffer. This is the vstream + variant of the peekfd() routine for kernel read buffers. + File: util/vstream.c. + + Feature: directory scanning support for hashed mail queue + directories. So far the results are disappointing: with + depth = 2 (16 directories with 16 subdirectories), mailq + takes 5 seconds with an empty queue unless all directories + happen to be cached in memory. We need a bit map before + hashed queue directories become practical. Depth=1 hashing + doesn't slow down mailq much, but doesn't help much either. + Files: util/scan_dir.c, global/mail_scan_dir.c. + +19990221 + + Workaround: with "ignore_mx_lookup_error = yes", the SMTP + client always performs an A lookup when an MX lookup could + not be completed, rather than treating MX lookup failure + as a temporary error condition. Unfortunately there are + many broken DNS servers on the Internet. File: smtp/smtp_addr.c. + +19990222 + + Performance: rewrote the guts of the smtp-sink test server + so it can do pipelining without losing performance. + +19990223 + + Workaround: hotmail.com sometimes drops the connection + after "." (causing misleading diagnostics to be logged) or + waits minutes after receiving QUIT. Solution: do not wait + for the response to QUIT. File: smtp/smtp_proto.c. This + is turned off with: "smtp_skip_quit_response = no". + +19990224 + + Feature: the pipe mailer accepts user=username:groupname, + based on code submitted by Philip A. Prindeville, Mirapoint, + Inc., USA. File: pipe/pipe.c. + + Workaround: use file locking to prevent multiple processes + from select()ing on the same socket. This causes performance + problems on large BSD systems. Files: master/*_server.c. + +19990225 + + Bugfix: with "inet_interfaces = 127.0.0.1", don't bind to + the loopback interface. Problem reported by Steve Bellovin + of AT&T. File: smtp/smtp_addr.c. + + Feature: "postsuper" command to remove stale queue files + to update queues after changes to the queue structure + parameters (hash_queue_names, hash_queue_depth). This + command is to be run from the postfix-script maintenance + shell script. + +19990301 + + Feature: new postconf -h (suppress `name = ' in output) + option to make the program easier to use in, e.g., shell + scripts. + + Feature: dict_unix module so you can add the UNIX passwd + table to the SMTPD access control list. + +19990302 + + Feature: "luser_relay = destination" captures mail for + non-existent local recipients. This works only when the + local delivery agent does mailbox delivery (including + delivery via mailbox_command), not when mailbox delivery + is delegated to another message transport. + + Feature: new reject_non_fqdn_{hostname,sender,recipient} + restrictions to require fully.qualified.domain forms in + HELO, MAIL FROM and RCPT TO commands (while still allowing + the <> sender address). + +19990304 + + Bugfix: backed out the 19990119 change to always insert + Return-Path: if that header is not present. The pipe and + local agents now are responsible for prepending Return-Path:. + Files: cleanup/cleanup_message.c, global/mail_copy.[hc], + pipe/pipe.c, global/header_opts.c. This causes an incompatible + change to the pipe flags parameter, because Return-Path: + now must be requested explicitly. + +19990305 + + Bugfix: showq (the mailq server) incorrectly assumed that + all recipients of a deferred message are listed in the + corresponding defer logfile. It now lists all recipients. + Files: showq/showq.c, cleanup/cleanup_envelope.c (ensure + that sender records always precede recipient records). + + Cleanup: smtpd HELO restrictions validate [numerical] forms. + Files: util/valid_hostname.c, smtpd/smtpd_check.c. Initial + code by Philip A. Prindeville, Mirapoint, Inc., USA. + +19990306 + + Cleanup: re-vamped the valid_hostname module, and added a + maximal label length (63) requirement. + + Feature: fallback_relay parameter to specify extra backup + hosts in case the regular relay hosts are not found or not + available. Files: smtp/smtp_addr.c. + + Feature: "always_bcc = address" specifies where to send a + copy of each message that enters he system. However, if + that copy bounces, the sender will be informed of the + bounce. Files: smtpd/smtpd.c, pickup/pickup.c + + Compatibility: the transport map will now route on top-level + domains, so you can dump all of .bitnet to a bitnet relay. + +19990307 + + Feature: LDAP lookups, updated by Jon Hensley, Merit + Network, USA. + + Feature: regular expression (PCRE) support by Andrew + McNamara, connect.com.au Pty. Ltd., Australia. In order to + use this code specify pcre:/file/name. You can use this + anywhere you would use a DB or DBM file, NIS or LDAP. + See: PCRE_README for how to enable this code. + + Feature: "delay_warning_time = 4" causes Postfix to send + a "your mail is delayed" notice after approx. 4 hours. + Daniel Eisenbud, University of California at Berkeley. + Files: qmgr/qmgr_active.c, qmgr/qmgr_message. Postmaster + notices for delayed mail are disabled by default. In order + to receive postmaster notices, specify "notify_classes = + ... delay ...". + + Cleanup: do not send undeliverable bounced mail to postmaster. + This was causing lots of pain with junk mail from bogus + sender addresses to non-existent recipients. This change + was reversed 19990311. + +19990308 + + Bugfix: the dotforward routine was too eager with throwing + away extension information, so that the Delivered-To: info + would differ for \mailbox and |command. Problem reported + by Rafi Sadowski, Open University, Israel. + + Bugfix: seems I never got around to fix the btree access + method. I finally did. Problem reported by: Matt Smith, + AvTel Communications Inc., USA. + +19990311 + + Back by popular demand: with "notify_classes = 2bounce ..." + Postfix will send undeliverable bounced mail to postmaster. + The default is to not send double bounces. This change + reverses a change made on 19990307. + +19990312 + + Feature: configurable exit handler for server skeletons. + Philip A. Prindeville, Mirapoint, Inc., USA. Files: + master/*server.c. + + Feature: mail_spool_directory configuration parameter to + specify the UNIX mail spool directory. The default setting + is system dependent. + +19990313 + + Cleanup: share file descriptors for resolve and rewrite + client connections. This puts less strain on the trivial-rewrite + service. + + Portability: support for UnixWare 2.1 by Dmitry E. Kiselyov, + Nizhny Novgorod City Health Emergency Station. + + Feature: configurable delays in the smtpstone test programs. + With input by Philip A. Prindeville, Mirapoint, Inc., USA. + Files: smtpstone/*.c. + + Bugfix: a "signal 11" problem in the trivial-rewrite program + that would occasionally happen after "postfix reload". + Reason: some rewrite clients would clobber their input, + and when they had to retransmit the query, the input would + be a zero-length string, which trivial-rewrite isn't supposed + to receive. + +19990314 + + Feature: "mailbox_transport = cyrus" delegates all local + mailbox delivery to a master.cf entry called "cyrus" (the + same trick for procmail), including users not found in the + UNIX passwd database. This gives the flexibility of $name + expansions by the pipe mailer, without losing local aliases + and ~/.forward processing. Result of discussions with Rupa + Schomaker, RS Consulting. + +19990315 + + Feature: the mydestination parameter can now be an empty + string, for hosts that don't receive any mail locally. Be + sure to specify a default route for mail that comes to the + machine or mail will loop. + +19990316 + + Bugfix: the SMTPD check scaffolding didn't apply the same + sanity checks as the production code. Problem reported by + Alain Thivillon, Hervé Schauer Consultants, France. File: + smtpd/smtpd_check.c. + + Portability: some systems can have more than 59 seconds in + a minute. Based on a fix by Liviu Daia, Institute of + Mathematics, Romanian Academy. File: global/mail_date.c. + + Enhancement: include the client network address in the + rejected by RBL response. Lamont Jones, Hewlett-Packard. + + Workaround: use fstat() to figure out if the maildrop is + world-writable. access() uses the real uid, which stinks. + + Robustness: don't do partial address lookups (user@, domain, + user, @domain) with regexp-style tables. + + Security: don't allow regexp-style tables to be used for + aliases. It would be too easy to slip in "|command" or + :include: or /file/name. + +19990317 + + Feature: "fallback_transport = cyrus" delegates non-UNIX + recipients to a master.cf entry called "cyrus", allowing + you to have both UNIX and non-UNIX mailboxes side by side. + +Future: + + Planned: must be able to list the same hash table in + virtual_maps and mydestination. + + Planned: delivered-to cache instead of table. + + Planned: hide internal loop info so that delivered-to is + no longer required. + + Planned: DNS lookups for address canonicalization. + + Planned: no more slow-down when qmgr reaches the end of + the in-memory recipient list while there's still more + recipients in the queue file. + + Planned: pop pre-authentication by Joerg Henne. + + Planned: ident lookup by Jon Ribbens. + + Planned: $logname, $home, $shell, $user, $sender expansions + in shell commands, mailbox_command, forward_path. + + Planned: forward_path, for example $home/.forward (default) + or /var/forward/$logname (for mail servers). + + Planned: control From_, Return-Path, Delivered-To and other + features with local delivery to mailbox or command. diff --git a/postfix/INSTALL b/postfix/INSTALL index 126049c85..9ea47b9c6 100644 --- a/postfix/INSTALL +++ b/postfix/INSTALL @@ -54,6 +54,7 @@ If your system is supported, it is one of BSD/OS 4.x FreeBSD 2.x FreeBSD 3.x + FreeBSD 4.x HP-UX 9.x HP-UX 10.x HP-UX 11.x @@ -110,7 +111,7 @@ of the compiler: % make makefiles CC="purify cc" % make -and so on. On some cases, optimization is turned off automatically. +and so on. In some cases, optimization is turned off automatically. In order to build with non-default settings, for example, with a configuration directory other than /etc/postfix, use: @@ -172,7 +173,7 @@ Installing Postfix by hand takes only a few steps. # chmod 755 /etc/postfix # cp /some/where/postfix/conf/* /etc/postfix # chmod 644 /etc/postfix/* - # chmod 755 /etc/postfix/postfix-script + # chmod 755 /etc/postfix/postfix-script* This also installs the LICENSE file, as required. diff --git a/postfix/LDAP_README b/postfix/LDAP_README new file mode 100644 index 000000000..e07308c30 --- /dev/null +++ b/postfix/LDAP_README @@ -0,0 +1,197 @@ +BUILDING WITH LDAP SUPPORT +========================== + +You need to have LDAP libraries and include files installed somewhere on +your system, and you need to configure the Postfix Makefiles +accordingly. + +If you're using the libraries from the UM distribution +(http://www.umich.edu/~dirsvcs/ldap/ldap.html) or OpenLDAP +(http://www.openldap.org), something like this should work: + + % make tidy + % make makefiles CCARGS="-I/some/where/include -DHAS_LDAP" \ + AUXLIBS="/some/where/libldap.a /some/where/liblber.a" + +The `make tidy' command is needed only if you have previously built +Postfix without LDAP support. + +If your LDAP libraries were built with Kerberos support, you'll also +need to include your Kerberos libraries in this line. Note that the KTH +Kerberos IV libraries might conflict with Postfix's lib/libdns.a, which +defines dns_lookup. If that happens, you'll probably want to link with +LDAP libraries that lack Kerberos support just to build Postfix, as it +doesn't yet support Kerberos binds to the LDAP server anyway. Sorry +about the bother. + +If you're using one of the Netscape LDAP SDKs, you'll need to change the +AUXLIBS line to point to libldap10.so or libldapssl30.so or whatever you +have, and you may need to use the -R option so the executables can find +it at runtime. + +USING LDAP LOOKUPS +================== + +In order to use LDAP lookups, define at least one LDAP source as a table +lookup in main.cf, for example: + + alias_maps = hash:/etc/aliases, ldap:ldapsource + +Each LDAP source can have the following parameters, which should be +prefixed in main.cf with the name you've given the source in its +definition. To continue the example, the first parameter below, +"server_host", would be defined in main.cf as "ldapsource_server_host". +Defaults are given in parentheses: + + server_host (localhost) + The name of the host running the LDAP server, e.g. + ldapsource_server_host = ldap.your.com + It should be possible with all the libraries mentioned above to + specify multiple servers separated by spaces, with the libraries + trying them in order should the first one fail. + + server_port (389) + The port the LDAP server listens on, e.g. + ldapsource_server_port = 778 + + search_base (no default) + The base at which to conduct the search, e.g. + ldapsource_search_base = dc=your, dc=com + + timeout (10 seconds) + The number of seconds a search can take before timing out, e.g. + ldapsource_timeout = 5 + + query_filter (mailacceptinggeneralid=%s) + The RFC2254 filter used to search the directory, where %s is a + substitute for the address Postfix is trying to resolve, e.g. + ldapsource_query_filter = (&(mail=%s)(paid_up=true)) + + result_attribute (maildrop) + The attribute Postfix will read from any directory entries + returned by the lookup, to be resolved to an email address. + ldapsource_result = mailbox + + bind (yes) + Whether or not to bind to the LDAP server. Newer LDAP + implementations don't require clients to bind, which saves + time. Example: + ldapsource_bind = no + + bind_dn ("") + If you do have to bind, do it with this distinguished name. + Example: + ldapsource_bind_dn = uid=postfix, dc=your, dc=com + + bind_pw ("") + The password for the distinguished name above. If you have to + have this, you probably want to make main.cf readable only by + the Postfix user. Example: + ldapsource_bind_pw = postfixpw + +Don't use quotes in these variables; at least, not until the Postfix +configuration routines understand how to deal with quoted strings. + +EXAMPLE +======= + +Here's a basic example. In main.cf, you have these configuration +parameters defined: + +alias_maps = hash:/etc/aliases, ldap:ldapsource +ldapsource_server_host = ldap.my.com +ldapsource_search_base = dc=my, dc=com + +Upon receiving mail for a local address "ldapuser" that isn't found in +the /etc/aliases database, Postfix will search the LDAP server listening +at port 389 on ldap.my.com. It will bind anonymously, search for any +directory entries whose mailacceptinggeneralid attribute is "ldapuser", +read the "maildrop" attributes of those found, and build a list of their +maildrops, which will be treated as RFC822 addresses to which the +message will be delivered. + +NOTES AND THINGS TO THINK ABOUT +=============================== + +- You probably want to make sure that mailacceptinggeneralids are + unique, and that not just anyone can specify theirs as postmaster or + root, say. + +- An entry can have an arbitrary number of maildrops. Maildrops can also + be comma-separated lists of addresses. For example, you could define + an entry intended for use as a mailing list that looks like this + (Warning! Schema made up just for this example): + + dn: cn=Accounting Staff List, dc=my, dc=com + cn: Accounting Staff List + o: my.com + objectclass: maillist + mailacceptinggeneralid: accountingstaff + mailacceptinggeneralid: accounting-staff + maildrop: mylist-owner + maildrop: an-accountant + maildrop: some-other-accountant + maildrop: this, that, theother + +- If you use an LDAP map for lookups other than aliases, you may have to + make sure the lookup makes sense. In the case of virtual lookups, + maildrops like "|/some/program" are pretty useless. Your query_filter + should probably look something like this: + + virtual_query_filter = + (&(mailacceptinggeneralid=%s)(!(|(maildrop="*|*")(maildrop="*:*")))) + +- And for that matter, you may not want users able to specify their + maildrops as programs, particularly if they'd be executed on the + server. A safer local query_filter could look something like: + + local_query_filter = (&(mailacceptinggeneralid=%s)(|(!(maildrop="*|*"))(owner=cn=root, dc=your, dc=com))) + + So that if the object had a program as its maildrop and weren't owned + by "cn=root" it wouldn't be returned as a valid local user. This will + probably require some thought on your part to implement safely, + considering the ramifications of includes and programs. You may decide + it's not worth the bother to allow any of that nonsense in LDAP + lookups, ban it in the query_filter, and keep things like majordomo + lists in local alias databases. + +- It's not yet known how all this scales, but LDAP lookups are much more + expensive than checking a DB file. If you anticipate a lot of lookups, + it may pay to plan your directory to reduce the number of lookups. For + instance, rather than having a bunch of objects that serve as aliases + to just one object, you could simply add their mailacceptinggeneralids + to the target object. This: + + dn: uid=firstlast, dc=your, dc=com + maildrop: firstlast@mailbox.your.com + mailacceptinggeneralid: firstlast + mailacceptinggeneralid: First.Last + mailacceptinggeneralid: F.Last + + Not this: + + dn: uid=firstlast, dc=your, dc=com + maildrop: firstlast@mailbox.your.com + mailacceptinggeneralid: firstlast + + dn: cn=First.Last, dc=your, dc=com + maildrop: firstlast + mailacceptinggeneralid: First.Last + + dn: cn=F.Last, dc=your, dc=com + maildrop: firstlast + mailacceptinggeneralid: F.Last + + Any performance reports will be much appreciated on the postfix-users + list. + +CREDITS +======= + +Support for LDAP was initially written by Prabhat K Singh of VSNL, +Bombay, India, and then hideously bloated by John Hensley to support +multiple sources and more configurable attributes. The caching bits were +initially worked out by Prabhat, then munged to support the multiple +sources. Other contributions have been submitted to move toward better +support of Netscape/LDAPv3 libraries, and any other improvements are of +course welcome. diff --git a/postfix/Makefile.in b/postfix/Makefile.in index e718d0104..cccfb2469 100644 --- a/postfix/Makefile.in +++ b/postfix/Makefile.in @@ -4,7 +4,7 @@ OPTS = "CC=$(CC)" DIRS = util global dns master postfix smtpstone fsstone sendmail \ pickup cleanup smtpd local trivial-rewrite qmgr smtp bounce pipe \ showq postalias postcat postconf postdrop postkick postlock postlog \ - postmap # man html + postmap postsuper # man html default: update diff --git a/postfix/PCRE_README b/postfix/PCRE_README new file mode 100644 index 000000000..d407d5ef9 --- /dev/null +++ b/postfix/PCRE_README @@ -0,0 +1,45 @@ +To: wietse@porcupine.org (Wietse Venema) +Cc: postfix-users@postfix.org (Postfix users) +Subject: regexp map patch +In-reply-to: Your message of "Thu, 25 Feb 1999 19:51:25 CDT." + <19990226005125.69B3C4596E@spike.porcupine.org> +Date: Tue, 02 Mar 1999 11:04:02 +1100 +From: Andrew McNamara +Message-Id: <19990302000403.074C7ED7D@melang.off.connect.com.au> +Sender: owner-postfix-users@postfix.org +Precedence: bulk +Return-Path: + +I've written [code] to add a regexp map type. It utilises the PCRE +library (Perl Compatible Regular Expressions), which can be obtained +from: + + ftp://ftp.cus.cam.ac.uk/pub/software/programs/pcre/ + +You will need to add -DHAS_PCRE and a -I for the PCRE header to CCARGS, +and add the path to the PCRE library to AUXLIBS, for example: + + make -f Makefile.init makefiles 'CCARGS=-DHAS_PCRE -I../../pcre-2.04' \ + 'AUXLIBS=../../pcre-2.04/libpcre.a' + +One possible use is to add a line to main.cf: + + smtpd_recipient_restrictions = pcre:/opt/postfix/etc/smtprecipient + +The regular expressions are read from the file specified and compiled - +a sample regexp file for this usage is included in the patch. + +Any feedback is appreciated (from Wietse in particular :-). Have +fun. + +[I've changed the code so that it can be used for other Postfix +table lookups, not just for junk mail control. In particular, +regular expressions in canonical tables could be very useful. + +For the sake of robustness, I have disabled the matching of partial +addresses (user@, domain, user, @domain) that is normally done with +Postfix access control tables, canonical maps and virtual maps. + +As a side effect, pcre maps can only match user@domain strings, so +that regexps cannot be used for local alias database lookups. That +would be a security exposure anyway -- Wietse.] diff --git a/postfix/RELEASE_NOTES b/postfix/RELEASE_NOTES index 0e9cef7ce..712ed3c0c 100644 --- a/postfix/RELEASE_NOTES +++ b/postfix/RELEASE_NOTES @@ -1,3 +1,88 @@ +Incompatible changes with postfix-alpha-19990317: +================================================= + +- You MUST install the new version of /etc/postfix/postfix-script. + +- The pipe mailer "flags" syntax has changed. You now explicitly +MUST specify the R flag in order to generate a Return-Path: message +header (as needed by, for example, cyrus). + +Major changes since postfix-beta-19990122-pl01: +=============================================== + +A detailed record of changes is given in the HISTORY file. + +- Less postmaster mail. Undeliverable bounce messages (double +bounces) are now discarded. Specify "notify_classes = 2bounce..." +to get copies of double bounces. Specify "notify_classes = bounce..." +to get copies of normal and double bounces. + +- Improved LDAP client code by John Hensley of Merit Network, USA. +See LDAP_README for details. + +- Perl-compatible regular expression support for lookup maps by +Andrew McNamara, connect.com.au Pty. Ltd., Australia.. Example: +"check_recipient_access pcre:/etc/postfix/sample-pcre.cf". Regular +expressions provide a powerful tool not only for SMTP access control +but also for address rewriting. See PCRE_README for details. + +- Automatic notification of delayed mail (disabled by default). +With "delay_warning_time = 4", Postfix informs senders when mail +has not been delivered after 4 hours. Initial version of the code +by Daniel Eisenbud, University of California at Berkeley. In order +to get postmaster copies of such warnings, specify "notify_classes += delay...". + +- More configurable local delivery: "mail_spool_directory" to +specify the UNIX mail spool directory; "mailbox_transport" to +delegate all mailbox delivery to, for example, cyrus, and +"fallback_transport" to delegate delivery of only non-UNIX users. +And all this without losing local aliases and local .forward +processing. See config/main.cf and config/master.cf. + +- Several changes to improve Postfix behavior under worst-case +conditions (frequent Postfix restarts/reloads combined with lots +if inbound mail, intermittent connectivity problems, SMTP servers +that become comatose after receiving QUIT). + +- More NFS-friendly mailbox delivery. The local delivery agent +now avoids using root privileges where possible. + +- For sites that do not receive mail at all, mydestination can now +be an empty string. Be sure to set up a transport table entry to +prevent mail from looping. + +- New "postsuper" utility to clean up stale files from Postfix +queues. + +- Workaround for BSD select() collisions that cause performance +problems on large BSD systems. + +- Use "$alias_maps, unix:passwd.byname" in smtpd access tables to +recognize known local users. We're almost there with stopping +unknown users at the RCPT TO command - the syntax for virtual maps +and sendmail access tables is too different. + +- Several questionable but useful features to capture mail: +"always_bcc = address" to capture a copy of every message that +enters the system, and "luser_relay = address" to capture mail for +unknown recipients (does not work when mailbox_transport or +fallback_transport are being used). + +- Junk mail controls: new reject_non_fqdn_{hostname,sender,recipient} +restrictions to reject non-FQDN arguments in HELO, MAIL FROM and +RCPT TO commands, and stricter checking of numeric HELO arguments. + +- "fallback_relay" feature for sites that use DNS but that can't +talk to the entire world. The fall-back relay gets the mail when +a destination is not found in the DNS or when the destination is +found but not reachable. + +- Several questionable controls that can help to keep mail going: +specify "smtp_skip_4xx_greeting = yes" to skip SMTP servers that +greet with 4XX, "ignore_mx_lookup_error = yes" to look up an A +record when a DNS server does not respond to an MX query. + Incompatible changes with postfix-beta-19990122-pl01: ===================================================== diff --git a/postfix/bounce/.indent.pro b/postfix/bounce/.indent.pro index e59d396ad..b38eded21 100644 --- a/postfix/bounce/.indent.pro +++ b/postfix/bounce/.indent.pro @@ -6,6 +6,7 @@ -TBOUNCE_STAT -TCLEANUP_STATE -TCLIENT_LIST +-TCLNT_STREAM -TCONFIG_BOOL_FN_TABLE -TCONFIG_BOOL_TABLE -TCONFIG_INT_FN_TABLE @@ -25,6 +26,8 @@ -TDICT_NISPLUS -TDICT_NODE -TDICT_OPEN_INFO +-TDICT_PCRE +-TDICT_UNIX -TDNS_FIXED -TDNS_REPLY -TDNS_RR @@ -68,8 +71,14 @@ -TRECIPIENT_LIST -TREC_TYPE_NAME -TRESOLVE_REPLY +-TRESPONSE -TSCAN_DIR +-TSCAN_INFO +-TSCAN_OBJ +-TSESSION -TSINGLE_SERVER +-TSINK_COMMAND +-TSINK_STATE -TSMTPD_STATE -TSMTPD_TOKEN -TSMTP_ADDR diff --git a/postfix/bounce/Makefile.in b/postfix/bounce/Makefile.in index 3279399a6..d9d93153a 100644 --- a/postfix/bounce/Makefile.in +++ b/postfix/bounce/Makefile.in @@ -1,7 +1,7 @@ SHELL = /bin/sh -SRCS = bounce.c bounce_append_service.c bounce_flush_service.c \ +SRCS = bounce.c bounce_append_service.c bounce_notify_service.c \ bounce_cleanup.c -OBJS = bounce.o bounce_append_service.o bounce_flush_service.o \ +OBJS = bounce.o bounce_append_service.o bounce_notify_service.o \ bounce_cleanup.o HDRS = TESTSRC = @@ -91,31 +91,31 @@ bounce_cleanup.o: ../include/vbuf.h bounce_cleanup.o: ../include/mail_queue.h bounce_cleanup.o: ../include/vstream.h bounce_cleanup.o: bounce_service.h -bounce_flush_service.o: bounce_flush_service.c -bounce_flush_service.o: ../include/sys_defs.h -bounce_flush_service.o: ../include/msg.h -bounce_flush_service.o: ../include/vstring.h -bounce_flush_service.o: ../include/vbuf.h -bounce_flush_service.o: ../include/vstream.h -bounce_flush_service.o: ../include/vstring_vstream.h -bounce_flush_service.o: ../include/mymalloc.h -bounce_flush_service.o: ../include/stringops.h -bounce_flush_service.o: ../include/events.h -bounce_flush_service.o: ../include/line_wrap.h -bounce_flush_service.o: ../include/name_mask.h -bounce_flush_service.o: ../include/mail_queue.h -bounce_flush_service.o: ../include/mail_proto.h -bounce_flush_service.o: ../include/iostuff.h -bounce_flush_service.o: ../include/quote_822_local.h -bounce_flush_service.o: ../include/mail_params.h -bounce_flush_service.o: ../include/canon_addr.h -bounce_flush_service.o: ../include/is_header.h -bounce_flush_service.o: ../include/record.h -bounce_flush_service.o: ../include/rec_type.h -bounce_flush_service.o: ../include/config.h -bounce_flush_service.o: ../include/post_mail.h -bounce_flush_service.o: ../include/cleanup_user.h -bounce_flush_service.o: ../include/mail_addr.h -bounce_flush_service.o: ../include/mark_corrupt.h -bounce_flush_service.o: ../include/mail_error.h -bounce_flush_service.o: bounce_service.h +bounce_notify_service.o: bounce_notify_service.c +bounce_notify_service.o: ../include/sys_defs.h +bounce_notify_service.o: ../include/msg.h +bounce_notify_service.o: ../include/vstring.h +bounce_notify_service.o: ../include/vbuf.h +bounce_notify_service.o: ../include/vstream.h +bounce_notify_service.o: ../include/vstring_vstream.h +bounce_notify_service.o: ../include/mymalloc.h +bounce_notify_service.o: ../include/stringops.h +bounce_notify_service.o: ../include/events.h +bounce_notify_service.o: ../include/line_wrap.h +bounce_notify_service.o: ../include/name_mask.h +bounce_notify_service.o: ../include/mail_queue.h +bounce_notify_service.o: ../include/mail_proto.h +bounce_notify_service.o: ../include/iostuff.h +bounce_notify_service.o: ../include/quote_822_local.h +bounce_notify_service.o: ../include/mail_params.h +bounce_notify_service.o: ../include/canon_addr.h +bounce_notify_service.o: ../include/is_header.h +bounce_notify_service.o: ../include/record.h +bounce_notify_service.o: ../include/rec_type.h +bounce_notify_service.o: ../include/config.h +bounce_notify_service.o: ../include/post_mail.h +bounce_notify_service.o: ../include/cleanup_user.h +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 diff --git a/postfix/bounce/bounce.c b/postfix/bounce/bounce.c index 1698130d7..f550c2190 100644 --- a/postfix/bounce/bounce.c +++ b/postfix/bounce/bounce.c @@ -101,6 +101,8 @@ * Tunables. */ int var_bounce_limit; +int var_max_queue_time; +int var_delay_warn_time; char *var_notify_classes; /* @@ -150,9 +152,9 @@ static int bounce_append_proto(char *service_name, VSTREAM *client) STR(recipient), STR(why))); } -/* bounce_flush_proto - bounce_flush server protocol */ +/* bounce_notify_proto - bounce_notify server protocol */ -static int bounce_flush_proto(char *service_name, VSTREAM *client) +static int bounce_notify_proto(char *service_name, VSTREAM *client, int flush) { int flags; @@ -173,7 +175,7 @@ static int bounce_flush_proto(char *service_name, VSTREAM *client) return (-1); } if (msg_verbose) - msg_info("bounce_flush_proto: service=%s queue=%s id=%s sender=%s", + msg_info("bounce_notify_proto: service=%s queue=%s id=%s sender=%s", service_name, STR(queue_name), STR(queue_id), STR(sender)); /* @@ -186,8 +188,8 @@ static int bounce_flush_proto(char *service_name, VSTREAM *client) /* * Execute the request. */ - return (bounce_flush_service(service_name, STR(queue_name), - STR(queue_id), STR(sender))); + return (bounce_notify_service(service_name, STR(queue_name), + STR(queue_id), STR(sender), flush)); } /* bounce_service - parse bounce command type and delegate */ @@ -210,11 +212,16 @@ static void bounce_service(VSTREAM *client, char *service_name, char **argv) * Read and validate the first parameter of the client request. Let the * request-specific protocol routines take care of the remainder. */ +#define REALLY_BOUNCE 1 +#define JUST_WARN 0 + if (mail_scan(client, "%d", &command) != 1) { msg_warn("malformed request"); status = -1; } else if (command == BOUNCE_CMD_FLUSH) { - status = bounce_flush_proto(service_name, client); + status = bounce_notify_proto(service_name, client, REALLY_BOUNCE); + } else if (command == BOUNCE_CMD_WARN) { + status = bounce_notify_proto(service_name, client, JUST_WARN); } else if (command == BOUNCE_CMD_APPEND) { status = bounce_append_proto(service_name, client); } else { @@ -263,6 +270,8 @@ int main(int argc, char **argv) { static CONFIG_INT_TABLE int_table[] = { VAR_BOUNCE_LIMIT, DEF_BOUNCE_LIMIT, &var_bounce_limit, 1, 0, + VAR_MAX_QUEUE_TIME, DEF_MAX_QUEUE_TIME, &var_max_queue_time, 1, 0, + VAR_DELAY_WARN_TIME, DEF_DELAY_WARN_TIME, &var_delay_warn_time, 0, 0, 0, }; static CONFIG_STR_TABLE str_table[] = { diff --git a/postfix/bounce/bounce_flush_service.c b/postfix/bounce/bounce_notify_service.c similarity index 53% rename from postfix/bounce/bounce_flush_service.c rename to postfix/bounce/bounce_notify_service.c index 32f79d764..ae8889bc8 100644 --- a/postfix/bounce/bounce_flush_service.c +++ b/postfix/bounce/bounce_notify_service.c @@ -1,27 +1,30 @@ /*++ /* NAME -/* bounce_flush_service 3 +/* bounce_notify_service 3 /* SUMMARY /* send non-delivery report to sender, server side /* SYNOPSIS /* #include "bounce_service.h" /* -/* int bounce_flush_service(queue_name, queue_id, sender) +/* int bounce_notify_service(queue_name, queue_id, sender, flush) /* char *queue_name; /* char *queue_id; /* char *sender; +/* int flush; /* DESCRIPTION -/* This module implements the server side of the bounce_flush() -/* (send bounce message) request. +/* This module implements the server side of the bounce_notify() +/* (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 a copy of the diagnostics with message headers is sent to -/* the postmaster. The result is non-zero when the operation +/* 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 single bounce is sent, the sender address is the empty -/* address. When a double bounce is sent, the sender is taken -/* from the configuration parameter \fIdouble_bounce_sender\fR. +/* 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" @@ -88,27 +91,35 @@ /* bounce_header - generate bounce message header */ -static int bounce_header(VSTREAM *bounce, VSTRING *buf, char *dest) +static int bounce_header(VSTREAM *bounce, VSTRING *buf, const char *dest, 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); - post_mail_fprintf(bounce, *dest == 0 ? - "Subject: Postmaster Copy: Undelivered Mail" : - "Subject: Undelivered Mail Returned to Sender"); - quote_822_local(buf, *dest == 0 ? mail_addr_postmaster() : dest); - post_mail_fprintf(bounce, "To: %s", STR(buf)); + + if (flush) { + post_mail_fputs(bounce, STREQ(dest, mail_addr_postmaster()) ? + "Subject: Postmaster Copy: Undelivered Mail" : + "Subject: Undelivered Mail Returned to Sender"); + } else { + post_mail_fputs(bounce, STREQ(dest, mail_addr_postmaster()) ? + "Subject: Postmaster Warning: Delayed Mail" : + "Subject: Delayed Mail (still being retried)"); + } + post_mail_fprintf(bounce, "To: %s", STR(quote_822_local(buf, dest))); post_mail_fputs(bounce, ""); return (vstream_ferror(bounce)); } /* bounce_boilerplate - generate boiler-plate text */ -static int bounce_boilerplate(VSTREAM *bounce, VSTRING *buf) +static int bounce_boilerplate(VSTREAM *bounce, VSTRING *buf, int flush) { /* @@ -121,19 +132,38 @@ static int bounce_boilerplate(VSTREAM *bounce, VSTRING *buf) post_mail_fprintf(bounce, "This is the %s program at host %s.", var_mail_name, var_myhostname); post_mail_fputs(bounce, ""); - post_mail_fprintf(bounce, + if (flush) { + post_mail_fputs(bounce, "I'm sorry to have to inform you that the message returned"); - post_mail_fprintf(bounce, + 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))); - post_mail_fputs(bounce, ""); - post_mail_fprintf(bounce, + 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, + 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); return (vstream_ferror(bounce)); @@ -262,92 +292,140 @@ static int bounce_original(char *service, VSTREAM *bounce, VSTRING *buf, return (status); } -/* bounce_flush_service - send a bounce */ +/* bounce_notify_service - send a bounce */ -int bounce_flush_service(char *service, char *queue_name, - char *queue_id, char *recipient) +int bounce_notify_service(char *service, char *queue_name, + char *queue_id, char *recipient, int flush) { VSTRING *buf = vstring_alloc(100); - const char *double_bounce_addr; - int status = 1; + int bounce_status = 1; + int postmaster_status = 1; VSTREAM *bounce; + int notify_mask = name_mask(mail_error_masks, var_notify_classes); -#define NULL_RECIPIENT MAIL_ADDR_EMPTY /* special address */ #define NULL_SENDER MAIL_ADDR_EMPTY /* special address */ -#define TO_POSTMASTER(addr) (*(addr) == 0) #define NULL_CLEANUP_FLAGS 0 #define BOUNCE_HEADERS 1 #define BOUNCE_ALL 0 /* - * The choice of sender address depends on recipient address. For a - * single bounce (typically 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 local delivery agent gives - * special treatment to the resulting bounce message. + * The choice of sender address depends on recipient the 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_addr = mail_addr_double_bounce(); /* - * Connect to the cleanup service, and request that the cleanup service - * takes no special actions in case of problems. + * 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 ((bounce = post_mail_fopen_nowait(TO_POSTMASTER(recipient) ? - double_bounce_addr : 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) == 0 - && bounce_boilerplate(bounce, buf) == 0 - && bounce_diagnostics(service, bounce, buf, queue_id) == 0) - bounce_original(service, bounce, buf, queue_name, queue_id, BOUNCE_ALL); - - /* - * Finish the bounce, and retrieve the completion status. - */ - status = post_mail_fclose(bounce); + if (strcasecmp(recipient, mail_addr_double_bounce()) == 0) { + bounce_status = 0; } /* - * If not sending to the postmaster or double-bounce pseudo accounts, - * send a postmaster copy as if it is a double bounce, so it will not - * bounce in case of error. This time, block while waiting for resources - * to become available. We know they were available just a split second - * ago. + * Single bounce failed. Optionally send a double bounce to postmaster. */ - if (status == 0 && !TO_POSTMASTER(recipient) - && strcasecmp(recipient, double_bounce_addr) != 0 - && (MAIL_ERROR_BOUNCE & name_mask(mail_error_masks, var_notify_classes))) { +#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 { + if ((bounce = post_mail_fopen_nowait(mail_addr_double_bounce(), + mail_addr_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, mail_addr_postmaster(), flush) + && bounce_diagnostics(service, bounce, buf, queue_id) == 0) + bounce_original(service, bounce, buf, queue_name, queue_id, + flush ? BOUNCE_ALL : BOUNCE_HEADERS); + bounce_status = post_mail_fclose(bounce); + } + } + } - /* - * Send the text with reason for the bounce, and the headers of the - * original message. Don't bother sending the boiler-plate text. - */ - bounce = post_mail_fopen(double_bounce_addr, NULL_RECIPIENT, - NULL_CLEANUP_FLAGS, "BOUNCE"); - if (bounce_header(bounce, buf, NULL_RECIPIENT) == 0 - && bounce_diagnostics(service, bounce, buf, queue_id) == 0) - bounce_original(service, bounce, buf, queue_name, queue_id, BOUNCE_HEADERS); + /* + * 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, flush) == 0 + && bounce_boilerplate(bounce, buf, flush) == 0 + && bounce_diagnostics(service, bounce, buf, queue_id) == 0) + bounce_original(service, bounce, buf, queue_name, queue_id, + flush ? BOUNCE_ALL : BOUNCE_HEADERS); + bounce_status = post_mail_fclose(bounce); + } /* - * Finish the bounce, and update the completion status. + * 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. */ - status |= post_mail_fclose(bounce); +#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. + */ + if ((bounce = post_mail_fopen_nowait(mail_addr_double_bounce(), + mail_addr_postmaster(), + NULL_CLEANUP_FLAGS, + "BOUNCE")) != 0) { + if (!bounce_header(bounce, buf, mail_addr_postmaster(), flush) + && bounce_diagnostics(service, bounce, buf, queue_id) == 0) + bounce_original(service, bounce, buf, queue_name, queue_id, + BOUNCE_HEADERS); + postmaster_status = post_mail_fclose(bounce); + } + if (postmaster_status) + msg_warn("postmaster notice failed while bouncing to %s", + recipient); + } } /* * Examine the completion status. Delete the bounce log file only when - * the bounce was posted successfully. + * the bounce was posted successfully, and only if we are bouncing for + * real, not just warning. */ - if (status == 0 && mail_queue_remove(service, queue_id) && errno != ENOENT) + if (flush != 0 && bounce_status == 0 && mail_queue_remove(service, queue_id) + && errno != ENOENT) msg_fatal("remove %s %s: %m", service, queue_id); /* @@ -355,5 +433,5 @@ int bounce_flush_service(char *service, char *queue_name, */ vstring_free(buf); - return (status); + return (bounce_status); } diff --git a/postfix/bounce/bounce_service.h b/postfix/bounce/bounce_service.h index 8584b9810..5385e550f 100644 --- a/postfix/bounce/bounce_service.h +++ b/postfix/bounce/bounce_service.h @@ -19,9 +19,9 @@ extern int bounce_append_service(char *, char *, char *, char *); /* - * bounce_flush_service.c + * bounce_notify_service.c */ -extern int bounce_flush_service(char *, char *, char *, char *); +extern int bounce_notify_service(char *, char *, char *, char *, int); /* * bounce_cleanup.c diff --git a/postfix/cleanup/.indent.pro b/postfix/cleanup/.indent.pro index e59d396ad..b38eded21 100644 --- a/postfix/cleanup/.indent.pro +++ b/postfix/cleanup/.indent.pro @@ -6,6 +6,7 @@ -TBOUNCE_STAT -TCLEANUP_STATE -TCLIENT_LIST +-TCLNT_STREAM -TCONFIG_BOOL_FN_TABLE -TCONFIG_BOOL_TABLE -TCONFIG_INT_FN_TABLE @@ -25,6 +26,8 @@ -TDICT_NISPLUS -TDICT_NODE -TDICT_OPEN_INFO +-TDICT_PCRE +-TDICT_UNIX -TDNS_FIXED -TDNS_REPLY -TDNS_RR @@ -68,8 +71,14 @@ -TRECIPIENT_LIST -TREC_TYPE_NAME -TRESOLVE_REPLY +-TRESPONSE -TSCAN_DIR +-TSCAN_INFO +-TSCAN_OBJ +-TSESSION -TSINGLE_SERVER +-TSINK_COMMAND +-TSINK_STATE -TSMTPD_STATE -TSMTPD_TOKEN -TSMTP_ADDR diff --git a/postfix/cleanup/cleanup.c b/postfix/cleanup/cleanup.c index 72c64d665..ef6a62d41 100644 --- a/postfix/cleanup/cleanup.c +++ b/postfix/cleanup/cleanup.c @@ -169,6 +169,7 @@ char *var_masq_domains; /* masquerade domains */ char *var_masq_exceptions; /* users not masqueraded */ int var_dup_filter_limit; /* recipient dup filter */ char *var_empty_addr; /* destination of bounced bounces */ +int var_delay_warn_time; /* delay that triggers warning */ /* * Mappings. @@ -411,6 +412,7 @@ int main(int argc, char **argv) VAR_HOPCOUNT_LIMIT, DEF_HOPCOUNT_LIMIT, &var_hopcount_limit, 1, 0, VAR_HEADER_LIMIT, DEF_HEADER_LIMIT, &var_header_limit, 1, 0, VAR_DUP_FILTER_LIMIT, DEF_DUP_FILTER_LIMIT, &var_dup_filter_limit, 0, 0, + VAR_DELAY_WARN_TIME, DEF_DELAY_WARN_TIME, &var_delay_warn_time, 0, 0, 0, }; static CONFIG_STR_TABLE str_table[] = { diff --git a/postfix/cleanup/cleanup_envelope.c b/postfix/cleanup/cleanup_envelope.c index 5824139f5..fbef42f97 100644 --- a/postfix/cleanup/cleanup_envelope.c +++ b/postfix/cleanup/cleanup_envelope.c @@ -57,6 +57,7 @@ void cleanup_envelope(void) { 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 @@ -78,6 +79,12 @@ void cleanup_envelope(void) if (cleanup_sender == 0 || cleanup_time == 0) { msg_warn("missing sender or time envelope record"); 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; } @@ -106,6 +113,11 @@ void cleanup_envelope(void) if (cleanup_sender == 0) cleanup_sender = mystrdup(STR(clean_addr)); } else if (type == REC_TYPE_RCPT) { + if (cleanup_sender == 0) { /* protect showq */ + msg_warn("envelope recipient precedes sender"); + 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) @@ -115,6 +127,11 @@ void cleanup_envelope(void) 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); } diff --git a/postfix/cleanup/cleanup_message.c b/postfix/cleanup/cleanup_message.c index 32ec19327..3e5d1dce1 100644 --- a/postfix/cleanup/cleanup_message.c +++ b/postfix/cleanup/cleanup_message.c @@ -275,19 +275,6 @@ static void cleanup_missing_headers(void) TOK822 *token; char *from; - /* - * Add a missing Return-Path: header when the sender address is not - * empty. XXX Shouldn't this depend on the delivery transport being used? - * But, it is very tempting to use RFC822 as the basis for our canonical - * message representation. And, it is easy to delete an unwanted header. - */ - if (*cleanup_sender != 0 - && (cleanup_headers_seen & (1 << HDR_RETURN_PATH)) == 0) { - quote_822_local(cleanup_temp1, cleanup_sender); - cleanup_out_format(REC_TYPE_NORM, "Return-Path: <%s>", - vstring_str(cleanup_temp1)); - } - /* * Add a missing (Resent-)Message-Id: header. The message ID gives the * time in GMT units, plus the local queue ID. @@ -364,7 +351,7 @@ void cleanup_message(void) */ if ((mesg_offset = vstream_ftell(cleanup_dst)) < 0) msg_fatal("%s: vstream_ftell %s: %m", myname, cleanup_path); - cleanup_out_format(REC_TYPE_MESG, REC_TYPE_MESG_FORMAT, 0); + cleanup_out_format(REC_TYPE_MESG, REC_TYPE_MESG_FORMAT, 0L); if ((data_offset = vstream_ftell(cleanup_dst)) < 0) msg_fatal("%s: vstream_ftell %s: %m", myname, cleanup_path); diff --git a/postfix/cleanup/cleanup_rewrite.c b/postfix/cleanup/cleanup_rewrite.c index 31cc6ef44..1ae6871d8 100644 --- a/postfix/cleanup/cleanup_rewrite.c +++ b/postfix/cleanup/cleanup_rewrite.c @@ -77,23 +77,27 @@ void cleanup_rewrite_external(VSTRING *result, const char *addr) void cleanup_rewrite_tree(TOK822 *tree) { - VSTRING *temp = vstring_alloc(100); + VSTRING *dst = vstring_alloc(100); + VSTRING *src = vstring_alloc(100); - tok822_externalize(temp, tree->head, TOK822_STR_DEFL); - cleanup_rewrite_external(temp, STR(temp)); + tok822_externalize(src, tree->head, TOK822_STR_DEFL); + cleanup_rewrite_external(dst, STR(src)); tok822_free_tree(tree->head); - tree->head = tok822_scan(STR(temp), &tree->tail); - vstring_free(temp); + tree->head = tok822_scan(STR(dst), &tree->tail); + vstring_free(dst); + vstring_free(src); } /* cleanup_rewrite_internal - rewrite address internal form */ void cleanup_rewrite_internal(VSTRING *result, const char *addr) { - VSTRING *temp = vstring_alloc(100); - - quote_822_local(temp, addr); - cleanup_rewrite_external(temp, STR(temp)); - unquote_822_local(result, STR(temp)); - vstring_free(temp); + VSTRING *dst = vstring_alloc(100); + VSTRING *src = vstring_alloc(100); + + quote_822_local(src, addr); + cleanup_rewrite_external(dst, STR(src)); + unquote_822_local(result, STR(dst)); + vstring_free(dst); + vstring_free(src); } diff --git a/postfix/conf/main.cf b/postfix/conf/main.cf index dcdd3cea3..d47113a76 100644 --- a/postfix/conf/main.cf +++ b/postfix/conf/main.cf @@ -212,16 +212,48 @@ program_directory = /some/where/postfix/bin # #home_mailbox = Mailbox #home_mailbox = Maildir/ - -# The mailbox_command specifies the optional external command to use -# instead of mailbox delivery. The command is run with proper HOME, -# SHELL and LOGNAME settings. + +# The mail_spool_directory parameter specifies the directory where +# UNIX-style mailboxes are kept. The default setting depends on the +# system type. +# +# mail_spool_directory = /var/mail +# mail_spool_directory = /var/spool/mail + +# The mailbox_command parameter specifies the optional external +# command to use instead of mailbox delivery. The command is run as +# the recipient with proper HOME, SHELL and LOGNAME settings. +# Exception: delivery for root is done as $default_user. # # Avoid shell meta characters because they will force Postfix to run # an expensive shell process. Procmail alone is expensive enough. # #mailbox_command = /some/where/procmail +# The mailbox_transport specifies the optional transport in master.cf +# to use after processing aliases and .forward files. This parameter +# has precedence over the mailbox_command, fallback_transport and +# luser_relay parameters. +# +#mailbox_transport = cyrus + +# The fallback_transport specifies the optional transport in master.cf +# to use for recipients that are not found in the UNIX passwd database. +# This parameter has precedence over the luser_relay parameter. +# +#fallback_transport = + +# The luser_relay parameter specifies an optional destination +# (@domain, address, "|command", /file/name) for unknown recipients. +# By default, mail for unknown local recipients is bounced. +# +# Specify @domain in order to keep the original recipient name. +# If an address is specified, and if a recipient delimiter is +# specified, the original recipient name is appended to the addres +# localpart. +# +# luser_relay = + # JUNK MAIL CONTROLS # # The controls listed here are only a very small subset. See the file @@ -244,7 +276,7 @@ program_directory = /some/where/postfix/bin # list this system as their primary or backup MX host. See the # permit_mx_backup restriction in the file sample-smtpd.cf. # -#relay_domains = $mydestination, $virtual_domains +#relay_domains = $mydestination, $virtual_maps # The mynetworks parameter specifies the list of networks that are # local to this machine. The list is used by the anti-UCE software diff --git a/postfix/conf/master.cf b/postfix/conf/master.cf index 595cbca3f..9b991f612 100644 --- a/postfix/conf/master.cf +++ b/postfix/conf/master.cf @@ -41,6 +41,12 @@ # option enables symbolic debugging (see the debugger_command variable # in the main.cf configuration file). # +# In order to use the "uucp" message tranport below, set up entries +# in the transport table. +# +# In order to use the "cyrus" message transport below, configure it +# in main.cf as the mailbox_transport. +# # ========================================================================== # service type private unpriv chroot wakeup maxproc command + args # (yes) (yes) (yes) (never) (50) @@ -55,7 +61,7 @@ defer unix - - n - 0 bounce smtp unix - - n - - smtp showq unix n - n - - showq local unix - n n - - local -#local unix - n n - - pipe -# user=cyrus argv=/cyrus/bin/deliver -e -q -m ${extension} ${user} +cyrus unix - n n - - pipe + flags=R user=cyrus argv=/cyrus/bin/deliver -e -q -m ${extension} ${user} uucp unix - n n - - pipe flags=F user=uucp argv=uux -r -n -z -a$sender - $nexthop!rmail ($recipient) diff --git a/postfix/conf/postfix-script-nosgid b/postfix/conf/postfix-script-nosgid index a4545bf7d..6a5ac4fc1 100755 --- a/postfix/conf/postfix-script-nosgid +++ b/postfix/conf/postfix-script-nosgid @@ -141,7 +141,7 @@ reload) exit 1 } $INFO refreshing the Postfix mail system - kill -HUP `cat pid/master.pid` + kill -HUP `sed 1q pid/master.pid` ;; flush) @@ -150,7 +150,7 @@ flush) $FATAL no Postfix queue directory $queue_directory! exit 1 } - $command_directory/postkick public qmgr DFA + $command_directory/postkick public qmgr IDFA ;; check) @@ -204,6 +204,8 @@ check) ! \( -type p -o -type s \) ! -user $mail_owner \ -exec $WARN not owned by $mail_owner: {} \; + $command_directory/postsuper || exit 1 + find corrupt -type f -exec $WARN damaged message: {} \; # XXX also: look for weird stuff, weird permissions, etc. diff --git a/postfix/conf/postfix-script-sgid b/postfix/conf/postfix-script-sgid index 62ba510fd..cfbc171c9 100755 --- a/postfix/conf/postfix-script-sgid +++ b/postfix/conf/postfix-script-sgid @@ -141,7 +141,7 @@ reload) exit 1 } $INFO refreshing the Postfix mail system - kill -HUP `cat pid/master.pid` + kill -HUP `sed 1q pid/master.pid` ;; flush) @@ -150,7 +150,7 @@ flush) $FATAL no Postfix queue directory $queue_directory! exit 1 } - $command_directory/postkick public qmgr DFA + $command_directory/postkick public qmgr IDFA ;; check) @@ -205,6 +205,8 @@ check) ! \( -type p -o -type s \) ! -user $mail_owner \ -exec $WARN not owned by $mail_owner: {} \; + $command_directory/postsuper || exit 1 + find corrupt -type f -exec $WARN damaged message: {} \; # XXX also: look for weird stuff, weird permissions, etc. diff --git a/postfix/conf/sample-local.cf b/postfix/conf/sample-local.cf index 66f978029..f0ccfcc42 100644 --- a/postfix/conf/sample-local.cf +++ b/postfix/conf/sample-local.cf @@ -58,9 +58,28 @@ default_privs = nobody # home_mailbox = Maildir/ home_mailbox = -# The mailbox_command specifies the optional external command to use -# instead of mailbox delivery. The command is run with proper HOME, -# SHELL and LOGNAME settings. +# The luser_relay parameter specifies an optional destination +# (@domain, address, "|command", /file/name) for unknown recipients. +# By default, mail for unknown local recipients is bounced. +# +# Specify @domain in order to keep the original recipient name. +# If an address is specified, and if a recipient delimiter is +# specified, the original recipient name is appended to the addres +# localpart. +# +# luser_relay = + +# The mail_spool_directory parameter specifies the directory where +# UNIX-style mailboxes are kept. The default setting depends on the +# system type. +# +# mail_spool_directory = /var/mail +# mail_spool_directory = /var/spool/mail + +# The mailbox_command parameter specifies the optional external +# command to use instead of mailbox delivery. The command is run +# as the recipient with proper HOME, SHELL and LOGNAME settings. +# Exception: delivery for root is done as $default_user. # # Avoid shell meta characters because they will force Postfix to run # an expensive shell process. Procmail alone is expensive enough. @@ -68,6 +87,19 @@ home_mailbox = # mailbox_command = /some/where/procmail mailbox_command = +# The mailbox_transport specifies the optional transport in master.cf +# to use after processing aliases and .forward files. This parameter +# has precedence over the mailbox_command, fallback_transport and +# luser_relay parameters. +# +mailbox_transport = + +# The fallback_transport specifies the optional transport in master.cf +# to use for recipients that are not found in the UNIX passwd database. +# This parameter has precedence over the luser_relay parameter. +# +fallback_transport = + # # RATE CONTROLS # diff --git a/postfix/conf/sample-misc.cf b/postfix/conf/sample-misc.cf index c211f0133..d8b25fdc8 100644 --- a/postfix/conf/sample-misc.cf +++ b/postfix/conf/sample-misc.cf @@ -1,6 +1,12 @@ # This file contains example settings for miscellaneous Postfix # configuration parameters. +# The always_bcc parameter specifies an optional address that +# receives a copy of each message that enters the Postfix system, +# not including bounces that are generated locally. +# +always_bcc = + # The default_database_type parameter specifies the default database # type to use in postalias(1) and postmap(1) commands. On many UNIX # systems the default type is either `dbm' or `hash'. The default is @@ -27,16 +33,26 @@ double_bounce_sender = double-bounce # levels below the queue directories listed in the hash_queue_names # parameter. # -# Multiple subdirectory levels can speed up directory searches by +# Multiple subdirectory levels can speed up directory access by # reducing the number of files per directory. # +# After changing the hash_queue_names or hash_queue_depth parameter, +# run "postfix reload" and "postfix check". +# hash_queue_depth = 2 # The hash_queue_names parameter specifies the names of queue # directories that are split across multiple subdirectory levels. -# Currently, hashing cannot be used for the maildrop, incoming, active -# or deferred directories. Hashing MUST be used for the defer logfile -# directory, or mail system performance will suffer. +# Hashing MUST NOT be used with a world-writable maildrop directory. +# Hashing MUST be used for the defer logfile directory, or mail system +# performance will suffer. +# +# Unfortunately, hashing the incoming or deferred queue can actually +# slow the mail system down (mailq with an empty queue can take +# several seconds) so it should be done only in case of emergency. +# +# After changing the hash_queue_names or hash_queue_depth parameter, +# run "postfix reload" and "postfix check". # hash_queue_names = defer @@ -165,7 +181,8 @@ myorigin = $myhostname # policy (anti-UCE violations) and protocol error (broken mailers) # reports. # -# notify_classes = bounce,policy,protocol,resource,software +# notify_classes = bounce,delay,policy,protocol,resource,software +# notify_classes = 2bounce,resource,software notify_classes = resource,software # The process_id_directory specifies a lock file directory relative @@ -230,3 +247,9 @@ relocated_maps = # mail system is under heavy load. # trigger_timeout = 10 + +# The delay_warning_time specifies after how many hours a warning +# is sent that mail has not yet been delivered. By default, no warning +# is sent. +# +# delay_warning_time = 0 diff --git a/postfix/conf/sample-pcre.cf b/postfix/conf/sample-pcre.cf new file mode 100644 index 000000000..2bfa1a122 --- /dev/null +++ b/postfix/conf/sample-pcre.cf @@ -0,0 +1,52 @@ +# +# Sample pcre (PERL-compatible regular expression) map file +# +# The first field is a perl-like regular expression. The expression +# delimiter can be any character except whitespace, or characters +# that have special meaning to the regexp library (traditionally +# the forward slash is used). The regular expression can contain +# whitespace. +# +# By default, matching is case-INsensative, although following +# the second slash with an 'i' will reverse this. Other flags are +# supported, but the only other useful one is 'U', which makes +# matching ungreedy (see PCRE documentation and source for more +# info). +# +# The second field is the "replacement" string - the text +# returned by the match. When used for smtpd checks, this would +# be a helpful message to misguided users (or an offensive +# message to spammers), although it could also be a domain name +# or other data for use as a transport, virtual, or other map. +# +# Substitution of sub-strings from the matched expression is +# possible using the conventional perl syntax. The macros in the +# replacement string may need to be protected with curly braces +# if they aren't followed by whitespace (see the examples +# below). +# +# Lines starting with whitespace are continuation lines - they are +# appended to the previous line (there should be no whitespace +# before your regular expression!) +# +# This code was originally developed for SPAM control. However +# it seems that it can be used equally well for address rewriting +# by virtual or canonical lookups. Using this for aliases might +# be stretching things, though. +# + +# Protect your outgoing majordomo exploders +# +/^(?!owner-)(.*)-outgoing@(connect.com.au)$/ 550 Use ${1}@${2} instead + + +# Bounce friend@whatever, except when whatever is our domain (you would +# be better just bouncing all friend@ mail - this is just an example). +# +/^friend@(?!connect.com.au).*$/ 550 Stick this in your pipe $0 + +# A multi-line response +# +/^noddy@connect.com.au$/ + 550 This user is a funny one. You really don't want to send mail to them + as it only makes their head spin. diff --git a/postfix/conf/sample-smtp.cf b/postfix/conf/sample-smtp.cf index e1868b4b4..64ac7d751 100644 --- a/postfix/conf/sample-smtp.cf +++ b/postfix/conf/sample-smtp.cf @@ -1,6 +1,39 @@ # This file contains example settings of Postfix configuration # parameters that control the SMTP client program. +# +# MISCELLANEOUS CONTROLS +# + +# The fallback_relay parameter specifies zero or more hosts or domains +# to hand off mail to if a message destination is not found, or if a +# destination is unreachable. +# +# By default, mail is bounced when a destination is not found, and +# delivery is deferred if a destination is unreachable. +# +fallback_relay = + +# The ignore_mx_lookup_error parameter controls what happens when a +# name server fails to respond to an MX lookup request. By default, +# Postfix defers delivery and tries again after some delay. Specify +# "ignore_mx_lookup_error = yes" to force an A record lookup instead. +# +ignore_mx_lookup_error = no + +# The smtp_skip_4xx_greeting parameter controls what happens when +# an SMTP server greets us with a 4XX status code. By default, Postfix +# backs off. Specify "smtp_skip_4xx_greeting = yes" to move on the +# the next mail exchanger. +# +smtp_skip_4xx_greeting = no + +# The smtp_skip_quit_response parameter controls whether the SMTP +# client waits for the response to the QUIT command. The default is +# to not wait. +# +smtp_skip_quit_response = yes + # # RATE CONTROLS # diff --git a/postfix/conf/sample-smtpd.cf b/postfix/conf/sample-smtpd.cf index 51c4e87f3..6bea9dc24 100644 --- a/postfix/conf/sample-smtpd.cf +++ b/postfix/conf/sample-smtpd.cf @@ -99,6 +99,7 @@ smtpd_helo_required = no # permit_mynetworks: permit if the client address matches $mynetworks. # reject_invalid_hostname: reject HELO hostname with bad syntax. # reject_unknown_hostname: reject HELO hostname without DNS A record. +# reject_non_fqdn_hostname: reject HELO hostname that is not in FQDN form # maptype:mapname: look up HELO hostname or parent domains. # Reject if result is REJECT or "[45]xx text" # Permit otherwise. @@ -130,6 +131,8 @@ smtpd_helo_restrictions = # Permit otherwise. # check_client_access maptype:mapname: see smtpd_client_restrictions. # check_helo_access maptype:mapname: see smtpd_helo_restrictions. +# reject_non_fqdn_hostname: reject HELO hostname that is not in FQDN form +# reject_non_fqdn_sender: reject sender address that is not in FQDN form # reject: reject the request. Place this at the end of a restriction. # permit: permit the request. Place this at the end of a restriction. # @@ -162,6 +165,9 @@ smtpd_sender_restrictions = # check_client_access maptype:mapname: see smtpd_client_restrictions. # check_helo_access maptype:mapname: see smtpd_helo_restrictions. # check_sender_access maptype:mapname: see smtpd_sender_restrictions. +# reject_non_fqdn_hostname: reject HELO hostname that is not in FQDN form +# reject_non_fqdn_sender: reject sender address that is not in FQDN form +# reject_non_fqdn_recipient: reject recipient address that is not in FQDN form # reject: reject the request. Place this at the end of a restriction. # permit: permit the request. Place this at the end of a restriction. # @@ -206,7 +212,7 @@ maps_rbl_domains = rbl.maps.vix.com # permit_mx_backup restriction, in the description of the # smtpd_recipient_restrictions parameter. # -relay_domains = $mydestination, $virtual_domains +relay_domains = $mydestination, $virtual_maps # # RESPONSE CODES diff --git a/postfix/contrib/regexp/pcre b/postfix/contrib/regexp/pcre new file mode 100644 index 000000000..21bcdbbd6 --- /dev/null +++ b/postfix/contrib/regexp/pcre @@ -0,0 +1,565 @@ +From owner-postfix-users@postfix.org Mon Mar 1 19:04:41 1999 +Delivered-To: wietse@porcupine.org +Received: from russian-caravan.cloud9.net (russian-caravan.cloud9.net [168.100.1.4]) + by spike.porcupine.org (Postfix) with ESMTP id 5785A45A72 + for ; Mon, 1 Mar 1999 19:04:36 -0500 (EST) +Received: by russian-caravan.cloud9.net (Postfix) + id 5F5D37638F; Mon, 1 Mar 1999 19:04:08 -0500 (EST) +Delivered-To: postfix-users-outgoing@cloud9.net +Received: by russian-caravan.cloud9.net (Postfix, from userid 54) + id 322AF76398; Mon, 1 Mar 1999 19:04:08 -0500 (EST) +Delivered-To: postfix-users@cloud9.net +Received: from melang.off.connect.com.au (melang.off.connect.com.au [202.21.9.1]) + by russian-caravan.cloud9.net (Postfix) with ESMTP id 6E87C7638F + for ; Mon, 1 Mar 1999 19:04:04 -0500 (EST) +Received: from connect.com.au (localhost [127.0.0.1]) + by melang.off.connect.com.au (Postfix) with ESMTP + id 074C7ED7D; Tue, 2 Mar 1999 11:04:02 +1100 (EST) +To: wietse@porcupine.org (Wietse Venema) +Cc: postfix-users@postfix.org (Postfix users) +Subject: regexp map patch +In-reply-to: Your message of "Thu, 25 Feb 1999 19:51:25 CDT." + <19990226005125.69B3C4596E@spike.porcupine.org> +Date: Tue, 02 Mar 1999 11:04:02 +1100 +From: Andrew McNamara +Message-Id: <19990302000403.074C7ED7D@melang.off.connect.com.au> +Sender: owner-postfix-users@postfix.org +Precedence: bulk +Return-Path: +Status: RO + +I've written a patch to add a regexp map type. It utilises the PCRE +library (Perl Compatible Regular Expressions), which can be obtained +from: + + ftp://ftp.cus.cam.ac.uk/pub/software/programs/pcre/ + +You will need to add -DHAS_PCRE and a -I for the PCRE header to CCARGS, +and add the path to the PCRE library to AUXLIBS, for example: + + make -f Makefile.init makefiles 'CCARGS=-DHAS_PCRE -I../../pcre-2.04' \ + 'AUXLIBS=../../pcre-2.04/libpcre.a' + +One possible use is to add a line to main.cf: + + smtpd_recipient_restrictions = regexp:/opt/postfix/etc/smtprecipient + +The regular expressions are read from the file specified and compiled - +a sample regexp file for this usage is included in the patch. + +Any feedback is appreciated (from Wietse in particular :-). Have fun. + +diff -u --recursive orig/postfix-beta-19990122-pl01/conf/sample-regexp postfix-beta-19990122-pl01/conf/sample-regexp +--- orig/postfix-beta-19990122-pl01/conf/sample-regexp Tue Mar 2 10:42:43 1999 ++++ postfix-beta-19990122-pl01/conf/sample-regexp Tue Mar 2 10:51:49 1999 +@@ -0,0 +1,51 @@ ++# ++# Sample regexp map source file ++# ++# The first field is a perl-like regular express. The expression ++# delimiter can be any character except whitespace, or characters ++# that have special meaning to the regexp library (traditionally ++# the forward slash is used). The expression can contain ++# whitespace. ++# ++# By default, matching is case-INsensative, although following ++# the second slash with an 'i' will reverse this. Other flags are ++# supported, but the only other useful one is 'U', which makes ++# matching ungreedy (see PCRE documentation and source for more ++# info). ++# ++# The second field is the "replacement" string - the text ++# returned by the match. When used for smtpd checks, this would ++# be a helpful message to misguided users (or an offensive ++# message to spammers), although it could also be a domain name ++# or other data for use as a transport, virtual, or other map. ++# ++# Substitution of sub-strings from the matched expression is ++# possible using the conventional perl syntax. The macros in the ++# replacement string may need to be protected with curly braces ++# if they aren't followed by whitespace (see the examples ++# below). ++# ++# If no second field is given, the text "REJECT" is returned - ++# this string is magic to the check functions in smtpd, and ++# results in an "administratively denied relay" message. ++# ++# Lines starting with whitespace are continuation lines - they are ++# appended to the previous line (there should be no whitespace ++# before your regular expression!) ++ ++ ++# Protect your outgoing majordomo exploders ++# ++/^(.*)-outgoing@(connect.com.au)$/ 550 Use ${1}@${2} instead ++ ++ ++# Bounce friend@whatever, except when whatever is our domain (you would ++# be better just bouncing all friend@ mail - this is just an example). ++# ++/^friend@(?!connect.com.au).*$/ 550 Stick this in your pipe $0 ++ ++# A multi-line response ++# ++/^noddy@connect.com.au$/ ++ 550 This user is a funny one. You really don't want to send mail to them ++ as it only makes their head spin. +diff -u --recursive orig/postfix-beta-19990122-pl01/util/Makefile.in postfix-beta-19990122-pl01/util/Makefile.in +--- orig/postfix-beta-19990122-pl01/util/Makefile.in Sun Jan 31 15:16:15 1999 ++++ postfix-beta-19990122-pl01/util/Makefile.in Fri Feb 26 15:57:24 1999 +@@ -18,7 +18,7 @@ + timed_wait.c translit.c trimblanks.c unix_connect.c unix_listen.c \ + unix_trigger.c unsafe.c username.c valid_hostname.c vbuf.c \ + vbuf_print.c vstream.c vstream_popen.c vstring.c vstring_vstream.c \ +- writable.c write_buf.c write_wait.c doze.c ++ writable.c write_buf.c write_wait.c doze.c dict_pcre.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_ni.o dict_nis.o \ +@@ -38,7 +38,7 @@ + timed_wait.o translit.o trimblanks.o unix_connect.o unix_listen.o \ + unix_trigger.o unsafe.o username.o valid_hostname.o vbuf.o \ + vbuf_print.o vstream.o vstream_popen.o vstring.o vstring_vstream.o \ +- writable.o write_buf.o write_wait.o doze.o ++ writable.o write_buf.o write_wait.o doze.o dict_pcre.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_ni.h dict_nis.h \ + dict_nisplus.h dir_forest.h events.h exec_command.h find_inet.h \ +@@ -51,7 +51,7 @@ + ring.h safe.h safe_open.h sane_accept.h scan_dir.h set_eugid.h \ + set_ugid.h sigdelay.h split_at.h stat_as.h stringops.h sys_defs.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 ++ vbuf.h vbuf_print.h vstream.h vstring.h vstring_vstream.h dict_pcre.h + TESTSRC = fifo_open.c fifo_rdwr_bug.c fifo_rdonly_bug.c + WARN = -W -Wformat -Wimplicit -Wmissing-prototypes \ + -Wparentheses -Wstrict-prototypes -Wswitch -Wuninitialized \ +diff -u --recursive orig/postfix-beta-19990122-pl01/util/dict_open.c postfix-beta-19990122-pl01/util/dict_open.c +--- orig/postfix-beta-19990122-pl01/util/dict_open.c Sat Dec 12 05:55:34 1998 ++++ postfix-beta-19990122-pl01/util/dict_open.c Fri Feb 26 15:07:51 1999 +@@ -100,6 +100,7 @@ + #include + #include + #include ++#include + #include + #include + +@@ -131,6 +132,9 @@ + #endif + #ifdef HAS_LDAP + "ldap", dict_ldap_open, ++#endif ++#ifdef HAS_PCRE ++ "regexp", dict_pcre_open, + #endif + 0, + }; +diff -u --recursive orig/postfix-beta-19990122-pl01/util/dict_pcre.c postfix-beta-19990122-pl01/util/dict_pcre.c +--- orig/postfix-beta-19990122-pl01/util/dict_pcre.c Tue Mar 2 10:42:30 1999 ++++ postfix-beta-19990122-pl01/util/dict_pcre.c Tue Mar 2 10:39:37 1999 +@@ -0,0 +1,349 @@ ++/*++ ++/* NAME ++/* dict_pcre 3 ++/* SUMMARY ++/* dictionary manager interface to PCRE regular expression library ++/* SYNOPSIS ++/* #include ++/* ++/* DICT *dict_pcre_open(name, flags) ++/* const char *name; ++/* int flags; ++/* DESCRIPTION ++/* dict_pcre_open() opens the named file and compiles the contained ++/* regular expressions. ++/* SEE ALSO ++/* dict(3) generic dictionary manager ++/* 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 ++/* ++/* Andrew McNamara ++/* andrewm@connect.com.au ++/* connect.com.au Pty. Ltd. ++/* Level 3, 213 Miller St ++/* North Sydney, NSW, Australia ++/*--*/ ++ ++#include "sys_defs.h" ++ ++#ifdef HAS_PCRE ++ ++/* System library. */ ++ ++#include /* sprintf() prototype */ ++#include ++#include ++#include ++#include ++ ++/* Utility library. */ ++ ++#include "mymalloc.h" ++#include "msg.h" ++#include "safe.h" ++#include "vstream.h" ++#include "vstring.h" ++#include "stringops.h" ++#include "readline.h" ++#include "dict.h" ++#include "dict_pcre.h" ++#include "mac_parse.h" ++ ++/* PCRE library */ ++ ++#include "pcre.h" ++ ++#define PCRE_MAX_CAPTURE 99 /* Max strings captured by regexp - */ ++ /* essentially the max number of (..) */ ++ ++struct dict_pcre_list { ++ pcre *pattern; /* The compiled pattern */ ++ pcre_extra *hints; /* Hints to speed pattern execution */ ++ char *replace; /* Replacement string */ ++ int lineno; /* Source file line number */ ++ struct dict_pcre_list *next; /* Next regexp in dict */ ++}; ++ ++typedef struct { ++ DICT dict; /* generic members */ ++ char *map; /* map name */ ++ int flags; /* unused at the moment */ ++ struct dict_pcre_list *head; ++} DICT_PCRE; ++ ++static dict_pcre_init = 0; /* flag need to init pcre library */ ++ ++/* ++ * dict_pcre_update - not supported ++ */ ++static void dict_pcre_update(DICT *dict, const char *unused_name, ++ const char *unused_value) ++{ ++ DICT_PCRE *dict_pcre = (DICT_PCRE *) dict; ++ ++ msg_fatal("dict_pcre_update: attempt to update regexp map %s", ++ dict_pcre->map); ++} ++ ++/* ++ * Context for macro expansion callback. ++ */ ++struct dict_pcre_context { ++ const char *dict_name; /* source dict name */ ++ int lineno; /* source file line number */ ++ VSTRING *buf; /* target string buffer */ ++ const char *subject; /* str against which we match */ ++ int offsets[ PCRE_MAX_CAPTURE * 3 ];/* Cut substrings */ ++ int matches; /* Count of cuts */ ++}; ++ ++/* ++ * Macro expansion callback - replace $0-${99} with strings cut from ++ * matched string. ++ */ ++static void dict_pcre_action( int type, VSTRING *buf, char *ptr ) ++{ ++ struct dict_pcre_context *ctxt = (struct dict_pcre_context *) ptr; ++ const char *pp; ++ int n, ret; ++ ++ if( type == MAC_PARSE_VARNAME ) { ++ n = atoi( vstring_str( buf )); ++ ret = pcre_get_substring( ctxt->subject, ctxt->offsets, ctxt->matches, ++ n, &pp ); ++ if( ret < 0 ) { ++ if( ret == PCRE_ERROR_NOSUBSTRING ) ++ msg_warn( "regexp %s, line %d: replace index out of range", ++ ctxt->dict_name, ctxt->lineno ); ++ else ++ msg_warn( "regexp %s, line %d: pcre_get_substring error: %d", ++ ctxt->dict_name, ctxt->lineno, ret ); ++ return; ++ } ++ vstring_strcat( ctxt->buf, pp ); ++ } else ++ /* Straight text - duplicate with no substitution */ ++ vstring_strcat( ctxt->buf, vstring_str(buf)); ++} ++ ++/* ++ * Look up regexp dict and perform string substitution on matched ++ * strings. ++ */ ++static const char *dict_pcre_lookup(DICT *dict, const char *name) ++{ ++ DICT_PCRE *dict_pcre = (DICT_PCRE *) dict; ++ struct dict_pcre_list *pcre_list; ++ int name_len = strlen( name ); ++ struct dict_pcre_context ctxt; ++ static VSTRING *buf; ++ ++/* msg_info("dict_pcre_lookup: %s: %s", dict_pcre->map, name );*/ ++ ++ /* Search for a matching expression */ ++ for( pcre_list = dict_pcre->head; pcre_list; pcre_list = pcre_list->next ) { ++ if( pcre_list->pattern ) { ++ ctxt.matches = pcre_exec( pcre_list->pattern, pcre_list->hints, ++ name, name_len, 0, ctxt.offsets, PCRE_MAX_CAPTURE * 3 ); ++ if( ctxt.matches != PCRE_ERROR_NOMATCH ) { ++ if( ctxt.matches > 0 ) ++ break; /* Got a match! */ ++ else { ++ /* An error */ ++ switch( ctxt.matches ) { ++ case 0: ++ msg_warn( "regexp map %s, line %d: too many (...)", ++ dict_pcre->map, pcre_list->lineno ); ++ break; ++ case PCRE_ERROR_NULL: ++ case PCRE_ERROR_BADOPTION: ++ msg_fatal( "regexp map %s, line %d: bad args to re_exec", ++ dict_pcre->map, pcre_list->lineno ); ++ break; ++ case PCRE_ERROR_BADMAGIC: ++ case PCRE_ERROR_UNKNOWN_NODE: ++ msg_fatal( "regexp map %s, line %d: corrupt compiled regexp", ++ dict_pcre->map, pcre_list->lineno ); ++ break; ++ default: ++ msg_fatal( "regexp map %s, line %d: unknown re_exec error: %d", ++ dict_pcre->map, pcre_list->lineno, ctxt.matches ); ++ break; ++ } ++ return( (char *)0 ); ++ } ++ } ++ } ++ } ++ ++ /* If we've got a match, */ ++ if ( ctxt.matches > 0 ) { ++ /* And we've got a replacement string, */ ++ if ( pcre_list->replace ) { ++ /* Then perform substitution on replacement string */ ++ if( buf == 0 ) ++ buf = vstring_alloc(10); ++ VSTRING_RESET(buf); ++ ctxt.buf = buf; ++ ctxt.subject = name; ++ ctxt.dict_name = dict_pcre->map; ++ ctxt.lineno = pcre_list->lineno; ++ ++ mac_parse( pcre_list->replace, dict_pcre_action, (char *)&ctxt ); ++ ++ VSTRING_TERMINATE(buf); ++ return( vstring_str( buf )); ++ } else ++ /* No replacement string, so just return dummy */ ++ return( "REJECT" ); ++ } ++ ++ return ( (char *)0 ); ++} ++ ++/* dict_pcre_close - close pcre dictionary */ ++ ++static void dict_pcre_close(DICT *dict) ++{ ++ DICT_PCRE *dict_pcre = (DICT_PCRE *) dict; ++ struct dict_pcre_list *pcre_list; ++ ++ for( pcre_list = dict_pcre->head; pcre_list; pcre_list = pcre_list->next ) { ++ if( pcre_list->pattern ) ++ myfree((char *) pcre_list->pattern); ++ if( pcre_list->hints ) ++ myfree((char *) pcre_list->hints); ++ if( pcre_list->replace ) ++ myfree((char *) pcre_list->replace); ++ } ++ myfree((char *) dict_pcre); ++} ++ ++/* ++ * dict_pcre_open - load and compile a file containing regular expressions ++ */ ++DICT *dict_pcre_open(const char *map, int unused_flags) ++{ ++ DICT_PCRE *dict_pcre; ++ VSTREAM *map_fp; ++ VSTRING *line_buffer; ++ struct dict_pcre_list *pcre_list = NULL, *pl; ++ int lineno = 0; ++ char *regexp, *p, re_delimiter; ++ int re_options; ++ pcre *pattern; ++ pcre_extra *hints; ++ const char *error; ++ int errptr; ++ ++ line_buffer = vstring_alloc(100); ++ ++ dict_pcre = (DICT_PCRE *) mymalloc(sizeof(*dict_pcre)); ++ dict_pcre->dict.lookup = dict_pcre_lookup; ++ dict_pcre->dict.update = dict_pcre_update; ++ dict_pcre->dict.close = dict_pcre_close; ++ dict_pcre->dict.fd = -1; ++ dict_pcre->map = mystrdup(map); ++ dict_pcre->flags = 0; ++ dict_pcre->head = NULL; ++ ++ if (dict_pcre_init == 0) { ++ pcre_malloc = (void *)mymalloc; ++ pcre_free = (void *)myfree; ++ dict_pcre_init = 1; ++ } ++ ++ if(( map_fp = vstream_fopen( map, O_RDONLY, 0 )) == 0 ) { ++ msg_fatal("open %s: %m", map ); ++ } ++ while (readline(line_buffer, map_fp, &lineno)) { ++ ++ if (*vstring_str(line_buffer) == '#') /* Skip comments */ ++ continue; ++ ++ p = vstring_str(line_buffer); ++ re_delimiter = *p++; ++ regexp = p; ++ ++ /* Search for second delimiter, handling backslash escape */ ++ while( *p ) { ++ if( *p == re_delimiter && ++ ( p > vstring_str(line_buffer) && *(p - 1) != '\\' )) ++ break; ++ ++p; ++ } ++ ++ if (!*p) { ++ msg_warn("%s, line %d: no closing regexp delimiter: %c", ++ VSTREAM_PATH(map_fp), lineno, re_delimiter ); ++ continue; ++ } ++ *p++ = '\0'; /* Null term the regexp */ ++ ++ /* Now parse any regexp options */ ++ re_options = PCRE_CASELESS; ++ while( *p && !ISSPACE( *p )) { ++ switch( *p ) { ++ case 'i': re_options ^= PCRE_CASELESS; break; ++ case 'm': re_options ^= PCRE_MULTILINE; break; ++ case 's': re_options ^= PCRE_DOTALL; break; ++ case 'x': re_options ^= PCRE_EXTENDED; break; ++ case 'A': re_options ^= PCRE_ANCHORED; break; ++ case 'E': re_options ^= PCRE_DOLLAR_ENDONLY; break; ++ case 'U': re_options ^= PCRE_UNGREEDY; break; ++ case 'X': re_options ^= PCRE_EXTRA; break; ++ default: ++ msg_warn("%s, line %d: unknown regexp option '%c'", ++ VSTREAM_PATH(map_fp), lineno, *p ); ++ } ++ ++p; ++ } ++ ++ while( *p && ISSPACE( *p )) ++ ++p; ++ ++ /* Compile the patern */ ++ pattern = pcre_compile( regexp, re_options, &error, &errptr, NULL ); ++ if( pattern == NULL ) { ++ msg_warn("%s, line %d: error in regex at offset %d: %s", ++ VSTREAM_PATH(map_fp), lineno, errptr, error ); ++ continue; ++ } ++ hints = pcre_study( pattern, 0, &error ); ++ if( error != NULL ) { ++ msg_warn("%s, line %d: error while studying regex: %s", ++ VSTREAM_PATH(map_fp), lineno, error ); ++ myfree( (char *)pattern ); ++ continue; ++ } ++ ++ /* Add it to the list */ ++ pl = (struct dict_pcre_list *)mymalloc( sizeof( struct dict_pcre_list )); ++ ++ /* Save the replacement string (if any) */ ++ pl->replace = ( *p ? mystrdup( p ) : NULL ); ++ pl->pattern = pattern; ++ pl->hints = hints; ++ pl->next = NULL; ++ pl->lineno = lineno; ++ ++ if( pcre_list == NULL ) ++ dict_pcre->head = pl; ++ else ++ pcre_list->next = pl; ++ pcre_list = pl; ++ } ++ ++ vstring_free(line_buffer); ++ vstream_fclose(map_fp); ++ ++ return (&dict_pcre->dict); ++} ++#endif /*HAS_PCRE*/ +diff -u --recursive orig/postfix-beta-19990122-pl01/util/dict_pcre.h postfix-beta-19990122-pl01/util/dict_pcre.h +--- orig/postfix-beta-19990122-pl01/util/dict_pcre.h Tue Mar 2 10:42:32 1999 ++++ postfix-beta-19990122-pl01/util/dict_pcre.h Mon Mar 1 18:17:23 1999 +@@ -0,0 +1,41 @@ ++#ifndef _DICT_PCRE_H_INCLUDED_ ++#define _DICT_PCRE_H_INCLUDED_ ++ ++/*++ ++/* NAME ++/* dict_pcre 3h ++/* SUMMARY ++/* dictionary manager interface to PCRE regular expression library ++/* SYNOPSIS ++/* #include ++/* DESCRIPTION ++/* .nf ++ ++ /* ++ * Utility library. ++ */ ++#include ++ ++ /* ++ * External interface. ++ */ ++extern DICT *dict_pcre_open(const char *, int); ++ ++/* 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 ++/* ++/* Andrew McNamara ++/* andrewm@connect.com.au ++/* connect.com.au Pty. Ltd. ++/* Level 3, 213 Miller St ++/* North Sydney, NSW, Australia ++/*--*/ ++ ++#endif + + --- +Andrew McNamara (Senior System Administrator) + +connect.com.au Pty Ltd +Lvl 3, 213 Miller St, North Sydney, NSW 2060, Australia +Phone: +61 2 9959 5959, Fax: +61 2 9966 1960 + + + diff --git a/postfix/dns/.indent.pro b/postfix/dns/.indent.pro index e59d396ad..b38eded21 100644 --- a/postfix/dns/.indent.pro +++ b/postfix/dns/.indent.pro @@ -6,6 +6,7 @@ -TBOUNCE_STAT -TCLEANUP_STATE -TCLIENT_LIST +-TCLNT_STREAM -TCONFIG_BOOL_FN_TABLE -TCONFIG_BOOL_TABLE -TCONFIG_INT_FN_TABLE @@ -25,6 +26,8 @@ -TDICT_NISPLUS -TDICT_NODE -TDICT_OPEN_INFO +-TDICT_PCRE +-TDICT_UNIX -TDNS_FIXED -TDNS_REPLY -TDNS_RR @@ -68,8 +71,14 @@ -TRECIPIENT_LIST -TREC_TYPE_NAME -TRESOLVE_REPLY +-TRESPONSE -TSCAN_DIR +-TSCAN_INFO +-TSCAN_OBJ +-TSESSION -TSINGLE_SERVER +-TSINK_COMMAND +-TSINK_STATE -TSMTPD_STATE -TSMTPD_TOKEN -TSMTP_ADDR diff --git a/postfix/dns/dns.h b/postfix/dns/dns.h index f2cf03366..0d3496c84 100644 --- a/postfix/dns/dns.h +++ b/postfix/dns/dns.h @@ -58,7 +58,7 @@ */ typedef struct DNS_FIXED { unsigned short type; /* T_A, T_CNAME, etc. */ - unsigned short class; /* T_A, T_CNAME, etc. */ + unsigned short class; /* C_IN, etc. */ unsigned int ttl; /* always */ unsigned length; /* record length */ } DNS_FIXED; @@ -70,7 +70,7 @@ typedef struct DNS_FIXED { typedef struct DNS_RR { char *name; /* name, mystrdup()ed */ unsigned short type; /* T_A, T_CNAME, etc. */ - unsigned short class; /* T_A, T_CNAME, etc. */ + unsigned short class; /* C_IN, etc. */ unsigned int ttl; /* always */ unsigned short pref; /* T_MX only */ struct DNS_RR *next; /* linkage */ @@ -95,6 +95,7 @@ extern unsigned dns_type(const char *); extern DNS_RR *dns_rr_create(const char *, DNS_FIXED *, unsigned, const char *, unsigned); extern void dns_rr_free(DNS_RR *); +extern DNS_RR *dns_rr_copy(DNS_RR *); extern DNS_RR *dns_rr_append(DNS_RR *, DNS_RR *); extern DNS_RR *dns_rr_sort(DNS_RR *, int (*) (DNS_RR *, DNS_RR *)); diff --git a/postfix/dns/dns_rr.c b/postfix/dns/dns_rr.c index e6bb5f7a5..39cbf1920 100644 --- a/postfix/dns/dns_rr.c +++ b/postfix/dns/dns_rr.c @@ -16,6 +16,9 @@ /* void dns_rr_free(list) /* DNS_RR *list; /* +/* DNS_RR *dns_rr_copy(record) +/* DNS_RR *record; +/* /* DNS_RR *dns_rr_append(list, record) /* DNS_RR *list; /* DNS_RR *record; @@ -38,6 +41,8 @@ /* dns_rr_free() releases the resource used by of zero or more /* resource records. /* +/* dns_rr_copy() makes a copy of a resource record. +/* /* dns_rr_append() appends a resource record to a (list of) resource /* record(s). /* @@ -102,6 +107,23 @@ void dns_rr_free(DNS_RR *rr) } } +/* dns_rr_copy - copy resource record */ + +DNS_RR *dns_rr_copy(DNS_RR *src) +{ + int len = sizeof(*src) + src->data_len - 1; + DNS_RR *dst; + + /* + * Combine struct assignment and data copy in one block copy operation. + */ + dst = (DNS_RR *) mymalloc(len); + memcpy((char *) dst, (char *) src, len); + dst->name = mystrdup(src->name); + dst->next = 0; + return (dst); +} + /* dns_rr_append - append resource record to list */ DNS_RR *dns_rr_append(DNS_RR *list, DNS_RR *rr) diff --git a/postfix/dns/snd.h b/postfix/dns/snd.h new file mode 100644 index 000000000..e69de29bb diff --git a/postfix/fsstone/.indent.pro b/postfix/fsstone/.indent.pro index e59d396ad..b38eded21 100644 --- a/postfix/fsstone/.indent.pro +++ b/postfix/fsstone/.indent.pro @@ -6,6 +6,7 @@ -TBOUNCE_STAT -TCLEANUP_STATE -TCLIENT_LIST +-TCLNT_STREAM -TCONFIG_BOOL_FN_TABLE -TCONFIG_BOOL_TABLE -TCONFIG_INT_FN_TABLE @@ -25,6 +26,8 @@ -TDICT_NISPLUS -TDICT_NODE -TDICT_OPEN_INFO +-TDICT_PCRE +-TDICT_UNIX -TDNS_FIXED -TDNS_REPLY -TDNS_RR @@ -68,8 +71,14 @@ -TRECIPIENT_LIST -TREC_TYPE_NAME -TRESOLVE_REPLY +-TRESPONSE -TSCAN_DIR +-TSCAN_INFO +-TSCAN_OBJ +-TSESSION -TSINGLE_SERVER +-TSINK_COMMAND +-TSINK_STATE -TSMTPD_STATE -TSMTPD_TOKEN -TSMTP_ADDR diff --git a/postfix/global/.indent.pro b/postfix/global/.indent.pro index e59d396ad..b38eded21 100644 --- a/postfix/global/.indent.pro +++ b/postfix/global/.indent.pro @@ -6,6 +6,7 @@ -TBOUNCE_STAT -TCLEANUP_STATE -TCLIENT_LIST +-TCLNT_STREAM -TCONFIG_BOOL_FN_TABLE -TCONFIG_BOOL_TABLE -TCONFIG_INT_FN_TABLE @@ -25,6 +26,8 @@ -TDICT_NISPLUS -TDICT_NODE -TDICT_OPEN_INFO +-TDICT_PCRE +-TDICT_UNIX -TDNS_FIXED -TDNS_REPLY -TDNS_RR @@ -68,8 +71,14 @@ -TRECIPIENT_LIST -TREC_TYPE_NAME -TRESOLVE_REPLY +-TRESPONSE -TSCAN_DIR +-TSCAN_INFO +-TSCAN_OBJ +-TSESSION -TSINGLE_SERVER +-TSINK_COMMAND +-TSINK_STATE -TSMTPD_STATE -TSMTPD_TOKEN -TSMTP_ADDR diff --git a/postfix/global/Makefile.in b/postfix/global/Makefile.in index 9a077980d..2eb474973 100644 --- a/postfix/global/Makefile.in +++ b/postfix/global/Makefile.in @@ -8,14 +8,15 @@ SRCS = been_here.c bounce.c canon_addr.c clean_env.c cleanup_strerror.c \ mail_command_write.c mail_connect.c mail_copy.c mail_date.c \ mail_error.c mail_flush.c mail_open_ok.c mail_params.c \ mail_pathname.c mail_print.c mail_queue.c mail_run.c mail_scan.c \ - mail_task.c mail_trigger.c maps.c mark_corrupt.c mkmap_db.c \ - mkmap_dbm.c mkmap_open.c mynetworks.c mypwd.c namadr_list.c \ - off_cvt.c opened.c own_inet_addr.c pipe_command.c post_mail.c \ - quote_822_local.c rec_streamlf.c rec_type.c recipient_list.c \ - record.c remove.c resolve_clnt.c resolve_local.c rewrite_clnt.c \ - sent.c smtp_stream.c split_addr.c string_list.c sys_exits.c \ - timed_ipc.c tok822_find.c tok822_node.c tok822_parse.c \ - tok822_resolve.c tok822_rewrite.c tok822_tree.c mail_stream.c + mail_scan_dir.c mail_stream.c mail_task.c mail_trigger.c maps.c \ + mark_corrupt.c mkmap_db.c mkmap_dbm.c mkmap_open.c mynetworks.c \ + mypwd.c namadr_list.c off_cvt.c opened.c own_inet_addr.c \ + peer_name.c pipe_command.c post_mail.c quote_822_local.c \ + rec_streamlf.c rec_type.c recipient_list.c record.c remove.c \ + resolve_clnt.c resolve_local.c rewrite_clnt.c sent.c smtp_stream.c \ + split_addr.c string_list.c sys_exits.c timed_ipc.c tok822_find.c \ + tok822_node.c tok822_parse.c tok822_resolve.c tok822_rewrite.c \ + tok822_tree.c clnt_stream.c deliver_pass.c OBJS = been_here.o bounce.o canon_addr.o clean_env.o cleanup_strerror.o \ config.o config_bool.o config_int.o config_str.o debug_peer.o \ debug_process.o defer.o deliver_completed.o deliver_flock.o \ @@ -25,27 +26,29 @@ OBJS = been_here.o bounce.o canon_addr.o clean_env.o cleanup_strerror.o \ mail_command_write.o mail_connect.o mail_copy.o mail_date.o \ mail_error.o mail_flush.o mail_open_ok.o mail_params.o \ mail_pathname.o mail_print.o mail_queue.o mail_run.o mail_scan.o \ - mail_task.o mail_trigger.o maps.o mark_corrupt.o mkmap_db.o \ - mkmap_dbm.o mkmap_open.o mynetworks.o mypwd.o namadr_list.o \ - off_cvt.o opened.o own_inet_addr.o pipe_command.o post_mail.o \ - quote_822_local.o rec_streamlf.o rec_type.o recipient_list.o \ - record.o remove.o resolve_clnt.o resolve_local.o rewrite_clnt.o \ - sent.o smtp_stream.o split_addr.o string_list.o sys_exits.o \ - timed_ipc.o tok822_find.o tok822_node.o tok822_parse.o \ - tok822_resolve.o tok822_rewrite.o tok822_tree.o mail_stream.o + mail_scan_dir.o mail_stream.o mail_task.o mail_trigger.o maps.o \ + mark_corrupt.o mkmap_db.o mkmap_dbm.o mkmap_open.o mynetworks.o \ + mypwd.o namadr_list.o off_cvt.o opened.o own_inet_addr.o \ + peer_name.o pipe_command.o post_mail.o quote_822_local.o \ + rec_streamlf.o rec_type.o recipient_list.o record.o remove.o \ + resolve_clnt.o resolve_local.o rewrite_clnt.o sent.o smtp_stream.o \ + split_addr.o string_list.o sys_exits.o timed_ipc.o tok822_find.o \ + tok822_node.o tok822_parse.o tok822_resolve.o tok822_rewrite.o \ + tok822_tree.o clnt_stream.o deliver_pass.o HDRS = been_here.h bounce.h canon_addr.h clean_env.h cleanup_user.h \ config.h debug_peer.h debug_process.h defer.h deliver_completed.h \ deliver_flock.h deliver_request.h domain_list.h dot_lockfile.h \ file_id.h header_opts.h is_header.h mail_addr.h mail_addr_crunch.h \ mail_addr_find.h mail_addr_map.h mail_copy.h mail_date.h \ mail_error.h mail_flush.h mail_open_ok.h mail_params.h \ - mail_proto.h mail_queue.h mail_run.h mail_task.h mail_version.h \ - maps.h mark_corrupt.h mkmap.h mynetworks.h mypwd.h namadr_list.h \ - off_cvt.h opened.h own_inet_addr.h pipe_command.h post_mail.h \ + mail_proto.h mail_queue.h mail_run.h mail_scan_dir.h mail_stream.h \ + mail_task.h mail_version.h maps.h mark_corrupt.h mkmap.h \ + mynetworks.h mypwd.h namadr_list.h off_cvt.h opened.h \ + own_inet_addr.h peer_name.h pipe_command.h post_mail.h \ quote_822_local.h rec_streamlf.h rec_type.h recipient_list.h \ record.h resolve_clnt.h resolve_local.h rewrite_clnt.h sent.h \ smtp_stream.h split_addr.h string_list.h sys_exits.h timed_ipc.h \ - tok822.h mail_stream.h + tok822.h clnt_stream.h TESTSRC = rec2stream.c stream2rec.c recdump.c WARN = -W -Wformat -Wimplicit -Wmissing-prototypes \ -Wparentheses -Wstrict-prototypes -Wswitch -Wuninitialized \ @@ -244,6 +247,17 @@ cleanup_strerror.o: ../include/sys_defs.h cleanup_strerror.o: ../include/vstring.h cleanup_strerror.o: ../include/vbuf.h cleanup_strerror.o: cleanup_user.h +clnt_stream.o: clnt_stream.c +clnt_stream.o: ../include/sys_defs.h +clnt_stream.o: ../include/msg.h +clnt_stream.o: ../include/mymalloc.h +clnt_stream.o: ../include/vstream.h +clnt_stream.o: ../include/vbuf.h +clnt_stream.o: ../include/events.h +clnt_stream.o: ../include/iostuff.h +clnt_stream.o: mail_proto.h +clnt_stream.o: mail_params.h +clnt_stream.o: clnt_stream.h config.o: config.c config.o: ../include/sys_defs.h config.o: ../include/msg.h @@ -316,6 +330,16 @@ deliver_flock.o: ../include/vbuf.h deliver_flock.o: ../include/myflock.h deliver_flock.o: mail_params.h deliver_flock.o: deliver_flock.h +deliver_pass.o: deliver_pass.c +deliver_pass.o: ../include/sys_defs.h +deliver_pass.o: ../include/msg.h +deliver_pass.o: ../include/vstring.h +deliver_pass.o: ../include/vbuf.h +deliver_pass.o: ../include/vstream.h +deliver_pass.o: mail_proto.h +deliver_pass.o: ../include/iostuff.h +deliver_pass.o: deliver_request.h +deliver_pass.o: recipient_list.h deliver_request.o: deliver_request.c deliver_request.o: ../include/sys_defs.h deliver_request.o: ../include/msg.h @@ -519,6 +543,10 @@ mail_scan.o: ../include/vstring_vstream.h mail_scan.o: ../include/mymalloc.h mail_scan.o: mail_proto.h mail_scan.o: ../include/iostuff.h +mail_scan_dir.o: mail_scan_dir.c +mail_scan_dir.o: ../include/sys_defs.h +mail_scan_dir.o: ../include/scan_dir.h +mail_scan_dir.o: mail_scan_dir.h mail_stream.o: mail_stream.c mail_stream.o: ../include/sys_defs.h mail_stream.o: ../include/msg.h @@ -641,6 +669,11 @@ own_inet_addr.o: ../include/inet_addr_host.h own_inet_addr.o: ../include/stringops.h own_inet_addr.o: mail_params.h own_inet_addr.o: own_inet_addr.h +peer_name.o: peer_name.c +peer_name.o: ../include/sys_defs.h +peer_name.o: ../include/msg.h +peer_name.o: ../include/valid_hostname.h +peer_name.o: peer_name.h pipe_command.o: pipe_command.c pipe_command.o: ../include/sys_defs.h pipe_command.o: ../include/msg.h @@ -731,6 +764,7 @@ resolve_clnt.o: ../include/events.h resolve_clnt.o: ../include/iostuff.h resolve_clnt.o: mail_proto.h resolve_clnt.o: mail_params.h +resolve_clnt.o: clnt_stream.h resolve_clnt.o: resolve_clnt.h resolve_local.o: resolve_local.c resolve_local.o: ../include/sys_defs.h @@ -752,6 +786,7 @@ rewrite_clnt.o: ../include/iostuff.h rewrite_clnt.o: quote_822_local.h rewrite_clnt.o: mail_proto.h rewrite_clnt.o: mail_params.h +rewrite_clnt.o: clnt_stream.h rewrite_clnt.o: rewrite_clnt.h sent.o: sent.c sent.o: ../include/sys_defs.h diff --git a/postfix/global/bounce.h b/postfix/global/bounce.h index 6d1afb1ce..d7cf2f1d9 100644 --- a/postfix/global/bounce.h +++ b/postfix/global/bounce.h @@ -31,6 +31,7 @@ extern int bounce_flush(int, const char *, const char *, const char *); */ #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 */ /* * Flags. diff --git a/postfix/global/clnt_stream.c b/postfix/global/clnt_stream.c new file mode 100644 index 000000000..4513247c3 --- /dev/null +++ b/postfix/global/clnt_stream.c @@ -0,0 +1,207 @@ +/*++ +/* NAME +/* clnt_stream 3 +/* SUMMARY +/* client endpoint maintenance +/* SYNOPSIS +/* #include +/* +/* CLNT_STREAM *clnt_stream_create(class, service, timeout) +/* const char *class; +/* const char *service; +/* int timeout; +/* +/* VSTREAM *clnt_stream_access(clnt_stream) +/* CLNT_STREAM *clnt_stream; +/* +/* void clnt_stream_recover(clnt_stream) +/* CLNT_STREAM *clnt_stream; +/* +/* void clnt_stream_free(clnt_stream) +/* CLNT_STREAM *clnt_stream; +/* DESCRIPTION +/* This module maintains local IPC client endpoints that automatically +/* disconnect after a being idle for a configurable amount of time, +/* and that transparently handle most server-initiated disconnects. +/* Server disconnect is detected by read-selecting the client endpoint. +/* The code assumes that the server has disconnected when the endpoint +/* becomes readable. +/* +/* clnt_stream_create() instantiates a client endpoint. +/* +/* clnt_stream_access() returns an open stream to the service specified +/* to clnt_stream_create(). The stream instance may change between calls. +/* +/* clnt_stream_recover() recovers from a server-initiated disconnect +/* that happened in the middle of an I/O operation. +/* +/* clnt_stream_free() destroys of the specified client endpoint. +/* DIAGNOSTICS +/* Warnings: communication failure. Fatal error: mail system is down, +/* out of memory. +/* SEE ALSO +/* mail_proto(3h) low-level mail component glue. +/* 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 +#include +#include +#include + +/* Global library. */ + +#include "mail_proto.h" +#include "mail_params.h" +#include "clnt_stream.h" + +/* Application-specific. */ + + /* + * CLNT_STREAM is an opaque structure. None of the access methods can easily + * be implemented as a macro, and access is not performance critica anyway. + */ +struct CLNT_STREAM { + VSTREAM *vstream; /* buffered I/O */ + int timeout; /* time before client disconnect */ + char *class; /* server class */ + char *service; /* server name */ +}; + +static void clnt_stream_close(CLNT_STREAM *); + +/* clnt_stream_event - server-initiated disconnect or client-side timeout */ + +static void clnt_stream_event(int unused_event, char *context) +{ + CLNT_STREAM *clnt_stream = (CLNT_STREAM *) context; + + /* + * Sanity check. This routine causes the stream to be closed, so it + * cannot be called when the stream is already closed. + */ + if (clnt_stream->vstream == 0) + msg_panic("clnt_stream_event: stream is closed"); + + clnt_stream_close(clnt_stream); +} + +/* clnt_stream_open - connect to service */ + +static void clnt_stream_open(CLNT_STREAM *clnt_stream) +{ + + /* + * Sanity check. + */ + if (clnt_stream->vstream) + msg_panic("clnt_stream_open: stream is open"); + + /* + * Schedule a read event so that we can clean up when the remote side + * disconnects, and schedule a timer event so that we can cleanup an idle + * connection. Note that both events are handled by the same routine. + */ + clnt_stream->vstream = mail_connect_wait(clnt_stream->class, + clnt_stream->service); + close_on_exec(vstream_fileno(clnt_stream->vstream), CLOSE_ON_EXEC); + event_enable_read(vstream_fileno(clnt_stream->vstream), clnt_stream_event, + (char *) clnt_stream); + event_request_timer(clnt_stream_event, (char *) clnt_stream, + clnt_stream->timeout); +} + +/* clnt_stream_close - disconnect from service */ + +static void clnt_stream_close(CLNT_STREAM *clnt_stream) +{ + + /* + * Sanity check. + */ + if (clnt_stream->vstream == 0) + msg_panic("clnt_stream_close: stream is closed"); + + /* + * Be sure to disable read and timer events. + */ + if (msg_verbose) + msg_info("%s stream disconnect", clnt_stream->service); + event_disable_readwrite(vstream_fileno(clnt_stream->vstream)); + event_cancel_timer(clnt_stream_event, (char *) clnt_stream); + (void) vstream_fclose(clnt_stream->vstream); + clnt_stream->vstream = 0; +} + +/* clnt_stream_recover - recover from server-initiated disconnect */ + +void clnt_stream_recover(CLNT_STREAM *clnt_stream) +{ + + /* + * Clean up. Don't re-connect until the caller needs it. + */ + if (clnt_stream->vstream) + clnt_stream_close(clnt_stream); +} + +/* clnt_stream_access - access a client stream */ + +VSTREAM *clnt_stream_access(CLNT_STREAM *clnt_stream) +{ + + /* + * Open a stream or restart the idle timer. + */ + if (clnt_stream->vstream == 0) { + clnt_stream_open(clnt_stream); + } else { + event_request_timer(clnt_stream_event, (char *) clnt_stream, + clnt_stream->timeout); + } + return (clnt_stream->vstream); +} + +/* clnt_stream_create - create client stream connection */ + +CLNT_STREAM *clnt_stream_create(const char *class, const char *service, + int timeout) +{ + CLNT_STREAM *clnt_stream; + + /* + * Don't open the stream until the caller needs it. + */ + clnt_stream = (CLNT_STREAM *) mymalloc(sizeof(*clnt_stream)); + clnt_stream->vstream = 0; + clnt_stream->timeout = timeout; + clnt_stream->class = mystrdup(class); + clnt_stream->service = mystrdup(service); + return (clnt_stream); +} + +/* clnt_stream_free - destroy client stream instance */ + +void clnt_stream_free(CLNT_STREAM *clnt_stream) +{ + if (clnt_stream->vstream) + clnt_stream_close(clnt_stream); + myfree(clnt_stream->class); + myfree(clnt_stream->service); + myfree((char *) clnt_stream); +} diff --git a/postfix/global/clnt_stream.h b/postfix/global/clnt_stream.h new file mode 100644 index 000000000..a3d480a17 --- /dev/null +++ b/postfix/global/clnt_stream.h @@ -0,0 +1,40 @@ +#ifndef _CLNT_STREAM_H_INCLUDED_ +#define _CLNT_STREAM_H_INCLUDED_ + +/*++ +/* NAME +/* clnt_stream 3h +/* SUMMARY +/* client socket maintenance +/* SYNOPSIS +/* #include +/* DESCRIPTION +/* .nf + + /* + * Utility library. + */ +#include + + /* + * External interface. + */ +typedef struct CLNT_STREAM CLNT_STREAM; + +extern CLNT_STREAM *clnt_stream_create(const char *, const char *, int); +extern VSTREAM *clnt_stream_access(CLNT_STREAM *); +extern void clnt_stream_recover(CLNT_STREAM *); +extern void clnt_stream_free(CLNT_STREAM *); + +/* 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/global/defer.c b/postfix/global/defer.c index ebb0b3ae9..f59e41ccb 100644 --- a/postfix/global/defer.c +++ b/postfix/global/defer.c @@ -28,6 +28,12 @@ /* const char *queue; /* const char *id; /* const char *sender; +/* +/* int defer_warn(flags, queue, id, sender) +/* int flags; +/* const char *queue; +/* const char *id; +/* const char *sender; /* DESCRIPTION /* This module implements a client interface to the defer service, /* which maintains a per-message logfile with status records for @@ -43,6 +49,9 @@ /* sender, including the defer log that was built with defer_append(). /* The result is zero in case of success, non-zero otherwise. /* +/* defer_warn() sends a warning message that the mail in question has +/* been deferred. It does not flush the log. +/* /* Arguments: /* .IP flags /* The bit-wise OR of zero or more of the following (specify @@ -154,3 +163,18 @@ int defer_flush(int flags, const char *queue, const char *id, return (-1); } } + +/* defer_warn - send a copy of the defer log to the sender as a warning bounce + * do not flush the log */ + +int defer_warn(int flags, const char *queue, const char *id, + const char *sender) +{ + if (mail_command_write(MAIL_CLASS_PRIVATE, MAIL_SERVICE_DEFER, + "%d %d %s %s %s", BOUNCE_CMD_WARN, + flags, queue, id, sender) == 0) { + return (0); + } else { + return (-1); + } +} diff --git a/postfix/global/defer.h b/postfix/global/defer.h index 7458f459a..ae6385cfc 100644 --- a/postfix/global/defer.h +++ b/postfix/global/defer.h @@ -31,6 +31,8 @@ extern int vdefer_append(int, const char *, const char *, const char *, time_t, const char *, va_list); extern int defer_flush(int, const char *, const char *, const char *); +extern int defer_warn(int, const char *, const char *, const char *); + /* LICENSE /* .ad /* .fi diff --git a/postfix/global/deliver_completed.c b/postfix/global/deliver_completed.c index 485c794f9..d2bb1ef2d 100644 --- a/postfix/global/deliver_completed.c +++ b/postfix/global/deliver_completed.c @@ -11,7 +11,9 @@ /* long offset; /* DESCRIPTION /* deliver_completed() crosses off the specified recipient from -/* an open queue file. +/* an open queue file. A -1 offset means ignore the request - +/* this is used for delivery requests that are passed on from +/* one delivery agent to another. /* DIAGNOSTICS /* Fatal error: unable to update the queue file. /* LICENSE @@ -46,6 +48,9 @@ void deliver_completed(VSTREAM *stream, long offset) { char *myname = "deliver_completed"; + if (offset == -1) + return; + if (offset <= 0) msg_panic("%s: bad offset %ld", myname, offset); diff --git a/postfix/global/deliver_pass.c b/postfix/global/deliver_pass.c new file mode 100644 index 000000000..31196be1c --- /dev/null +++ b/postfix/global/deliver_pass.c @@ -0,0 +1,148 @@ +/*++ +/* NAME +/* deliver_pass 3 +/* SUMMARY +/* deliver request pass_through +/* SYNOPSIS +/* #include +/* +/* int deliver_pass(class, service, request, address, offset) +/* const char *class; +/* const char *service; +/* DELIVER_REQUEST *request; +/* const char *address; +/* long offset; +/* DESCRIPTION +/* This module implements the client side of the `queue manager +/* to delivery agent' protocol, passing one recipient on from +/* one delivery agent to another. +/* +/* Arguments: +/* .IP class +/* Destination delivery agent service class +/* .IP service +/* Destination delivery agent service name. +/* .IP request +/* Delivery request with queue file information. +/* .IP address +/* Recipient envelope address. +/* .IP offset +/* Recipient offset in queue file. +/* DIAGNOSTICS +/* LICENSE +/* .ad +/* .fi +/* The Secure Mailer license must be distributed with this software. +/* BUGS +/* One recipient at a time; this is OK for mailbox deliveries. +/* +/* Hop status information cannot be passed back. +/* AUTHOR(S) +/* Wietse Venema +/* IBM T.J. Watson Research +/* P.O. Box 704 +/* Yorktown Heights, NY 10598, USA +/*--*/ + +/* System library. */ + +#include + +/* Utility library. */ + +#include +#include +#include + +/* Global library. */ + +#include +#include + +/* deliver_pass_initial_reply - retrieve initial delivery process response */ + +static int deliver_pass_initial_reply(VSTREAM *stream) +{ + int stat; + + if (mail_scan(stream, "%d", &stat) != 1) { + msg_warn("%s: malformed response", VSTREAM_PATH(stream)); + stat = -1; + } + return (stat); +} + +/* deliver_pass_send_request - send delivery request to delivery process */ + +static int deliver_pass_send_request(VSTREAM *stream, DELIVER_REQUEST *request, + const char *addr, long offs) +{ + int stat; + + mail_print(stream, "%s %s %ld %ld %s %s %s %s %ld %ld %s %s", + request->queue_name, request->queue_id, + request->data_offset, request->data_size, + addr, request->sender, + request->errors_to, request->return_receipt, + request->arrival_time, + offs, addr, "0"); + + if (vstream_fflush(stream)) { + msg_warn("%s: bad write: %m", VSTREAM_PATH(stream)); + stat = -1; + } else { + stat = 0; + } + return (stat); +} + +/* deliver_pass_final_reply - retrieve final delivery status response */ + +static int deliver_pass_final_reply(VSTREAM *stream, VSTRING *reason) +{ + int stat; + + if (mail_scan(stream, "%s %d", reason, &stat) != 2) { + msg_warn("%s: malformed response", VSTREAM_PATH(stream)); + stat = -1; + } + return (stat); +} + +/* deliver_pass - deliver one per-site queue entry */ + +int deliver_pass(const char *class, const char *service, + DELIVER_REQUEST *request, const char *addr, long offs) +{ + VSTREAM *stream; + VSTRING *reason; + int status; + + /* + * Initialize. + */ + stream = mail_connect_wait(class, service); + reason = vstring_alloc(1); + + /* + * Get the delivery process initial response. Send the queue file info + * and recipient info to the delivery process. Retrieve the delivery + * agent status report. The numerical status code indicates if delivery + * should be tried again. The reason text is sent only when a destination + * should be avoided for a while, so that the queue manager can log why + * it does not even try to schedule delivery to the affected recipients. + * XXX Can't pass back hop status info because the problem is with a + * different transport. + */ + if ((status = deliver_pass_initial_reply(stream)) == 0 + && (status = deliver_pass_send_request(stream, request, addr, offs)) == 0) + status = deliver_pass_final_reply(stream, reason); + + /* + * Clean up. + */ + vstream_fclose(stream); + vstring_free(reason); + + return (status); +} diff --git a/postfix/global/deliver_request.h b/postfix/global/deliver_request.h index d0e3ce6c1..695110fdc 100644 --- a/postfix/global/deliver_request.h +++ b/postfix/global/deliver_request.h @@ -43,6 +43,8 @@ typedef struct VSTREAM _deliver_vstream_; extern DELIVER_REQUEST *deliver_request_read(_deliver_vstream_ *); extern int deliver_request_done(_deliver_vstream_ *, DELIVER_REQUEST *, int); +extern int deliver_pass(const char *, const char *, DELIVER_REQUEST *, const char *, long); + /* LICENSE /* .ad /* .fi diff --git a/postfix/global/header_opts.c b/postfix/global/header_opts.c index 91b102a80..e14e31f3c 100644 --- a/postfix/global/header_opts.c +++ b/postfix/global/header_opts.c @@ -66,7 +66,7 @@ static HEADER_OPTS header_opts[] = { "Resent-Reply-To", HDR_RESENT_REPLY_TO, HDR_OPT_RECIP | HDR_OPT_RR, "Resent-Sender", HDR_RESENT_SENDER, HDR_OPT_SENDER | HDR_OPT_RR, "Resent-To", HDR_RESENT_TO, HDR_OPT_XRECIP | HDR_OPT_RR, - "Return-Path", HDR_RETURN_PATH, HDR_OPT_SENDER, + "Return-Path", HDR_RETURN_PATH, HDR_OPT_DROP | HDR_OPT_SENDER, "Return-Receipt-To", HDR_RETURN_RECEIPT_TO, HDR_OPT_RECIP, "Sender", HDR_SENDER, HDR_OPT_SENDER, "To", HDR_TO, HDR_OPT_XRECIP, diff --git a/postfix/global/mail_addr_crunch.c b/postfix/global/mail_addr_crunch.c index e7e7118bf..560602d50 100644 --- a/postfix/global/mail_addr_crunch.c +++ b/postfix/global/mail_addr_crunch.c @@ -59,7 +59,8 @@ ARGV *mail_addr_crunch(const char *string, const char *extension) { - VSTRING *buf = vstring_alloc(100); + VSTRING *extern_addr = vstring_alloc(100); + VSTRING *canon_addr = vstring_alloc(100); ARGV *argv = argv_alloc(1); TOK822 *tree; TOK822 **addr_list; @@ -80,24 +81,25 @@ ARGV *mail_addr_crunch(const char *string, const char *extension) tree = tok822_parse(string); addr_list = tok822_grep(tree, TOK822_ADDR); for (tpp = addr_list; *tpp; tpp++) { - tok822_externalize(buf, tpp[0]->head, TOK822_STR_DEFL); - canon_addr_external(buf, STR(buf)); + tok822_externalize(extern_addr, tpp[0]->head, TOK822_STR_DEFL); + canon_addr_external(canon_addr, STR(extern_addr)); if (extension) { - if ((ratsign = strrchr(STR(buf), '@')) == 0) { - vstring_strcat(buf, extension); + if ((ratsign = strrchr(STR(canon_addr), '@')) == 0) { + vstring_strcat(canon_addr, extension); } else { - VSTRING_SPACE(buf, extlen + 1); + VSTRING_SPACE(canon_addr, extlen + 1); memmove(ratsign + extlen, ratsign, strlen(ratsign) + 1); memcpy(ratsign, extension, extlen); - VSTRING_SKIP(buf); + VSTRING_SKIP(canon_addr); } } - argv_add(argv, STR(buf), ARGV_END); + argv_add(argv, STR(canon_addr), ARGV_END); } argv_terminate(argv); myfree((char *) addr_list); tok822_free_tree(tree); - vstring_free(buf); + vstring_free(canon_addr); + vstring_free(extern_addr); return (argv); } diff --git a/postfix/global/mail_copy.c b/postfix/global/mail_copy.c index 02cace24f..6e58bf046 100644 --- a/postfix/global/mail_copy.c +++ b/postfix/global/mail_copy.c @@ -44,6 +44,9 @@ /* .IP MAIL_COPY_DELIVERED /* Prepend a Delivered-To: header with the name of the /* \fIdelivered\fR attribute. +/* .IP MAIL_COPY_RETURN_PATH +/* Prepend a Return-Path: header with the value of the +/* \fIsender\fR attribute. /* .RE /* The manifest constant MAIL_COPY_MBOX is a convenient shorthand for /* all MAIL_COPY_XXX options that are appropriate for mailbox delivery. @@ -122,14 +125,21 @@ int mail_copy(const char *sender, const char *delivered, /* * Prepend a bunch of headers to the message. */ - if (flags & MAIL_COPY_FROM) { + if (flags & (MAIL_COPY_FROM | MAIL_COPY_RETURN_PATH)) { if (sender == 0) msg_panic("%s: null sender", myname); - time(&now); - vstream_fprintf(dst, "From %s %s", *sender == 0 ? - MAIL_ADDR_MAIL_DAEMON : - vstring_str(quote_822_local(buf, sender)), - asctime(localtime(&now))); + quote_822_local(buf, sender); + if (flags & MAIL_COPY_FROM) { + time(&now); + vstream_fprintf(dst, "From %s %s", *sender == 0 ? + MAIL_ADDR_MAIL_DAEMON : + vstring_str(buf), + asctime(localtime(&now))); + } + if (flags & MAIL_COPY_RETURN_PATH) { + vstream_fprintf(dst, "Return-Path: <%s>\n", + *sender ? vstring_str(buf) : ""); + } } if (flags & MAIL_COPY_DELIVERED) { if (delivered == 0) diff --git a/postfix/global/mail_copy.h b/postfix/global/mail_copy.h index e25436ae7..ffa9682f0 100644 --- a/postfix/global/mail_copy.h +++ b/postfix/global/mail_copy.h @@ -27,7 +27,8 @@ extern int mail_copy(const char *, const char *, VSTREAM *, VSTREAM *, #define MAIL_COPY_TOFILE (1<<1) /* fsync, ftruncate() */ #define MAIL_COPY_FROM (1<<2) /* prepend From_ */ #define MAIL_COPY_DELIVERED (1<<3) /* prepend Delivered-To: */ -#define MAIL_COPY_MBOX 017 /* all turned on */ +#define MAIL_COPY_RETURN_PATH (1<<4) /* prepend Return-Path: */ +#define MAIL_COPY_MBOX (~0) /* all turned on */ #define MAIL_COPY_NONE 0 /* all turned off */ /* LICENSE diff --git a/postfix/global/mail_date.c b/postfix/global/mail_date.c index 57708702e..94ec5399a 100644 --- a/postfix/global/mail_date.c +++ b/postfix/global/mail_date.c @@ -31,6 +31,7 @@ #include #include +#include /* Utility library. */ @@ -44,8 +45,9 @@ /* * Application-specific. */ -#define DAY_SEC (24 * HOUR_SEC) /* seconds in a day */ -#define HOUR_SEC (60) /* seconds in an hour */ +#define DAY_MIN (24 * HOUR_MIN) /* minutes in a day */ +#define HOUR_MIN 60 /* minutes in an hour */ +#define MIN_SEC 60 /* seconds in a minute */ /* mail_date - return formatted time */ @@ -68,18 +70,29 @@ const char *mail_date(time_t when) /* * POSIX does not require that struct tm has a tm_gmtoff field, so we * must compute the time offset from UTC by hand. + * + * Starting with the difference in hours/minutes between 24-hour clocks, + * adjust for differences in years, in yeardays, and in (leap) seconds. + * + * Assume 0..23 hours in a day, 0..59 minutes in an hour. The library spec + * has changed: we can no longer assume that there are 0..59 seconds in a + * minute. */ gmt = *gmtime(&when); lt = localtime(&when); - gmtoff = (lt->tm_hour - gmt.tm_hour) * HOUR_SEC + lt->tm_min - gmt.tm_min; + gmtoff = (lt->tm_hour - gmt.tm_hour) * HOUR_MIN + lt->tm_min - gmt.tm_min; if (lt->tm_year < gmt.tm_year) - gmtoff -= DAY_SEC; + gmtoff -= DAY_MIN; else if (lt->tm_year > gmt.tm_year) - gmtoff += DAY_SEC; + gmtoff += DAY_MIN; else if (lt->tm_yday < gmt.tm_yday) - gmtoff -= DAY_SEC; + gmtoff -= DAY_MIN; else if (lt->tm_yday > gmt.tm_yday) - gmtoff += DAY_SEC; + gmtoff += DAY_MIN; + if (lt->tm_sec <= gmt.tm_sec - MIN_SEC) + gmtoff -= 1; + else if (lt->tm_sec >= gmt.tm_sec + MIN_SEC) + gmtoff += 1; /* * First, format the date and wall-clock time. XXX The %e format (day of @@ -99,10 +112,10 @@ const char *mail_date(time_t when) /* * Then, add the UTC offset. */ - if (gmtoff < -DAY_SEC || gmtoff > DAY_SEC) + if (gmtoff < -DAY_MIN || gmtoff > DAY_MIN) msg_panic("UTC time offset %d is larger than one day", gmtoff); - vstring_sprintf_append(vp, "%+03d%02d", (int) (gmtoff / HOUR_SEC), - (int) (gmtoff % HOUR_SEC)); + vstring_sprintf_append(vp, "%+03d%02d", (int) (gmtoff / HOUR_MIN), + (int) (gmtoff % HOUR_MIN)); /* * Finally, add the time zone name. diff --git a/postfix/global/mail_error.c b/postfix/global/mail_error.c index 7c1ee7cb1..9355c051f 100644 --- a/postfix/global/mail_error.c +++ b/postfix/global/mail_error.c @@ -15,10 +15,12 @@ /* /* The following is a list of implemented names, with the /* corresponding bit masks indicated in parentheses: -/* .IP "bounce (MAIL_ERROR_BOUNCE) +/* .IP "bounce (MAIL_ERROR_BOUNCE)" /* A message could not be delivered because it was too large, /* because was sent via too many hops, because the recipient /* does not exist, and so on. +/* .IP "2bounce (MAIL_ERROR_2BOUNCE)" +/* A bounce message could not be delivered. /* .IP "policy (MAIL_ERROR_POLICY)" /* Policy violation. This depends on what restrictions have /* been configured locally. @@ -64,6 +66,8 @@ */ NAME_MASK mail_error_masks[] = { "bounce", MAIL_ERROR_BOUNCE, + "2bounce", MAIL_ERROR_2BOUNCE, + "delay", MAIL_ERROR_DELAY, "policy", MAIL_ERROR_POLICY, "protocol", MAIL_ERROR_PROTOCOL, "resource", MAIL_ERROR_RESOURCE, diff --git a/postfix/global/mail_error.h b/postfix/global/mail_error.h index 4cb75412a..ffe7b3806 100644 --- a/postfix/global/mail_error.h +++ b/postfix/global/mail_error.h @@ -24,6 +24,8 @@ #define MAIL_ERROR_BOUNCE (1<<2) #define MAIL_ERROR_SOFTWARE (1<<3) #define MAIL_ERROR_RESOURCE (1<<4) +#define MAIL_ERROR_2BOUNCE (1<<5) +#define MAIL_ERROR_DELAY (1<<6) extern NAME_MASK mail_error_masks[]; diff --git a/postfix/global/mail_flush.c b/postfix/global/mail_flush.c index e5fa32ce7..42a48a6ab 100644 --- a/postfix/global/mail_flush.c +++ b/postfix/global/mail_flush.c @@ -13,8 +13,8 @@ /* DESCRIPTION /* This module triggers delivery of backed up mail. /* -/* mail_flush_deferred() triggers delivery of all mail in the -/* deferred queue. +/* mail_flush_deferred() triggers delivery of all deferred +/* or incoming mail. /* /* mail_flush_site() triggers delivery of all mail queued for /* the named site. This routine may degenerate into a @@ -51,6 +51,7 @@ int mail_flush_deferred(void) QMGR_REQ_FLUSH_DEAD, /* all hosts, all transports */ QMGR_REQ_SCAN_ALL, /* all time stamps */ QMGR_REQ_SCAN_DEFERRED, /* scan deferred queue */ + QMGR_REQ_SCAN_INCOMING, /* scan incoming queue */ }; /* diff --git a/postfix/global/mail_params.c b/postfix/global/mail_params.c index 189434003..0191de98b 100644 --- a/postfix/global/mail_params.c +++ b/postfix/global/mail_params.c @@ -244,7 +244,7 @@ void mail_params_init() static CONFIG_STR_TABLE other_str_defaults[] = { VAR_MAIL_NAME, DEF_MAIL_NAME, &var_mail_name, 1, 0, VAR_MAIL_OWNER, DEF_MAIL_OWNER, &var_mail_owner, 1, 0, - VAR_MYDEST, DEF_MYDEST, &var_mydest, 1, 0, + VAR_MYDEST, DEF_MYDEST, &var_mydest, 0, 0, VAR_MYORIGIN, DEF_MYORIGIN, &var_myorigin, 1, 0, VAR_RELAYHOST, DEF_RELAYHOST, &var_relayhost, 0, 0, VAR_PROGRAM_DIR, DEF_PROGRAM_DIR, &var_program_dir, 1, 0, @@ -334,7 +334,7 @@ void mail_params_init() time(&var_starttime); /* - * If have seen this happen just too often. + * I have seen this happen just too often. */ if (strcasecmp(var_myhostname, var_relayhost) == 0) msg_fatal("myhostname == relayhost"); diff --git a/postfix/global/mail_params.h b/postfix/global/mail_params.h index f07cb73f1..61978087a 100644 --- a/postfix/global/mail_params.h +++ b/postfix/global/mail_params.h @@ -105,6 +105,10 @@ extern char *var_masq_exceptions; #define DEF_RELAYHOST "" extern char *var_relayhost; +#define VAR_FALLBACK_RELAY "fallback_relay" +#define DEF_FALLBACK_RELAY "" +extern char *var_fallback_relay; + #define VAR_DISABLE_DNS "disable_dns_lookups" #define DEF_DISABLE_DNS 0 extern bool var_disable_dns; @@ -176,6 +180,14 @@ extern char *var_db_type; #define LOG_FACILITY LOG_MAIL #endif + /* + * Big brother: who receives a blank-carbon copy of all mail that enters + * this mail system. + */ +#define VAR_ALWAYS_BCC "always_bcc" +#define DEF_ALWAYS_BCC "" +extern char *var_always_bcc; + /* * trivial rewrite/resolve service: mapping tables. */ @@ -278,6 +290,17 @@ extern char *var_local_cmd_shell; #define DEF_ALIAS_DB_MAP ALIAS_DB_MAP /* sys_defs.h */ extern char *var_alias_db_map; +#define VAR_LUSER_RELAY "luser_relay" +#define DEF_LUSER_RELAY "" +extern char *var_luser_relay; + + /* + * Local delivery: mailbox delivery. + */ +#define VAR_MAIL_SPOOL_DIR "mail_spool_directory" +#define DEF_MAIL_SPOOL_DIR _PATH_MAILDIR +extern char *var_mail_spool_dir; + #define VAR_HOME_MAILBOX "home_mailbox" #define DEF_HOME_MAILBOX "" extern char *var_home_mailbox; @@ -286,6 +309,21 @@ extern char *var_home_mailbox; #define DEF_MAILBOX_COMMAND "" extern char *var_mailbox_command; +#define VAR_MAILBOX_TRANSP "mailbox_transport" +#define DEF_MAILBOX_TRANSP "" +extern char *var_mailbox_transport; + +#define VAR_FALLBACK_TRANSP "fallback_transport" +#define DEF_FALLBACK_TRANSP "" +extern char *var_fallback_transport; + + /* + * Local delivery: path to per-user forwarding file. + */ +#define VAR_FORWARD_PATH "forward_path" +#define DEF_FORWARD_PATH "$home/.forward" +extern char *var_forward_path; + #define VAR_RCPT_DELIM "recipient_delimiter" #define DEF_RCPT_DELIM "" extern char *var_rcpt_delim; @@ -330,6 +368,10 @@ extern int var_max_backoff_time; #define DEF_MAX_QUEUE_TIME 5 extern int var_max_queue_time; +#define VAR_DELAY_WARN_TIME "delay_warning_time" +#define DEF_DELAY_WARN_TIME 0 +extern int var_delay_warn_time; + #define VAR_QMGR_ACT_LIMIT "qmgr_message_active_limit" #define DEF_QMGR_ACT_LIMIT 1000 @@ -376,6 +418,13 @@ extern char *var_defer_xports; #define DEF_PROC_LIMIT 50 extern int var_proc_limit; + /* + * Master: default time to wait after service is throttled. + */ +#define VAR_THROTTLE_TIME "service_throttle_time" +#define DEF_THROTTLE_TIME 60 +extern int var_throttle_time; + /* * Any subsystem: default maximum number of clients serviced before a mail * subsystem terminates (except queue manager). @@ -469,6 +518,18 @@ extern int var_smtp_data2_tmout; #define DEF_SMTP_QUIT_TMOUT 300 extern int var_smtp_quit_tmout; +#define VAR_SMTP_SKIP_4XX "smtp_skip_4xx_greeting" +#define DEF_SMTP_SKIP_4XX 0 +extern bool var_smtp_skip_4xx_greeting; + +#define VAR_IGN_MX_LOOKUP_ERR "ignore_mx_lookup_error" +#define DEF_IGN_MX_LOOKUP_ERR 0 +extern bool var_ign_mx_lookup_err; + +#define VAR_SKIP_QUIT_RESP "smtp_skip_quit_response" +#define DEF_SKIP_QUIT_RESP 1 +extern bool var_skip_quit_resp; + /* * SMTP server. The soft error limit determines how many errors an SMTP * client may make before we start to slow down; the hard error limit @@ -643,6 +704,13 @@ extern int var_bad_name_code; #define DEF_UNK_NAME_CODE 450 extern int var_unk_name_code; +#define REJECT_NON_FQDN_HOSTNAME "reject_non_fqdn_hostname" +#define REJECT_NON_FQDN_SENDER "reject_non_fqdn_sender" +#define REJECT_NON_FQDN_RCPT "reject_non_fqdn_recipient" +#define VAR_NON_FQDN_CODE "non_fqdn_reject_code" +#define DEF_NON_FQDN_CODE 504 +extern int var_non_fqdn_code; + #define REJECT_UNKNOWN_ADDRESS "reject_unknown_address" #define VAR_UNK_ADDR_CODE "unknown_address_reject_code" #define DEF_UNK_ADDR_CODE 450 diff --git a/postfix/global/mail_queue.c b/postfix/global/mail_queue.c index cecdfb31a..4cde268e7 100644 --- a/postfix/global/mail_queue.c +++ b/postfix/global/mail_queue.c @@ -26,6 +26,9 @@ /* const char *queue_name; /* const char *queue_id; /* +/* int mail_queue_mkdirs(path) +/* const char *path; +/* /* int mail_queue_rename(queue_id, old_queue, new_queue) /* const char *queue_id; /* const char *old_queue; @@ -66,6 +69,10 @@ /* is written to a private buffer that may be overwritten upon the /* next call. /* +/* mail_queue_mkdirs() creates missing parent directories +/* for the file named in \fBpath\fR. A non-zero result means +/* that the operation failed. +/* /* mail_queue_rename() renames a queue file. A non-zero result /* means the operation failed. /* @@ -204,7 +211,7 @@ const char *mail_queue_path(VSTRING *buf, const char *queue_name, /* mail_queue_mkdirs - fill in missing directories */ -static int mail_queue_mkdirs(const char *path) +int mail_queue_mkdirs(const char *path) { char *myname = "mail_queue_mkdirs"; char *saved_path = mystrdup(path); diff --git a/postfix/global/mail_queue.h b/postfix/global/mail_queue.h index f052f4533..acba6a3d0 100644 --- a/postfix/global/mail_queue.h +++ b/postfix/global/mail_queue.h @@ -40,6 +40,7 @@ extern int mail_queue_rename(const char *, const char *, const char *); extern int mail_queue_remove(const char *, const char *); extern const char *mail_queue_dir(VSTRING *, const char *, const char *); extern const char *mail_queue_path(VSTRING *, const char *, const char *); +extern int mail_queue_mkdirs(const char *); extern int mail_queue_name_ok(const char *); extern int mail_queue_id_ok(const char *); diff --git a/postfix/global/mail_scan_dir.c b/postfix/global/mail_scan_dir.c new file mode 100644 index 000000000..f011536c2 --- /dev/null +++ b/postfix/global/mail_scan_dir.c @@ -0,0 +1,62 @@ +/*++ +/* NAME +/* mail_scan_dir 3 +/* SUMMARY +/* mail queue directory scanning support +/* SYNOPSIS +/* #include +/* +/* char *mail_scan_dir_next(scan) +/* SCAN_DIR *scan; +/* DESCRIPTION +/* The \fBmail_scan_dir_next\fR() routine is a wrapper around +/* scan_dir_next() that understands the structure of a Postfix +/* mail queue. The result is a queue ID or a null pointer. +/* SEE ALSO +/* scan_dir(3) directory scanner +/* 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 + +/* Global library. */ + +#include + +/* mail_scan_dir_next - return next queue file */ + +char *mail_scan_dir_next(SCAN_DIR *scan) +{ + char *name; + + /* + * Exploit the fact that mail queue subdirectories have one-letter names, + * so we don't have to stat() every file in sight. This is a win because + * many dirent implementations do not return file type information. + */ + for (;;) { + if ((name = scan_dir_next(scan)) == 0) { + if (scan_dir_pop(scan) == 0) + return (0); + } else if (strlen(name) == 1) { + scan_dir_push(scan, name); + } else { + return (name); + } + } +} diff --git a/postfix/global/mail_scan_dir.h b/postfix/global/mail_scan_dir.h new file mode 100644 index 000000000..fda13260f --- /dev/null +++ b/postfix/global/mail_scan_dir.h @@ -0,0 +1,35 @@ +#ifndef _MAIL_SCAN_DIR_H_INCLUDED_ +#define _MAIL_SCAN_DIR_H_INCLUDED_ + +/*++ +/* NAME +/* mail_scan_dir 3h +/* SUMMARY +/* mail directory scanner support +/* SYNOPSIS +/* #include +/* DESCRIPTION +/* .nf + + /* + * Utility library. + */ +#include + + /* + * External interface. + */ +extern char *mail_scan_dir_next(SCAN_DIR *); + +/* 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/global/mail_version.c b/postfix/global/mail_version.c new file mode 100644 index 000000000..e69de29bb diff --git a/postfix/global/mail_version.h b/postfix/global/mail_version.h index b53b9f47d..fb32eac87 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 "Beta-19990122-pl01" +#define DEF_MAIL_VERSION "Beta-19990317" extern char *var_mail_version; /* LICENSE diff --git a/postfix/util/peer_name.c b/postfix/global/peer_name.c similarity index 100% rename from postfix/util/peer_name.c rename to postfix/global/peer_name.c diff --git a/postfix/util/peer_name.h b/postfix/global/peer_name.h similarity index 100% rename from postfix/util/peer_name.h rename to postfix/global/peer_name.h diff --git a/postfix/global/rec_type.c b/postfix/global/rec_type.c index 6b72e8171..c03aa1dba 100644 --- a/postfix/global/rec_type.c +++ b/postfix/global/rec_type.c @@ -46,6 +46,7 @@ REC_TYPE_NAME rec_type_names[] = { REC_TYPE_FROM, "sender", REC_TYPE_DONE, "done", REC_TYPE_RCPT, "recipient", + REC_TYPE_WARN, "warning_message_time", REC_TYPE_MESG, "message_content", REC_TYPE_CONT, "unterminated", REC_TYPE_NORM, "normal_data", @@ -57,7 +58,7 @@ REC_TYPE_NAME rec_type_names[] = { 0, 0, }; -/* rec_type_name - map record type ro printable name */ +/* rec_type_name - map record type to printable name */ const char *rec_type_name(int type) { diff --git a/postfix/global/rec_type.h b/postfix/global/rec_type.h index 3a27f80ec..15ad19ee1 100644 --- a/postfix/global/rec_type.h +++ b/postfix/global/rec_type.h @@ -31,6 +31,7 @@ #define REC_TYPE_FROM 'S' /* sender, required */ #define REC_TYPE_DONE 'D' /* delivered recipient, optional */ #define REC_TYPE_RCPT 'R' /* todo recipient, optional */ +#define REC_TYPE_WARN 'W' /* warning message time */ #define REC_TYPE_MESG 'M' /* start message records */ @@ -50,7 +51,7 @@ * record groups. The first member in each set is the record type that * indicates the end of that record group. */ -#define REC_TYPE_ENVELOPE "MCTFSDR" +#define REC_TYPE_ENVELOPE "MCTFSDRW" #define REC_TYPE_CONTENT "XLN" #define REC_TYPE_EXTRACT "EDRPre" #define REC_TYPE_NOEXTRACT "E" @@ -60,14 +61,22 @@ * content size. This is the format of the position field. It is a * fixed-width field so it can be updated in place. */ -#define REC_TYPE_SIZE_FORMAT "%9d" /* content size format */ +#define REC_TYPE_SIZE_FORMAT "%15ld" /* content size format */ /* * The record at the beginning of the message content records specifies the * position of the next record group. This is the format of the position * field. It is a fixed-width field so it can be updated in place. */ -#define REC_TYPE_MESG_FORMAT "%9d" /* message length format */ +#define REC_TYPE_MESG_FORMAT "%15ld" /* message length format */ + + /* + * The warn record specifies when the next warning that the message + * was deferred should be sent. It is updated in place by qmgr, so + * changing this value when there are deferred mesages in the queue + * is dangerous! + */ +#define REC_TYPE_WARN_FORMAT "%15ld" /* warning time format */ /* * Programmatic interface. diff --git a/postfix/global/resolve_clnt.c b/postfix/global/resolve_clnt.c index 605de1013..3c6ef7ce0 100644 --- a/postfix/global/resolve_clnt.c +++ b/postfix/global/resolve_clnt.c @@ -70,57 +70,15 @@ #include "mail_proto.h" #include "mail_params.h" +#include "clnt_stream.h" #include "resolve_clnt.h" /* Application-specific. */ -static VSTREAM *resolve_fp = 0; -static void resolve_clnt_disconnect(void); - -/* resolve_clnt_read - disconnect after EOF */ - -static void resolve_clnt_read(int unused_event, char *unused_context) -{ - resolve_clnt_disconnect(); -} - -/* resolve_clnt_time - disconnect after idle timeout */ - -static void resolve_clnt_time(char *unused_context) -{ - resolve_clnt_disconnect(); -} - -/* resolve_clnt_disconnect - disconnect from resolve service */ - -static void resolve_clnt_disconnect(void) -{ - - /* - * Be sure to disable read and timer events. - */ - if (msg_verbose) - msg_info("resolve service disconnect"); - event_disable_readwrite(vstream_fileno(resolve_fp)); - event_cancel_timer(resolve_clnt_time, (char *) 0); - (void) vstream_fclose(resolve_fp); - resolve_fp = 0; -} - -/* resolve_clnt_connect - connect to resolve service */ - -static void resolve_clnt_connect(void) -{ - - /* - * Register a read event so that we can clean up when the remote side - * disconnects, and a timer event so we can cleanup an idle connection. - */ - resolve_fp = mail_connect_wait(MAIL_CLASS_PRIVATE, MAIL_SERVICE_REWRITE); - close_on_exec(vstream_fileno(resolve_fp), CLOSE_ON_EXEC); - event_enable_read(vstream_fileno(resolve_fp), resolve_clnt_read, (char *) 0); - event_request_timer(resolve_clnt_time, (char *) 0, var_ipc_idle_limit); -} + /* + * XXX this is shared with the rewrite client to save a file descriptor. + */ +extern CLNT_STREAM *rewrite_clnt_stream; /* resolve_clnt_init - initialize reply */ @@ -136,27 +94,35 @@ void resolve_clnt_init(RESOLVE_REPLY *reply) void resolve_clnt_query(const char *addr, RESOLVE_REPLY *reply) { char *myname = "resolve_clnt_query"; + VSTREAM *stream; + + /* + * Sanity check. The result must not clobber the input because we may + * have to retransmit the request. + */ +#define STR vstring_str + + if (addr == STR(reply->recipient)) + msg_panic("%s: result clobbers input", myname); /* * Keep trying until we get a complete response. The resolve service is * CPU bound; making the client asynchronous would just complicate the * code. */ -#define STR vstring_str + if (rewrite_clnt_stream == 0) + rewrite_clnt_stream = clnt_stream_create(MAIL_CLASS_PRIVATE, + MAIL_SERVICE_REWRITE, var_ipc_idle_limit); for (;;) { - if (resolve_fp == 0) - resolve_clnt_connect(); - else - event_request_timer(resolve_clnt_time, (char *) 0, - var_ipc_idle_limit); - if (mail_print(resolve_fp, "%s %s", RESOLVE_ADDR, addr) - || vstream_fflush(resolve_fp)) { - if (msg_verbose || errno != EPIPE) + stream = clnt_stream_access(rewrite_clnt_stream); + if (mail_print(stream, "%s %s", RESOLVE_ADDR, addr) + || vstream_fflush(stream)) { + if (msg_verbose || (errno != EPIPE && errno != ENOENT)) msg_warn("%s: bad write: %m", myname); - } else if (mail_scan(resolve_fp, "%s %s %s", reply->transport, + } else if (mail_scan(stream, "%s %s %s", reply->transport, reply->nexthop, reply->recipient) != 3) { - if (msg_verbose || errno != EPIPE) + if (msg_verbose || (errno != EPIPE && errno != ENOENT)) msg_warn("%s: bad read: %m", myname); } else { if (msg_verbose) @@ -171,7 +137,7 @@ void resolve_clnt_query(const char *addr, RESOLVE_REPLY *reply) break; } sleep(10); /* XXX make configurable */ - resolve_clnt_disconnect(); + clnt_stream_recover(rewrite_clnt_stream); } } diff --git a/postfix/global/rewrite_clnt.c b/postfix/global/rewrite_clnt.c index 59e225019..d6e383577 100644 --- a/postfix/global/rewrite_clnt.c +++ b/postfix/global/rewrite_clnt.c @@ -61,83 +61,51 @@ #include "mail_proto.h" #include "mail_params.h" +#include "clnt_stream.h" #include "rewrite_clnt.h" /* Application-specific. */ -static VSTREAM *rewrite_fp = 0; -static void rewrite_clnt_disconnect(void); + /* + * XXX this is shared with the resolver client to save a file descriptor. + */ +CLNT_STREAM *rewrite_clnt_stream = 0; -/* rewrite_clnt_read - disconnect after EOF */ - -static void rewrite_clnt_read(int unused_event, char *unused_context) -{ - rewrite_clnt_disconnect(); -} - -/* rewrite_clnt_time - disconnect after timeout */ - -static void rewrite_clnt_time(char *unused_context) -{ - rewrite_clnt_disconnect(); -} - -/* rewrite_clnt_disconnect - disconnect from rewrite service */ - -static void rewrite_clnt_disconnect(void) -{ - - /* - * Be sure to disable read and timer events. - */ - if (msg_verbose) - msg_info("rewrite service disconnect"); - event_disable_readwrite(vstream_fileno(rewrite_fp)); - event_cancel_timer(rewrite_clnt_time, (char *) 0); - (void) vstream_fclose(rewrite_fp); - rewrite_fp = 0; -} - -/* rewrite_clnt_connect - connect to rewrite service */ +/* rewrite_clnt - rewrite address to (transport, next hop, recipient) */ -static void rewrite_clnt_connect(void) +VSTRING *rewrite_clnt(const char *rule, const char *addr, VSTRING *result) { + char *myname = "rewrite_clnt"; + VSTREAM *stream; /* - * Register a read event so that we can clean up when the remote side - * disconnects, and a timer event so we can cleanup an idle connection. + * Sanity check. An address must be in externalized form. The result must + * not clobber the input, because we may have to retransmit the query. */ - rewrite_fp = mail_connect_wait(MAIL_CLASS_PRIVATE, MAIL_SERVICE_REWRITE); - close_on_exec(vstream_fileno(rewrite_fp), CLOSE_ON_EXEC); - event_enable_read(vstream_fileno(rewrite_fp), rewrite_clnt_read, (char *) 0); - event_request_timer(rewrite_clnt_time, (char *) 0, var_ipc_idle_limit); -} - -/* rewrite_clnt - rewrite address to (transport, next hop, recipient) */ +#define STR vstring_str -VSTRING *rewrite_clnt(const char *rule, const char *addr, VSTRING *result) -{ - char *myname = "rewrite_clnt"; + if (*addr == 0) + msg_panic("rewrite_clnt: empty address"); + if (addr == STR(result)) + msg_panic("rewrite_clnt: result clobbers input"); /* * Keep trying until we get a complete response. The rewrite service is * CPU bound and making the client asynchronous would just complicate the * code. */ -#define STR vstring_str + if (rewrite_clnt_stream == 0) + rewrite_clnt_stream = clnt_stream_create(MAIL_CLASS_PRIVATE, + MAIL_SERVICE_REWRITE, var_ipc_idle_limit); for (;;) { - if (rewrite_fp == 0) - rewrite_clnt_connect(); - else - event_request_timer(rewrite_clnt_time, (char *) 0, - var_ipc_idle_limit); - if (mail_print(rewrite_fp, "%s %s %s", REWRITE_ADDR, rule, addr), - vstream_fflush(rewrite_fp)) { - if (msg_verbose || errno != EPIPE) + stream = clnt_stream_access(rewrite_clnt_stream); + if (mail_print(stream, "%s %s %s", REWRITE_ADDR, rule, addr), + vstream_fflush(stream)) { + if (msg_verbose || (errno != EPIPE && errno != ENOENT)) msg_warn("%s: bad write: %m", myname); - } else if (mail_scan(rewrite_fp, "%s", result) != 1) { - if (msg_verbose || errno != EPIPE) + } else if (mail_scan(stream, "%s", result) != 1) { + if (msg_verbose || (errno != EPIPE && errno != ENOENT)) msg_warn("%s: bad read: %m", myname); } else { if (msg_verbose) @@ -149,7 +117,7 @@ VSTRING *rewrite_clnt(const char *rule, const char *addr, VSTRING *result) return (result); } sleep(10); /* XXX make configurable */ - rewrite_clnt_disconnect(); + clnt_stream_recover(rewrite_clnt_stream); } } @@ -157,16 +125,18 @@ VSTRING *rewrite_clnt(const char *rule, const char *addr, VSTRING *result) VSTRING *rewrite_clnt_internal(const char *ruleset, const char *addr, VSTRING *result) { - VSTRING *temp = vstring_alloc(100); + VSTRING *src = vstring_alloc(100); + VSTRING *dst = vstring_alloc(100); /* * Convert the address from internal address form to external RFC822 * form, then rewrite it. After rewriting, convert to internal form. */ - quote_822_local(temp, addr); - rewrite_clnt(ruleset, STR(temp), temp); - unquote_822_local(result, STR(temp)); - vstring_free(temp); + quote_822_local(src, addr); + rewrite_clnt(ruleset, STR(src), dst); + unquote_822_local(result, STR(dst)); + vstring_free(src); + vstring_free(dst); return (result); } diff --git a/postfix/html/Makefile.in b/postfix/html/Makefile.in index 2715e7b0c..f60655d26 100644 --- a/postfix/html/Makefile.in +++ b/postfix/html/Makefile.in @@ -5,7 +5,8 @@ DAEMONS = bounce.8.html cleanup.8.html defer.8.html local.8.html \ smtp.8.html smtpd.8.html trivial-rewrite.8.html COMMANDS= mailq.1.html newaliases.1.html postalias.1.html postcat.1.html \ postconf.1.html postfix.1.html postkick.1.html postlock.1.html \ - postlog.1.html postdrop.1.html postmap.1.html sendmail.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 @@ -86,6 +87,9 @@ postlog.1.html: ../postlog/postlog.c postmap.1.html: ../postmap/postmap.c srctoman $? | nroff -man | man2html | postlink >$@ +postsuper.1.html: ../postsuper/postsuper.c + srctoman $? | nroff -man | man2html | postlink >$@ + sendmail.1.html: ../sendmail/sendmail.c srctoman $? | nroff -man | man2html | postlink >$@ diff --git a/postfix/html/anatomy.html b/postfix/html/anatomy.html deleted file mode 100644 index d3e569d3c..000000000 --- a/postfix/html/anatomy.html +++ /dev/null @@ -1,56 +0,0 @@ - - - - -Postfix Mail System Anatomy - - - - - -

Postfix -Mail System Anatomy

- -
- -Up one level | Postfix -Overview | Postfix Anatomy | Postfix -Configuration | Postfix FAQ - -

Table of contents

- -
    - -
  • Receiving mail. What path a -message takes on its way into the Postfix mail system, and what -programs are involved. Lots of daemons, mostly. - -

    - -

  • Delivering mail. An introduction -to the machinery that is responsible for delivering mail: queues, -a queue manager, and more daemons. - -

    - -

  • Behind the scenes. What happens -behind the scenes while mail is flowing through the Postfix system: -another manager, and still more daemons. - -

    - -

  • Command-line utilities. Auxiliary -programs that people interact with when using the Postfix mail -system. No daemons here. - -
- -
- -Up one level | Postfix -Overview | Postfix Anatomy | Postfix -Configuration | Postfix FAQ - - - - diff --git a/postfix/html/architecture.html b/postfix/html/architecture.html index 82eea2515..33fc45cc1 100644 --- a/postfix/html/architecture.html +++ b/postfix/html/architecture.html @@ -13,7 +13,7 @@ Overview - Global Architecture
-Up one level | Up one level | Introduction | Goals and features | Global architecture | Queue Management | Security @@ -102,7 +102,7 @@ it has been standard practice for years in many places.
-Up one level | Up one level | Introduction | Goals and features | Global architecture | Queue Management | Security diff --git a/postfix/html/backstage.html b/postfix/html/backstage.html index 1cacf5b21..5ce551c04 100644 --- a/postfix/html/backstage.html +++ b/postfix/html/backstage.html @@ -13,7 +13,7 @@ Anatomy - Behind the Scenes
-Up one level | Up one level | Receiving Mail | Delivering Mail | Behind the Scenes | Command-line Utilities @@ -61,7 +61,7 @@ href="mailq.1.html">mailq command.
-Up one level | Up one level | Receiving Mail | Delivering Mail | Behind the Scenes | Command-line Utilities diff --git a/postfix/html/basic.html b/postfix/html/basic.html index 60b195f9f..19635f6f9 100644 --- a/postfix/html/basic.html +++ b/postfix/html/basic.html @@ -12,7 +12,7 @@
-Up one level | Basic Configuration | Up one level | Basic Configuration | UCE Controls | Rate Controls | Resource Controls | Address Manipulation @@ -177,7 +177,7 @@ report only serious problems (resource, software) to postmaster:
-
Example: +
Default:
notify_classes = resource, software @@ -189,9 +189,23 @@ report only serious problems (resource, software) to postmaster:
-
bounce
Inform the postmaster of undeliverable -mail. For privacy reasons, the postmaster receives the message -headers only. +
bounce
Send postmaster copies of undeliverable +mail. If mail is undeliverable, a so-called single bounce message +is sent, with a copy of the message that was not delivered. For +privacy reasons, the postmaster copy of a single bounce message is +truncated after the original message headers. If a single bounce +message is undeliverable, the postmaster receives a double bounce +message with a copy of the entire single bounce message. See also +the luser_relay feature. + +

+ +

2bounce
Send double bounces to the postmaster. + +

+ +

delay
Inform the postmaster of delayed mail. +In this case, the postmaster receives message headers only.

@@ -333,7 +347,7 @@ on the virtual interfaces or you would have a mailer loop.


-Up one level | Basic Configuration | Up one level | Basic Configuration | UCE Controls | Rate Controls | Resource Controls | Address Manipulation diff --git a/postfix/html/big-picture.html b/postfix/html/big-picture.html index 10d89c3b2..5d33e2748 100644 --- a/postfix/html/big-picture.html +++ b/postfix/html/big-picture.html @@ -19,7 +19,7 @@ The figure shows the main Postfix system components, and the main information flows between them. Postfix system components are -introduced in the Postfix anatomy +introduced in the Postfix anatomy documentation.

diff --git a/postfix/html/commands.html b/postfix/html/commands.html index b250e9ccb..0b1e5ce63 100644 --- a/postfix/html/commands.html +++ b/postfix/html/commands.html @@ -13,7 +13,7 @@ Anatomy - Command-line Utilities


-Up one level | Up one level | Receiving Mail | Delivering Mail | Behind the Scenes | Command-line Utilities @@ -89,11 +89,18 @@ Postfix lookup tables such as canonical, virtual and others. It is a cousin of the UNIX makemap command. +

+ +

  • The postsuper command maintains +the Postfix queue. It removes old temporary files, and moves queue +files into the right directory after a change in the hashing depth +of queue directories. This command is run at mail system startup time. +
    -Up one level | Up one level | Receiving Mail | Delivering Mail | Behind the Scenes | Command-line Utilities diff --git a/postfix/html/config.html b/postfix/html/config.html deleted file mode 100644 index 2566ed1d4..000000000 --- a/postfix/html/config.html +++ /dev/null @@ -1,67 +0,0 @@ - - - - - Postfix Mail System Configuration - - - - - -

    Postfix Mail System Configuration

    - -
    - -Up one level | Postfix -Overview | Postfix Anatomy | Postfix -Configuration | Postfix FAQ - -

    - -

    Table of contents

    - -
      - -
    • Basic configuration. All you need -to know about Postfix for the most common types of installation, -assuming that you are already familiar with sendmail aliases and -forwarding. - -

      - -

    • UCE controls. A description of the -Postfix mechanisms to reject unwanted mail, a.k.a. UCE, and to -restrict unauthorized mail relaying. - -

      - -

    • Rate controls. How to achieve -performance without hosing your system or your neighbor's systems, -and how to deal with broken or malicious client programs. - -

      - -

    • - -Resource controls. How to set Postfix -memory and file system resource budgets, and how to deal with -temporary problems. - -

      - -

    • Address manipulation. How to do -address rewriting, message routing, and message transport selection -without ever having to use an address rewriting language. This -section covers aliases and virtual domains as well. - -
    - -
    - -Up one level | Postfix -Overview | Postfix Anatomy | Postfix -Configuration | Postfix FAQ - - - - diff --git a/postfix/html/delivering.html b/postfix/html/delivering.html index 9fc2296e2..12036a192 100644 --- a/postfix/html/delivering.html +++ b/postfix/html/delivering.html @@ -13,7 +13,7 @@ Anatomy - Delivering Mail
    -Up one level | Up one level | Receiving Mail | Delivering Mail | Behind the Scenes | Command-line Utilities @@ -129,7 +129,7 @@ addresses.
    -Up one level | Up one level | Receiving Mail | Delivering Mail | Behind the Scenes | Command-line Utilities diff --git a/postfix/html/faq.html b/postfix/html/faq.html index 3e38aa5bb..512b9701e 100644 --- a/postfix/html/faq.html +++ b/postfix/html/faq.html @@ -14,9 +14,7 @@
    -Up one level | Postfix -Overview | Postfix Anatomy | Postfix Configuration | Postfix FAQ +Up one level | Postfix FAQ

    Table of contents

    @@ -26,6 +24,8 @@ href="config.html">Postfix Configuration | Postfix FAQ
  • Running Postfix on a firewall +
  • Delivering some users locally while sending mail as user@domain +
  • Support for maildir-style mailboxes
  • Using Procmail for local delivery @@ -40,6 +40,10 @@ href="config.html">Postfix Configuration | Postfix FAQ
  • Using UUCP as the default transport +
  • Sending mail to a FAX machine + +
  • Undefined symbols: ___dn_expand, ___res_init etc. +
  • Using DB libraries on Solaris etc. @@ -87,9 +91,54 @@ specify the gateway host itself:

  • If you want to deliver internal mail directly without going through -the intranet mail gateway, you have to specify a routing entry in the -transport table, and you have to -enable transport table lookups. +the intranet mail gateway, there are two possibilities. + +

    + +

      + +
    1. Specify the intranet mail gateway as the fall-back relay for +all mail with an unknown or unreachable destination: + +

      + +

      + +
      + +
      main.cf: + +
      fallback_relay = $mydomain + +
      + +

      + +This assumes that your organization has set up multiple internal +MX hosts for the local domain. + +

      + +If your intranet does not use MX records internally, you have to +specify the gateway host itself: + +

      + +

      + +
      main.cf: + +
      fallback_relay = gateway.my.domain + +
      + +
      + +

      + +

    2. Specify routing information for the local domain in the transport table, and enable transport table lookups.

      @@ -107,6 +156,8 @@ enable transport table lookups.

      my.domain   smtp: +
      .my.domain   smtp: +
  • @@ -116,6 +167,8 @@ enable transport table lookups. Specify dbm:/etc/postfix/transport if your system uses dbm files instead of db. + +

  • Execute the command postfix reload to make the @@ -187,6 +240,67 @@ that table is ignored for destinations that match $mydestination. That's an implementation error, and it will be removed. +
    + +

    Delivering some users locally while sending mail as user@domain

    + +
      + +
    • In order to send mail as user@domain, edit +/etc/postfix/main.cf and specify that the local domain +is to be appended to addresses that do not have a domain: + +

      + +

      + +
      myorigin = $mydomain + +
      + +

      + +

    • In order to receive some users locally, such as root or +postmaster, + +

      + +

        + +
      • edit /etc/postfix/main.cf and specify a virtual lookup table: + +

        + +

        + +
        virtual_maps = hash:/etc/postfix/virtual + +
        + +

        + +

      • edit /etc/postfix/virtual and specify non-default destinations: + +

        + +

        + +
        root   root@my.host.name + +
        postmaster   postmaster@my.host.name + +
        + +
      + +

      + +

    • Execute the command postmap /etc/postfix/virtual to +update the table, and postfix reload to make the changes +effective. + +
    +

    Support for maildir-style mailboxes

    @@ -448,6 +562,70 @@ changes effective. +
    + +

    Sending mail to a FAX machine

    + +The following information is by Joerg Henne: + +

    +Over here we are using the scheme @fax.our.domain with Postfix and +HylaFax. Here's the setup used: + +

    +In master.cf: + +

    + +

    +    fax       unix  -       n       n       -       -       pipe
    +	flags= user=fax argv=/usr/bin/faxmail -d -n ${user}
    +
    + +

    + +In the transports map: + +

    + +

    +    fax.your.domain   fax:localhost
    +
    + +

    + +Note: be sure to not advertise fax.your.domain in the DNS... + +


    + +

    Undefined symbols: ___dn_expand, ___res_init etc.

    + +Question: When I build Postfix I get the following errors: + +

    + +

    +    ld: Undefined symbol
    +       ___dn_expand
    +       ___res_init
    +       ___res_search
    +    *** Error code 1
    +
    + +

    + +Answer: you're mixing BIND version 8 include files with a +different version of the resolver library. + +

    + +Fix: use the right include files. For example: + +

    + +

    +    make makefiles CCARGS="-I/usr/include".
    +

    @@ -469,16 +647,23 @@ version which has a db-1.85 compatible interface.

    -Use the following commands in the Postfix top-level directory: +Use the following commands in the Postfix top-level directory. +The LD_LIBRARY_PATH unsets may be required to avoid linking in the +wrong libraries.

    -
    % make tidy +
    % LD_LIBRARY_PATH=   (Bourne-shell syntax) + +
    % unsetenv LD_LIBRARY_PATH   (C-shell syntax) + +
    % make tidy -
    % make makefiles CCARGS="-DHAS_DB --I/some/where/include" AUXLIBS=/some/where/libdb.a +
    % make makefiles CCARGS="-DHAS_DB -DPATH_DB_H='<db_185.h>' +-I/some/where/include" +AUXLIBS=/some/where/libdb.a -
    % make +
    % make
    @@ -496,9 +681,7 @@ to get rid of the bogus file, or the linker will fail to find
    -Up one level | Postfix -Overview | Postfix Anatomy | Postfix Configuration | Postfix FAQ +Up one level | Postfix FAQ diff --git a/postfix/html/goals.html b/postfix/html/goals.html index ec019edd8..1b8436d52 100644 --- a/postfix/html/goals.html +++ b/postfix/html/goals.html @@ -13,7 +13,7 @@ Overview - Goals and Features
    -Up one level | Up one level | Introduction | Goals and features | Global architecture | Queue Management | -Up one level | Up one level | Introduction | Goals and features | Global architecture | Queue Management | -Postfix Mail System Documentation +Wietse's Postfix Project -

    Postfix Mail System Documentation

    +

    Wietse's Postfix Project


    -

    Table of contents

    +Postfix Overview | Postfix Anatomy | Postfix +Configuration | Postfix FAQ -
      +

      -

    • Postfix Overview. A gentle introduction -to the Postfix mail system. +
      All programmers are optimists -- Frederick P. +Brooks, Jr.

      -

    • Postfix Anatomy. The programs that -make up the Postfix mail system, and how mail flows through it. +First of all, thank you for your interest in the Postfix project.

      -

    • Postfix Configuration. How to configure -the system, from the very basic to the very advanced. +What is Postfix? It is Wietse Venema's attempt to provide an +alternative to the widely-used Sendmail program. Sendmail is +responsible for an estimated 70% of all e-mail delivered on the +Internet. With an estimated 100 million users, that's billions of +messages daily. A stunning number.

      -

    • Postfix FAQ. Look here for quick answers -to common questions. - -
    +Postfix attempts to be fast, easy to administer, and secure, while +at the same time being sendmail compatible enough to not upset +existing users.
    -
      - -
    • About the author. -The author of TCP Wrapper, co-author of SATAN, and other software. - -
    • +Postfix Overview | Postfix Anatomy | Postfix +Configuration | Postfix FAQ diff --git a/postfix/html/local.8.html b/postfix/html/local.8.html index f7b0aebb4..dbc3cffcc 100644 --- a/postfix/html/local.8.html +++ b/postfix/html/local.8.html @@ -77,29 +77,46 @@ LOCAL(8) LOCAL(8) listed in a Delivered-To: header, the message is bounced. MAILBOX DELIVERY - The per-user mailbox is either a file in the default UNIX - mailbox directory (/var/mail/user or /var/spool/mail/user) - or it is a file in the user's home directory with a name - specified via the home_mailbox configuration parameter. - Specify a path name ending in / for qmail-compatible - maildir delivery. Mailbox delivery can be delegated to an - external command specified with the mailbox_command con- - figuration parameter. + The default per-user mailbox is a file in the UNIX mail + spool directory (/var/mail/user or /var/spool/mail/user); + the location can be specified with the mail_spool_direc- + tory configuration parameter. + + Alternatively, the per-user mailbox can be a file in the + user's home directory with a name specified via the + home_mailbox configuration parameter. Specify a relative + path name. Specify a name ending in / for qmail-compatible + maildir delivery. - The local daemon prepends a "From sender time_stamp" enve- - lope header to each message, prepends a Delivered-To: - header with the envelope recipient address, prepends a > - character to lines beginning with "From ", and appends an - empty line. The envelope sender address is available in - the Return-Path: header. The mailbox is locked for exclu- - sive access while delivery is in progress. In case of - problems, an attempt is made to truncate the mailbox to - its original length. + Mailbox delivery can be delegated to an external command + specified with the mailbox_command configuration parame- + ter. The command executes with the privileges of the + recipient user (exception: in case of delivery as root, + the command executes with the privileges of default_user). + + Mailbox delivery can be delegated to alternative message + transports specified in the master.cf file. The mail- + box_transport configuration parameter specifies a message + transport that is to be used for all local recipients, + regardless of whether they are found in the UNIX passwd + database. The fallback_transport parameter specifies a + message transport for recipients that are not found in the + UNIX passwd database. + + In the case of UNIX-style mailbox delivery, the local dae- + mon prepends a "From sender time_stamp" envelope header to + each message, prepends a Delivered-To: header with the + envelope recipient address, prepends a Return-Path: header + with the envelope sender address, prepends a > character + to lines beginning with "From ", and appends an empty + line. The mailbox is locked for exclusive access while + delivery is in progress. In case of problems, an attempt + is made to truncate the mailbox to its original length. In the case of maildir delivery, the local daemon prepends - a Delivered-To: header with the envelope recipient - address. The envelope sender address is available in the - Return-Path: header. + a Delivered-To: header with the envelope recipient address + and prepends a Return-Path: header with the envelope + sender address. EXTERNAL COMMAND DELIVERY The allow_mail_to_commands configuration parameter @@ -107,10 +124,22 @@ LOCAL(8) LOCAL(8) ting (alias, forward) forbids command destinations in :include: files. - The command is executed directly where possible. Assis- - tance by the shell (/bin/sh on UNIX systems) is used only - when the command contains shell magic characters, or when - the command invokes a shell built-in command. + The command is executed directly where possible. + + + + 2 + + + + + +LOCAL(8) LOCAL(8) + + + Assistance by the shell (/bin/sh on UNIX systems) is used + only when the command contains shell magic characters, or + when the command invokes a shell built-in command. A limited amount of command output (standard output and standard error) is captured for inclusion with non-deliv- @@ -125,25 +154,13 @@ LOCAL(8) LOCAL(8) dependent default path, and the TZ (time zone) environment variable is always passed on without change. - - - - 2 - - - - - -LOCAL(8) LOCAL(8) - - The current working directory is the mail queue directory. The local daemon prepends a "From sender time_stamp" enve- lope header to each message, prepends a Delivered-To: - header with the recipient envelope address, and appends an - empty line. The envelope sender address is available in - the Return-Path: header. + header with the recipient envelope address, prepends a + Return-Path: header with the sender envelope address, and + appends an empty line. EXTERNAL FILE DELIVERY The allow_mail_to_files configuration parameter restricts @@ -174,6 +191,18 @@ LOCAL(8) LOCAL(8) For example, with "recipient_delimiter = +", mail for name+foo is delivered to the alias name+foo or to the + + + + 3 + + + + + +LOCAL(8) LOCAL(8) + + alias name, to the destinations listed in ~name/.for- ward+foo or in ~name/.forward, to the mailbox owned by the user name, or it is sent back as undeliverable. @@ -190,19 +219,6 @@ LOCAL(8) LOCAL(8) superuser, delivery is made with the rights specified with the default_privs configuration parameter. - - - - - 3 - - - - - -LOCAL(8) LOCAL(8) - - STANDARDS RFC 822 (ARPA Internet Text Messages) @@ -236,20 +252,24 @@ LOCAL(8) LOCAL(8) alias_maps List of alias databases. - home_mailbox - Pathname of a mailbox relative to a user's home - directory. Specify a path ending in / for maildir- - style delivery. - local_command_shell Shell to use for external command execution (for example, /some/where/smrsh -c). When a shell is specified, it is invoked even when the command con- - tains no shell built-in commands or meta charac- - ters. + tains no shell built-in commands or meta + + + + 4 + + + - mailbox_command - External command to use for mailbox delivery. + +LOCAL(8) LOCAL(8) + + + characters. owner_request_special Give special treatment to owner-xxx and xxx-request @@ -258,93 +278,128 @@ LOCAL(8) LOCAL(8) recipient_delimiter Separator between username and address extension. +Mailbox delivery + fallback_transport + Message transport for recipients that are not found + in the UNIX passwd database. This parameter over- + rides luser_relay. + home_mailbox + Pathname of a mailbox relative to a user's home + directory. Specify a path ending in / for maildir- + style delivery. - 4 - - - + luser_relay + Destination (@domain or address) for non-existent + users. The address can be any destination that is + valid in an alias file. + mail_spool_directory + Directory with UNIX-style mailboxes. The default + pathname is system dependent. -LOCAL(8) LOCAL(8) + mailbox_command + External command to use for mailbox delivery. The + command executes with the recipient privileges + (exception: root). + mailbox_transport + Message transport to use for mailbox delivery to + all local recipients, whether or not they are found + in the UNIX passwd database. This parameter over- + rides all other configuration parameters that con- + trol mailbox delivery, including luser_relay. Locking controls deliver_lock_attempts - Limit the number of attempts to acquire an exclu- + Limit the number of attempts to acquire an exclu- sive lock on a mailbox or external file. deliver_lock_delay - Time in seconds between successive attempts to + Time in seconds between successive attempts to acquire an exclusive lock. stale_lock_time Limit the time after which a stale lock is removed. + + + + + 5 + + + + + +LOCAL(8) LOCAL(8) + + Resource controls command_time_limit - Limit the amount of time for delivery to external + Limit the amount of time for delivery to external command. duplicate_filter_limit - Limit the size of the duplicate filter for results + Limit the size of the duplicate filter for results from alias etc. expansion. line_length_limit - Limit the amount of memory used for processing a + Limit the amount of memory used for processing a partial input line. local_destination_concurrency_limit Limit the number of parallel deliveries to the same - user. The default limit is taken from the + user. The default limit is taken from the default_destination_concurrency_limit parameter. local_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. Security controls allow_mail_to_commands - Restrict the usage of mail delivery to external + Restrict the usage of mail delivery to external command. allow_mail_to_files - Restrict the usage of mail delivery to external + Restrict the usage of mail delivery to external file. default_privs - Default rights for delivery to external file or + Default rights for delivery to external file or command. HISTORY - The Delivered-To: header appears in the qmail system by + The Delivered-To: header appears in the qmail system by Daniel Bernstein. - The maildir structure appears in the qmail system by + The maildir structure appears in the qmail system by Daniel Bernstein. +SEE ALSO + aliases(5) format of alias database + bounce(8) non-delivery status reports + postalias(1) create/update alias database + syslogd(8) system logging + qmgr(8) queue manager +LICENSE + The Secure Mailer license must be distributed with this + software. - 5 + 6 -LOCAL(8) LOCAL(8) -SEE ALSO - aliases(5) format of alias database - bounce(8) non-delivery status reports - postalias(1) create/update alias database - syslogd(8) system logging - qmgr(8) queue manager -LICENSE - The Secure Mailer license must be distributed with this - software. +LOCAL(8) LOCAL(8) + AUTHOR(S) Wietse Venema @@ -392,7 +447,18 @@ LOCAL(8) LOCAL(8) - 6 + + + + + + + + + + + + 7 diff --git a/postfix/html/master.8.html b/postfix/html/master.8.html index 2f7ebabc4..c93f0ce9c 100644 --- a/postfix/html/master.8.html +++ b/postfix/html/master.8.html @@ -139,6 +139,10 @@ MASTER(8) MASTER(8) child process. + service_throttle_time + Time to avoid forking a server that appears to be + broken. + FILES /etc/postfix/main.cf: global configuration file. /etc/postfix/master.cf: master process configuration file. @@ -150,7 +154,7 @@ MASTER(8) MASTER(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) @@ -185,10 +189,6 @@ MASTER(8) MASTER(8) - - - - diff --git a/postfix/html/motivation.html b/postfix/html/motivation.html index 9eccb0923..f27c9569a 100644 --- a/postfix/html/motivation.html +++ b/postfix/html/motivation.html @@ -13,7 +13,7 @@ Overview - Introduction
      -Up one level | Introduction | Up one level | Introduction | Goals and features | Global architecture | Queue Management | -Up one level | Introduction | Up one level | Introduction | Goals and features | Global architecture | Queue Management | - - - -Postfix Mail System Overview - - - - - -

      Postfix -Mail System Overview

      - -
      - -Up one level | Postfix Overview | Postfix Anatomy | Postfix Configuration | Postfix -FAQ - -

      Table of contents

      - -
        - -
      • Introduction. How the Postfix -mail system came into being, and what it attempts to achieve. - -

        - -

      • Goals and features. A summary of the -specific problems that Postfix tries to solve. - -

        - -

      • Global architecture. The global -architecture of the Postfix mail system. - -

        - -

      • Queue management. The Postfix queue -organization, and the strategies that it uses for delivery. - -

        - -

      • Security. Postfix was designed to -just receive and deliver mail, without opening holes for intruders. - -
      - -
      - -Up one level | Postfix Overview | Postfix Anatomy | Postfix Configuration | Postfix -FAQ - - - - diff --git a/postfix/html/pickup.8.html b/postfix/html/pickup.8.html index 1afe00722..bf92b71f8 100644 --- a/postfix/html/pickup.8.html +++ b/postfix/html/pickup.8.html @@ -48,17 +48,17 @@ PICKUP(8) PICKUP(8) command after a configuration change. Miscellaneous + always_bcc + Address to send a copy of each message that enters + the system. + mail_owner - The process privileges used while not opening a + The process privileges used while not opening a maildrop file. queue_directory Top-level directory of the Postfix queue. -SEE ALSO - cleanup(8) message canonicalization - master(8) process manager - syslogd(8) system logging @@ -71,8 +71,13 @@ PICKUP(8) PICKUP(8) PICKUP(8) PICKUP(8) +SEE ALSO + cleanup(8) message canonicalization + master(8) process manager + 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) @@ -117,11 +122,6 @@ PICKUP(8) PICKUP(8) - - - - - diff --git a/postfix/html/pipe.8.html b/postfix/html/pipe.8.html index c5eb2c0ad..2e9f14f62 100644 --- a/postfix/html/pipe.8.html +++ b/postfix/html/pipe.8.html @@ -30,7 +30,7 @@ PIPE(8) PIPE(8) file at the end of a service definition. The syntax is as follows: - flags=F> (optional) + flags=FR> (optional) Optional message processing flags. By default, a message is copied unchanged. @@ -40,25 +40,25 @@ PIPE(8) PIPE(8) F flag also causes an empty line to be appended to the message. - > Prepend > to lines starting with "From ". - This expected by, for example, UUCP soft- + R Prepend a Return-Path: message header with + the envelope sender address. + + > Prepend > to lines starting with "From ". + This is expected by, for example, UUCP soft- ware. user=username (required) + + user=username:groupname The external command is executed with the rights of - the specified username. The software refuses to - execute commands with root privileges, or with the - privileges of the mail system owner. + the specified username. The software refuses to + execute commands with root privileges, or with the + privileges of the mail system owner. If groupname + is specified, the corresponding group ID is used + instead of the group ID of of username. argv=command... (required) - The command to be executed. This must be specified - as the last command attribute. The command is exe- - cuted directly, i.e. without interpretation of - shell meta characters by a shell command inter- - preter. - - In the command argument vector, the following - macros are recognized and replaced with + The command to be executed. This must be specified @@ -71,24 +71,31 @@ PIPE(8) PIPE(8) PIPE(8) PIPE(8) - corresponding information from the Postfix queue - manager delivery request: + as the last command attribute. The command is exe- + cuted directly, i.e. without interpretation of + shell meta characters by a shell command inter- + preter. + + In the command argument vector, the following + macros are recognized and replaced with correspond- + ing information from the Postfix queue manager + delivery request: ${extension} - This macro expands to the extension part of - a recipient address. For example, with an + This macro expands to the extension part of + a recipient address. For example, with an address user+foo@domain the extension is - foo. A command-line argument that contains - ${extension} expands into as many command- + foo. A command-line argument that contains + ${extension} expands into as many command- line arguments as there are recipients. ${mailbox} - This macro expands to the complete local - part of a recipient address. For example, - with an address user+foo@domain the mailbox - is user+foo. A command-line argument that - contains ${mailbox} expands into as many - command-line arguments as there are recipi- + This macro expands to the complete local + part of a recipient address. For example, + with an address user+foo@domain the mailbox + is user+foo. A command-line argument that + contains ${mailbox} expands into as many + command-line arguments as there are recipi- ents. ${nexthop} @@ -96,35 +103,28 @@ PIPE(8) PIPE(8) ${recipient} This macro expands to the complete recipient - address. A command-line argument that con- + address. A command-line argument that con- tains ${recipient} expands into as many com- mand-line arguments as there are recipients. ${sender} - This macro expands to the envelope sender + This macro expands to the envelope sender address. ${user} This macro expands to the username part of a - recipient address. For example, with an + recipient address. For example, with an address user+foo@domain the username part is user. A command-line argument that contains - ${user} expands into as many command-line + ${user} expands into as many command-line arguments as there are recipients. - In addition to the form ${name}, the forms $name and - $(name) are also recognized. Specify $$ where a single $ + In addition to the form ${name}, the forms $name and + $(name) are also recognized. Specify $$ where a single $ is wanted. DIAGNOSTICS - Command exit status codes are expected to follow the con- - ventions defined in <sysexits.h>. - - Problems and transactions are logged to syslogd(8). Cor- - rupted message files are marked so that the queue manager - can move them to the corrupt queue for further inspection. - - + Command exit status codes are expected to follow the @@ -137,46 +137,52 @@ PIPE(8) PIPE(8) PIPE(8) PIPE(8) + conventions defined in <sysexits.h>. + + Problems and transactions are logged to syslogd(8). Cor- + rupted message files are marked so that the queue manager + can move them to the corrupt queue for further inspection. + SECURITY - This program needs a dual personality 1) to access the - private Postfix queue and IPC mechanisms, and 2) to exe- + This program needs a dual personality 1) to access the + private Postfix queue and IPC mechanisms, and 2) to exe- cute external commands as the specified user. It is there- fore security sensitive. CONFIGURATION PARAMETERS - The following main.cf parameters are especially relevant - to this program. See the Postfix main.cf file for syntax - details and for default values. Use the postfix reload + The following main.cf parameters are especially relevant + to this program. See the Postfix main.cf file for syntax + details and for default values. Use the postfix reload command after a configuration change. Miscellaneous mail_owner - The process privileges used while not running an + The process privileges used while not running an external command. Resource controls - In the text below, transport is the first field in a mas- + In the text below, transport is the first field in a mas- ter.cf entry. transport_destination_concurrency_limit Limit the number of parallel deliveries to the same - destination, for delivery via the named transport. - The default limit is taken from the default_desti- - nation_concurrency_limit parameter. The limit is + destination, for delivery via the named transport. + The default limit is taken from the default_desti- + nation_concurrency_limit parameter. The limit is enforced by the Postfix queue manager. transport_destination_recipient_limit - Limit the number of recipients per message deliv- - ery, for delivery via the named transport. The - default limit is taken from the default_destina- - tion_recipient_limit parameter. The limit is + Limit the number of recipients per message deliv- + ery, for delivery via the named transport. The + default limit is taken from the default_destina- + tion_recipient_limit parameter. The limit is enforced by the Postfix queue manager. transport_time_limit - Limit the time for delivery to external command, - for delivery via the named transport. The default - limit is taken from the command_time_limit parame- - ter. The limit is enforced by the Postfix queue + Limit the time for delivery to external command, + for delivery via the named transport. The default + limit is taken from the command_time_limit parame- + ter. The limit is enforced by the Postfix queue manager. SEE ALSO @@ -185,12 +191,6 @@ PIPE(8) PIPE(8) qmgr(8) queue manager syslogd(8) system logging -LICENSE - The Secure Mailer license must be distributed with this - software. - -AUTHOR(S) - Wietse Venema @@ -203,6 +203,12 @@ PIPE(8) PIPE(8) PIPE(8) PIPE(8) +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 @@ -247,12 +253,6 @@ PIPE(8) PIPE(8) - - - - - - diff --git a/postfix/html/postconf.1.html b/postfix/html/postconf.1.html index 29cfc8db0..84f33515b 100644 --- a/postfix/html/postconf.1.html +++ b/postfix/html/postconf.1.html @@ -9,17 +9,20 @@ POSTCONF(1) POSTCONF(1) postconf - Postfix configuration utility SYNOPSIS - postconf [-d] [-n] [-v] [parameter ...] + postconf [-d] [-h] [-n] [-v] [parameter ...] DESCRIPTION The postconf command prints the actual value of parameter - (all known parameters by default). + (all known parameters by default), one parameter per line. Options: - -d Print default parameter settings instead of actual + -d Print default parameter settings instead of actual settings. + -h Show parameter values only, not the name = informa- + tion that normally precedes the value. + -n Print non-default parameter settings only. -v Enable verbose mode for debugging purposes. Multi- @@ -55,9 +58,6 @@ POSTCONF(1) POSTCONF(1) - - - diff --git a/postfix/html/postdrop.1.html b/postfix/html/postdrop.1.html index 38855b4a9..a994ce37b 100644 --- a/postfix/html/postdrop.1.html +++ b/postfix/html/postdrop.1.html @@ -21,7 +21,7 @@ POSTDROP(1) POSTDROP(1) The postdrop command is automatically invoked by the send- mail(1) mail posting agent when the maildrop queue direc- - tory is not writable. + tory is not world-writable. Options: diff --git a/postfix/html/postsuper.1.html b/postfix/html/postsuper.1.html new file mode 100644 index 000000000..feddac517 --- /dev/null +++ b/postfix/html/postsuper.1.html @@ -0,0 +1,134 @@ +
      +
      +
      +
      +POSTSUPER(1)                                         POSTSUPER(1)
      +
      +
      +NAME
      +       postsuper - Postfix super intendent
      +
      +SYNOPSIS
      +       postsuper [-p] [-s] [-v] [directory ...]
      +
      +DESCRIPTION
      +       The  postsuper  command does small maintenance jobs on the
      +       named Postfix queue directories (default: all).  Directory
      +       names  are  relative to the Postfix top-level queue direc-
      +       tory.
      +
      +       By default, postsuper performs  the  operations  requested
      +       with the -s and -p command-line options.  postsuper always
      +       tries to remove objects that are neither files nor  direc-
      +       tories.   Use  of this command is restricted to the super-
      +       user.
      +
      +       Options:
      +
      +       -s     Structure check.  Move queue files that are in  the
      +              wrong place in the file system hierarchy and remove
      +              subdirectories that  are  no  longer  needed.  File
      +              rearrangements  are necessary after a change in the
      +              hash_queue_names and/or hash_queue_depth configura-
      +              tion  parameters.  It  is highly recommended to run
      +              this check once before Postfix startup.
      +
      +       -p     Purge stale files (files that are left  over  after
      +              system or software crashes).
      +
      +       -v     Enable  verbose mode for debugging purposes. Multi-
      +              ple -v options make the software increasingly  ver-
      +              bose.
      +
      +DIAGNOSTICS
      +       Problems  are reported to the standard error stream and to
      +       syslogd.
      +
      +CONFIGURATION PARAMETERS
      +       See the Postfix main.cf file for syntax  details  and  for
      +       default values.
      +
      +       hash_queue_depth
      +              Number of subdirectory levels for hashed queues.
      +
      +       hash_queue_names
      +              The  names of queues that are organized into multi-
      +              ple levels of subdirectories.
      +
      +LICENSE
      +       The Secure Mailer license must be  distributed  with  this
      +       software.
      +
      +
      +
      +
      +                                                                1
      +
      +
      +
      +
      +
      +POSTSUPER(1)                                         POSTSUPER(1)
      +
      +
      +AUTHOR(S)
      +       Wietse Venema
      +       IBM T.J. Watson Research
      +       P.O. Box 704
      +       Yorktown Heights, NY 10598, USA
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +                                                                2
      +
      +
      +
      diff --git a/postfix/html/qmgr.8.html b/postfix/html/qmgr.8.html index d9f0bac43..3769b015e 100644 --- a/postfix/html/qmgr.8.html +++ b/postfix/html/qmgr.8.html @@ -98,13 +98,11 @@ QMGR(8) QMGR(8) by slowly adjusting the number of parallel deliver- ies to the same destination. - round robin and random walk + round robin The queue manager sorts delivery requests by desti- nation. Round-robin selection prevents one desti- nation from dominating deliveries to other destina- - tions. Random walk prevents one problematic mes- - sage from blocking deliveries of other mail to the - same destination. + tions. exponential backoff Mail that cannot be delivered upon the first @@ -124,7 +122,9 @@ QMGR(8) QMGR(8) actions (the message is followed by the symbolic constant used internally by the software): - + D (QMGR_REQ_SCAN_DEFERRED) + Start a deferred queue scan. If a deferred queue + scan is already in progress, that scan will be @@ -137,9 +137,6 @@ QMGR(8) QMGR(8) QMGR(8) QMGR(8) - D (QMGR_REQ_SCAN_DEFERRED) - Start a deferred queue scan. If a deferred queue - scan is already in progress, that scan will be restarted as soon as it finishes. I (QMGR_REQ_SCAN_INCOMING) @@ -191,6 +188,9 @@ QMGR(8) QMGR(8) BUGS A single queue manager process has to compete for disk access with multiple front-end processes such as smtpd. A + sudden burst of inbound mail can negatively impact out- + bound delivery rates. + @@ -203,9 +203,6 @@ QMGR(8) QMGR(8) QMGR(8) QMGR(8) - sudden burst of inbound mail can negatively impact out- - bound delivery rates. - CONFIGURATION PARAMETERS The following main.cf parameters are especially relevant to this program. See the Postfix main.cf file for syntax @@ -256,7 +253,10 @@ QMGR(8) QMGR(8) ken delivery transport. Concurrency controls - In the text below, transport is the first field in a + In the text below, transport is the first field in a mas- + ter.cf entry. + + @@ -269,8 +269,6 @@ QMGR(8) QMGR(8) QMGR(8) QMGR(8) - master.cf entry. - initial_destination_concurrency Initial per-destination concurrency level for par- allel delivery to the same destination. @@ -326,6 +324,8 @@ QMGR(8) QMGR(8) + + 5 diff --git a/postfix/html/queuing.html b/postfix/html/queuing.html index fe12f6165..13652fb2c 100644 --- a/postfix/html/queuing.html +++ b/postfix/html/queuing.html @@ -13,7 +13,7 @@ Postfix Overview - Queue Management
      -Up one level | Up one level | Introduction | Goals and features | Global architecture | Queue Management | Security @@ -64,17 +64,15 @@ start algorithm

      Fairness

      Apart from the thundering herd controls, the Postfix delivery -strategy is based on round-robin selection and random -walks. The queue manager sorts message recipients in the active -queue by destination, makes round-robin walks along all -destination queues, and makes random walks within each -destination queue. +strategy is based on round-robin selection. The queue +manager sorts message recipients in the active queue by destination, +and makes round-robin walks along all destination queues.

      On the average, Postfix will do simultaneous deliveries to the same domain only when there is not enough work to keep all outbound SMTP -channels busy. So, when AOL goes off-line and comes back, it will +channels busy. So, when AOL goes off-line and comes back, it should not stop the system from delivering to other sites.

      @@ -108,7 +106,7 @@ mail backlog.


      -Up one level | Up one level | Introduction | Goals and features | Global architecture | Queue Management | Security diff --git a/postfix/html/rate.html b/postfix/html/rate.html index da7d46f0c..576c06560 100644 --- a/postfix/html/rate.html +++ b/postfix/html/rate.html @@ -14,7 +14,7 @@
      -Up one level | +Up one level | Basic Configuration | UCE Controls | Rate Controls | Resource Controls | Address Manipulation @@ -395,7 +395,7 @@ That's of course no excuse. I'm still looking for a good solution.
      -Up one level | +Up one level | Basic Configuration | UCE Controls | Rate Controls | Resource Controls | Address Manipulation diff --git a/postfix/html/receiving.html b/postfix/html/receiving.html index 89dafba81..5168e6434 100644 --- a/postfix/html/receiving.html +++ b/postfix/html/receiving.html @@ -13,7 +13,7 @@ Anatomy - Receiving Mail
      -Up one level | Receiving Mail | Up one level | Receiving Mail | Delivering Mail | Behind the Scenes | Command-line Utilities @@ -107,7 +107,7 @@ href="rewrite.html">table lookup.
      -Up one level | Receiving Mail | Up one level | Receiving Mail | Delivering Mail | Behind the Scenes | Command-line Utilities diff --git a/postfix/html/resource.html b/postfix/html/resource.html index 386432a91..cabadc150 100644 --- a/postfix/html/resource.html +++ b/postfix/html/resource.html @@ -12,7 +12,7 @@
      -Up one level | Basic +Up one level | Basic Configuration | UCE Controls | Rate Controls | Resource Controls | Address Manipulation @@ -135,7 +135,7 @@ href="rate.html#backoff">delivery rate control documentation.
      An upper limit on the number of messages in the active queue. For an introduction to the Postfix queue organization see -the Postfix overview documentation. +the Postfix overview documentation.

      @@ -267,7 +267,7 @@ an apparently defunct Postfix delivery service.


      -Up one level | Basic +Up one level | Basic Configuration | UCE Controls | Rate Controls | Resource Controls | Address Manipulation diff --git a/postfix/html/rewrite.html b/postfix/html/rewrite.html index 483807576..4673d0409 100644 --- a/postfix/html/rewrite.html +++ b/postfix/html/rewrite.html @@ -12,7 +12,7 @@
      -Up one level | +Up one level | Basic Configuration | UCE Controls | Rate Controls | Resource Controls | Address Manipulation @@ -73,6 +73,14 @@ Local delivery:
    • Alias database +

      + +

    • Per-user .forward files + +

      + +

    • Non-existent users +

    Rewrite addresses to standard form

    @@ -392,9 +400,68 @@ are performed with the rights of the alias database owner. A default userid, default_privs, is used for deliveries to commands/files in root-owned aliases. +

    Per-user .forward files

    + +Users can control their own mail delivery by specifying destinations +in a file called .forward in their home directories. The +syntax of these files is the same as with system aliases, except +that the lookup key and colon are not present. + +

    Non-existent users

    + +When the local delivery agent finds that a message recipient does +not exist, the message is normally bounced to the sender ("user +unknown"). Sometimes it is desirable to forward mail for non-existing +recipients to another machine. For this purpose you can specify +an alternative destination with the luser_relay configuration +parameter. + +

    + +Alternatively, mail for non-existent recipients can be delegated +to an entirely different message transport, as specified with the +fallback_transport configuration parameter. For details, +see the local delivery agent. + +

    + +luser_relay can specify any number of destinations that are +valid in an alias file. In fact, the same restrictions for command +and file destinations apply as for true aliases. + +

    + +In addition, some luser_relay destinations can receive +special treatment: + +

    + +

    + +
    luser_relay = @some.where.else + +
    The entire original recipient localpart is prepended. For +example, mail for unknown+foo is sent to +unknown+foo@some.where.else. + +

    + +

    luser_relay = someone@some.where.else +
    luser_relay = someone + +
    If no recipient_delimiter has been specified, mail is +sent to the luser_relay address. If the recipient_delimiter +has been specified, the entire original recipient localpart is +appended as an address extension to the luser_relay address. +For example, with recipient_delimiter = +, mail for +unknown+foo is sent to someone+unknown+foo@some.where.else +and someone+unknown+foo, respectively. + +
    +
    -Up one level | +Up one level | Basic Configuration | UCE Controls | Rate Controls | Resource Controls | Address Manipulation diff --git a/postfix/html/security.html b/postfix/html/security.html index 63530cecb..3dfcc7962 100644 --- a/postfix/html/security.html +++ b/postfix/html/security.html @@ -13,7 +13,7 @@ Overview - Security
    -Up one level | Up one level | Introduction | Goals and features | Global architecture | Queue Management | Security @@ -170,7 +170,7 @@ to prevent runaway conditions that only make problems worse.
    -Up one level | Up one level | Introduction | Goals and features | Global architecture | Queue Management | Security diff --git a/postfix/html/sendmail.1.html b/postfix/html/sendmail.1.html index 143cd03c5..421cb3141 100644 --- a/postfix/html/sendmail.1.html +++ b/postfix/html/sendmail.1.html @@ -25,10 +25,10 @@ SENDMAIL(1) SENDMAIL(1) By default, sendmail reads a message from standard input and arranges for delivery. sendmail attempts to create a - queue file in the maildrop directory. If the process has - no write permission, the message is piped through the - postdrop(1) command, which is expected to execute with - suitable privileges. + queue file in the maildrop directory. If that directory is + not world-writable, the message is piped through the post- + drop(1) command, which is expected to execute with suit- + able privileges. Specific command aliases are provided for other common modes of operation: @@ -165,32 +165,32 @@ SENDMAIL(1) SENDMAIL(1) Set option x to value. Use the equivalent configu- ration parameter in main.cf instead. - -q Flush the mail queue. This is implemented by kick- + -r sender + Set the envelope sender address. This is the + address where delivery problems are sent to, unless + the message contains an Errors-To: message header. + + -q Flush the mail queue. This is implemented by kick- ing the qmgr(8) daemon. -qinterval (ignored) - The interval between queue runs. Use the + The interval between queue runs. Use the queue_run_delay configuration parameter instead. - -t Extract recipients from message headers. This - requires that no recipients be specified on the + -t Extract recipients from message headers. This + requires that no recipients be specified on the command line. -v Enable verbose logging for debugging purposes. Mul- - tiple -v options make the software increasingly + tiple -v options make the software increasingly verbose. SECURITY - By design, this program is not set-user (or group) id. - However, it must handle data from untrusted users or - untrusted machines. Thus, the usual precautions need to + By design, this program is not set-user (or group) id. + However, it must handle data from untrusted users or + untrusted machines. Thus, the usual precautions need to be taken against malicious inputs. -DIAGNOSTICS - Problems are logged to syslogd(8) and to the standard - error stream. - - @@ -203,6 +203,10 @@ SENDMAIL(1) SENDMAIL(1) SENDMAIL(1) SENDMAIL(1) +DIAGNOSTICS + Problems are logged to syslogd(8) and to the standard + error stream. + ENVIRONMENT MAIL_CONFIG Directory with Postfix configuration files. @@ -212,7 +216,7 @@ SENDMAIL(1) SENDMAIL(1) MAIL_DEBUG Enable debugging with an external command, as spec- - ified with the debugger_command configuration + ified with the debugger_command configuration parameter. FILES @@ -220,13 +224,13 @@ SENDMAIL(1) SENDMAIL(1) /etc/postfix, configuration files CONFIGURATION PARAMETERS - See the Postfix main.cf file for syntax details and for - default values. Use the postfix reload command after a + See the Postfix main.cf file for syntax details and for + default values. Use the postfix reload command after a configuration change. alias_database - Default alias database(s) for newaliases. The - default value for this parameter is system-spe- + Default alias database(s) for newaliases. The + default value for this parameter is system-spe- cific. bounce_size_limit @@ -242,20 +246,16 @@ SENDMAIL(1) SENDMAIL(1) initialized. debug_peer_level - Increment in verbose logging level when a remote + Increment in verbose logging level when a remote host matches a pattern in the debug_peer_list parameter. debug_peer_list - List of domain or network patterns. When a remote - host matches a pattern, increase the verbose log- - ging level by the amount specified in the + List of domain or network patterns. When a remote + host matches a pattern, increase the verbose log- + ging level by the amount specified in the debug_peer_level parameter. - fork_attempts - Number of attempts to fork() a process before giv- - ing up. - @@ -269,32 +269,36 @@ SENDMAIL(1) SENDMAIL(1) SENDMAIL(1) SENDMAIL(1) + fork_attempts + Number of attempts to fork() a process before giv- + ing up. + fork_delay - Delay in seconds between successive fork() + Delay in seconds between successive fork() attempts. hopcount_limit Limit the number of Received: message headers. mail_owner - The owner of the mail queue and of most Postfix + The owner of the mail queue and of most Postfix processes. command_directory - Directory with Postfix support commands (default: + Directory with Postfix support commands (default: $program_directory). daemon_directory - Directory with Postfix daemon programs (default: + Directory with Postfix daemon programs (default: $program_directory). queue_directory - Top-level directory of the Postfix queue. This is + Top-level directory of the Postfix queue. This is also the root directory of Postfix daemons that run chrooted. queue_run_delay - The time between successive scans of the deferred + The time between successive scans of the deferred queue. SEE ALSO @@ -309,7 +313,7 @@ SENDMAIL(1) SENDMAIL(1) 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) @@ -322,10 +326,6 @@ SENDMAIL(1) SENDMAIL(1) - - - - 5 diff --git a/postfix/html/smtp.8.html b/postfix/html/smtp.8.html index d6cef08c7..036a46c70 100644 --- a/postfix/html/smtp.8.html +++ b/postfix/html/smtp.8.html @@ -84,61 +84,79 @@ SMTP(8) SMTP(8) ging level by the amount specified in the debug_peer_level parameter. + fallback_relay + Hosts to hand off mail to if a message destination + is not found or if a destination is unreachable. + + ignore_mx_lookup_error + When a name server fails to respond to an MX query, + search for an A record instead of assuming that the + name server will recover. + inet_interfaces The network interface addresses that this mail sys- - tem receives mail on. When any of those addresses + tem receives mail on. When any of those addresses appears in the list of mail exchangers for a remote - destination, the list is truncated to avoid mail + destination, the list is truncated to avoid mail delivery loops. notify_classes - When this parameter includes the protocol class, - send mail to the postmaster with transcripts of + When this parameter includes the protocol class, + send mail to the postmaster with transcripts of SMTP sessions with protocol errors. + 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. + 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 + + + + + 2 + + + + + +SMTP(8) SMTP(8) + + 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. - - - 2 - - - - - -SMTP(8) SMTP(8) - - 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 @@ -146,7 +164,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 @@ -155,11 +173,11 @@ 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, + Timeout in seconds for sending the QUIT command, and for receiving the server response. SEE ALSO @@ -169,9 +187,22 @@ 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. + + + + + 3 + + + + + +SMTP(8) SMTP(8) + + AUTHOR(S) Wietse Venema IBM T.J. Watson Research @@ -194,7 +225,42 @@ SMTP(8) SMTP(8) - 3 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 4 diff --git a/postfix/html/smtpd.8.html b/postfix/html/smtpd.8.html index 497fd1c20..1f3636dba 100644 --- a/postfix/html/smtpd.8.html +++ b/postfix/html/smtpd.8.html @@ -74,19 +74,23 @@ SMTPD(8) SMTPD(8) command after a configuration change. Miscellaneous + always_bcc + Address to send a copy of each message that enters + the system. + command_directory Location of Postfix support commands (default: $program_directory). debug_peer_level - Increment in verbose logging level when a remote + Increment in verbose logging level when a remote host matches a pattern in the debug_peer_list parameter. debug_peer_list - List of domain or network patterns. When a remote - host matches a pattern, increase the verbose log- - ging level by the amount specified in the + List of domain or network patterns. When a remote + host matches a pattern, increase the verbose log- + ging level by the amount specified in the debug_peer_level parameter. hopcount_limit @@ -95,37 +99,33 @@ SMTPD(8) SMTPD(8) notify_classes List of error classes. Of special interest are: - policy When a client violates any policy, mail a + policy When a client violates any policy, mail a transcript of the entire SMTP session to the postmaster. protocol - When a client violates the SMTP protocol or + When a client violates the SMTP protocol or issues an unimplemented command, mail a transcript of the entire SMTP session to the postmaster. smtpd_banner - Text that follows the 220 status code in the SMTP + Text that follows the 220 status code in the SMTP greeting banner. smtpd_recipient_limit - Restrict the number of recipients that the SMTP + Restrict the number of recipients that the SMTP server accepts per message delivery. smtpd_timeout - Limit the time to send a server response and to + Limit the time to send a server response and to receive a client request. Resource controls line_length_limit - Limit the amount of memory in bytes used for the + Limit the amount of memory in bytes used for the handling of partial input lines. - message_size_limit - Limit the total size in bytes of a message, includ- - ing on-disk storage for envelope information. - 2 @@ -137,9 +137,13 @@ SMTPD(8) SMTPD(8) SMTPD(8) SMTPD(8) + message_size_limit + Limit the total size in bytes of a message, includ- + ing on-disk storage for envelope information. + queue_minfree - Minimal amount of free space in bytes in the queue - file system for the SMTP server to accept any mail + Minimal amount of free space in bytes in the queue + file system for the SMTP server to accept any mail at all. Tarpitting @@ -149,11 +153,11 @@ SMTPD(8) SMTPD(8) smtpd_soft_error_limit When an SMTP client has made this number of errors, - wait error_count seconds before responding to any + wait error_count seconds before responding to any client request. smtpd_hard_error_limit - Disconnect after a client has made this number of + Disconnect after a client has made this number of errors. UCE control restrictions @@ -162,19 +166,19 @@ SMTPD(8) SMTPD(8) tem. smtpd_helo_required - Require that clients introduce themselves at the + Require that clients introduce themselves at the beginning of an SMTP session. smtpd_helo_restrictions - Restrict what client hostnames are allowed in HELO + Restrict what client hostnames are allowed in HELO and EHLO commands. smtpd_sender_restrictions - Restrict what sender addresses are allowed in MAIL + Restrict what sender addresses are allowed in MAIL FROM commands. smtpd_recipient_restrictions - Restrict what recipient addresses are allowed in + Restrict what recipient addresses are allowed in RCPT TO commands. smtpd_etrn_restrictions @@ -182,15 +186,11 @@ SMTPD(8) SMTPD(8) mands, and what clients may issue ETRN commands. maps_rbl_domains - List of DNS domains that publish the addresses of + List of DNS domains that publish the addresses of blacklisted hosts. relay_domains - Restrict what domains or networks this mail system - will relay mail from or to. - -UCE control responses - + Restrict what domains or networks this mail system @@ -203,37 +203,40 @@ SMTPD(8) SMTPD(8) SMTPD(8) SMTPD(8) + will relay mail from or to. + +UCE control responses access_map_reject_code - Server response when a client violates an access + Server response when a client violates an access database restriction. invalid_hostname_reject_code - Server response when a client violates the + Server response when a client violates the reject_invalid_hostname restriction. maps_rbl_reject_code - Server response when a client violates the + Server response when a client violates the maps_rbl_domains restriction. reject_code - Response code when the client matches a reject + Response code when the client matches a reject restriction. relay_domains_reject_code - Server response when a client attempts to violate + Server response when a client attempts to violate the mail relay policy. unknown_address_reject_code - Server response when a client violates the + Server response when a client violates the reject_unknown_address restriction. unknown_client_reject_code - Server response when a client without address to - name mapping violates the reject_unknown_clients + Server response when a client without address to + name mapping violates the reject_unknown_clients restriction. unknown_hostname_reject_code - Server response when a client violates the + Server response when a client violates the reject_unknown_hostname restriction. SEE ALSO @@ -242,7 +245,7 @@ SMTPD(8) SMTPD(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) @@ -257,9 +260,6 @@ SMTPD(8) SMTPD(8) - - - 4 diff --git a/postfix/html/uce.html b/postfix/html/uce.html index b86c7f79f..86c81d8ed 100644 --- a/postfix/html/uce.html +++ b/postfix/html/uce.html @@ -13,7 +13,7 @@ Postfix Configuration - UCE Controls
    -Up one level | Basic +Up one level | Basic Configuration | UCE Controls | Rate Controls | Resource Controls | Address Manipulation @@ -275,6 +275,15 @@ response code to rejected requests (default: 450).

    + + +

    reject_non_fqdn_hostname
    Reject the request when +the hostname in the client HELO (EHLO) command is not in fully-qualified +domain form. The non_fqdn_reject_code specifies the +response code to rejected requests (default: 504). + +

    +

    check_helo_access maptype:mapname @@ -375,12 +384,23 @@ request if the result is anything else. The access_map_reject_code

    + + +

    reject_non_fqdn_sender
    Reject the request when +the address in the client MAIL FROM command is not in fully-qualified +domain form. The non_fqdn_reject_code specifies the +response code to rejected requests (default: 504). + +

    +

    permit_naked_ip_address
    reject_invalid_hostname
    reject_unknown_hostname +
    reject_non_fqdn_hostname +
    check_helo_access maptype:mapname
    See HELO (EHLO) hostname restrictions. @@ -486,8 +506,19 @@ for rejected requests (default: 550).

    + + +

    reject_non_fqdn_recipient
    Reject the request when +the address in the client RCPT TO command is not in fully-qualified +domain form. The non_fqdn_reject_code specifies the +response code to rejected requests (default: 504). + +

    +

    reject_unknown_address +
    reject_non_fqdn_sender +
    check_sender_access maptype:mapname
    See sender address restrictions. @@ -500,6 +531,8 @@ for rejected requests (default: 550).
    reject_unknown_hostname +
    reject_non_fqdn_hostname +
    check_helo_access maptype:mapname
    See HELO (EHLO) hostname restrictions. @@ -670,7 +703,7 @@ appear as part of a client name/address restriction list.
    Default: -
    maps_rbl_domains = rbl.maps.vix.com +
    maps_rbl_domains = rbl.maps.vix.com, dul.maps.vix.com

    @@ -727,7 +760,7 @@ tables listed in $relay_domains.


    -Up one level | Basic +Up one level | Basic Configuration | UCE Controls | Rate Controls | Resource Controls | Address Manipulation diff --git a/postfix/local/.indent.pro b/postfix/local/.indent.pro index e59d396ad..b38eded21 100644 --- a/postfix/local/.indent.pro +++ b/postfix/local/.indent.pro @@ -6,6 +6,7 @@ -TBOUNCE_STAT -TCLEANUP_STATE -TCLIENT_LIST +-TCLNT_STREAM -TCONFIG_BOOL_FN_TABLE -TCONFIG_BOOL_TABLE -TCONFIG_INT_FN_TABLE @@ -25,6 +26,8 @@ -TDICT_NISPLUS -TDICT_NODE -TDICT_OPEN_INFO +-TDICT_PCRE +-TDICT_UNIX -TDNS_FIXED -TDNS_REPLY -TDNS_RR @@ -68,8 +71,14 @@ -TRECIPIENT_LIST -TREC_TYPE_NAME -TRESOLVE_REPLY +-TRESPONSE -TSCAN_DIR +-TSCAN_INFO +-TSCAN_OBJ +-TSESSION -TSINGLE_SERVER +-TSINK_COMMAND +-TSINK_STATE -TSMTPD_STATE -TSMTPD_TOKEN -TSMTP_ADDR diff --git a/postfix/local/Makefile.in b/postfix/local/Makefile.in index 99c60e1ec..487b63d30 100644 --- a/postfix/local/Makefile.in +++ b/postfix/local/Makefile.in @@ -1,10 +1,10 @@ SHELL = /bin/sh SRCS = alias.c command.c delivered.c dotforward.c file.c forward.c \ include.c indirect.c local.c mailbox.c recipient.c resolve.c token.c \ - deliver_attr.c feature.c maildir.c biff_notify.c + deliver_attr.c feature.c maildir.c biff_notify.c unknown.c OBJS = alias.o command.o delivered.o dotforward.o file.o forward.o \ include.o indirect.o local.o mailbox.o recipient.o resolve.o token.o \ - deliver_attr.o feature.o maildir.o biff_notify.o + deliver_attr.o feature.o maildir.o biff_notify.o unknown.o HDRS = local.h TESTSRC = WARN = -W -Wformat -Wimplicit -Wmissing-prototypes \ @@ -277,8 +277,8 @@ mailbox.o: ../include/set_eugid.h mailbox.o: ../include/mail_copy.h mailbox.o: ../include/safe_open.h mailbox.o: ../include/deliver_flock.h -mailbox.o: ../include/bounce.h mailbox.o: ../include/defer.h +mailbox.o: ../include/bounce.h mailbox.o: ../include/sent.h mailbox.o: ../include/mypwd.h mailbox.o: ../include/been_here.h @@ -318,7 +318,6 @@ recipient.o: ../include/dict.h recipient.o: ../include/vstream.h recipient.o: ../include/vbuf.h recipient.o: ../include/bounce.h -recipient.o: ../include/defer.h recipient.o: ../include/mail_params.h recipient.o: ../include/split_addr.h recipient.o: local.h @@ -356,3 +355,18 @@ token.o: ../include/resolve_clnt.h token.o: ../include/mail_params.h token.o: local.h token.o: ../include/been_here.h +unknown.o: unknown.c +unknown.o: ../include/sys_defs.h +unknown.o: ../include/msg.h +unknown.o: ../include/stringops.h +unknown.o: ../include/mymalloc.h +unknown.o: ../include/been_here.h +unknown.o: ../include/mail_params.h +unknown.o: ../include/bounce.h +unknown.o: local.h +unknown.o: ../include/htable.h +unknown.o: ../include/vstream.h +unknown.o: ../include/vbuf.h +unknown.o: ../include/vstring.h +unknown.o: ../include/tok822.h +unknown.o: ../include/resolve_clnt.h diff --git a/postfix/local/alias.c b/postfix/local/alias.c index 15654e6e8..90bfe4f9a 100644 --- a/postfix/local/alias.c +++ b/postfix/local/alias.c @@ -136,7 +136,7 @@ int deliver_alias(LOCAL_STATE state, USER_ATTR usr_attr, int *statusp) */ state.level++; if (msg_verbose) - msg_info("%s[%d]: %s", myname, state.level, state.msg_attr.local); + MSG_LOG_STATE(myname, state); /* * Do this only once. diff --git a/postfix/local/command.c b/postfix/local/command.c index 2ef58fe53..1502a46d2 100644 --- a/postfix/local/command.c +++ b/postfix/local/command.c @@ -82,6 +82,13 @@ int deliver_command(LOCAL_STATE state, USER_ATTR usr_attr, char *command) ARGV *env; int copy_flags; + /* + * Make verbose logging easier to understand. + */ + state.level++; + if (msg_verbose) + MSG_LOG_STATE(myname, state); + /* * DUPLICATE ELIMINATION * @@ -114,7 +121,7 @@ int deliver_command(LOCAL_STATE state, USER_ATTR usr_attr, char *command) /* * Deliver. */ - copy_flags = MAIL_COPY_FROM; + copy_flags = MAIL_COPY_FROM | MAIL_COPY_RETURN_PATH; if ((state.msg_attr.features & FEATURE_NODELIVERED) == 0) copy_flags |= MAIL_COPY_DELIVERED; diff --git a/postfix/local/dotforward.c b/postfix/local/dotforward.c index 5e4cffaec..2c0f2c001 100644 --- a/postfix/local/dotforward.c +++ b/postfix/local/dotforward.c @@ -100,13 +100,14 @@ int deliver_dotforward(LOCAL_STATE state, USER_ATTR usr_attr, int *statusp) int forward_found = NO; int lookup_status; int addr_count; + char *extension; /* * Make verbose logging easier to understand. */ state.level++; if (msg_verbose) - msg_info("%s[%d]: %s", myname, state.level, state.msg_attr.local); + MSG_LOG_STATE(myname, state); /* * DUPLICATE/LOOP ELIMINATION @@ -169,8 +170,8 @@ int deliver_dotforward(LOCAL_STATE state, USER_ATTR usr_attr, int *statusp) state.msg_attr.owner = state.msg_attr.recipient; /* - * Assume that usernames do not have file system meta characters. Open the - * .forward file as the user. Ignore files that aren't regular files, + * Assume that usernames do not have file system meta characters. Open + * the .forward file as the user. Ignore files that aren't regular files, * files that are owned by the wrong user, or files that have world write * permission enabled. We take no special precautions to deal with home * directories imported via NFS, because mailbox and .forward files @@ -185,19 +186,20 @@ int deliver_dotforward(LOCAL_STATE state, USER_ATTR usr_attr, int *statusp) status = 0; path = vstring_alloc(100); - if (state.msg_attr.extension && strchr(state.msg_attr.extension, '/')) { + extension = state.msg_attr.extension; + if (extension && strchr(extension, '/')) { msg_warn("%s: address with illegal extension: %s", state.msg_attr.queue_id, state.msg_attr.recipient); - state.msg_attr.extension = 0; + extension = 0; } - if (state.msg_attr.extension != 0) { + if (extension != 0) { vstring_sprintf(path, "%s/.forward%c%s", mypwd->pw_dir, - var_rcpt_delim[0], state.msg_attr.extension); + var_rcpt_delim[0], extension); if ((lookup_status = lstat_as(STR(path), &st, usr_attr.uid, usr_attr.gid)) < 0) - state.msg_attr.extension = 0; + extension = 0; } - if (state.msg_attr.extension == 0) { + if (extension == 0) { vstring_sprintf(path, "%s/.forward", mypwd->pw_dir); lookup_status = lstat_as(STR(path), &st, usr_attr.uid, usr_attr.gid); } diff --git a/postfix/local/file.c b/postfix/local/file.c index d9ff9592b..3b8dcc449 100644 --- a/postfix/local/file.c +++ b/postfix/local/file.c @@ -79,6 +79,7 @@ int deliver_file(LOCAL_STATE state, USER_ATTR usr_attr, char *path) { + char *myname = "deliver_file"; struct stat st; int fd; VSTREAM *dst; @@ -86,6 +87,13 @@ int deliver_file(LOCAL_STATE state, USER_ATTR usr_attr, char *path) int status; int copy_flags; + /* + * Make verbose logging easier to understand. + */ + state.level++; + if (msg_verbose) + MSG_LOG_STATE(myname, state); + /* * DUPLICATE ELIMINATION * diff --git a/postfix/local/include.c b/postfix/local/include.c index d9f0786bc..38a8d72ad 100644 --- a/postfix/local/include.c +++ b/postfix/local/include.c @@ -88,7 +88,7 @@ int deliver_include(LOCAL_STATE state, USER_ATTR usr_attr, char *path) */ state.level++; if (msg_verbose) - msg_info("%s[%d]: %s", myname, state.level, path); + MSG_LOG_STATE(myname, state); /* * DUPLICATE ELIMINATION diff --git a/postfix/local/local.c b/postfix/local/local.c index 22dcccbe3..1daed8a35 100644 --- a/postfix/local/local.c +++ b/postfix/local/local.c @@ -62,29 +62,44 @@ /* MAILBOX DELIVERY /* .ad /* .fi -/* The per-user mailbox is either a file in the default UNIX mailbox -/* directory (\fB/var/mail/\fIuser\fR or \fB/var/spool/mail/\fIuser\fR) -/* or it is a file in the user's home directory with a name specified -/* via the \fBhome_mailbox\fR configuration parameter. Specify a path -/* name ending in \fB/\fR for \fBqmail\fR-compatible \fBmaildir\fR -/* delivery. +/* The default per-user mailbox is a file in the UNIX mail spool +/* directory (\fB/var/mail/\fIuser\fR or \fB/var/spool/mail/\fIuser\fR); +/* the location can be specified with the \fBmail_spool_directory\fR +/* configuration parameter. +/* +/* Alternatively, the per-user mailbox can be a file in the user's home +/* directory with a name specified via the \fBhome_mailbox\fR +/* configuration parameter. Specify a relative path name. Specify a name +/* ending in \fB/\fR for \fBqmail\fR-compatible \fBmaildir\fR delivery. +/* /* Mailbox delivery can be delegated to an external command specified -/* with the \fBmailbox_command\fR configuration parameter. +/* with the \fBmailbox_command\fR configuration parameter. The command +/* executes with the privileges of the recipient user (exception: in +/* case of delivery as root, the command executes with the privileges +/* of \fBdefault_user\fR). /* -/* The \fBlocal\fR daemon prepends a "\fBFrom \fIsender time_stamp\fR" +/* Mailbox delivery can be delegated to alternative message transports +/* specified in the \fBmaster.cf\fR file. +/* The \fBmailbox_transport\fR configuration parameter specifies a +/* message transport that is to be used for all local recipients, +/* regardless of whether they are found in the UNIX passwd database. +/* The \fBfallback_transport\fR parameter specifies a message transport +/* for recipients that are not found in the UNIX passwd database. +/* +/* In the case of UNIX-style mailbox delivery, +/* the \fBlocal\fR daemon prepends a "\fBFrom \fIsender time_stamp\fR" /* envelope header to each message, prepends a \fBDelivered-To:\fR header -/* with the envelope recipient address, prepends a \fB>\fR character to -/* lines beginning with "\fBFrom \fR", and appends an empty line. -/* The envelope sender address is available in the \fBReturn-Path:\fR -/* header. +/* with the envelope recipient address, prepends a \fBReturn-Path:\fR +/* header with the envelope sender address, prepends a \fB>\fR character +/* to lines beginning with "\fBFrom \fR", and appends an empty line. /* The mailbox is locked for exclusive access while delivery is in /* progress. In case of problems, an attempt is made to truncate the /* mailbox to its original length. /* /* In the case of \fBmaildir\fR delivery, the local daemon prepends -/* a \fBDelivered-To:\fR header with the envelope recipient address. -/* The envelope sender address is available in the \fBReturn-Path:\fR -/* header. +/* a \fBDelivered-To:\fR header with the envelope recipient address +/* and prepends a \fBReturn-Path:\fR header with the envelope sender +/* address. /* EXTERNAL COMMAND DELIVERY /* .ad /* .fi @@ -114,9 +129,9 @@ /* /* The \fBlocal\fR daemon prepends a "\fBFrom \fIsender time_stamp\fR" /* envelope header to each message, prepends a \fBDelivered-To:\fR -/* header with the recipient envelope address, and appends an empty line. -/* The envelope sender address is available in the \fBReturn-Path:\fR -/* header. +/* header with the recipient envelope address, prepends a +/* \fBReturn-Path:\fR header with the sender envelope address, +/* and appends an empty line. /* EXTERNAL FILE DELIVERY /* .ad /* .fi @@ -197,21 +212,41 @@ /* .fi /* .IP \fBalias_maps\fR /* List of alias databases. -/* .IP \fBhome_mailbox\fR -/* Pathname of a mailbox relative to a user's home directory. -/* Specify a path ending in \fB/\fR for maildir-style delivery. /* .IP \fBlocal_command_shell\fR /* Shell to use for external command execution (for example, /* /some/where/smrsh -c). /* When a shell is specified, it is invoked even when the command /* contains no shell built-in commands or meta characters. -/* .IP \fBmailbox_command\fR -/* External command to use for mailbox delivery. /* .IP \fBowner_request_special\fR /* Give special treatment to \fBowner-\fIxxx\fR and \fIxxx\fB-request\fR /* addresses. /* .IP \fBrecipient_delimiter\fR /* Separator between username and address extension. +/* .SH Mailbox delivery +/* .ad +/* .fi +/* .IP \fBfallback_transport\fR +/* Message transport for recipients that are not found in the UNIX +/* passwd database. +/* This parameter overrides \fBluser_relay\fR. +/* .IP \fBhome_mailbox\fR +/* Pathname of a mailbox relative to a user's home directory. +/* Specify a path ending in \fB/\fR for maildir-style delivery. +/* .IP \fBluser_relay\fR +/* Destination (\fI@domain\fR or \fIaddress\fR) for non-existent users. +/* The \fIaddress\fR can be any destination that is valid in an alias +/* file. +/* .IP \fBmail_spool_directory\fR +/* Directory with UNIX-style mailboxes. The default pathname is system +/* dependent. +/* .IP \fBmailbox_command\fR +/* External command to use for mailbox delivery. The command executes +/* with the recipient privileges (exception: root). +/* .IP \fBmailbox_transport\fR +/* Message transport to use for mailbox delivery to all local +/* recipients, whether or not they are found in the UNIX passwd database. +/* This parameter overrides all other configuration parameters that +/* control mailbox delivery, including \fBluser_relay\fR. /* .SH "Locking controls" /* .ad /* .fi @@ -283,6 +318,9 @@ #include #include #include +#ifdef USE_PATHS_H +#include +#endif /* Utility library. */ @@ -305,6 +343,7 @@ #include #include #include +#include /* Single server skeleton. */ @@ -326,7 +365,11 @@ char *var_home_mailbox; char *var_mailbox_command; char *var_rcpt_fdelim; char *var_local_cmd_shell; +char *var_luser_relay; int var_biff; +char *var_mail_spool_dir; +char *var_mailbox_transport; +char *var_fallback_transport; int local_cmd_deliver_mask; int local_file_deliver_mask; @@ -376,6 +419,7 @@ static int local_deliver(DELIVER_REQUEST *rqst, char *service) RESET_OWNER_ATTR(state.msg_attr, state.level); RESET_USER_ATTR(usr_attr, state.level); state.loop_info = delivered_init(state.msg_attr); /* delivered-to */ + state.request = rqst; /* * Iterate over each recipient named in the delivery request. When the @@ -480,6 +524,10 @@ int main(int argc, char **argv) VAR_ALLOW_FILES, DEF_ALLOW_FILES, &var_allow_files, 0, 0, VAR_RCPT_FDELIM, DEF_RCPT_FDELIM, &var_rcpt_fdelim, 0, 0, VAR_LOCAL_CMD_SHELL, DEF_LOCAL_CMD_SHELL, &var_local_cmd_shell, 0, 0, + VAR_LUSER_RELAY, DEF_LUSER_RELAY, &var_luser_relay, 0, 0, + VAR_MAIL_SPOOL_DIR, DEF_MAIL_SPOOL_DIR, &var_mail_spool_dir, 0, 0, + VAR_MAILBOX_TRANSP, DEF_MAILBOX_TRANSP, &var_mailbox_transport, 0, 0, + VAR_FALLBACK_TRANSP, DEF_FALLBACK_TRANSP, &var_fallback_transport, 0, 0, 0, }; static CONFIG_BOOL_TABLE bool_table[] = { diff --git a/postfix/local/local.h b/postfix/local/local.h index a20b27852..ee4a88488 100644 --- a/postfix/local/local.h +++ b/postfix/local/local.h @@ -20,6 +20,7 @@ */ #include #include +#include /* * User attributes: these control the privileges for delivery to external @@ -97,6 +98,7 @@ typedef struct LOCAL_STATE { DELIVER_ATTR msg_attr; /* message attributes */ BH_TABLE *dup_filter; /* internal duplicate filter */ HTABLE *loop_info; /* external loop filter */ + DELIVER_REQUEST *request; /* as from queue manager */ } LOCAL_STATE; #define RESET_OWNER_ATTR(msg_attr, level) { \ @@ -122,6 +124,12 @@ typedef struct LOCAL_STATE { #define OPENED_ATTR(attr) attr.queue_id, attr.sender #define COPY_ATTR(attr) attr.sender, attr.delivered, attr.fp +#define MSG_LOG_STATE(m, s) \ + msg_info("%s[%d]: local %s recip %s exten %s deliver %s", m, \ + s.level, s.msg_attr.local, s.msg_attr.recipient, \ + s.msg_attr.extension ? s.msg_attr.extension : "", \ + s.msg_attr.delivered ? s.msg_attr.delivered : "") + /* * "inner" nodes of the delivery graph. */ @@ -138,11 +146,12 @@ extern int deliver_resolve_addr(LOCAL_STATE, USER_ATTR, char *); /* * "leaf" nodes of the delivery graph. */ -extern int deliver_mailbox(LOCAL_STATE, USER_ATTR); +extern int deliver_mailbox(LOCAL_STATE, USER_ATTR, int *); extern int deliver_command(LOCAL_STATE, USER_ATTR, char *); extern int deliver_file(LOCAL_STATE, USER_ATTR, char *); extern int deliver_indirect(LOCAL_STATE); extern int deliver_maildir(LOCAL_STATE, USER_ATTR, char *); +extern int deliver_unknown(LOCAL_STATE, USER_ATTR); /* * Restrictions on delivery to sensitive destinations. diff --git a/postfix/local/mailbox.c b/postfix/local/mailbox.c index 576bca22b..bb7d736b8 100644 --- a/postfix/local/mailbox.c +++ b/postfix/local/mailbox.c @@ -6,9 +6,10 @@ /* SYNOPSIS /* #include "local.h" /* -/* int deliver_mailbox(state, usr_attr) +/* int deliver_mailbox(state, usr_attr, statusp) /* LOCAL_STATE state; /* USER_ATTR usr_attr; +/* int *statusp; /* DESCRIPTION /* deliver_mailbox() delivers to mailbox, with duplicate /* suppression. The default is direct mailbox delivery to @@ -17,6 +18,8 @@ /* and when a \fImailbox_command\fR has been configured, the message /* is piped into the command instead. /* +/* A zero result means that the named user was not found. +/* /* Arguments: /* .IP state /* The attributes that specify the message, recipient and more. @@ -24,8 +27,11 @@ /* A table with the results from expanding aliases or lists. /* .IP usr_attr /* Attributes describing user rights and environment. +/* .IP statusp +/* Delivery status: see below. /* DIAGNOSTICS -/* deliver_mailbox() returns non-zero when delivery should be tried again, +/* The message delivery status is non-zero when delivery should be tried +/* again. /* LICENSE /* .ad /* .fi @@ -44,9 +50,6 @@ #include #include #include -#ifdef USE_PATHS_H -#include -#endif /* Utility library. */ @@ -66,22 +69,27 @@ #ifdef USE_DOT_LOCK #include #endif -#include #include #include #include #include #include +#include /* Application-specific. */ #include "local.h" #include "biff_notify.h" +#define YES 1 +#define NO 0 + /* deliver_mailbox_file - deliver to recipient mailbox */ static int deliver_mailbox_file(LOCAL_STATE state, USER_ATTR usr_attr) { + char *myname = "deliver_mailbox_file"; + char *spool_dir; char *mailbox; VSTRING *why; VSTREAM *dst; @@ -89,9 +97,18 @@ static int deliver_mailbox_file(LOCAL_STATE state, USER_ATTR usr_attr) int copy_flags; VSTRING *biff; long end; + struct stat st; + uid_t spool_uid; + gid_t spool_gid; + uid_t chown_uid; + gid_t chown_gid; + /* + * Make verbose logging easier to understand. + */ + state.level++; if (msg_verbose) - msg_info("deliver_mailbox_file: %s", state.msg_attr.recipient); + MSG_LOG_STATE(myname, state); /* * Initialize. Assume the operation will fail. Set the delivered @@ -102,29 +119,67 @@ static int deliver_mailbox_file(LOCAL_STATE state, USER_ATTR usr_attr) state.msg_attr.delivered = state.msg_attr.recipient; status = -1; why = vstring_alloc(100); - if (*var_home_mailbox) + if (*var_home_mailbox) { + spool_dir = 0; mailbox = concatenate(usr_attr.home, "/", var_home_mailbox, (char *) 0); - else - mailbox = concatenate(_PATH_MAILDIR, "/", state.msg_attr.local, (char *) 0); + } else { + spool_dir = var_mail_spool_dir; + mailbox = concatenate(spool_dir, "/", state.msg_attr.local, (char *) 0); + } + + /* + * Mailbox delivery with least privilege. As long as we do not use root + * privileges this code may also work over NFS. + * + * If delivering to the recipient's home directory, perform all operations + * (including file locking) as that user (Mike Muuss, Army Research + * Laboratory, USA). + * + * If delivering to the mail spool directory, and the spool directory is + * world-writable, deliver as the recipient; if the spool directory is + * group-writable, use the recipient user id and the mail spool group id. + * + * Otherwise, use root privileges and chown the mailbox. + */ + if (spool_dir == 0 + || stat(spool_dir, &st) < 0 + || (st.st_mode & S_IWOTH) != 0) { + spool_uid = usr_attr.uid; + spool_gid = usr_attr.gid; + } else if ((st.st_mode & S_IWGRP) != 0) { + spool_uid = usr_attr.uid; + spool_gid = st.st_gid; + } else { + spool_uid = 0; + spool_gid = 0; + } + if (spool_uid == usr_attr.uid) { + chown_uid = -1; + chown_gid = -1; + } else { + chown_uid = usr_attr.uid; + chown_gid = usr_attr.gid; + } + if (msg_verbose) + msg_info("spool_uid/gid %d/%d chown_uid/gid %d/%d", + spool_uid, spool_gid, chown_uid, chown_gid); /* * Lock the mailbox and open/create the mailbox file. Depending on the * type of locking used, we lock first or we open first. * * Write the file as the recipient, so that file quota work. - * - * Create lock files as root, for non-writable directories. */ copy_flags = MAIL_COPY_MBOX; if (state.msg_attr.features & FEATURE_NODELIVERED) copy_flags &= ~MAIL_COPY_DELIVERED; - set_eugid(0, 0); + set_eugid(spool_uid, spool_gid); #ifdef USE_DOT_LOCK if (dot_lockfile(mailbox, why) >= 0) { #endif dst = safe_open(mailbox, O_APPEND | O_WRONLY | O_CREAT, - S_IRUSR | S_IWUSR, usr_attr.uid, usr_attr.gid, why); + S_IRUSR | S_IWUSR, chown_uid, chown_gid, why); set_eugid(usr_attr.uid, usr_attr.gid); if (dst != 0) { end = vstream_fseek(dst, (off_t) 0, SEEK_END); @@ -143,7 +198,7 @@ static int deliver_mailbox_file(LOCAL_STATE state, USER_ATTR usr_attr) } } #ifdef USE_DOT_LOCK - set_eugid(0, 0); + set_eugid(spool_uid, spool_gid); dot_unlockfile(mailbox); } #endif @@ -161,7 +216,7 @@ static int deliver_mailbox_file(LOCAL_STATE state, USER_ATTR usr_attr) /* deliver_mailbox - deliver to recipient mailbox */ -int deliver_mailbox(LOCAL_STATE state, USER_ATTR usr_attr) +int deliver_mailbox(LOCAL_STATE state, USER_ATTR usr_attr, int *statusp) { char *myname = "deliver_mailbox"; int status; @@ -173,29 +228,30 @@ int deliver_mailbox(LOCAL_STATE state, USER_ATTR usr_attr) */ state.level++; if (msg_verbose) - msg_info("%s[%d]: %s", myname, state.level, state.msg_attr.recipient); - - /* - * Strip quoting that was prepended to defeat alias/forward expansion. - */ - if (state.msg_attr.recipient[0] == '\\') - state.msg_attr.recipient++, state.msg_attr.local++; + MSG_LOG_STATE(myname, state); /* * DUPLICATE ELIMINATION * - * Don't deliver more than once to this mailbox. + * Don't come here more than once, whether or not the recipient exists. */ if (been_here(state.dup_filter, "mailbox %s", state.msg_attr.local)) - return (0); + return (YES); + + /* + * Delegate mailbox delivery to another message transport. + */ + if (*var_mailbox_transport) { + *statusp = deliver_pass(MAIL_CLASS_PRIVATE, var_mailbox_transport, + state.request, state.msg_attr.recipient, -1L); + return (YES); + } /* - * Bounce the message when this recipient does not exist. XXX Should - * quote_822_local() the recipient. + * Skip delivery when this recipient does not exist. */ if ((mbox_pwd = mypwnam(state.msg_attr.local)) == 0) - return (bounce_append(BOUNCE_FLAG_KEEP, BOUNCE_ATTR(state.msg_attr), - "unknown user: \"%s\"", state.msg_attr.local)); + return (NO); /* * No early returns or we have a memory leak. @@ -209,7 +265,7 @@ int deliver_mailbox(LOCAL_STATE state, USER_ATTR usr_attr) SET_USER_ATTR(usr_attr, mbox_pwd, state.level); /* - * Deliver to mailbox or to external delivery agent. + * Deliver to mailbox or to external command. */ #define LAST_CHAR(s) (s[strlen(s) - 1]) @@ -226,5 +282,6 @@ int deliver_mailbox(LOCAL_STATE state, USER_ATTR usr_attr) * Cleanup. */ mypwfree(mbox_pwd); - return (status); + *statusp = status; + return (YES); } diff --git a/postfix/local/maildir.c b/postfix/local/maildir.c index 0cbad5a9b..50d843606 100644 --- a/postfix/local/maildir.c +++ b/postfix/local/maildir.c @@ -69,6 +69,7 @@ int deliver_maildir(LOCAL_STATE state, USER_ATTR usr_attr, char *path) { + char *myname = "deliver_maildir"; char *newdir; char *tmpdir; char *curdir; @@ -81,8 +82,12 @@ int deliver_maildir(LOCAL_STATE state, USER_ATTR usr_attr, char *path) int copy_flags; static int count; + /* + * Make verbose logging easier to understand. + */ + state.level++; if (msg_verbose) - msg_info("deliver_maildir: %s %s", state.msg_attr.recipient, path); + MSG_LOG_STATE(myname, state); /* * Initialize. Assume the operation will fail. Set the delivered @@ -95,7 +100,7 @@ int deliver_maildir(LOCAL_STATE state, USER_ATTR usr_attr, char *path) buf = vstring_alloc(100); why = vstring_alloc(100); - copy_flags = MAIL_COPY_TOFILE; + copy_flags = MAIL_COPY_TOFILE | MAIL_COPY_RETURN_PATH; if ((state.msg_attr.features & FEATURE_NODELIVERED) == 0) copy_flags |= MAIL_COPY_DELIVERED; @@ -133,7 +138,7 @@ int deliver_maildir(LOCAL_STATE state, USER_ATTR usr_attr, char *path) if (mail_copy(COPY_ATTR(state.msg_attr), dst, copy_flags, why) == 0) { if (link(tmpfile, newfile) < 0 && (errno != ENOENT - || (make_dirs(curdir,0700), make_dirs(newdir, 0700)) < 0 + || (make_dirs(curdir, 0700), make_dirs(newdir, 0700)) < 0 || link(tmpfile, newfile) < 0)) { vstring_sprintf(why, "link to %s: %m", newfile); } else { diff --git a/postfix/local/recipient.c b/postfix/local/recipient.c index fd68fc1a3..8456b72fd 100644 --- a/postfix/local/recipient.c +++ b/postfix/local/recipient.c @@ -79,7 +79,6 @@ /* Global library. */ #include -#include #include #include @@ -91,13 +90,29 @@ static int deliver_switch(LOCAL_STATE state, USER_ATTR usr_attr) { + char *myname = "deliver_switch"; int status; + /* + * Make verbose logging easier to understand. + */ + state.level++; + if (msg_verbose) + MSG_LOG_STATE(myname, state); + + /* * \user is special: it means don't do any alias or forward expansion. */ - if (state.msg_attr.recipient[0] == '\\') - return (deliver_mailbox(state, usr_attr)); + if (state.msg_attr.recipient[0] == '\\') { + state.msg_attr.recipient++; + if (*var_rcpt_delim) + state.msg_attr.extension = + split_addr(state.msg_attr.local, *var_rcpt_delim); + if (deliver_mailbox(state, usr_attr, &status) == 0) + status = deliver_unknown(state, usr_attr); + return (status); + } /* * Otherwise, alias expansion has highest precedence. @@ -137,10 +152,11 @@ static int deliver_switch(LOCAL_STATE state, USER_ATTR usr_attr) /* * Delivery to local user. First try expansion of the recipient's - * $HOME/.forward file. Do mailbox delivery as a last resort. + * $HOME/.forward file, then mailbox delivery. */ - if (deliver_dotforward(state, usr_attr, &status) == 0) - status = deliver_mailbox(state, usr_attr); + if (deliver_dotforward(state, usr_attr, &status) == 0 + && deliver_mailbox(state, usr_attr, &status) == 0) + status = deliver_unknown(state, usr_attr); return (status); } @@ -148,10 +164,15 @@ static int deliver_switch(LOCAL_STATE state, USER_ATTR usr_attr) int deliver_recipient(LOCAL_STATE state, USER_ATTR usr_attr) { + char *myname = "deliver_recipient"; int rcpt_stat; + /* + * Make verbose logging easier to understand. + */ + state.level++; if (msg_verbose) - msg_info("deliver_recipient: %s", state.msg_attr.recipient); + MSG_LOG_STATE(myname, state); /* * With each level of recursion, detect and break external message @@ -173,6 +194,7 @@ int deliver_recipient(LOCAL_STATE state, USER_ATTR usr_attr) msg_warn("no @ in recipient address: %s", state.msg_attr.local); lowercase(state.msg_attr.local); state.msg_attr.features = feature_control(state.msg_attr.local); + state.msg_attr.extension = 0; /* * Run the recipient through the delivery switch. diff --git a/postfix/local/resolve.c b/postfix/local/resolve.c index d76824904..2e0f339da 100644 --- a/postfix/local/resolve.c +++ b/postfix/local/resolve.c @@ -86,11 +86,19 @@ int deliver_resolve_addr(LOCAL_STATE state, USER_ATTR usr_attr, char *addr) int deliver_resolve_tree(LOCAL_STATE state, USER_ATTR usr_attr, TOK822 *addr) { + char *myname = "deliver_resolve_tree"; RESOLVE_REPLY reply; int status; int ext_len; char *ratsign; + /* + * Make verbose logging easier to understand. + */ + state.level++; + if (msg_verbose) + MSG_LOG_STATE(myname, state); + /* * Initialize. */ diff --git a/postfix/local/unknown.c b/postfix/local/unknown.c new file mode 100644 index 000000000..0689289a1 --- /dev/null +++ b/postfix/local/unknown.c @@ -0,0 +1,182 @@ +/*++ +/* NAME +/* unknown 3 +/* SUMMARY +/* delivery of unknown recipients +/* SYNOPSIS +/* #include "local.h" +/* +/* int deliver_unknown(state, usr_attr) +/* LOCAL_STATE state; +/* USER_ATTR usr_attr; +/* DESCRIPTION +/* deliver_unknown() delivers a message for unknown recipients. +/* .IP \(bu +/* If an alternative message transport is specified via the +/* fallback_transport parameter, delivery is delegated to the +/* named transport. +/* .IP \(bu +/* If an alternative address is specified via the luser_relay +/* configuration parameter, mail is forwarded to that address. +/* .IP \(bu +/* Otherwise the recipient is bounced. +/* .PP +/* If the luser_relay parameter specifies a @domain, the entire +/* original recipient localpart is prepended. For example: with +/* "luser_relay = @some.where", unknown+foo becomes +/* unknown+foo@some.where. +/* +/* Otherwise, the luser_relay parameter can specify any number of +/* destinations that are valid in an alias file or in a .forward file. +/* For example, a destination could be an address, a "|command" or +/* a /file/name. The luser_relay feature is treated as an alias, and +/* the usual restrictions for command and file destinations apply. +/* +/* If the luser_relay destination is a mail address, and the +/* recipient delimiter has been defined, the entire original recipient +/* localpart is appended as an address extension. For example: with +/* "luser_relay = someone@some.where", unknown+foo becomes +/* someone+unknown+foo@some.where. +/* +/* Arguments: +/* .IP state +/* Message delivery attributes (sender, recipient etc.). +/* Attributes describing alias, include or forward expansion. +/* A table with the results from expanding aliases or lists. +/* A table with delivered-to: addresses taken from the message. +/* .IP usr_attr +/* Attributes describing user rights and environment. +/* DIAGNOSTICS +/* The result status is non-zero when delivery should be tried again. +/* 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 +#include + +/* Global library. */ + +#include +#include +#include +#include + +/* Application-specific. */ + +#include "local.h" + +/* deliver_unknown - delivery for unknown recipients */ + +int deliver_unknown(LOCAL_STATE state, USER_ATTR usr_attr) +{ + char *myname = "deliver_unknown"; + int status; + char *dest; + char *saved_extension; + + /* + * Make verbose logging easier to understand. + */ + state.level++; + if (msg_verbose) + MSG_LOG_STATE(myname, state); + + /* + * DUPLICATE/LOOP ELIMINATION + * + * Don't deliver the same user twice. + */ + if (been_here(state.dup_filter, "%s %s", myname, state.msg_attr.local)) + return (0); + + /* + * The fall-back transport specifies a delivery machanism that handles + * users not found in the aliases or UNIX passwd databases. + */ + if (*var_fallback_transport) + return (deliver_pass(MAIL_CLASS_PRIVATE, var_fallback_transport, + state.request, state.msg_attr.recipient, -1L)); + + /* + * Bounce the message when no luser relay is specified. + */ + if (*var_luser_relay == 0) + return (bounce_append(BOUNCE_FLAG_KEEP, BOUNCE_ATTR(state.msg_attr), + "unknown user: \"%s\"", state.msg_attr.local)); + + /* + * EXTERNAL LOOP CONTROL + * + * Set the delivered message attribute to the recipient, so that this + * message will list the correct forwarding address. + */ + state.msg_attr.delivered = state.msg_attr.recipient; + + /* + * DELIVERY POLICY + * + * The luser relay is just another alias. Update the expansion type + * attribute, so we can decide if deliveries to |command and /file/name + * are allowed at all. + */ + state.msg_attr.exp_type = EXPAND_TYPE_ALIAS; + + /* + * DELIVERY RIGHTS + * + * What rights to use for |command and /file/name deliveries? The luser + * relay is a root-owned alias, so we use default rights. + */ + RESET_USER_ATTR(usr_attr, state.level); + + /* + * If the luser destination is specified as @domain, prepend the + * localpart. The local resolver will append the optional address + * extension, so we don't do that here. + */ + if (*var_luser_relay == '@') { /* @domain */ + dest = concatenate(state.msg_attr.local, var_luser_relay, (char *) 0); + status = deliver_token_string(state, usr_attr, dest, (int *) 0); + myfree(dest); + } + + /* + * Otherwise, optionally arrange for the local resolver to append the + * entire localpart, including the optional address extension, to the + * destination localpart. + */ + else { /* other */ + if ((saved_extension = state.msg_attr.extension) != 0) + state.msg_attr.extension = concatenate(state.msg_attr.local, + var_rcpt_delim, + state.msg_attr.extension, + (char *) 0); + else if (*var_rcpt_delim) + state.msg_attr.extension = state.msg_attr.local; + status = deliver_token_string(state, usr_attr, var_luser_relay, + (int *) 0); + if (saved_extension != 0) + myfree(state.msg_attr.extension); + state.msg_attr.extension = saved_extension; + } + + /* + * Done. + */ + return (status); +} diff --git a/postfix/makedefs b/postfix/makedefs index 47ce8a5f9..61274aebf 100644 --- a/postfix/makedefs +++ b/postfix/makedefs @@ -59,10 +59,22 @@ case "$SYSTEM.$RELEASE" in RANLIB=echo SYSLIBS="-lresolv -lsocket -lnsl" ;; + UNIX_SV.4.2*) case "`uname -v`" in + 2.1*) SYSTYPE=UW21 + CC=/usr/bin/cc + RANLIB=echo + SYSLIBS="-lresolv -lsocket -lnsl -lc -L/usr/ucblib -lucb" + ;; + *) echo "Seems to be UnixWare`uname -v`. Untested." 1>&2; + exit 1;; + esac + ;; FreeBSD.2*) SYSTYPE=FREEBSD2 ;; FreeBSD.3*) SYSTYPE=FREEBSD3 ;; + FreeBSD.4*) SYSTYPE=FREEBSD4 + ;; OpenBSD.2*) SYSTYPE=OPENBSD2 ;; NetBSD.1*) SYSTYPE=NETBSD1 @@ -109,6 +121,7 @@ case "$SYSTEM.$RELEASE" in case "$CC" in cc|*/cc|xlc|*/xlc) OPT=;; esac + CCARGS="$CCARGS -D_ALL_SOURCE" ;; *) echo "Unknown AIX version: `uname -v`." 1>&2; exit 1;; esac;; diff --git a/postfix/man/Makefile.in b/postfix/man/Makefile.in index 953a86936..01f1e374d 100644 --- a/postfix/man/Makefile.in +++ b/postfix/man/Makefile.in @@ -5,7 +5,8 @@ DAEMONS = man8/bounce.8 man8/defer.8 man8/cleanup.8 man8/local.8 \ man8/smtp.8 man8/smtpd.8 man8/trivial-rewrite.8 COMMANDS= man1/postalias.1 man1/postcat.1 man1/postconf.1 man1/postfix.1 \ man1/postkick.1 man1/postlock.1 man1/postlog.1 man1/postdrop.1 \ - man1/postmap.1 man1/sendmail.1 man1/mailq.1 man1/newaliases.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 @@ -85,6 +86,9 @@ man1/postlog.1: ../postlog/postlog.c man1/postmap.1: ../postmap/postmap.c srctoman $? >$@ +man1/postsuper.1: ../postsuper/postsuper.c + srctoman $? >$@ + man1/sendmail.1: ../sendmail/sendmail.c srctoman $? >$@ diff --git a/postfix/man/man1/postconf.1 b/postfix/man/man1/postconf.1 index bc34bcd0c..4f386a6d3 100644 --- a/postfix/man/man1/postconf.1 +++ b/postfix/man/man1/postconf.1 @@ -9,16 +9,21 @@ Postfix configuration utility .na .nf .fi -\fBpostconf\fR [\fB-d\fR] [\fB-n\fR] [\fB-v\fR] [\fIparameter ...\fR] +\fBpostconf\fR [\fB-d\fR] [\fB-h\fR] [\fB-n\fR] [\fB-v\fR] +[\fIparameter ...\fR] .SH DESCRIPTION .ad .fi The \fBpostconf\fR command prints the actual value of -\fIparameter\fR (all known parameters by default). +\fIparameter\fR (all known parameters by default), one +parameter per line. Options: .IP \fB-d\fR Print default parameter settings instead of actual settings. +.IP \fB-h\fR +Show parameter values only, not the \fIname =\fR information +that normally precedes the value. .IP \fB-n\fR Print non-default parameter settings only. .IP \fB-v\fR diff --git a/postfix/man/man1/postdrop.1 b/postfix/man/man1/postdrop.1 index e76860f1f..42a67bbf9 100644 --- a/postfix/man/man1/postdrop.1 +++ b/postfix/man/man1/postdrop.1 @@ -20,7 +20,7 @@ group write permission to the \fBmaildrop\fR queue directory. The \fBpostdrop\fR command is automatically invoked by the \fBsendmail\fR(1) mail posting agent when the \fBmaildrop\fR -queue directory is not writable. +queue directory is not world-writable. Options: .IP \fB-v\fR diff --git a/postfix/man/man1/postsuper.1 b/postfix/man/man1/postsuper.1 new file mode 100644 index 000000000..996a0a9cc --- /dev/null +++ b/postfix/man/man1/postsuper.1 @@ -0,0 +1,68 @@ +.TH POSTSUPER 1 +.ad +.fi +.SH NAME +postsuper +\- +Postfix super intendent +.SH SYNOPSIS +.na +.nf +.fi +\fBpostsuper\fR [\fB-p\fR] [\fB-s\fR] [\fB-v\fR] [\fIdirectory ...\fR] +.SH DESCRIPTION +.ad +.fi +The \fBpostsuper\fR command does small maintenance jobs on the named +Postfix queue directories (default: all). +Directory names are relative to the Postfix top-level queue directory. + +By default, \fBpostsuper\fR performs the operations requested with the +\fB-s\fR and \fB-p\fR command-line options. +\fBpostsuper\fR always tries to remove objects that are neither files +nor directories. Use of this command is restricted to the super-user. + +Options: +.IP \fB-s\fR +Structure check. Move queue files that are in the wrong place +in the file system hierarchy and remove subdirectories that are +no longer needed. File rearrangements are necessary after a change +in the \fBhash_queue_names\fR and/or \fBhash_queue_depth\fR +configuration parameters. It is highly recommended to run this +check once before Postfix startup. +.IP \fB-p\fR +Purge stale files (files that are left over after system or +software crashes). +.IP \fB-v\fR +Enable verbose mode for debugging purposes. Multiple \fB-v\fR +options make the software increasingly verbose. +.SH DIAGNOSTICS +.ad +.fi +Problems are reported to the standard error stream and to +\fBsyslogd\fR. +.SH CONFIGURATION PARAMETERS +.na +.nf +.ad +.fi +See the Postfix \fBmain.cf\fR file for syntax details and for +default values. +.IP \fBhash_queue_depth\fR +Number of subdirectory levels for hashed queues. +.IP \fBhash_queue_names\fR +The names of queues that are organized into multiple levels of +subdirectories. +.SH LICENSE +.na +.nf +.ad +.fi +The Secure Mailer license must be distributed with this software. +.SH AUTHOR(S) +.na +.nf +Wietse Venema +IBM T.J. Watson Research +P.O. Box 704 +Yorktown Heights, NY 10598, USA diff --git a/postfix/man/man1/sendmail.1 b/postfix/man/man1/sendmail.1 index 287debf45..1c60645a9 100644 --- a/postfix/man/man1/sendmail.1 +++ b/postfix/man/man1/sendmail.1 @@ -25,8 +25,8 @@ Sendmail command-line options are recognized but silently ignored. By default, \fBsendmail\fR reads a message from standard input and arranges for delivery. \fBsendmail\fR attempts to create -a queue file in the \fBmaildrop\fR directory. If the process has -no write permission, the message is piped through the +a queue file in the \fBmaildrop\fR directory. If that directory +is not world-writable, the message is piped through the \fBpostdrop\fR(1) command, which is expected to execute with suitable privileges. @@ -118,6 +118,10 @@ The sender is never eliminated from alias etc. expansions. .IP "\fB-o \fIx value\fR (ignored)" Set option \fIx\fR to \fIvalue\fR. Use the equivalent configuration parameter in \fBmain.cf\fR instead. +.IP "\fB-r \fIsender\fR" +Set the envelope sender address. This is the address where +delivery problems are sent to, unless the message contains an +\fBErrors-To:\fR message header. .IP \fB-q\fR Flush the mail queue. This is implemented by kicking the \fBqmgr\fR(8) daemon. diff --git a/postfix/man/man8/local.8 b/postfix/man/man8/local.8 index 4fd9efaa4..d16dbd529 100644 --- a/postfix/man/man8/local.8 +++ b/postfix/man/man8/local.8 @@ -74,29 +74,44 @@ mail arrives for a recipient that is already listed in a .nf .ad .fi -The per-user mailbox is either a file in the default UNIX mailbox -directory (\fB/var/mail/\fIuser\fR or \fB/var/spool/mail/\fIuser\fR) -or it is a file in the user's home directory with a name specified -via the \fBhome_mailbox\fR configuration parameter. Specify a path -name ending in \fB/\fR for \fBqmail\fR-compatible \fBmaildir\fR -delivery. +The default per-user mailbox is a file in the UNIX mail spool +directory (\fB/var/mail/\fIuser\fR or \fB/var/spool/mail/\fIuser\fR); +the location can be specified with the \fBmail_spool_directory\fR +configuration parameter. + +Alternatively, the per-user mailbox can be a file in the user's home +directory with a name specified via the \fBhome_mailbox\fR +configuration parameter. Specify a relative path name. Specify a name +ending in \fB/\fR for \fBqmail\fR-compatible \fBmaildir\fR delivery. + Mailbox delivery can be delegated to an external command specified -with the \fBmailbox_command\fR configuration parameter. +with the \fBmailbox_command\fR configuration parameter. The command +executes with the privileges of the recipient user (exception: in +case of delivery as root, the command executes with the privileges +of \fBdefault_user\fR). -The \fBlocal\fR daemon prepends a "\fBFrom \fIsender time_stamp\fR" +Mailbox delivery can be delegated to alternative message transports +specified in the \fBmaster.cf\fR file. +The \fBmailbox_transport\fR configuration parameter specifies a +message transport that is to be used for all local recipients, +regardless of whether they are found in the UNIX passwd database. +The \fBfallback_transport\fR parameter specifies a message transport +for recipients that are not found in the UNIX passwd database. + +In the case of UNIX-style mailbox delivery, +the \fBlocal\fR daemon prepends a "\fBFrom \fIsender time_stamp\fR" envelope header to each message, prepends a \fBDelivered-To:\fR header -with the envelope recipient address, prepends a \fB>\fR character to -lines beginning with "\fBFrom \fR", and appends an empty line. -The envelope sender address is available in the \fBReturn-Path:\fR -header. +with the envelope recipient address, prepends a \fBReturn-Path:\fR +header with the envelope sender address, prepends a \fB>\fR character +to lines beginning with "\fBFrom \fR", and appends an empty line. The mailbox is locked for exclusive access while delivery is in progress. In case of problems, an attempt is made to truncate the mailbox to its original length. In the case of \fBmaildir\fR delivery, the local daemon prepends -a \fBDelivered-To:\fR header with the envelope recipient address. -The envelope sender address is available in the \fBReturn-Path:\fR -header. +a \fBDelivered-To:\fR header with the envelope recipient address +and prepends a \fBReturn-Path:\fR header with the envelope sender +address. .SH EXTERNAL COMMAND DELIVERY .na .nf @@ -128,9 +143,9 @@ The current working directory is the mail queue directory. The \fBlocal\fR daemon prepends a "\fBFrom \fIsender time_stamp\fR" envelope header to each message, prepends a \fBDelivered-To:\fR -header with the recipient envelope address, and appends an empty line. -The envelope sender address is available in the \fBReturn-Path:\fR -header. +header with the recipient envelope address, prepends a +\fBReturn-Path:\fR header with the sender envelope address, +and appends an empty line. .SH EXTERNAL FILE DELIVERY .na .nf @@ -225,21 +240,41 @@ a configuration change. .fi .IP \fBalias_maps\fR List of alias databases. -.IP \fBhome_mailbox\fR -Pathname of a mailbox relative to a user's home directory. -Specify a path ending in \fB/\fR for maildir-style delivery. .IP \fBlocal_command_shell\fR Shell to use for external command execution (for example, /some/where/smrsh -c). When a shell is specified, it is invoked even when the command contains no shell built-in commands or meta characters. -.IP \fBmailbox_command\fR -External command to use for mailbox delivery. .IP \fBowner_request_special\fR Give special treatment to \fBowner-\fIxxx\fR and \fIxxx\fB-request\fR addresses. .IP \fBrecipient_delimiter\fR Separator between username and address extension. +.SH Mailbox delivery +.ad +.fi +.IP \fBfallback_transport\fR +Message transport for recipients that are not found in the UNIX +passwd database. +This parameter overrides \fBluser_relay\fR. +.IP \fBhome_mailbox\fR +Pathname of a mailbox relative to a user's home directory. +Specify a path ending in \fB/\fR for maildir-style delivery. +.IP \fBluser_relay\fR +Destination (\fI@domain\fR or \fIaddress\fR) for non-existent users. +The \fIaddress\fR can be any destination that is valid in an alias +file. +.IP \fBmail_spool_directory\fR +Directory with UNIX-style mailboxes. The default pathname is system +dependent. +.IP \fBmailbox_command\fR +External command to use for mailbox delivery. The command executes +with the recipient privileges (exception: root). +.IP \fBmailbox_transport\fR +Message transport to use for mailbox delivery to all local +recipients, whether or not they are found in the UNIX passwd database. +This parameter overrides all other configuration parameters that +control mailbox delivery, including \fBluser_relay\fR. .SH "Locking controls" .ad .fi diff --git a/postfix/man/man8/master.8 b/postfix/man/man8/master.8 index 8cf9fd3d7..eaf281c4c 100644 --- a/postfix/man/man8/master.8 +++ b/postfix/man/man8/master.8 @@ -111,6 +111,8 @@ Limit the time in seconds that a child process waits between service requests. .IP \fBmax_use\fR Limit the number of service requests handled by a child process. +.IP \fBservice_throttle_time\fR +Time to avoid forking a server that appears to be broken. .SH FILES .na .nf diff --git a/postfix/man/man8/pickup.8 b/postfix/man/man8/pickup.8 index 5b17e81d0..a54291acf 100644 --- a/postfix/man/man8/pickup.8 +++ b/postfix/man/man8/pickup.8 @@ -59,6 +59,8 @@ a configuration change. .SH Miscellaneous .ad .fi +.IP \fBalways_bcc\fR +Address to send a copy of each message that enters the system. .IP \fBmail_owner\fR The process privileges used while not opening a \fBmaildrop\fR file. .IP \fBqueue_directory\fR diff --git a/postfix/man/man8/pipe.8 b/postfix/man/man8/pipe.8 index df6a6ef39..f602d935b 100644 --- a/postfix/man/man8/pipe.8 +++ b/postfix/man/man8/pipe.8 @@ -30,7 +30,7 @@ to the \fBbounce\fR(8) or \fBdefer\fR(8) daemon as appropriate. .fi The external command attributes are given in the \fBmaster.cf\fR file at the end of a service definition. The syntax is as follows: -.IP "\fBflags=F>\fR (optional)" +.IP "\fBflags=FR>\fR (optional)" Optional message processing flags. By default, a message is copied unchanged. .RS @@ -39,15 +39,21 @@ Prepend a "\fBFrom \fIsender time_stamp\fR" envelope header to the message content. This is expected by, for example, \fBUUCP\fR software. The \fBF\fR flag also causes an empty line to be appended to the message. +.IP \fBR\fR +Prepend a \fBReturn-Path:\fR message header with the envelope sender +address. .IP \fB>\fR -Prepend \fB>\fR to lines starting with "\fBFrom \fR". This expected +Prepend \fB>\fR to lines starting with "\fBFrom \fR". This is expected by, for example, \fBUUCP\fR software. .RE .IP "\fBuser\fR=\fIusername\fR (required)" +.IP "\fBuser\fR=\fIusername\fR:\fIgroupname\fR" The external command is executed with the rights of the specified \fIusername\fR. The software refuses to execute commands with root privileges, or with the privileges of the -mail system owner. +mail system owner. If \fIgroupname\fR is specified, the +corresponding group ID is used instead of the group ID of +of \fIusername\fR. .IP "\fBargv\fR=\fIcommand\fR... (required)" The command to be executed. This must be specified as the last command attribute. diff --git a/postfix/man/man8/qmgr.8 b/postfix/man/man8/qmgr.8 index e18309f07..4e799bd4a 100644 --- a/postfix/man/man8/qmgr.8 +++ b/postfix/man/man8/qmgr.8 @@ -82,12 +82,10 @@ of new mail. .IP "\fBslow start\fR" This strategy eliminates "thundering herd" problems by slowly adjusting the number of parallel deliveries to the same destination. -.IP "\fBround robin\fR and \fBrandom walk\fR" +.IP "\fBround robin\fR The queue manager sorts delivery requests by destination. Round-robin selection prevents one destination from dominating deliveries to other destinations. -Random walk prevents one problematic message from blocking -deliveries of other mail to the same destination. .IP "\fBexponential backoff\fR" Mail that cannot be delivered upon the first attempt is deferred. The time interval between delivery attempts is doubled after each diff --git a/postfix/man/man8/smtp.8 b/postfix/man/man8/smtp.8 index d41894ebd..38093ac2b 100644 --- a/postfix/man/man8/smtp.8 +++ b/postfix/man/man8/smtp.8 @@ -78,6 +78,12 @@ pattern in the \fBdebug_peer_list\fR parameter. List of domain or network patterns. When a remote host matches a pattern, increase the verbose logging level by the amount specified in the \fBdebug_peer_level\fR parameter. +.IP \fBfallback_relay\fR +Hosts to hand off mail to if a message destination is not found +or if a destination is unreachable. +.IP \fBignore_mx_lookup_error\fR +When a name server fails to respond to an MX query, search for an +A record instead of assuming that the name server will recover. .IP \fBinet_interfaces\fR The network interface addresses that this mail system receives mail on. When any of those addresses appears in the list of mail @@ -86,6 +92,10 @@ avoid mail delivery loops. .IP \fBnotify_classes\fR 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_quit_response\fR +Do not wait for the server response after sending QUIT. .SH "Resource controls" .ad .fi diff --git a/postfix/man/man8/smtpd.8 b/postfix/man/man8/smtpd.8 index af86f29f0..c838208b3 100644 --- a/postfix/man/man8/smtpd.8 +++ b/postfix/man/man8/smtpd.8 @@ -71,6 +71,8 @@ a configuration change. .SH Miscellaneous .ad .fi +.IP \fBalways_bcc\fR +Address to send a copy of each message that enters the system. .IP \fBcommand_directory\fR Location of Postfix support commands (default: \fB$program_directory\fR). diff --git a/postfix/master/.indent.pro b/postfix/master/.indent.pro index e59d396ad..b38eded21 100644 --- a/postfix/master/.indent.pro +++ b/postfix/master/.indent.pro @@ -6,6 +6,7 @@ -TBOUNCE_STAT -TCLEANUP_STATE -TCLIENT_LIST +-TCLNT_STREAM -TCONFIG_BOOL_FN_TABLE -TCONFIG_BOOL_TABLE -TCONFIG_INT_FN_TABLE @@ -25,6 +26,8 @@ -TDICT_NISPLUS -TDICT_NODE -TDICT_OPEN_INFO +-TDICT_PCRE +-TDICT_UNIX -TDNS_FIXED -TDNS_REPLY -TDNS_RR @@ -68,8 +71,14 @@ -TRECIPIENT_LIST -TREC_TYPE_NAME -TRESOLVE_REPLY +-TRESPONSE -TSCAN_DIR +-TSCAN_INFO +-TSCAN_OBJ +-TSESSION -TSINGLE_SERVER +-TSINK_COMMAND +-TSINK_STATE -TSMTPD_STATE -TSMTPD_TOKEN -TSMTP_ADDR diff --git a/postfix/master/Makefile.in b/postfix/master/Makefile.in index 56bbaf13c..dcf254d15 100644 --- a/postfix/master/Makefile.in +++ b/postfix/master/Makefile.in @@ -199,6 +199,8 @@ multi_server.o: ../include/msg_vstream.h multi_server.o: ../include/mymalloc.h multi_server.o: ../include/stringops.h multi_server.o: ../include/sane_accept.h +multi_server.o: ../include/myflock.h +multi_server.o: ../include/safe_open.h multi_server.o: ../include/mail_task.h multi_server.o: ../include/debug_process.h multi_server.o: ../include/mail_params.h @@ -221,6 +223,8 @@ single_server.o: ../include/events.h single_server.o: ../include/iostuff.h single_server.o: ../include/stringops.h single_server.o: ../include/sane_accept.h +single_server.o: ../include/myflock.h +single_server.o: ../include/safe_open.h single_server.o: ../include/mail_params.h single_server.o: ../include/mail_task.h single_server.o: ../include/debug_process.h @@ -243,6 +247,8 @@ trigger_server.o: ../include/events.h trigger_server.o: ../include/iostuff.h trigger_server.o: ../include/stringops.h trigger_server.o: ../include/sane_accept.h +trigger_server.o: ../include/myflock.h +trigger_server.o: ../include/safe_open.h trigger_server.o: ../include/mail_params.h trigger_server.o: ../include/mail_task.h trigger_server.o: ../include/debug_process.h diff --git a/postfix/master/mail_server.h b/postfix/master/mail_server.h index 883fd9218..ed7043e33 100644 --- a/postfix/master/mail_server.h +++ b/postfix/master/mail_server.h @@ -23,9 +23,11 @@ #define MAIL_SERVER_PRE_INIT 10 #define MAIL_SERVER_POST_INIT 11 #define MAIL_SERVER_LOOP 12 +#define MAIL_SERVER_EXIT 13 typedef void (*MAIL_SERVER_INIT_FN) (void); typedef int (*MAIL_SERVER_LOOP_FN) (void); +typedef void (*MAIL_SERVER_EXIT_FN) (void); /* * single_server.c diff --git a/postfix/master/master.c b/postfix/master/master.c index fba9d1fe3..2f78b3d1c 100644 --- a/postfix/master/master.c +++ b/postfix/master/master.c @@ -97,6 +97,8 @@ /* service requests. /* .IP \fBmax_use\fR /* Limit the number of service requests handled by a child process. +/* .IP \fBservice_throttle_time\fR +/* Time to avoid forking a server that appears to be broken. /* FILES /* /etc/postfix/main.cf: global configuration file. /* /etc/postfix/master.cf: master process configuration file. diff --git a/postfix/master/master.h b/postfix/master/master.h index 6ca860bd2..f09af8770 100644 --- a/postfix/master/master.h +++ b/postfix/master/master.h @@ -62,8 +62,6 @@ typedef struct MASTER_SERV { * manager runs at high privilege level and has to be kept simple. */ #define MASTER_DEF_MIN_IDLE 1 /* preferred # of idle processes */ -#define MASTER_DEF_MAX_PROC 100 /* max # of processes within service */ -#define MASTER_DEF_THROTTLE_DELAY 100 /* fork delay when in trouble */ /* * Structure of child process. diff --git a/postfix/master/master_ent.c b/postfix/master/master_ent.c index e0bde144a..b0f451c90 100644 --- a/postfix/master/master_ent.c +++ b/postfix/master/master_ent.c @@ -360,7 +360,7 @@ MASTER_SERV *get_master_ent() /* * Backoff time in case a service is broken. */ - serv->throttle_delay = MASTER_DEF_THROTTLE_DELAY; /* XXX config */ + serv->throttle_delay = var_throttle_time; /* * Shared channel for child status updates. @@ -378,6 +378,8 @@ MASTER_SERV *get_master_ent() */ serv->args = argv_alloc(0); argv_add(serv->args, command, (char *) 0); + if (serv->max_proc == 1) + argv_add(serv->args, "-l", (char *) 0); if (strcmp(basename(command), name) != 0) argv_add(serv->args, "-n", name, (char *) 0); argv_add(serv->args, "-t", transport, (char *) 0); diff --git a/postfix/master/master_sig.c b/postfix/master/master_sig.c index 4931cb9c2..edab09101 100644 --- a/postfix/master/master_sig.c +++ b/postfix/master/master_sig.c @@ -90,6 +90,10 @@ static void master_sigchld(int sig, int code, struct sigcontext * scp) master_gotsigchld = sig; if (scp != NULL && scp->sc_syscall == SYS_select) { scp->sc_syscall_action = SIG_RETURN; +#ifndef SA_RESTART + } else if (scp != NULL) { + scp->sc_syscall_action = SIG_RESTART; +#endif } } diff --git a/postfix/master/master_spawn.c b/postfix/master/master_spawn.c index a7bfe6e75..a1b5215c2 100644 --- a/postfix/master/master_spawn.c +++ b/postfix/master/master_spawn.c @@ -79,7 +79,7 @@ static void master_unthrottle(MASTER_SERV *serv); /* master_unthrottle_wrapper - in case (char *) != (struct *) */ -static void master_unthrottle_wrapper(char *ptr) +static void master_unthrottle_wrapper(int unused_event, char *ptr) { MASTER_SERV *serv = (MASTER_SERV *) ptr; diff --git a/postfix/master/master_vars.c b/postfix/master/master_vars.c index 6689794b5..669147a95 100644 --- a/postfix/master/master_vars.c +++ b/postfix/master/master_vars.c @@ -46,6 +46,7 @@ * Tunable parameters. */ int var_proc_limit; +int var_throttle_time; /* master_vars_init - initialize from global Postfix configuration file */ @@ -54,6 +55,7 @@ void master_vars_init(void) char *path; static CONFIG_INT_TABLE int_table[] = { VAR_PROC_LIMIT, DEF_PROC_LIMIT, &var_proc_limit, 1, 0, + VAR_THROTTLE_TIME, DEF_THROTTLE_TIME, &var_throttle_time, 1, 0, 0, }; @@ -63,4 +65,3 @@ void master_vars_init(void) fset_master_ent(path); myfree(path); } - diff --git a/postfix/master/master_wakeup.c b/postfix/master/master_wakeup.c index 4e3a1a994..7070d84d6 100644 --- a/postfix/master/master_wakeup.c +++ b/postfix/master/master_wakeup.c @@ -70,7 +70,7 @@ /* master_wakeup_timer_event - wakeup event handler */ -static void master_wakeup_timer_event(char *context) +static void master_wakeup_timer_event(int unused_event, char *context) { char *myname = "master_wakeup_timer_event"; MASTER_SERV *serv = (MASTER_SERV *) context; @@ -129,7 +129,7 @@ void master_wakeup_init(MASTER_SERV *serv) if (msg_verbose) msg_info("%s: service %s time %d", myname, serv->name, serv->wakeup_time); - master_wakeup_timer_event((char *) serv); + master_wakeup_timer_event(0, (char *) serv); } /* master_wakeup_cleanup - cancel wakeup timer */ diff --git a/postfix/master/multi_server.c b/postfix/master/multi_server.c index 3cf0a013f..a3d04b48e 100644 --- a/postfix/master/multi_server.c +++ b/postfix/master/multi_server.c @@ -71,6 +71,9 @@ /* or whenever nothing has happened for a specified amount of time. /* The result value of the function specifies how long to wait until /* the next event. Specify -1 to wait for "as long as it takes". +/* .IP "MAIL_SERVER_EXIT (void *(void))" +/* A pointer to function that is executed immediately before normal +/* process termination. /* .PP /* multi_server_disconnect() should be called by the application /* when a client disconnects. @@ -130,6 +133,10 @@ #include #include #include +#ifndef NO_SELECT_COLLISION +#include +#include +#endif /* Global library. */ @@ -157,6 +164,21 @@ static int use_count; static void (*multi_server_service) (VSTREAM *, char *, char **); static char *multi_server_name; static char **multi_server_argv; +static void (*multi_server_onexit) (void); + +#ifndef NO_SELECT_COLLISION +static VSTREAM *multi_server_lock; + +#endif + +/* multi_server_exit - normal termination */ + +static NORETURN multi_server_exit(void) +{ + if (multi_server_onexit) + multi_server_onexit(); + exit(0); +} /* multi_server_abort - terminate after abnormal master exit */ @@ -164,16 +186,16 @@ static void multi_server_abort(int unused_event, char *unused_context) { if (msg_verbose) msg_info("master disconnect -- exiting"); - exit(0); + multi_server_exit(); } /* multi_server_timeout - idle time exceeded */ -static void multi_server_timeout(char *unused_context) +static void multi_server_timeout(int unused_event, char *unused_context) { if (msg_verbose) msg_info("idle timeout -- exiting"); - exit(0); + multi_server_exit(); } /* multi_server_disconnect - terminate client session */ @@ -194,6 +216,12 @@ static void multi_server_execute(int unused_event, char *context) { VSTREAM *stream = (VSTREAM *) context; +#ifndef NO_SELECT_COLLISION + if (multi_server_lock != 0 + && myflock(vstream_fileno(multi_server_lock), MYFLOCK_NONE) < 0) + msg_fatal("select unlock: %m"); +#endif + /* * Do not bother the application when the client disconnected. */ @@ -215,6 +243,12 @@ static void multi_server_accept(int unused_event, char *context) int fd; VSTREAM *stream; +#ifndef NO_SELECT_COLLISION + if (multi_server_lock != 0 + && myflock(vstream_fileno(multi_server_lock), MYFLOCK_NONE) < 0) + msg_fatal("select unlock: %m"); +#endif + /* * Be prepared for accept() to fail because some other process already * got the connection (the number of processes competing for clients is @@ -262,6 +296,13 @@ NORETURN multi_server_main(int argc, char **argv, MULTI_SERVER_FN service,...) int key; char *transport = 0; +#ifndef NO_SELECT_COLLISION + char *lock_path; + VSTRING *why; + +#endif + int alone = 0; + /* * Process environment options as early as we can. */ @@ -315,6 +356,9 @@ NORETURN multi_server_main(int argc, char **argv, MULTI_SERVER_FN service,...) case MAIL_SERVER_LOOP: loop = va_arg(ap, MAIL_SERVER_LOOP_FN); break; + case MAIL_SERVER_EXIT: + multi_server_onexit = va_arg(ap, MAIL_SERVER_EXIT_FN); + break; default: msg_panic("%s: unknown argument type: %d", myname, key); } @@ -326,7 +370,7 @@ NORETURN multi_server_main(int argc, char **argv, MULTI_SERVER_FN service,...) * stderr, because no-one is going to see them. */ opterr = 0; - while ((c = GETOPT(argc, argv, "cDi:m:n:s:St:uv")) > 0) { + while ((c = GETOPT(argc, argv, "cDi:lm:n:s:St:uv")) > 0) { switch (c) { case 'c': root_dir = var_queue_dir; @@ -338,6 +382,9 @@ NORETURN multi_server_main(int argc, char **argv, MULTI_SERVER_FN service,...) if ((var_idle_limit = atoi(optarg)) <= 0) msg_fatal("invalid max_idle time: %s", optarg); break; + case 'l': + alone = 1; + break; case 'm': if ((var_use_limit = atoi(optarg)) <= 0) msg_fatal("invalid max_use: %s", optarg); @@ -375,12 +422,43 @@ NORETURN multi_server_main(int argc, char **argv, MULTI_SERVER_FN service,...) msg_fatal("do not run this command by hand"); } + /* + * Can options be required? + */ + if (stream == 0) { + if (transport == 0) + msg_fatal("no transport type specified"); + if (strcasecmp(transport, MASTER_XPORT_NAME_INET) != 0 + && strcasecmp(transport, MASTER_XPORT_NAME_UNIX) != 0) + msg_fatal("unsupported transport type: %s", transport); + } + /* * Optionally start the debugger on ourself. */ if (debug_me) debug_process(); + /* + * Traditionally, BSD select() can't handle multiple processes selecting + * on the same socket, and wakes up every process in select(). See TCP/IP + * Illustrated volume 2 page 532. We avoid select() collisions with an + * external lock file. + */ +#ifndef NO_SELECT_COLLISION + if (stream == 0 && !alone) { + lock_path = concatenate(DEF_PID_DIR, "/", transport, + ".", service_name, (char *) 0); + why = vstring_alloc(1); + if ((multi_server_lock = safe_open(lock_path, O_CREAT | O_RDWR, 0600, + -1, -1, why)) == 0) + msg_fatal("%s", vstring_str(why)); + close_on_exec(vstream_fileno(multi_server_lock), CLOSE_ON_EXEC); + myfree(lock_path); + vstring_free(why); + } +#endif + /* * Run pre-jail initialization. */ @@ -413,18 +491,9 @@ NORETURN multi_server_main(int argc, char **argv, MULTI_SERVER_FN service,...) VSTREAM_CTL_END); service(stream, service_name, argv + optind); vstream_fflush(stream); - exit(0); + multi_server_exit(); } - /* - * Can options be required? - */ - if (transport == 0) - msg_fatal("no transport type specified"); - if (strcasecmp(transport, MASTER_XPORT_NAME_INET) != 0 - && strcasecmp(transport, MASTER_XPORT_NAME_UNIX) != 0) - msg_fatal("unsupported transport type: %s", transport); - /* * Running as a semi-resident server. Service connection requests. * Terminate when we have serviced a sufficient number of clients, when @@ -444,7 +513,12 @@ NORETURN multi_server_main(int argc, char **argv, MULTI_SERVER_FN service,...) close_on_exec(MASTER_STATUS_FD, CLOSE_ON_EXEC); while (var_use_limit == 0 || use_count < var_use_limit || client_count > 0) { delay = loop ? loop() : -1; +#ifndef NO_SELECT_COLLISION + if (multi_server_lock != 0 + && myflock(vstream_fileno(multi_server_lock), MYFLOCK_EXCLUSIVE) < 0) + msg_fatal("select lock: %m"); +#endif event_loop(delay); } - exit(0); + multi_server_exit(); } diff --git a/postfix/master/single_server.c b/postfix/master/single_server.c index 750176fac..4976ae02c 100644 --- a/postfix/master/single_server.c +++ b/postfix/master/single_server.c @@ -66,11 +66,14 @@ /* or whenever nothing has happened for a specified amount of time. /* The result value of the function specifies how long to wait until /* the next event. Specify -1 to wait for "as long as it takes". +/* .IP "MAIL_SERVER_EXIT (void *(void))" +/* A pointer to function that is executed immediately before normal +/* process termination. /* .PP /* The var_use_limit variable limits the number of clients that /* a server can service before it commits suicide. /* This value is taken from the global \fBmain.cf\fR configuration -/* file. Setting \fBvar_use_limit\fR to zero disables the client limit. +/* file. Setting \fBvar_idle_limit\fR to zero disables the client limit. /* /* The var_idle_limit variable limits the time that a service /* receives no client connection requests before it commits suicide. @@ -121,6 +124,10 @@ #include #include #include +#ifndef NO_SELECT_COLLISION +#include +#include +#endif /* Global library. */ @@ -147,6 +154,21 @@ static int use_count; static void (*single_server_service) (VSTREAM *, char *, char **); static char *single_server_name; static char **single_server_argv; +static void (*single_server_onexit) (void); + +#ifndef NO_SELECT_COLLISION +static VSTREAM *single_server_lock; + +#endif + +/* single_server_exit - normal termination */ + +static NORETURN single_server_exit(void) +{ + if (single_server_onexit) + single_server_onexit(); + exit(0); +} /* single_server_abort - terminate after abnormal master exit */ @@ -154,16 +176,16 @@ static void single_server_abort(int unused_event, char *unused_context) { if (msg_verbose) msg_info("master disconnect -- exiting"); - exit(0); + single_server_exit(); } /* single_server_timeout - idle time exceeded */ -static void single_server_timeout(char *unused_context) +static void single_server_timeout(int unused_event, char *unused_context) { if (msg_verbose) msg_info("idle timeout -- exiting"); - exit(0); + single_server_exit(); } /* single_server_accept - accept client connection request */ @@ -175,6 +197,12 @@ static void single_server_accept(int unused_event, char *context) int time_left = -1; int fd; +#ifndef NO_SELECT_COLLISION + if (single_server_lock != 0 + && myflock(vstream_fileno(single_server_lock), MYFLOCK_NONE) < 0) + msg_fatal("select unlock: %m"); +#endif + /* * Be prepared for accept() to fail because some other process already * got the connection. We use select() + accept(), instead of simply @@ -238,6 +266,13 @@ NORETURN single_server_main(int argc, char **argv, SINGLE_SERVER_FN service,...) int key; char *transport = 0; +#ifndef NO_SELECT_COLLISION + char *lock_path; + VSTRING *why; + +#endif + int alone = 0; + /* * Process environment options as early as we can. */ @@ -291,6 +326,9 @@ NORETURN single_server_main(int argc, char **argv, SINGLE_SERVER_FN service,...) case MAIL_SERVER_LOOP: loop = va_arg(ap, MAIL_SERVER_LOOP_FN); break; + case MAIL_SERVER_EXIT: + single_server_onexit = va_arg(ap, MAIL_SERVER_EXIT_FN); + break; default: msg_panic("%s: unknown argument type: %d", myname, key); } @@ -302,7 +340,7 @@ NORETURN single_server_main(int argc, char **argv, SINGLE_SERVER_FN service,...) * stderr, because no-one is going to see them. */ opterr = 0; - while ((c = GETOPT(argc, argv, "cDi:m:n:s:St:uv")) > 0) { + while ((c = GETOPT(argc, argv, "cDi:lm:n:s:St:uv")) > 0) { switch (c) { case 'c': root_dir = var_queue_dir; @@ -314,6 +352,9 @@ NORETURN single_server_main(int argc, char **argv, SINGLE_SERVER_FN service,...) if ((var_idle_limit = atoi(optarg)) <= 0) msg_fatal("invalid max_idle time: %s", optarg); break; + case 'l': + alone = 1; + break; case 'm': if ((var_use_limit = atoi(optarg)) <= 0) msg_fatal("invalid max_use: %s", optarg); @@ -351,12 +392,43 @@ NORETURN single_server_main(int argc, char **argv, SINGLE_SERVER_FN service,...) msg_fatal("do not run this command by hand"); } + /* + * Can options be required? + */ + if (stream == 0) { + if (transport == 0) + msg_fatal("no transport type specified"); + if (strcasecmp(transport, MASTER_XPORT_NAME_INET) != 0 + && strcasecmp(transport, MASTER_XPORT_NAME_UNIX) != 0) + msg_fatal("unsupported transport type: %s", transport); + } + /* * Optionally start the debugger on ourself. */ if (debug_me) debug_process(); + /* + * Traditionally, BSD select() can't handle multiple processes selecting + * on the same socket, and wakes up every process in select(). See TCP/IP + * Illustrated volume 2 page 532. We avoid select() collisions with an + * external lock file. + */ +#ifndef NO_SELECT_COLLISION + if (stream == 0 && !alone) { + lock_path = concatenate(DEF_PID_DIR, "/", transport, + ".", service_name, (char *) 0); + why = vstring_alloc(1); + if ((single_server_lock = safe_open(lock_path, O_CREAT | O_RDWR, 0600, + -1, -1, why)) == 0) + msg_fatal("%s", vstring_str(why)); + close_on_exec(vstream_fileno(single_server_lock), CLOSE_ON_EXEC); + myfree(lock_path); + vstring_free(why); + } +#endif + /* * Run pre-jail initialization. */ @@ -389,18 +461,9 @@ NORETURN single_server_main(int argc, char **argv, SINGLE_SERVER_FN service,...) VSTREAM_CTL_END); service(stream, service_name, argv + optind); vstream_fflush(stream); - exit(0); + single_server_exit(); } - /* - * Can options be required? - */ - if (transport == 0) - msg_fatal("no transport type specified"); - if (strcasecmp(transport, MASTER_XPORT_NAME_INET) != 0 - && strcasecmp(transport, MASTER_XPORT_NAME_UNIX) != 0) - msg_fatal("unsupported transport type: %s", transport); - /* * Running as a semi-resident server. Service connection requests. * Terminate when we have serviced a sufficient number of clients, when @@ -420,7 +483,12 @@ NORETURN single_server_main(int argc, char **argv, SINGLE_SERVER_FN service,...) close_on_exec(MASTER_STATUS_FD, CLOSE_ON_EXEC); while (var_use_limit == 0 || use_count < var_use_limit) { delay = loop ? loop() : -1; +#ifndef NO_SELECT_COLLISION + if (single_server_lock != 0 + && myflock(vstream_fileno(single_server_lock), MYFLOCK_EXCLUSIVE) < 0) + msg_fatal("select lock: %m"); +#endif event_loop(delay); } - exit(0); + single_server_exit(); } diff --git a/postfix/master/trigger_server.c b/postfix/master/trigger_server.c index 9338e0b4b..651adac31 100644 --- a/postfix/master/trigger_server.c +++ b/postfix/master/trigger_server.c @@ -73,6 +73,9 @@ /* or whenever nothing has happened for a specified amount of time. /* The result value of the function specifies how long to wait until /* the next event. Specify -1 to wait for "as long as it takes". +/* .IP "MAIL_SERVER_EXIT (void *(void))" +/* A pointer to function that is executed immediately before normal +/* process termination. /* .PP /* The var_use_limit variable limits the number of clients that /* a server can service before it commits suicide. @@ -129,6 +132,10 @@ #include #include #include +#ifndef NO_SELECT_COLLISION +#include +#include +#endif /* Global library. */ @@ -155,6 +162,34 @@ static TRIGGER_SERVER_FN trigger_server_service; static char *trigger_server_name; static char **trigger_server_argv; static void (*trigger_server_accept) (int, char *); +static void (*trigger_server_onexit) (void); + +#ifndef NO_SELECT_COLLISION +static VSTREAM *trigger_server_lock; + +#endif + +/* trigger_server_exit - normal termination */ + +static NORETURN trigger_server_exit(void) +{ + if (trigger_server_onexit) + trigger_server_onexit(); + exit(0); +} + +/* trigger_server_watchdog - something got stuck */ + +static NORETURN trigger_server_watchdog(int unused_sig) +{ + + /* + * This runs as a signal handler. We should not do anything that could + * involve memory managent, but exiting without explanation would be + * worse. + */ + msg_fatal("watchdog timer"); +} /* trigger_server_abort - terminate after abnormal master exit */ @@ -162,16 +197,16 @@ static void trigger_server_abort(int unused_event, char *unused_context) { if (msg_verbose) msg_info("master disconnect -- exiting"); - exit(0); + trigger_server_exit(); } /* trigger_server_timeout - idle time exceeded */ -static void trigger_server_timeout(char *unused_context) +static void trigger_server_timeout(int unused_event, char *unused_context) { if (msg_verbose) msg_info("idle timeout -- exiting"); - exit(0); + trigger_server_exit(); } /* trigger_server_wakeup - wake up application */ @@ -203,12 +238,19 @@ static void trigger_server_accept_fifo(int unused_event, char *context) char *myname = "trigger_server_accept_fifo"; int listen_fd = (int) context; +#ifndef NO_SELECT_COLLISION + if (trigger_server_lock != 0 + && myflock(vstream_fileno(trigger_server_lock), MYFLOCK_NONE) < 0) + msg_fatal("select unlock: %m"); +#endif + if (msg_verbose) msg_info("%s: trigger arrived", myname); /* * Some buggy systems cause Postfix to lock up. */ + signal(SIGALRM, trigger_server_watchdog); alarm(1000); /* @@ -227,6 +269,12 @@ static void trigger_server_accept_socket(int unused_event, char *context) int time_left = 0; int fd; +#ifndef NO_SELECT_COLLISION + if (trigger_server_lock != 0 + && myflock(vstream_fileno(trigger_server_lock), MYFLOCK_NONE) < 0) + msg_fatal("select unlock: %m"); +#endif + if (msg_verbose) msg_info("%s: trigger arrived", myname); @@ -282,6 +330,13 @@ NORETURN trigger_server_main(int argc, char **argv, TRIGGER_SERVER_FN service,.. int len; char *transport = 0; +#ifndef NO_SELECT_COLLISION + char *lock_path; + VSTRING *why; + +#endif + int alone = 0; + /* * Process environment options as early as we can. */ @@ -335,6 +390,9 @@ NORETURN trigger_server_main(int argc, char **argv, TRIGGER_SERVER_FN service,.. case MAIL_SERVER_LOOP: loop = va_arg(ap, MAIL_SERVER_LOOP_FN); break; + case MAIL_SERVER_EXIT: + trigger_server_onexit = va_arg(ap, MAIL_SERVER_EXIT_FN); + break; default: msg_panic("%s: unknown argument type: %d", myname, key); } @@ -346,7 +404,7 @@ NORETURN trigger_server_main(int argc, char **argv, TRIGGER_SERVER_FN service,.. * stderr, because no-one is going to see them. */ opterr = 0; - while ((c = GETOPT(argc, argv, "cDi:m:n:s:St:uv")) > 0) { + while ((c = GETOPT(argc, argv, "cDi:lm:n:s:St:uv")) > 0) { switch (c) { case 'c': root_dir = var_queue_dir; @@ -358,6 +416,9 @@ NORETURN trigger_server_main(int argc, char **argv, TRIGGER_SERVER_FN service,.. if ((var_idle_limit = atoi(optarg)) <= 0) msg_fatal("invalid max_idle time: %s", optarg); break; + case 'l': + alone = 1; + break; case 'm': if ((var_use_limit = atoi(optarg)) <= 0) msg_fatal("invalid max_use: %s", optarg); @@ -395,12 +456,59 @@ NORETURN trigger_server_main(int argc, char **argv, TRIGGER_SERVER_FN service,.. msg_fatal("do not run this command by hand"); } + /* + * Can options be required? + * + * XXX Initially this code was implemented with UNIX-domain sockets, but + * Solaris <= 2.5 UNIX-domain sockets misbehave hopelessly when the + * client disconnects before the server has accepted the connection. + * Symptom: the server accept() fails with EPIPE or EPROTO, but the + * socket stays readable, so that the program goes into a wasteful loop. + * + * The initial fix was to use FIFOs, but those turn out to have their own + * problems, witness the workarounds in the fifo_listen() routine. + * Therefore we support both FIFOs and UNIX-domain sockets, so that the + * user can choose whatever works best. + */ + if (stream == 0) { + if (transport == 0) + msg_fatal("no transport type specified"); + if (strcasecmp(transport, MASTER_XPORT_NAME_INET) == 0) + trigger_server_accept = trigger_server_accept_socket; + if (strcasecmp(transport, MASTER_XPORT_NAME_UNIX) == 0) + trigger_server_accept = trigger_server_accept_socket; + else if (strcasecmp(transport, MASTER_XPORT_NAME_FIFO) == 0) + trigger_server_accept = trigger_server_accept_fifo; + else + msg_fatal("unsupported transport type: %s", transport); + } + /* * Optionally start the debugger on ourself. */ if (debug_me) debug_process(); + /* + * Traditionally, BSD select() can't handle multiple processes selecting + * on the same socket, and wakes up every process in select(). See TCP/IP + * Illustrated volume 2 page 532. We avoid select() collisions with an + * external lock file. + */ +#ifndef NO_SELECT_COLLISION + if (stream == 0 && !alone) { + lock_path = concatenate(DEF_PID_DIR, "/", transport, + ".", service_name, (char *) 0); + why = vstring_alloc(1); + if ((trigger_server_lock = safe_open(lock_path, O_CREAT | O_RDWR, 0600, + -1, -1, why)) == 0) + msg_fatal("%s", vstring_str(why)); + close_on_exec(vstream_fileno(trigger_server_lock), CLOSE_ON_EXEC); + myfree(lock_path); + vstring_free(why); + } +#endif + /* * Run pre-jail initialization. */ @@ -430,34 +538,9 @@ NORETURN trigger_server_main(int argc, char **argv, TRIGGER_SERVER_FN service,.. msg_fatal("read: %m"); service(buf, len, service_name, argv + optind); vstream_fflush(stream); - exit(0); + trigger_server_exit(); } - /* - * Can options be required? - * - * XXX Initially this code was implemented with UNIX-domain sockets, but - * Solaris <= 2.5 UNIX-domain sockets misbehave hopelessly when the - * client disconnects before the server has accepted the connection. - * Symptom: the server accept() fails with EPIPE or EPROTO, but the - * socket stays readable, so that the program goes into a wasteful loop. - * - * The initial fix was to use FIFOs, but those turn out to have their own - * problems, witness the workarounds in the fifo_listen() routine. - * Therefore we support both FIFOs and UNIX-domain sockets, so that the - * user can choose whatever works best. - */ - if (transport == 0) - msg_fatal("no transport type specified"); - if (strcasecmp(transport, MASTER_XPORT_NAME_INET) == 0) - trigger_server_accept = trigger_server_accept_socket; - if (strcasecmp(transport, MASTER_XPORT_NAME_UNIX) == 0) - trigger_server_accept = trigger_server_accept_socket; - else if (strcasecmp(transport, MASTER_XPORT_NAME_FIFO) == 0) - trigger_server_accept = trigger_server_accept_fifo; - else - msg_fatal("unsupported transport type: %s", transport); - /* * Running as a semi-resident server. Service connection requests. * Terminate when we have serviced a sufficient number of clients, when @@ -477,7 +560,12 @@ NORETURN trigger_server_main(int argc, char **argv, TRIGGER_SERVER_FN service,.. close_on_exec(MASTER_STATUS_FD, CLOSE_ON_EXEC); while (var_use_limit == 0 || use_count < var_use_limit) { delay = loop ? loop() : -1; +#ifndef NO_SELECT_COLLISION + if (trigger_server_lock != 0 + && myflock(vstream_fileno(trigger_server_lock), MYFLOCK_EXCLUSIVE) < 0) + msg_fatal("select lock: %m"); +#endif event_loop(delay); } - exit(0); + trigger_server_exit(); } diff --git a/postfix/pickup/.indent.pro b/postfix/pickup/.indent.pro index e59d396ad..b38eded21 100644 --- a/postfix/pickup/.indent.pro +++ b/postfix/pickup/.indent.pro @@ -6,6 +6,7 @@ -TBOUNCE_STAT -TCLEANUP_STATE -TCLIENT_LIST +-TCLNT_STREAM -TCONFIG_BOOL_FN_TABLE -TCONFIG_BOOL_TABLE -TCONFIG_INT_FN_TABLE @@ -25,6 +26,8 @@ -TDICT_NISPLUS -TDICT_NODE -TDICT_OPEN_INFO +-TDICT_PCRE +-TDICT_UNIX -TDNS_FIXED -TDNS_REPLY -TDNS_RR @@ -68,8 +71,14 @@ -TRECIPIENT_LIST -TREC_TYPE_NAME -TRESOLVE_REPLY +-TRESPONSE -TSCAN_DIR +-TSCAN_INFO +-TSCAN_OBJ +-TSESSION -TSINGLE_SERVER +-TSINK_COMMAND +-TSINK_STATE -TSMTPD_STATE -TSMTPD_TOKEN -TSMTP_ADDR diff --git a/postfix/pickup/Makefile.in b/postfix/pickup/Makefile.in index 1edbf8c3b..fcb0c1c7b 100644 --- a/postfix/pickup/Makefile.in +++ b/postfix/pickup/Makefile.in @@ -53,6 +53,7 @@ depend: $(MAKES) @make -f Makefile.in Makefile # do not edit below this line - it is generated by 'make depend' +cleanup_extra.o: cleanup_extra.c pickup.o: pickup.c pickup.o: ../include/sys_defs.h pickup.o: ../include/msg.h diff --git a/postfix/pickup/pickup.c b/postfix/pickup/pickup.c index ed3aead02..7ef02cb0b 100644 --- a/postfix/pickup/pickup.c +++ b/postfix/pickup/pickup.c @@ -43,6 +43,8 @@ /* .SH Miscellaneous /* .ad /* .fi +/* .IP \fBalways_bcc\fR +/* Address to send a copy of each message that enters the system. /* .IP \fBmail_owner\fR /* The process privileges used while not opening a \fBmaildrop\fR file. /* .IP \fBqueue_directory\fR @@ -103,6 +105,8 @@ /* Application-specific. */ +char *var_always_bcc; + /* * Structure to bundle a bunch of information about a queue file. */ @@ -231,12 +235,15 @@ static int pickup_copy(VSTREAM *qfile, VSTREAM *cleanup, (int) info->st.st_uid, info->sender); myfree(info->sender); + if (*var_always_bcc) + rec_fputs(cleanup, REC_TYPE_RCPT, var_always_bcc); + /* * Message content segment. Send a dummy message length. Prepend a * Received: header to the message contents. For tracing purposes, * include the message file ownership, without revealing the login name. */ - rec_fprintf(cleanup, REC_TYPE_MESG, REC_TYPE_MESG_FORMAT, 0); + rec_fputs(cleanup, REC_TYPE_MESG, ""); rec_fprintf(cleanup, REC_TYPE_NORM, "Received: by %s (%s, from userid %d)", var_myhostname, var_mail_name, info->st.st_uid); rec_fprintf(cleanup, REC_TYPE_NORM, "\tid %s; %s", info->id, @@ -402,12 +409,17 @@ static void drop_privileges(void) int main(int argc, char **argv) { + static CONFIG_STR_TABLE str_table[] = { + VAR_ALWAYS_BCC, DEF_ALWAYS_BCC, &var_always_bcc, 0, 0, + 0, + }; /* * Use the multi-threaded skeleton, because no-one else should be * monitoring our service socket while this process runs. */ trigger_server_main(argc, argv, pickup_service, + MAIL_SERVER_STR_TABLE, str_table, MAIL_SERVER_POST_INIT, drop_privileges, 0); } diff --git a/postfix/pipe/.indent.pro b/postfix/pipe/.indent.pro index e59d396ad..b38eded21 100644 --- a/postfix/pipe/.indent.pro +++ b/postfix/pipe/.indent.pro @@ -6,6 +6,7 @@ -TBOUNCE_STAT -TCLEANUP_STATE -TCLIENT_LIST +-TCLNT_STREAM -TCONFIG_BOOL_FN_TABLE -TCONFIG_BOOL_TABLE -TCONFIG_INT_FN_TABLE @@ -25,6 +26,8 @@ -TDICT_NISPLUS -TDICT_NODE -TDICT_OPEN_INFO +-TDICT_PCRE +-TDICT_UNIX -TDNS_FIXED -TDNS_REPLY -TDNS_RR @@ -68,8 +71,14 @@ -TRECIPIENT_LIST -TREC_TYPE_NAME -TRESOLVE_REPLY +-TRESPONSE -TSCAN_DIR +-TSCAN_INFO +-TSCAN_OBJ +-TSESSION -TSINGLE_SERVER +-TSINK_COMMAND +-TSINK_STATE -TSMTPD_STATE -TSMTPD_TOKEN -TSMTP_ADDR diff --git a/postfix/pipe/pipe.c b/postfix/pipe/pipe.c index 408b43f9a..60c5e96a5 100644 --- a/postfix/pipe/pipe.c +++ b/postfix/pipe/pipe.c @@ -22,7 +22,7 @@ /* .fi /* The external command attributes are given in the \fBmaster.cf\fR /* file at the end of a service definition. The syntax is as follows: -/* .IP "\fBflags=F>\fR (optional)" +/* .IP "\fBflags=FR>\fR (optional)" /* Optional message processing flags. By default, a message is /* copied unchanged. /* .RS @@ -31,15 +31,21 @@ /* the message content. /* This is expected by, for example, \fBUUCP\fR software. The \fBF\fR /* flag also causes an empty line to be appended to the message. +/* .IP \fBR\fR +/* Prepend a \fBReturn-Path:\fR message header with the envelope sender +/* address. /* .IP \fB>\fR -/* Prepend \fB>\fR to lines starting with "\fBFrom \fR". This expected +/* Prepend \fB>\fR to lines starting with "\fBFrom \fR". This is expected /* by, for example, \fBUUCP\fR software. /* .RE /* .IP "\fBuser\fR=\fIusername\fR (required)" +/* .IP "\fBuser\fR=\fIusername\fR:\fIgroupname\fR" /* The external command is executed with the rights of the /* specified \fIusername\fR. The software refuses to execute /* commands with root privileges, or with the privileges of the -/* mail system owner. +/* mail system owner. If \fIgroupname\fR is specified, the +/* corresponding group ID is used instead of the group ID of +/* of \fIusername\fR. /* .IP "\fBargv\fR=\fIcommand\fR... (required)" /* The command to be executed. This must be specified as the /* last command attribute. @@ -149,6 +155,7 @@ #include #include #include +#include #include #ifdef STRCASECMP_IN_STRINGS_H @@ -236,7 +243,6 @@ typedef struct { * Structure for command-line parameters. */ typedef struct { - char *user; /* user name */ char **command; /* argument vector */ uid_t uid; /* command privileges */ gid_t gid; /* command privileges */ @@ -310,9 +316,9 @@ static ARGV *expand_argv(char **argv, RECIPIENT_LIST *rcpt_list) /* * This argument contains $user. Extract the plain user name. - * Either anything to the left of the extension delimiter - * or, in absence of the latter, anything to the left of - * the rightmost @. + * Either anything to the left of the extension delimiter or, + * in absence of the latter, anything to the left of the + * rightmost @. * * Beware: if the user name is blank (e.g. +user@host), the * argument is suppressed. This is necessary to allow for @@ -397,12 +403,16 @@ static void get_service_attr(PIPE_ATTR *attr, char **argv) { char *myname = "get_service_attr"; struct passwd *pwd; + struct group *grp; + char *user; /* user name */ + char *group; /* group name */ char *cp; /* * Initialize. */ - attr->user = 0; + user = 0; + group = 0; attr->command = 0; attr->flags = 0; @@ -423,6 +433,9 @@ static void get_service_attr(PIPE_ATTR *attr, char **argv) case '>': attr->flags |= MAIL_COPY_QUOTE; break; + case 'R': + attr->flags |= MAIL_COPY_RETURN_PATH; + break; default: msg_fatal("unknown flag: %c (ignored)", *cp); break; @@ -431,14 +444,23 @@ static void get_service_attr(PIPE_ATTR *attr, char **argv) } /* - * user=username + * user=username[:groupname] */ else if (strncasecmp("user=", *argv, sizeof("user=") - 1) == 0) { - attr->user = *argv + sizeof("user=") - 1; - if ((pwd = getpwnam(attr->user)) == 0) - msg_fatal("%s: unknown username: %s", myname, attr->user); + user = *argv + sizeof("user=") - 1; + if ((group = split_at(user, ':')) != 0) /* XXX clobbers argv */ + if (*group == 0) + group = 0; + if ((pwd = getpwnam(user)) == 0) + msg_fatal("%s: unknown username: %s", myname, user); attr->uid = pwd->pw_uid; - attr->gid = pwd->pw_gid; + if (group != 0) { + if ((grp = getgrnam(group)) == 0) + msg_fatal("%s: unknown group: %s", myname, group); + attr->gid = grp->gr_gid; + } else { + attr->gid = pwd->pw_gid; + } } /* @@ -460,7 +482,7 @@ static void get_service_attr(PIPE_ATTR *attr, char **argv) /* * Sanity checks. Verify that every member has an acceptable value. */ - if (attr->user == 0) + if (user == 0) msg_fatal("missing user= attribute"); if (attr->command == 0) msg_fatal("missing argv= attribute"); @@ -575,7 +597,7 @@ static int deliver_message(DELIVER_REQUEST *request, char *service, char **argv) msg_fatal("empty nexthop hostname"); if (rcpt_list->len <= 0) msg_fatal("recipient count: %d", rcpt_list->len); - if (attr.user == 0) { + if (attr.command == 0) { get_service_params(&conf, service); get_service_attr(&attr, argv); } diff --git a/postfix/postalias/.indent.pro b/postfix/postalias/.indent.pro index e59d396ad..b38eded21 100644 --- a/postfix/postalias/.indent.pro +++ b/postfix/postalias/.indent.pro @@ -6,6 +6,7 @@ -TBOUNCE_STAT -TCLEANUP_STATE -TCLIENT_LIST +-TCLNT_STREAM -TCONFIG_BOOL_FN_TABLE -TCONFIG_BOOL_TABLE -TCONFIG_INT_FN_TABLE @@ -25,6 +26,8 @@ -TDICT_NISPLUS -TDICT_NODE -TDICT_OPEN_INFO +-TDICT_PCRE +-TDICT_UNIX -TDNS_FIXED -TDNS_REPLY -TDNS_RR @@ -68,8 +71,14 @@ -TRECIPIENT_LIST -TREC_TYPE_NAME -TRESOLVE_REPLY +-TRESPONSE -TSCAN_DIR +-TSCAN_INFO +-TSCAN_OBJ +-TSESSION -TSINGLE_SERVER +-TSINK_COMMAND +-TSINK_STATE -TSMTPD_STATE -TSMTPD_TOKEN -TSMTP_ADDR diff --git a/postfix/postcat/.indent.pro b/postfix/postcat/.indent.pro index e59d396ad..b38eded21 100644 --- a/postfix/postcat/.indent.pro +++ b/postfix/postcat/.indent.pro @@ -6,6 +6,7 @@ -TBOUNCE_STAT -TCLEANUP_STATE -TCLIENT_LIST +-TCLNT_STREAM -TCONFIG_BOOL_FN_TABLE -TCONFIG_BOOL_TABLE -TCONFIG_INT_FN_TABLE @@ -25,6 +26,8 @@ -TDICT_NISPLUS -TDICT_NODE -TDICT_OPEN_INFO +-TDICT_PCRE +-TDICT_UNIX -TDNS_FIXED -TDNS_REPLY -TDNS_RR @@ -68,8 +71,14 @@ -TRECIPIENT_LIST -TREC_TYPE_NAME -TRESOLVE_REPLY +-TRESPONSE -TSCAN_DIR +-TSCAN_INFO +-TSCAN_OBJ +-TSESSION -TSINGLE_SERVER +-TSINK_COMMAND +-TSINK_STATE -TSMTPD_STATE -TSMTPD_TOKEN -TSMTP_ADDR diff --git a/postfix/postcat/postcat.c b/postfix/postcat/postcat.c index efa74f5a5..0f77e5421 100644 --- a/postfix/postcat/postcat.c +++ b/postfix/postcat/postcat.c @@ -101,6 +101,10 @@ static void postcat(VSTREAM *fp, VSTRING *buffer) time = atol(STR(buffer)); vstream_printf("arrival_time: %s", asctime(localtime(&time))); break; + case REC_TYPE_WARN: + time = atol(STR(buffer)); + vstream_printf("defer_warn_time: %s", asctime(localtime(&time))); + break; case REC_TYPE_CONT: vstream_printf("%s", STR(buffer)); break; diff --git a/postfix/postconf/.indent.pro b/postfix/postconf/.indent.pro index e59d396ad..b38eded21 100644 --- a/postfix/postconf/.indent.pro +++ b/postfix/postconf/.indent.pro @@ -6,6 +6,7 @@ -TBOUNCE_STAT -TCLEANUP_STATE -TCLIENT_LIST +-TCLNT_STREAM -TCONFIG_BOOL_FN_TABLE -TCONFIG_BOOL_TABLE -TCONFIG_INT_FN_TABLE @@ -25,6 +26,8 @@ -TDICT_NISPLUS -TDICT_NODE -TDICT_OPEN_INFO +-TDICT_PCRE +-TDICT_UNIX -TDNS_FIXED -TDNS_REPLY -TDNS_RR @@ -68,8 +71,14 @@ -TRECIPIENT_LIST -TREC_TYPE_NAME -TRESOLVE_REPLY +-TRESPONSE -TSCAN_DIR +-TSCAN_INFO +-TSCAN_OBJ +-TSESSION -TSINGLE_SERVER +-TSINK_COMMAND +-TSINK_STATE -TSMTPD_STATE -TSMTPD_TOKEN -TSMTP_ADDR diff --git a/postfix/postconf/Makefile.in b/postfix/postconf/Makefile.in index f532f954d..4c7043bff 100644 --- a/postfix/postconf/Makefile.in +++ b/postfix/postconf/Makefile.in @@ -30,7 +30,7 @@ update: ../bin/$(PROG) ../bin/$(PROG): $(PROG) cp $(PROG) ../bin -$(MAKES): +$(MAKES): $(INC_DIR)/mail_params.h sh extract.sh ../*/*.c printfck: $(OBJS) $(PROG) diff --git a/postfix/postconf/postconf.c b/postfix/postconf/postconf.c index a38c895a7..cbf755dc7 100644 --- a/postfix/postconf/postconf.c +++ b/postfix/postconf/postconf.c @@ -5,14 +5,19 @@ /* Postfix configuration utility /* SYNOPSIS /* .fi -/* \fBpostconf\fR [\fB-d\fR] [\fB-n\fR] [\fB-v\fR] [\fIparameter ...\fR] +/* \fBpostconf\fR [\fB-d\fR] [\fB-h\fR] [\fB-n\fR] [\fB-v\fR] +/* [\fIparameter ...\fR] /* DESCRIPTION /* The \fBpostconf\fR command prints the actual value of -/* \fIparameter\fR (all known parameters by default). +/* \fIparameter\fR (all known parameters by default), one +/* parameter per line. /* /* Options: /* .IP \fB-d\fR /* Print default parameter settings instead of actual settings. +/* .IP \fB-h\fR +/* Show parameter values only, not the \fIname =\fR information +/* that normally precedes the value. /* .IP \fB-n\fR /* Print non-default parameter settings only. /* .IP \fB-v\fR @@ -44,6 +49,10 @@ #include #endif +#ifdef USE_PATHS_H +#include +#endif + /* Utility library. */ #include @@ -71,6 +80,7 @@ */ #define SHOW_NONDEF (1<<0) /* show non-default settings */ #define SHOW_DEFS (1<<1) /* show default setting */ +#define SHOW_NAME (1<<2) /* show parameter name */ /* * Lookup table for in-core parameter info. @@ -225,6 +235,28 @@ static void hash_parameters(void) htable_enter(param_table, csft->name, (char *) csft); } +/* show_strval - show string-valued parameter */ + +static void show_strval(int mode, const char *name, const char *value) +{ + if (mode & SHOW_NAME) { + vstream_printf("%s = %s\n", name, value); + } else { + vstream_printf("%s\n", value); + } +} + +/* show_intval - show integer-valued parameter */ + +static void show_intval(int mode, const char *name, int value) +{ + if (mode & SHOW_NAME) { + vstream_printf("%s = %d\n", name, value); + } else { + vstream_printf("%d\n", value); + } +} + /* print_bool - print boolean parameter */ static void print_bool(int mode, CONFIG_BOOL_TABLE *cbt) @@ -232,18 +264,18 @@ static void print_bool(int mode, CONFIG_BOOL_TABLE *cbt) const char *value; if (mode & SHOW_DEFS) { - vstream_printf("%s = %s\n", cbt->name, cbt->defval ? "yes" : "no"); + show_strval(mode, cbt->name, cbt->defval ? "yes" : "no"); } else { value = dict_lookup(CONFIG_DICT, cbt->name); if ((mode & SHOW_NONDEF) == 0) { if (value == 0) { - vstream_printf("%s = %s\n", cbt->name, cbt->defval ? "yes" : "no"); + show_strval(mode, cbt->name, cbt->defval ? "yes" : "no"); } else { - vstream_printf("%s = %s\n", cbt->name, value); + show_strval(mode, cbt->name, value); } } else { if (value != 0) - vstream_printf("%s = %s\n", cbt->name, value); + show_strval(mode, cbt->name, value); } } } @@ -255,18 +287,18 @@ static void print_int(int mode, CONFIG_INT_TABLE *cit) const char *value; if (mode & SHOW_DEFS) { - vstream_printf("%s = %d\n", cit->name, cit->defval); + show_intval(mode, cit->name, cit->defval); } else { value = dict_lookup(CONFIG_DICT, cit->name); if ((mode & SHOW_NONDEF) == 0) { if (value == 0) { - vstream_printf("%s = %d\n", cit->name, cit->defval); + show_intval(mode, cit->name, cit->defval); } else { - vstream_printf("%s = %s\n", cit->name, value); + show_strval(mode, cit->name, value); } } else { if (value != 0) - vstream_printf("%s = %s\n", cit->name, value); + show_strval(mode, cit->name, value); } } } @@ -278,18 +310,18 @@ static void print_str(int mode, CONFIG_STR_TABLE *cst) const char *value; if (mode & SHOW_DEFS) { - vstream_printf("%s = %s\n", cst->name, cst->defval); + show_strval(mode, cst->name, cst->defval); } else { value = dict_lookup(CONFIG_DICT, cst->name); if ((mode & SHOW_NONDEF) == 0) { if (value == 0) { - vstream_printf("%s = %s\n", cst->name, cst->defval); + show_strval(mode, cst->name, cst->defval); } else { - vstream_printf("%s = %s\n", cst->name, value); + show_strval(mode, cst->name, value); } } else { if (value != 0) - vstream_printf("%s = %s\n", cst->name, value); + show_strval(mode, cst->name, value); } } } @@ -301,18 +333,18 @@ static void print_str_fn(int mode, CONFIG_STR_FN_TABLE *csft) const char *value; if (mode & SHOW_DEFS) { - vstream_printf("%s = %s\n", csft->name, csft->defval()); + show_strval(mode, csft->name, csft->defval()); } else { value = dict_lookup(CONFIG_DICT, csft->name); if ((mode & SHOW_NONDEF) == 0) { if (value == 0) { - vstream_printf("%s = %s\n", csft->name, csft->defval()); + show_strval(mode, csft->name, csft->defval()); } else { - vstream_printf("%s = %s\n", csft->name, value); + show_strval(mode, csft->name, value); } } else { if (value != 0) - vstream_printf("%s = %s\n", csft->name, value); + show_strval(mode, csft->name, value); } } } @@ -324,18 +356,18 @@ static void print_str_fn_2(int mode, CONFIG_STR_FN_TABLE *csft) const char *value; if (mode & SHOW_DEFS) { - vstream_printf("%s = %s\n", csft->name, csft->defval()); + show_strval(mode, csft->name, csft->defval()); } else { value = dict_lookup(CONFIG_DICT, csft->name); if ((mode & SHOW_NONDEF) == 0) { if (value == 0) { - vstream_printf("%s = %s\n", csft->name, csft->defval()); + show_strval(mode, csft->name, csft->defval()); } else { - vstream_printf("%s = %s\n", csft->name, value); + show_strval(mode, csft->name, value); } } else { if (value != 0) - vstream_printf("%s = %s\n", csft->name, value); + show_strval(mode, csft->name, value); } } } @@ -412,7 +444,7 @@ static void show_parameters(int mode, char **names) int main(int argc, char **argv) { int ch; - int mode = 0; + int mode = SHOW_NAME; int fd; struct stat st; @@ -432,13 +464,16 @@ int main(int argc, char **argv) /* * Parse JCL. */ - while ((ch = GETOPT(argc, argv, "dnv")) > 0) { + while ((ch = GETOPT(argc, argv, "dhnv")) > 0) { switch (ch) { case 'd': if (mode & SHOW_NONDEF) msg_fatal("specify one of -d and -n"); mode |= SHOW_DEFS; break; + case 'h': + mode &= ~SHOW_NAME; + break; case 'n': if (mode & SHOW_DEFS) msg_fatal("specify one of -d and -n"); @@ -448,7 +483,7 @@ int main(int argc, char **argv) msg_verbose++; break; default: - msg_fatal("usage: %s [-d (show default)] [-n (show non-default)] [-v] name...", argv[0]); + msg_fatal("usage: %s [-d (defaults)] [-h (no names)] [-n (non-defaults)] [-v] name...", argv[0]); } } diff --git a/postfix/postdrop/.indent.pro b/postfix/postdrop/.indent.pro index e59d396ad..b38eded21 100644 --- a/postfix/postdrop/.indent.pro +++ b/postfix/postdrop/.indent.pro @@ -6,6 +6,7 @@ -TBOUNCE_STAT -TCLEANUP_STATE -TCLIENT_LIST +-TCLNT_STREAM -TCONFIG_BOOL_FN_TABLE -TCONFIG_BOOL_TABLE -TCONFIG_INT_FN_TABLE @@ -25,6 +26,8 @@ -TDICT_NISPLUS -TDICT_NODE -TDICT_OPEN_INFO +-TDICT_PCRE +-TDICT_UNIX -TDNS_FIXED -TDNS_REPLY -TDNS_RR @@ -68,8 +71,14 @@ -TRECIPIENT_LIST -TREC_TYPE_NAME -TRESOLVE_REPLY +-TRESPONSE -TSCAN_DIR +-TSCAN_INFO +-TSCAN_OBJ +-TSESSION -TSINGLE_SERVER +-TSINK_COMMAND +-TSINK_STATE -TSMTPD_STATE -TSMTPD_TOKEN -TSMTP_ADDR diff --git a/postfix/postdrop/postdrop.c b/postfix/postdrop/postdrop.c index 4ff41f77b..d2267db61 100644 --- a/postfix/postdrop/postdrop.c +++ b/postfix/postdrop/postdrop.c @@ -14,7 +14,7 @@ /* /* The \fBpostdrop\fR command is automatically invoked by the /* \fBsendmail\fR(1) mail posting agent when the \fBmaildrop\fR -/* queue directory is not writable. +/* queue directory is not world-writable. /* /* Options: /* .IP \fB-v\fR diff --git a/postfix/postfix/.indent.pro b/postfix/postfix/.indent.pro index e59d396ad..b38eded21 100644 --- a/postfix/postfix/.indent.pro +++ b/postfix/postfix/.indent.pro @@ -6,6 +6,7 @@ -TBOUNCE_STAT -TCLEANUP_STATE -TCLIENT_LIST +-TCLNT_STREAM -TCONFIG_BOOL_FN_TABLE -TCONFIG_BOOL_TABLE -TCONFIG_INT_FN_TABLE @@ -25,6 +26,8 @@ -TDICT_NISPLUS -TDICT_NODE -TDICT_OPEN_INFO +-TDICT_PCRE +-TDICT_UNIX -TDNS_FIXED -TDNS_REPLY -TDNS_RR @@ -68,8 +71,14 @@ -TRECIPIENT_LIST -TREC_TYPE_NAME -TRESOLVE_REPLY +-TRESPONSE -TSCAN_DIR +-TSCAN_INFO +-TSCAN_OBJ +-TSESSION -TSINGLE_SERVER +-TSINK_COMMAND +-TSINK_STATE -TSMTPD_STATE -TSMTPD_TOKEN -TSMTP_ADDR diff --git a/postfix/postkick/.indent.pro b/postfix/postkick/.indent.pro index e59d396ad..b38eded21 100644 --- a/postfix/postkick/.indent.pro +++ b/postfix/postkick/.indent.pro @@ -6,6 +6,7 @@ -TBOUNCE_STAT -TCLEANUP_STATE -TCLIENT_LIST +-TCLNT_STREAM -TCONFIG_BOOL_FN_TABLE -TCONFIG_BOOL_TABLE -TCONFIG_INT_FN_TABLE @@ -25,6 +26,8 @@ -TDICT_NISPLUS -TDICT_NODE -TDICT_OPEN_INFO +-TDICT_PCRE +-TDICT_UNIX -TDNS_FIXED -TDNS_REPLY -TDNS_RR @@ -68,8 +71,14 @@ -TRECIPIENT_LIST -TREC_TYPE_NAME -TRESOLVE_REPLY +-TRESPONSE -TSCAN_DIR +-TSCAN_INFO +-TSCAN_OBJ +-TSESSION -TSINGLE_SERVER +-TSINK_COMMAND +-TSINK_STATE -TSMTPD_STATE -TSMTPD_TOKEN -TSMTP_ADDR diff --git a/postfix/postlock/.indent.pro b/postfix/postlock/.indent.pro index e59d396ad..b38eded21 100644 --- a/postfix/postlock/.indent.pro +++ b/postfix/postlock/.indent.pro @@ -6,6 +6,7 @@ -TBOUNCE_STAT -TCLEANUP_STATE -TCLIENT_LIST +-TCLNT_STREAM -TCONFIG_BOOL_FN_TABLE -TCONFIG_BOOL_TABLE -TCONFIG_INT_FN_TABLE @@ -25,6 +26,8 @@ -TDICT_NISPLUS -TDICT_NODE -TDICT_OPEN_INFO +-TDICT_PCRE +-TDICT_UNIX -TDNS_FIXED -TDNS_REPLY -TDNS_RR @@ -68,8 +71,14 @@ -TRECIPIENT_LIST -TREC_TYPE_NAME -TRESOLVE_REPLY +-TRESPONSE -TSCAN_DIR +-TSCAN_INFO +-TSCAN_OBJ +-TSESSION -TSINGLE_SERVER +-TSINK_COMMAND +-TSINK_STATE -TSMTPD_STATE -TSMTPD_TOKEN -TSMTP_ADDR diff --git a/postfix/postlog/.indent.pro b/postfix/postlog/.indent.pro index e59d396ad..b38eded21 100644 --- a/postfix/postlog/.indent.pro +++ b/postfix/postlog/.indent.pro @@ -6,6 +6,7 @@ -TBOUNCE_STAT -TCLEANUP_STATE -TCLIENT_LIST +-TCLNT_STREAM -TCONFIG_BOOL_FN_TABLE -TCONFIG_BOOL_TABLE -TCONFIG_INT_FN_TABLE @@ -25,6 +26,8 @@ -TDICT_NISPLUS -TDICT_NODE -TDICT_OPEN_INFO +-TDICT_PCRE +-TDICT_UNIX -TDNS_FIXED -TDNS_REPLY -TDNS_RR @@ -68,8 +71,14 @@ -TRECIPIENT_LIST -TREC_TYPE_NAME -TRESOLVE_REPLY +-TRESPONSE -TSCAN_DIR +-TSCAN_INFO +-TSCAN_OBJ +-TSESSION -TSINGLE_SERVER +-TSINK_COMMAND +-TSINK_STATE -TSMTPD_STATE -TSMTPD_TOKEN -TSMTP_ADDR diff --git a/postfix/postmap/.indent.pro b/postfix/postmap/.indent.pro index e59d396ad..b38eded21 100644 --- a/postfix/postmap/.indent.pro +++ b/postfix/postmap/.indent.pro @@ -6,6 +6,7 @@ -TBOUNCE_STAT -TCLEANUP_STATE -TCLIENT_LIST +-TCLNT_STREAM -TCONFIG_BOOL_FN_TABLE -TCONFIG_BOOL_TABLE -TCONFIG_INT_FN_TABLE @@ -25,6 +26,8 @@ -TDICT_NISPLUS -TDICT_NODE -TDICT_OPEN_INFO +-TDICT_PCRE +-TDICT_UNIX -TDNS_FIXED -TDNS_REPLY -TDNS_RR @@ -68,8 +71,14 @@ -TRECIPIENT_LIST -TREC_TYPE_NAME -TRESOLVE_REPLY +-TRESPONSE -TSCAN_DIR +-TSCAN_INFO +-TSCAN_OBJ +-TSESSION -TSINGLE_SERVER +-TSINK_COMMAND +-TSINK_STATE -TSMTPD_STATE -TSMTPD_TOKEN -TSMTP_ADDR diff --git a/postfix/postsuper/.indent.pro b/postfix/postsuper/.indent.pro new file mode 100644 index 000000000..b38eded21 --- /dev/null +++ b/postfix/postsuper/.indent.pro @@ -0,0 +1,98 @@ +-TALIAS_TOKEN +-TARGV +-TBH_TABLE +-TBINHASH +-TBINHASH_INFO +-TBOUNCE_STAT +-TCLEANUP_STATE +-TCLIENT_LIST +-TCLNT_STREAM +-TCONFIG_BOOL_FN_TABLE +-TCONFIG_BOOL_TABLE +-TCONFIG_INT_FN_TABLE +-TCONFIG_INT_TABLE +-TCONFIG_STR_FN_TABLE +-TCONFIG_STR_TABLE +-TDELIVER_ATTR +-TDELIVER_REQUEST +-TDICT +-TDICT_DB +-TDICT_DBM +-TDICT_ENV +-TDICT_HT +-TDICT_LDAP +-TDICT_NI +-TDICT_NIS +-TDICT_NISPLUS +-TDICT_NODE +-TDICT_OPEN_INFO +-TDICT_PCRE +-TDICT_UNIX +-TDNS_FIXED +-TDNS_REPLY +-TDNS_RR +-TDOMAIN_LIST +-TEXPAND_ATTR +-TFILE +-TFORWARD_INFO +-THEADER_OPTS +-THTABLE +-THTABLE_INFO +-TINET_ADDR_LIST +-TINT_TABLE +-TLOCAL_STATE +-TMAC_HEAD +-TMAC_PARSE +-TMAIL_PRINT +-TMAIL_SCAN +-TMAPS +-TMASTER_PROC +-TMASTER_SERV +-TMASTER_STATUS +-TMBLOCK +-TMKMAP +-TMKMAP_OPEN_INFO +-TMULTI_SERVER +-TMVECT +-TNAMADR_LIST +-TNAME_MASK +-TPEER_NAME +-TPICKUP_INFO +-TPIPE_ATTR +-TPIPE_PARAMS +-TQMGR_ENTRY +-TQMGR_MESSAGE +-TQMGR_QUEUE +-TQMGR_RCPT_LIST +-TQMGR_RECIPIENT +-TQMGR_SCAN +-TQMGR_TRANSPORT +-TRECIPIENT +-TRECIPIENT_LIST +-TREC_TYPE_NAME +-TRESOLVE_REPLY +-TRESPONSE +-TSCAN_DIR +-TSCAN_INFO +-TSCAN_OBJ +-TSESSION +-TSINGLE_SERVER +-TSINK_COMMAND +-TSINK_STATE +-TSMTPD_STATE +-TSMTPD_TOKEN +-TSMTP_ADDR +-TSMTP_CMD +-TSMTP_RESP +-TSMTP_SESSION +-TSMTP_STATE +-TSOCKADDR_SIZE +-TSTRING_TABLE +-TSYS_EXITS_TABLE +-TTOK822 +-TTRIGGER_SERVER +-TUSER_ATTR +-TVBUF +-TVSTREAM +-TVSTRING +-TWAIT_STATUS_T diff --git a/postfix/postsuper/.printfck b/postfix/postsuper/.printfck new file mode 100644 index 000000000..9ed900cb0 --- /dev/null +++ b/postfix/postsuper/.printfck @@ -0,0 +1,24 @@ +been_here_xt 2 0 +bounce_append 5 0 +cleanup_out_format 1 0 +defer_append 5 0 +mail_command 1 0 +mail_print 1 0 +msg_error 0 0 +msg_fatal 0 0 +msg_info 0 0 +msg_panic 0 0 +msg_warn 0 0 +opened 3 0 +qmgr_message_bounce 2 0 +rec_fprintf 2 0 +sent 4 0 +smtp_cmd 1 0 +smtp_mesg_fail 2 0 +smtp_printf 1 0 +smtp_rcpt_fail 3 0 +smtp_site_fail 2 0 +udp_syslog 1 0 +vstream_fprintf 1 0 +vstream_printf 0 0 +vstring_sprintf 1 0 diff --git a/postfix/postsuper/Makefile.in b/postfix/postsuper/Makefile.in new file mode 100644 index 000000000..c91df1d93 --- /dev/null +++ b/postfix/postsuper/Makefile.in @@ -0,0 +1,72 @@ +SHELL = /bin/sh +SRCS = postsuper.c +OBJS = postsuper.o +HDRS = +TESTSRC = +WARN = -W -Wformat -Wimplicit -Wmissing-prototypes \ + -Wparentheses -Wstrict-prototypes -Wswitch -Wuninitialized \ + -Wunused +DEFS = -I. -I$(INC_DIR) -D$(SYSTYPE) +CFLAGS = $(DEBUG) $(OPT) $(DEFS) +TESTPROG= +PROG = postsuper +INC_DIR = ../include +LIBS = ../lib/libglobal.a ../lib/libutil.a + +.c.o:; $(CC) $(CFLAGS) -c $*.c + +$(PROG): $(OBJS) $(LIBS) + $(CC) $(CFLAGS) -o $@ $(OBJS) $(LIBS) $(SYSLIBS) + +Makefile: Makefile.in + (set -e; echo "# DO NOT EDIT"; $(OPTS) sh ../makedefs; cat $?) >$@ + +test: $(TESTPROG) + +update: ../bin/$(PROG) + +../bin/$(PROG): $(PROG) + cp $(PROG) ../bin + +printfck: $(OBJS) $(PROG) + rm -rf printfck + mkdir printfck + sed '1,/^# do not edit/!d' Makefile >printfck/Makefile + set -e; for i in *.c; do printfck -f .printfck $$i >printfck/$$i; done + cd printfck; make "INC_DIR=../../include" `cd ..; ls *.o` + +lint: + lint $(DEFS) $(SRCS) $(LINTFIX) + +clean: + rm -f *.o *core $(PROG) $(TESTPROG) junk + rm -rf printfck + +tidy: clean + +depend: $(MAKES) + (sed '1,/^# do not edit/!d' Makefile.in; \ + set -e; for i in [a-z][a-z0-9]*.c; do \ + $(CC) -E $(DEFS) $(INCL) $$i | sed -n -e '/^# *1 *"\([^"]*\)".*/{' \ + -e 's//'`echo $$i|sed 's/c$$/o/'`': \1/' -e 'p' -e '}'; \ + done) | grep -v '[.][o][:][ ][/]' >$$$$ && mv $$$$ Makefile.in + @make -f Makefile.in Makefile + +# do not edit below this line - it is generated by 'make depend' +postsuper.o: postsuper.c +postsuper.o: ../include/sys_defs.h +postsuper.o: ../include/mymalloc.h +postsuper.o: ../include/msg.h +postsuper.o: ../include/msg_syslog.h +postsuper.o: ../include/vstream.h +postsuper.o: ../include/vbuf.h +postsuper.o: ../include/msg_vstream.h +postsuper.o: ../include/scan_dir.h +postsuper.o: ../include/vstring.h +postsuper.o: ../include/safe.h +postsuper.o: ../include/set_ugid.h +postsuper.o: ../include/argv.h +postsuper.o: ../include/mail_task.h +postsuper.o: ../include/config.h +postsuper.o: ../include/mail_params.h +postsuper.o: ../include/mail_queue.h diff --git a/postfix/postsuper/postsuper.c b/postfix/postsuper/postsuper.c new file mode 100644 index 000000000..5d575cee1 --- /dev/null +++ b/postfix/postsuper/postsuper.c @@ -0,0 +1,377 @@ +/*++ +/* NAME +/* postsuper 1 +/* SUMMARY +/* Postfix super intendent +/* SYNOPSIS +/* .fi +/* \fBpostsuper\fR [\fB-p\fR] [\fB-s\fR] [\fB-v\fR] [\fIdirectory ...\fR] +/* DESCRIPTION +/* The \fBpostsuper\fR command does small maintenance jobs on the named +/* Postfix queue directories (default: all). +/* Directory names are relative to the Postfix top-level queue directory. +/* +/* By default, \fBpostsuper\fR performs the operations requested with the +/* \fB-s\fR and \fB-p\fR command-line options. +/* \fBpostsuper\fR always tries to remove objects that are neither files +/* nor directories. Use of this command is restricted to the super-user. +/* +/* Options: +/* .IP \fB-s\fR +/* Structure check. Move queue files that are in the wrong place +/* in the file system hierarchy and remove subdirectories that are +/* no longer needed. File rearrangements are necessary after a change +/* in the \fBhash_queue_names\fR and/or \fBhash_queue_depth\fR +/* configuration parameters. It is highly recommended to run this +/* check once before Postfix startup. +/* .IP \fB-p\fR +/* Purge stale files (files that are left over after system or +/* software crashes). +/* .IP \fB-v\fR +/* Enable verbose mode for debugging purposes. Multiple \fB-v\fR +/* options make the software increasingly verbose. +/* DIAGNOSTICS +/* Problems are reported to the standard error stream and to +/* \fBsyslogd\fR. +/* CONFIGURATION PARAMETERS +/* .ad +/* .fi +/* See the Postfix \fBmain.cf\fR file for syntax details and for +/* default values. +/* .IP \fBhash_queue_depth\fR +/* Number of subdirectory levels for hashed queues. +/* .IP \fBhash_queue_names\fR +/* The names of queues that are organized into multiple levels of +/* subdirectories. +/* 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 +#include +#include /* remove() */ + +/* Utility library. */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Global library. */ + +#include +#include +#include +#include + +/* Application-specific. */ + +#define MAX_TEMP_AGE (60 * 60 * 24) /* temp file maximal age */ +#define STR vstring_str /* silly little macro */ + +#define ACTION_STRUCT (1<<0) /* fix file organization */ +#define ACTION_PURGE (1<<1) /* purge old temp files */ + +#define ACTION_DEFAULT (ACTION_STRUCT | ACTION_PURGE) + + /* + * Information about queue directories and what we expect to do there. If a + * file has unexpected owner permissions and is older than some threshold, + * the file is discarded. We don't step into maildrop subdirectories - if + * maildrop is writable, we might end up in the wrong place, deleting the + * wrong information. + */ +struct queue_info { + char *name; /* directory name */ + int perms; /* expected permissions */ + int flags; /* see below */ +}; + +#define RECURSE (1<<0) /* step into subdirectories */ +#define DONT_RECURSE 0 /* don't step into directories */ + +static struct queue_info queue_info[] = { + MAIL_QUEUE_MAILDROP, MAIL_QUEUE_STAT_READY, DONT_RECURSE, + MAIL_QUEUE_INCOMING, MAIL_QUEUE_STAT_READY, RECURSE, + MAIL_QUEUE_ACTIVE, MAIL_QUEUE_STAT_READY, RECURSE, + MAIL_QUEUE_DEFERRED, MAIL_QUEUE_STAT_READY, RECURSE, + MAIL_QUEUE_DEFER, 0600, RECURSE, + MAIL_QUEUE_BOUNCE, 0600, RECURSE, + 0, +}; + +/* super - check queue file location and clean up */ + +static void super(char **queues, int action) +{ + ARGV *hash_queue_names = argv_split(var_hash_queue_names, " \t\r\n,"); + VSTRING *actual_path = vstring_alloc(10); + VSTRING *wanted_path = vstring_alloc(10); + struct stat st; + char *queue_name; + SCAN_DIR *info; + char *path; + int actual_depth; + int wanted_depth; + char **cpp; + struct queue_info *qp; + + /* + * Make sure every file is in the right place, clean out stale files, and + * remove non-file/non-directory objects. + */ + while ((queue_name = *queues++) != 0) { + + /* + * Look up queue-specific properties: desired hashing depth, what + * file permissions to look for, and whether or not it is desirable + * to step into subdirectories. + */ + for (qp = queue_info; /* void */ ; qp++) { + if (qp->name == 0) + msg_fatal("unknown queue name: %s", queue_name); + if (strcmp(qp->name, queue_name) == 0) + break; + } + for (cpp = hash_queue_names->argv; /* void */ ; cpp++) { + if (*cpp == 0) { + wanted_depth = 0; + break; + } + if (strcmp(*cpp, queue_name) == 0) { + wanted_depth = var_hash_queue_depth; + break; + } + } + + /* + * Sanity check. Some queues just cannot be recursive. + */ + if (wanted_depth > 0 && (qp->flags & RECURSE) == 0) + msg_fatal("%s queue must not be hashed", queue_name); + + /* + * Other per-directory initialization. + */ + info = scan_dir_open(queue_name); + actual_depth = 0; + + for (;;) { + + /* + * If we reach the end of a subdirectory, return to its parent. + * Delete subdirectories that are no longer needed. + */ + if ((path = scan_dir_next(info)) == 0) { + if (actual_depth == 0) + break; + if (actual_depth > wanted_depth) { + if (rmdir(scan_dir_path(info)) < 0 && errno != ENOENT) + msg_warn("remove %s: %m", scan_dir_path(info)); + else if (msg_verbose) + msg_info("remove %s", scan_dir_path(info)); + } + scan_dir_pop(info); + actual_depth--; + continue; + } + + /* + * If we stumble upon a subdirectory, enter it, if it is + * considered safe to do so. Otherwise, try to remove the + * subdirectory at a later stage. + */ + if (strlen(path) == 1 && (qp->flags & RECURSE) != 0) { + actual_depth++; + scan_dir_push(info, path); + continue; + } + + /* + * See if this is a stale file or some non-file object. Be + * careful not to delete bounce or defer logs just because they + * are more than a couple days old. + */ + vstring_sprintf(actual_path, "%s/%s", scan_dir_path(info), path); + if (stat(STR(actual_path), &st) < 0) + continue; + if (S_ISDIR(st.st_mode)) { + if (rmdir(STR(actual_path)) < 0 && errno != ENOENT) + msg_warn("remove subdirectory %s: %m", STR(actual_path)); + else if (msg_verbose) + msg_info("remove subdirectory %s", STR(actual_path)); + continue; + } + if (!S_ISREG(st.st_mode) + || ((action & ACTION_PURGE) != 0 && + (st.st_mode & S_IRWXU) != qp->perms + && time((time_t *) 0) > st.st_mtime + MAX_TEMP_AGE)) { + if (remove(STR(actual_path)) < 0 && errno != ENOENT) + msg_warn("remove %s: %m", STR(actual_path)); + else if (msg_verbose) + msg_info("remove %s", STR(actual_path)); + continue; + } + + /* + * See if this file sits in the right place in the file system + * hierarchy. Its place may be wrong after a change to the + * hash_queue_{names,depth} parameter settings. The implied + * mkdir() operation is the main reason for this command to run + * with postfix privilege. The mail_queue_mkdirs() routine could + * be fixed to use the "right" privilege, but it is a good idea + * to do everying with the postfix owner privileges regardless, + * in order to limit the amount of damage that we can do. + */ + (void) mail_queue_path(wanted_path, queue_name, path); + if (strcmp(STR(actual_path), STR(wanted_path)) != 0) { + if (rename(STR(actual_path), STR(wanted_path)) < 0) + if (errno != ENOENT + || mail_queue_mkdirs(STR(wanted_path)) < 0 + || rename(STR(actual_path), STR(wanted_path)) < 0) + msg_fatal("rename %s to %s: %m", STR(actual_path), + STR(wanted_path)); + if (msg_verbose) + msg_info("rename %s to %s", STR(actual_path), + STR(wanted_path)); + } + } + scan_dir_close(info); + } + + /* + * Clean up. + */ + vstring_free(wanted_path); + vstring_free(actual_path); + argv_free(hash_queue_names); +} + +main(int argc, char **argv) +{ + int fd; + struct stat st; + char *slash; + int debug_me = 0; + int action = 0; + char **queues; + int c; + + /* + * Defaults. + */ + static char *default_queues[] = { + MAIL_QUEUE_MAILDROP, + MAIL_QUEUE_INCOMING, + MAIL_QUEUE_ACTIVE, + MAIL_QUEUE_DEFERRED, + MAIL_QUEUE_DEFER, + MAIL_QUEUE_BOUNCE, + 0, + }; + + /* + * Be consistent with file permissions. + */ + umask(022); + + /* + * To minimize confusion, make sure that the standard file descriptors + * are open before opening anything else. + */ + for (fd = 0; fd < 3; fd++) + if (fstat(fd, &st) == -1 && open("/dev/null", 2) != fd) + msg_fatal("open /dev/null: %m"); + + /* + * Process environment options as early as we can. We might be called + * from a set-uid (set-gid) program, so be careful with importing + * environment variables. + */ + if (safe_getenv(CONF_ENV_VERB)) + msg_verbose = 1; + if (safe_getenv(CONF_ENV_DEBUG)) + debug_me = 1; + + /* + * Initialize. Set up logging, read the global configuration file and + * extract configuration information. + */ + if ((slash = strrchr(argv[0], '/')) != 0) + argv[0] = slash + 1; + msg_vstream_init(argv[0], VSTREAM_ERR); + msg_syslog_init(mail_task(argv[0]), LOG_PID, LOG_FACILITY); + set_config_str(VAR_PROCNAME, var_procname = mystrdup(argv[0])); + + read_config(); + if (chdir(var_queue_dir)) + msg_fatal("chdir %s: %m", var_queue_dir); + + /* + * All file/directory updates must be done as the mail system owner. This + * is because Postfix daemons manipulate the queue with those same + * privileges, so directories must be created with the right ownership. + * + * Running as a non-root user is also required for security reasons. When + * the Postfix queue hierarchy is compromised, an attacker could trick us + * into entering other file hierarchies and afflicting damage. Running as + * a non-root user limits the damage to the already compromised mail + * owner. + */ + set_ugid(var_owner_uid, var_owner_gid); + + /* + * Parse JCL. + */ + while ((c = GETOPT(argc, argv, "spv")) > 0) { + switch (c) { + default: + msg_fatal("usage: %s [-s (fix structure)] [-p (purge stale files)]", + argv[0]); + case 's': + action |= ACTION_STRUCT; + break; + case 'p': + action |= ACTION_PURGE; + break; + case 'v': + msg_verbose++; + break; + } + } + + /* + * Execute the explicitly specified (or default) action, on the + * explicitly specified (or default) queues. + */ + if (action == 0) + action = ACTION_DEFAULT; + if (argv[optind] == 0) + queues = default_queues; + else + queues = argv + optind; + + super(queues, action); + + exit(0); +} diff --git a/postfix/qmgr/.indent.pro b/postfix/qmgr/.indent.pro index e59d396ad..b38eded21 100644 --- a/postfix/qmgr/.indent.pro +++ b/postfix/qmgr/.indent.pro @@ -6,6 +6,7 @@ -TBOUNCE_STAT -TCLEANUP_STATE -TCLIENT_LIST +-TCLNT_STREAM -TCONFIG_BOOL_FN_TABLE -TCONFIG_BOOL_TABLE -TCONFIG_INT_FN_TABLE @@ -25,6 +26,8 @@ -TDICT_NISPLUS -TDICT_NODE -TDICT_OPEN_INFO +-TDICT_PCRE +-TDICT_UNIX -TDNS_FIXED -TDNS_REPLY -TDNS_RR @@ -68,8 +71,14 @@ -TRECIPIENT_LIST -TREC_TYPE_NAME -TRESOLVE_REPLY +-TRESPONSE -TSCAN_DIR +-TSCAN_INFO +-TSCAN_OBJ +-TSESSION -TSINGLE_SERVER +-TSINK_COMMAND +-TSINK_STATE -TSMTPD_STATE -TSMTPD_TOKEN -TSMTP_ADDR diff --git a/postfix/qmgr/Makefile.in b/postfix/qmgr/Makefile.in index 4688f33bf..4219845f0 100644 --- a/postfix/qmgr/Makefile.in +++ b/postfix/qmgr/Makefile.in @@ -74,6 +74,7 @@ qmgr.o: ../include/iostuff.h qmgr.o: ../include/master_proto.h qmgr.o: ../include/mail_server.h qmgr.o: qmgr.h +qmgr.o: ../include/scan_dir.h qmgr.o: ../include/maps.h qmgr_active.o: qmgr_active.c qmgr_active.o: ../include/sys_defs.h @@ -89,7 +90,9 @@ qmgr_active.o: ../include/vstring.h qmgr_active.o: ../include/recipient_list.h qmgr_active.o: ../include/bounce.h qmgr_active.o: ../include/defer.h +qmgr_active.o: ../include/rec_type.h qmgr_active.o: qmgr.h +qmgr_active.o: ../include/scan_dir.h qmgr_active.o: ../include/maps.h qmgr_bounce.o: qmgr_bounce.c qmgr_bounce.o: ../include/sys_defs.h @@ -98,6 +101,7 @@ qmgr_bounce.o: ../include/deliver_completed.h qmgr_bounce.o: ../include/vstream.h qmgr_bounce.o: ../include/vbuf.h qmgr_bounce.o: qmgr.h +qmgr_bounce.o: ../include/scan_dir.h qmgr_bounce.o: ../include/maps.h qmgr_defer.o: qmgr_defer.c qmgr_defer.o: ../include/sys_defs.h @@ -107,6 +111,7 @@ qmgr_defer.o: ../include/vbuf.h qmgr_defer.o: ../include/defer.h qmgr_defer.o: ../include/bounce.h qmgr_defer.o: qmgr.h +qmgr_defer.o: ../include/scan_dir.h qmgr_defer.o: ../include/maps.h qmgr_deliver.o: qmgr_deliver.c qmgr_deliver.o: ../include/sys_defs.h @@ -120,7 +125,9 @@ qmgr_deliver.o: ../include/iostuff.h qmgr_deliver.o: ../include/mail_queue.h qmgr_deliver.o: ../include/mail_proto.h qmgr_deliver.o: ../include/recipient_list.h +qmgr_deliver.o: ../include/mail_params.h qmgr_deliver.o: qmgr.h +qmgr_deliver.o: ../include/scan_dir.h qmgr_deliver.o: ../include/maps.h qmgr_enable.o: qmgr_enable.c qmgr_enable.o: ../include/sys_defs.h @@ -128,6 +135,7 @@ qmgr_enable.o: ../include/msg.h qmgr_enable.o: ../include/vstream.h qmgr_enable.o: ../include/vbuf.h qmgr_enable.o: qmgr.h +qmgr_enable.o: ../include/scan_dir.h qmgr_enable.o: ../include/maps.h qmgr_entry.o: qmgr_entry.c qmgr_entry.o: ../include/sys_defs.h @@ -138,6 +146,7 @@ qmgr_entry.o: ../include/vstream.h qmgr_entry.o: ../include/vbuf.h qmgr_entry.o: ../include/mail_params.h qmgr_entry.o: qmgr.h +qmgr_entry.o: ../include/scan_dir.h qmgr_entry.o: ../include/maps.h qmgr_message.o: qmgr_message.c qmgr_message.o: ../include/sys_defs.h @@ -163,6 +172,7 @@ qmgr_message.o: ../include/maps.h qmgr_message.o: ../include/opened.h qmgr_message.o: ../include/resolve_clnt.h qmgr_message.o: qmgr.h +qmgr_message.o: ../include/scan_dir.h qmgr_move.o: qmgr_move.c qmgr_move.o: ../include/sys_defs.h qmgr_move.o: ../include/msg.h @@ -172,6 +182,7 @@ qmgr_move.o: ../include/mail_queue.h qmgr_move.o: ../include/vstring.h qmgr_move.o: ../include/vbuf.h qmgr_move.o: ../include/vstream.h +qmgr_move.o: ../include/mail_scan_dir.h qmgr_move.o: qmgr.h qmgr_move.o: ../include/maps.h qmgr_queue.o: qmgr_queue.c @@ -185,6 +196,7 @@ qmgr_queue.o: ../include/recipient_list.h qmgr_queue.o: qmgr.h qmgr_queue.o: ../include/vstream.h qmgr_queue.o: ../include/vbuf.h +qmgr_queue.o: ../include/scan_dir.h qmgr_queue.o: ../include/maps.h qmgr_rcpt_list.o: qmgr_rcpt_list.c qmgr_rcpt_list.o: ../include/sys_defs.h @@ -192,12 +204,14 @@ qmgr_rcpt_list.o: ../include/mymalloc.h qmgr_rcpt_list.o: qmgr.h qmgr_rcpt_list.o: ../include/vstream.h qmgr_rcpt_list.o: ../include/vbuf.h +qmgr_rcpt_list.o: ../include/scan_dir.h qmgr_rcpt_list.o: ../include/maps.h qmgr_scan.o: qmgr_scan.c qmgr_scan.o: ../include/sys_defs.h qmgr_scan.o: ../include/msg.h qmgr_scan.o: ../include/mymalloc.h qmgr_scan.o: ../include/scan_dir.h +qmgr_scan.o: ../include/mail_scan_dir.h qmgr_scan.o: qmgr.h qmgr_scan.o: ../include/vstream.h qmgr_scan.o: ../include/vbuf.h @@ -216,4 +230,5 @@ qmgr_transport.o: ../include/recipient_list.h qmgr_transport.o: ../include/config.h qmgr_transport.o: ../include/mail_params.h qmgr_transport.o: qmgr.h +qmgr_transport.o: ../include/scan_dir.h qmgr_transport.o: ../include/maps.h diff --git a/postfix/qmgr/qmgr.c b/postfix/qmgr/qmgr.c index 464ee90aa..007f298dd 100644 --- a/postfix/qmgr/qmgr.c +++ b/postfix/qmgr/qmgr.c @@ -70,12 +70,10 @@ /* .IP "\fBslow start\fR" /* This strategy eliminates "thundering herd" problems by slowly /* adjusting the number of parallel deliveries to the same destination. -/* .IP "\fBround robin\fR and \fBrandom walk\fR" +/* .IP "\fBround robin\fR /* The queue manager sorts delivery requests by destination. /* Round-robin selection prevents one destination from dominating /* deliveries to other destinations. -/* Random walk prevents one problematic message from blocking -/* deliveries of other mail to the same destination. /* .IP "\fBexponential backoff\fR" /* Mail that cannot be delivered upon the first attempt is deferred. /* The time interval between delivery attempts is doubled after each @@ -276,7 +274,7 @@ MAPS *qmgr_virtual; /* qmgr_deferred_run_event - queue manager heartbeat */ -static void qmgr_deferred_run_event(char *dummy) +static void qmgr_deferred_run_event(int unused_event, char *dummy) { /* @@ -322,9 +320,11 @@ static void qmgr_trigger_event(char *buf, int len, break; case QMGR_REQ_FLUSH_DEAD: deferred_flag |= QMGR_FLUSH_DEAD; + incoming_flag |= QMGR_FLUSH_DEAD; break; case QMGR_REQ_SCAN_ALL: deferred_flag |= QMGR_SCAN_ALL; + incoming_flag |= QMGR_SCAN_ALL; break; default: if (msg_verbose) @@ -406,18 +406,22 @@ static void qmgr_post_init(void) * This routine runs after the skeleton code has entered the chroot jail. * Prevent automatic process suicide after a limited number of client * requests or after a limited amount of idle time. Move any left-over - * entries from the active queue to the deferred queue, and give them a + * entries from the active queue to the incoming queue, and give them a * time stamp into the future, in order to allow ongoing deliveries to * finish first. Start scanning the incoming and deferred queues. + * Left-over active queue entries are moved to the incoming queue because + * the incoming queue has priority; moving left-overs to the deferred + * queue could cause anomalous delays when "postfix reload/start" are + * issued often. */ var_use_limit = 0; var_idle_limit = 0; - qmgr_move(MAIL_QUEUE_ACTIVE, MAIL_QUEUE_DEFERRED, - event_time() + var_queue_run_delay); + qmgr_move(MAIL_QUEUE_ACTIVE, MAIL_QUEUE_INCOMING, + event_time() + var_min_backoff_time); qmgr_incoming = qmgr_scan_create(MAIL_QUEUE_INCOMING); qmgr_deferred = qmgr_scan_create(MAIL_QUEUE_DEFERRED); qmgr_scan_request(qmgr_incoming, QMGR_SCAN_START); - qmgr_deferred_run_event((char *) 0); + qmgr_deferred_run_event(0, (char *) 0); } /* main - the main program */ diff --git a/postfix/qmgr/qmgr.h b/postfix/qmgr/qmgr.h index c8e6c9e2c..7660e4671 100644 --- a/postfix/qmgr/qmgr.h +++ b/postfix/qmgr/qmgr.h @@ -12,6 +12,7 @@ * Utility library. */ #include +#include /* * Global library. @@ -68,9 +69,13 @@ typedef struct QMGR_SCAN QMGR_SCAN; } #define QMGR_LIST_PREPEND(head, object) { \ - object->peers.prev = head->prev; \ + object->peers.prev = head.prev; \ object->peers.next = 0; \ - head.prev->next = object; \ + if (head.prev) { \ + head.prev->peers.next = object; \ + } else { \ + head.next = object; \ + } \ head.prev = object; \ } @@ -106,6 +111,7 @@ struct QMGR_TRANSPORT { int flags; /* blocked, etc. */ char *name; /* transport name */ int dest_concurrency_limit; /* concurrency per domain */ + int init_dest_concurrency; /* init. per-domain concurrency */ int recipient_limit; /* recipients per transaction */ struct HTABLE *queue_byname; /* queues indexed by domain */ QMGR_QUEUE_LIST queue_list; /* queues, round robin order */ @@ -215,6 +221,8 @@ struct QMGR_MESSAGE { int refcount; /* queue entries */ int single_rcpt; /* send one rcpt at a time */ long arrival_time; /* time when queued */ + long warn_offset; /* warning bounce flag offset */ + time_t warn_time; /* time next warning to be sent */ long data_offset; /* data seek offset */ char *queue_name; /* queue name */ char *queue_id; /* queue file */ @@ -232,6 +240,7 @@ extern MAPS *qmgr_relocated; extern MAPS *qmgr_virtual; extern void qmgr_message_free(QMGR_MESSAGE *); +extern void qmgr_message_update_warn(QMGR_MESSAGE *); extern QMGR_MESSAGE *qmgr_message_alloc(const char *, const char *, int); extern QMGR_MESSAGE *qmgr_message_realloc(QMGR_MESSAGE *); @@ -245,7 +254,7 @@ extern void qmgr_defer_recipient(QMGR_MESSAGE *, const char *, const char *); /* * qmgr_bounce.c */ -extern void qmgr_bounce_recipient(QMGR_MESSAGE *, QMGR_RCPT *, const char *, ...); +extern void qmgr_bounce_recipient(QMGR_MESSAGE *, QMGR_RCPT *, const char *,...); /* * qmgr_deliver.c diff --git a/postfix/qmgr/qmgr_active.c b/postfix/qmgr/qmgr_active.c index a9d04cea7..bd7b17bdb 100644 --- a/postfix/qmgr/qmgr_active.c +++ b/postfix/qmgr/qmgr_active.c @@ -102,6 +102,7 @@ #include #include #include +#include /* Application-specific. */ @@ -149,16 +150,10 @@ void qmgr_active_feed(QMGR_SCAN *scan_info, const char *queue_id) /* * Skip files that have time stamps into the future. They need to cool - * down. Future time stamps should appear only in the deferred queue. - * Look at the clock before looking at the queue file time stamp. On a - * busy day, several seconds may pass between queue manager wakeup and - * queue file scanning. + * down. Incoming and deferred files can have future time stamps. */ if ((scan_info->flags & QMGR_SCAN_ALL) == 0 && st.st_mtime > time((time_t *) 0) + 1) { - if (strcmp(scan_info->queue, MAIL_QUEUE_DEFERRED) != 0) - msg_warn("%s: queue %s: mtime %ld seconds into the future", - queue_id, scan_info->queue, st.st_mtime - event_time()); if (msg_verbose) msg_info("%s: skip %s (%ld seconds)", myname, queue_id, (long) (st.st_mtime - event_time())); @@ -281,16 +276,28 @@ void qmgr_active_done(QMGR_MESSAGE *message) * If we get to this point we have tried all recipients for this message. * If the message is too old, try to bounce it. */ +#define HOUR 3600 #define DAY 86400 - if (message->flags - && event_time() > message->arrival_time + var_max_queue_time * DAY) { - if (msg_verbose) - msg_info("%s: too old, bouncing %s", myname, message->queue_id); - message->flags = defer_flush(BOUNCE_FLAG_KEEP, - message->queue_name, - message->queue_id, - message->errors_to); + if (message->flags) { + if (event_time() > message->arrival_time + var_max_queue_time * DAY) { + if (msg_verbose) + msg_info("%s: too old, bouncing %s", myname, message->queue_id); + message->flags = defer_flush(BOUNCE_FLAG_KEEP, + message->queue_name, + message->queue_id, + message->errors_to); + } else if (message->warn_time > 0 + && event_time() > message->warn_time) { + if (msg_verbose) + msg_info("%s: sending defer warning for %s", myname, message->queue_id); + if (defer_warn(BOUNCE_FLAG_KEEP, + message->queue_name, + message->queue_id, + message->errors_to) == 0) { + qmgr_message_update_warn(message); + } + } } /* diff --git a/postfix/qmgr/qmgr_deliver.c b/postfix/qmgr/qmgr_deliver.c index cde38b091..8aefffceb 100644 --- a/postfix/qmgr/qmgr_deliver.c +++ b/postfix/qmgr/qmgr_deliver.c @@ -60,6 +60,7 @@ #include #include #include +#include /* Application-specific. */ @@ -135,6 +136,19 @@ static int qmgr_deliver_send_request(QMGR_ENTRY *entry, VSTREAM *stream) } } +/* qmgr_deliver_abort - transport response watchdog */ + +static void qmgr_deliver_abort(int unused_event, char *context) +{ + QMGR_ENTRY *entry = (QMGR_ENTRY *) context; + QMGR_QUEUE *queue = entry->queue; + QMGR_TRANSPORT *transport = queue->transport; + QMGR_MESSAGE *message = entry->message; + + msg_fatal("%s: timeout receiving delivery status from transport: %s", + message->queue_id, transport->name); +} + /* qmgr_deliver_update - process delivery status report */ static void qmgr_deliver_update(int unused_event, char *context) @@ -146,6 +160,11 @@ static void qmgr_deliver_update(int unused_event, char *context) VSTRING *reason = vstring_alloc(1); int status; + /* + * The message transport has responded. Stop the watchdog timer. + */ + event_cancel_timer(qmgr_deliver_abort, context); + /* * Retrieve the delivery agent status report. The numerical status code * indicates if delivery should be tried again. The reason text is sent @@ -267,4 +286,9 @@ void qmgr_deliver(QMGR_TRANSPORT *transport, VSTREAM *stream) entry->stream = stream; event_enable_read(vstream_fileno(stream), qmgr_deliver_update, (char *) entry); + + /* + * Guard against broken systems. + */ + event_request_timer(qmgr_deliver_abort, (char *) entry, var_ipc_timeout); } diff --git a/postfix/qmgr/qmgr_entry.c b/postfix/qmgr/qmgr_entry.c index 8277dbe9b..4c7c65e21 100644 --- a/postfix/qmgr/qmgr_entry.c +++ b/postfix/qmgr/qmgr_entry.c @@ -93,10 +93,9 @@ QMGR_ENTRY *qmgr_entry_select(QMGR_QUEUE *queue) { - int dir = (rand() & 256); /* low byte has short cycle */ QMGR_ENTRY *entry; - if ((entry = (dir ? queue->todo.prev : queue->todo.next)) != 0) { + if ((entry = queue->todo.prev) != 0) { QMGR_LIST_UNLINK(queue->todo, QMGR_ENTRY *, entry); queue->todo_refcount--; QMGR_LIST_APPEND(queue->busy, entry); diff --git a/postfix/qmgr/qmgr_message.c b/postfix/qmgr/qmgr_message.c index 4d4cfa48a..c63689048 100644 --- a/postfix/qmgr/qmgr_message.c +++ b/postfix/qmgr/qmgr_message.c @@ -19,6 +19,9 @@ /* /* void qmgr_message_free(message) /* QMGR_MESSAGE *message; +/* +/* void qmgr_message_update_warn(message) +/* QMGR_MESSAGE *message; /* DESCRIPTION /* This module performs en-gross operations on queue messages. /* @@ -50,6 +53,9 @@ /* qmgr_message_free() destroys an in-core message structure and makes /* the resources available for reuse. It is an error to destroy /* a message structure that is still referenced by queue entry structures. +/* +/* qmgr_message_update_warn() takes a closed message, opens it, updates +/* the warning field, and closes it again. /* DIAGNOSTICS /* Warnings: malformed message file. Fatal errors: out of memory. /* SEE ALSO @@ -136,6 +142,8 @@ static QMGR_MESSAGE *qmgr_message_create(const char *queue_name, message->errors_to = 0; message->return_receipt = 0; message->data_size = 0; + message->warn_offset = 0; + message->warn_time = 0; message->rcpt_offset = 0; qmgr_rcpt_list_init(&message->rcpt_list); return (message); @@ -263,6 +271,11 @@ static int qmgr_message_read(QMGR_MESSAGE *message) } else if (rec_type == REC_TYPE_RRTO) { if (message->return_receipt == 0) message->return_receipt = mystrdup(start); + } else if (rec_type == REC_TYPE_WARN) { + if (message->warn_offset == 0) { + message->warn_offset = curr_offset; + message->warn_time = atol(start); + } } } while (rec_type > 0 && rec_type != REC_TYPE_END); @@ -307,6 +320,23 @@ static int qmgr_message_read(QMGR_MESSAGE *message) } } +/* qmgr_message_update_warn - update the time of next delay warning */ + +void qmgr_message_update_warn(QMGR_MESSAGE *message) +{ + + /* + * XXX eventually this should let us schedule multiple warnings, right + * now it just allows for one. + */ + if (qmgr_message_open(message) + || vstream_fseek(message->fp, message->warn_offset, SEEK_SET) < 0 + || rec_fprintf(message->fp, REC_TYPE_WARN, REC_TYPE_WARN_FORMAT, 0L) < 0 + || vstream_fflush(message->fp)) + msg_fatal("update queue file %s: %m", VSTREAM_PATH(message->fp)); + qmgr_message_close(message); +} + /* qmgr_message_sort_compare - compare recipient information */ static int qmgr_message_sort_compare(const void *p1, const void *p2) diff --git a/postfix/qmgr/qmgr_move.c b/postfix/qmgr/qmgr_move.c index 255145102..7f86197ec 100644 --- a/postfix/qmgr/qmgr_move.c +++ b/postfix/qmgr/qmgr_move.c @@ -45,6 +45,7 @@ /* Global library. */ #include +#include /* Application-specific. */ @@ -67,7 +68,7 @@ void qmgr_move(const char *src_queue, const char *dst_queue, msg_info("start move queue %s -> %s", src_queue, dst_queue); queue_dir = scan_dir_open(src_queue); - while ((queue_id = scan_dir_next(queue_dir)) != 0) { + while ((queue_id = mail_scan_dir_next(queue_dir)) != 0) { if (mail_queue_id_ok(queue_id)) { if (time_stamp > 0) { tbuf.actime = tbuf.modtime = time_stamp; diff --git a/postfix/qmgr/qmgr_queue.c b/postfix/qmgr/qmgr_queue.c index 8a41c71b2..f210b547f 100644 --- a/postfix/qmgr/qmgr_queue.c +++ b/postfix/qmgr/qmgr_queue.c @@ -104,7 +104,7 @@ int qmgr_queue_count; /* qmgr_queue_unthrottle_wrapper - in case (char *) != (struct *) */ -static void qmgr_queue_unthrottle_wrapper(char *context) +static void qmgr_queue_unthrottle_wrapper(int unused_event, char *context) { QMGR_QUEUE *queue = (QMGR_QUEUE *) context; @@ -123,6 +123,7 @@ static void qmgr_queue_unthrottle_wrapper(char *context) void qmgr_queue_unthrottle(QMGR_QUEUE *queue) { char *myname = "qmgr_queue_unthrottle"; + QMGR_TRANSPORT *transport = queue->transport; if (msg_verbose) msg_info("%s: queue %s", myname, queue->name); @@ -136,6 +137,8 @@ void qmgr_queue_unthrottle(QMGR_QUEUE *queue) msg_panic("%s: queue %s: window 0 reason 0", myname, queue->name); myfree(queue->reason); queue->reason = 0; + queue->window = transport->init_dest_concurrency; + return; } /* @@ -144,9 +147,8 @@ void qmgr_queue_unthrottle(QMGR_QUEUE *queue) * to the actual concurrency + 1, so that qmgr_queue_throttle() takes * effect quickly. */ -#define LIMIT_OK(limit, count) ((limit) == 0 || ((count) < (limit))) - - if (LIMIT_OK(queue->transport->dest_concurrency_limit, queue->window)) + if (transport->dest_concurrency_limit == 0 + || transport->dest_concurrency_limit > queue->busy_refcount) queue->window = queue->busy_refcount + 1; } @@ -167,18 +169,15 @@ void qmgr_queue_throttle(QMGR_QUEUE *queue, const char *reason) /* * Decrease the destination's concurrency limit until we reach zero, at - * which point the destination is declared dead. Set the destination's - * concurrency limit to the actual concurrency - 1, so that throttle - * operations take effect quickly. + * which point the destination is declared dead. Decrease the concurrency + * limit by one, instead of using actual concurrency - 1, to avoid + * declaring a host dead after just one single delivery failure. */ - queue->window = queue->busy_refcount - 1; + if (queue->window > 0) + queue->window--; /* - * Special case for a transport that just was declared dead. Gradually - * increase the time between the attempts to contact this destination, up - * to a configurable upper limit. When the destination is unreachable for - * a substantial amount of time, a message will eventually become so old - * that it will be bounced. + * Special case for a site that just was declared dead. */ if (queue->window == 0) { queue->reason = mystrdup(reason); @@ -250,7 +249,6 @@ QMGR_QUEUE *qmgr_queue_create(QMGR_TRANSPORT *transport, const char *site) * If possible, choose an initial concurrency of > 1 so that one bad * message or one bad network won't slow us down unnecessarily. */ -#define LIMIT_OK(limit, count) ((limit) == 0 || ((count) < (limit))) queue = (QMGR_QUEUE *) mymalloc(sizeof(QMGR_QUEUE)); qmgr_queue_count++; @@ -258,14 +256,11 @@ QMGR_QUEUE *qmgr_queue_create(QMGR_TRANSPORT *transport, const char *site) queue->todo_refcount = 0; queue->busy_refcount = 0; queue->transport = transport; - if (LIMIT_OK(transport->dest_concurrency_limit, var_init_dest_concurrency)) - queue->window = var_init_dest_concurrency; - else - queue->window = transport->dest_concurrency_limit; + queue->window = transport->init_dest_concurrency; QMGR_LIST_INIT(queue->todo); QMGR_LIST_INIT(queue->busy); queue->reason = 0; - QMGR_LIST_APPEND(transport->queue_list, queue); + QMGR_LIST_PREPEND(transport->queue_list, queue); htable_enter(transport->queue_byname, site, (char *) queue); return (queue); } diff --git a/postfix/qmgr/qmgr_scan.c b/postfix/qmgr/qmgr_scan.c index f73e1e1ed..e0d9fc079 100644 --- a/postfix/qmgr/qmgr_scan.c +++ b/postfix/qmgr/qmgr_scan.c @@ -64,6 +64,10 @@ #include #include +/* Global library. */ + +#include + /* Application-specific. */ #include "qmgr.h" @@ -129,14 +133,14 @@ char *qmgr_scan_next(QMGR_SCAN *scan_info) * Restart the scan if we reach the end and a queue scan request has * arrived in the mean time. */ - if (scan_info->handle && (path = scan_dir_next(scan_info->handle)) == 0) { + if (scan_info->handle && (path = mail_scan_dir_next(scan_info->handle)) == 0) { scan_info->handle = scan_dir_close(scan_info->handle); if (msg_verbose && (scan_info->nflags & QMGR_SCAN_START) == 0) msg_info("done %s queue scan", scan_info->queue); } if (!scan_info->handle && (scan_info->nflags & QMGR_SCAN_START)) { qmgr_scan_start(scan_info); - path = scan_dir_next(scan_info->handle); + path = mail_scan_dir_next(scan_info->handle); } return (path); } diff --git a/postfix/qmgr/qmgr_transport.c b/postfix/qmgr/qmgr_transport.c index a10714f56..cf3a0d6c8 100644 --- a/postfix/qmgr/qmgr_transport.c +++ b/postfix/qmgr/qmgr_transport.c @@ -107,7 +107,7 @@ struct QMGR_TRANSPORT_ALLOC { /* qmgr_transport_unthrottle_wrapper - in case (char *) != (struct *) */ -static void qmgr_transport_unthrottle_wrapper(char *context) +static void qmgr_transport_unthrottle_wrapper(int unused_event, char *context) { qmgr_transport_unthrottle((QMGR_TRANSPORT *) context); } @@ -162,6 +162,15 @@ void qmgr_transport_throttle(QMGR_TRANSPORT *transport, const char *reason) } } +/* qmgr_transport_abort - transport connect watchdog */ + +static void qmgr_transport_abort(int unused_event, char *context) +{ + QMGR_TRANSPORT_ALLOC *alloc = (QMGR_TRANSPORT_ALLOC *) context; + + msg_fatal("timeout connecting to transport: %s", alloc->transport->name); +} + /* qmgr_transport_event - delivery process availability notice */ static void qmgr_transport_event(int unused_event, char *context) @@ -175,6 +184,11 @@ static void qmgr_transport_event(int unused_event, char *context) if (msg_verbose) msg_info("transport_event: %s", alloc->transport->name); + /* + * Connection request completed. Stop the watchdog timer. + */ + event_cancel_timer(qmgr_transport_abort, context); + /* * Disable further read events that end up calling this function. */ @@ -281,6 +295,11 @@ void qmgr_transport_alloc(QMGR_TRANSPORT *transport, QMGR_TRANSPORT_ALLOC_NOT alloc->notify = notify; transport->flags |= QMGR_TRANSPORT_STAT_BUSY; ENABLE_EVENTS(vstream_fileno(alloc->stream), EVENT_HANDLER, (char *) alloc); + + /* + * Guard against broken systems. + */ + event_request_timer(qmgr_transport_abort, (char *) alloc, var_ipc_timeout); } /* qmgr_transport_create - create transport instance */ @@ -305,6 +324,12 @@ QMGR_TRANSPORT *qmgr_transport_create(const char *name) get_config_int2(name, "_destination_recipient_limit", var_dest_rcpt_limit, 0, 0); + if (transport->dest_concurrency_limit == 0 + || transport->dest_concurrency_limit >= var_init_dest_concurrency) + transport->init_dest_concurrency = var_init_dest_concurrency; + else + transport->init_dest_concurrency = transport->dest_concurrency_limit; + transport->queue_byname = htable_create(0); QMGR_LIST_INIT(transport->queue_list); transport->reason = 0; diff --git a/postfix/sendmail/.indent.pro b/postfix/sendmail/.indent.pro index e59d396ad..b38eded21 100644 --- a/postfix/sendmail/.indent.pro +++ b/postfix/sendmail/.indent.pro @@ -6,6 +6,7 @@ -TBOUNCE_STAT -TCLEANUP_STATE -TCLIENT_LIST +-TCLNT_STREAM -TCONFIG_BOOL_FN_TABLE -TCONFIG_BOOL_TABLE -TCONFIG_INT_FN_TABLE @@ -25,6 +26,8 @@ -TDICT_NISPLUS -TDICT_NODE -TDICT_OPEN_INFO +-TDICT_PCRE +-TDICT_UNIX -TDNS_FIXED -TDNS_REPLY -TDNS_RR @@ -68,8 +71,14 @@ -TRECIPIENT_LIST -TREC_TYPE_NAME -TRESOLVE_REPLY +-TRESPONSE -TSCAN_DIR +-TSCAN_INFO +-TSCAN_OBJ +-TSESSION -TSINGLE_SERVER +-TSINK_COMMAND +-TSINK_STATE -TSMTPD_STATE -TSMTPD_TOKEN -TSMTP_ADDR diff --git a/postfix/sendmail/sendmail.c b/postfix/sendmail/sendmail.c index a7db22a4c..6c1535aae 100644 --- a/postfix/sendmail/sendmail.c +++ b/postfix/sendmail/sendmail.c @@ -19,8 +19,8 @@ /* /* By default, \fBsendmail\fR reads a message from standard input /* and arranges for delivery. \fBsendmail\fR attempts to create -/* a queue file in the \fBmaildrop\fR directory. If the process has -/* no write permission, the message is piped through the +/* a queue file in the \fBmaildrop\fR directory. If that directory +/* is not world-writable, the message is piped through the /* \fBpostdrop\fR(1) command, which is expected to execute with /* suitable privileges. /* @@ -112,6 +112,10 @@ /* .IP "\fB-o \fIx value\fR (ignored)" /* Set option \fIx\fR to \fIvalue\fR. Use the equivalent /* configuration parameter in \fBmain.cf\fR instead. +/* .IP "\fB-r \fIsender\fR" +/* Set the envelope sender address. This is the address where +/* delivery problems are sent to, unless the message contains an +/* \fBErrors-To:\fR message header. /* .IP \fB-q\fR /* Flush the mail queue. This is implemented by kicking the /* \fBqmgr\fR(8) daemon. @@ -303,6 +307,7 @@ static void enqueue(const char *sender, const char *full_name, char **recipients char *postdrop_command; uid_t uid = getuid(); int status; + struct stat st; /* * Initialize. @@ -324,10 +329,12 @@ static void enqueue(const char *sender, const char *full_name, char **recipients * Open the queue file. Save the queue file name, so the run-time error * handler can clean up in case of errors. * - * If the user has no write permission, let the postdrop command open the + * If the queue is not world-writable, let the postdrop command open the * queue file. */ - if (access(MAIL_QUEUE_MAILDROP, W_OK) == 0) { + if (stat(MAIL_QUEUE_MAILDROP, &st) < 0) + msg_fatal("No maildrop directory %s: %m", MAIL_QUEUE_MAILDROP); + if (st.st_mode & S_IWOTH) { handle = mail_stream_file(MAIL_QUEUE_MAILDROP, MAIL_CLASS_PUBLIC, MAIL_SERVICE_PICKUP); sendmail_path = mystrdup(VSTREAM_PATH(handle->stream)); @@ -382,7 +389,7 @@ static void enqueue(const char *sender, const char *full_name, char **recipients * delivered intact via SMTP. Strip leading From_ lines. For the benefit * of UUCP environments, also get rid of leading >>>From_ lines. */ - rec_fprintf(dst, REC_TYPE_MESG, REC_TYPE_MESG_FORMAT, 0); + rec_fprintf(dst, REC_TYPE_MESG, REC_TYPE_MESG_FORMAT, 0L); skip_from_ = 1; strip_cr = STRIP_CR_DUNNO; while ((type = rec_streamlf_get(VSTREAM_IN, buf, var_line_limit)) @@ -635,7 +642,7 @@ int main(int argc, char **argv) optind++; continue; } - if ((c = GETOPT(argc, argv, "B:C:F:IN:R:X:b:ce:f:h:imno:p:q:tvx")) <= 0) + if ((c = GETOPT(argc, argv, "B:C:F:IN:R:X:b:ce:f:h:imno:p:r:q:tvx")) <= 0) break; switch (c) { default: @@ -701,6 +708,9 @@ int main(int argc, char **argv) break; } break; + case 'r': /* obsoleted by -f */ + sender = optarg; + break; case 'q': if (optarg[0] && !ISDIGIT(optarg[0])) msg_fatal("-q%c is not implemented", optarg[0]); diff --git a/postfix/showq/.indent.pro b/postfix/showq/.indent.pro index e59d396ad..b38eded21 100644 --- a/postfix/showq/.indent.pro +++ b/postfix/showq/.indent.pro @@ -6,6 +6,7 @@ -TBOUNCE_STAT -TCLEANUP_STATE -TCLIENT_LIST +-TCLNT_STREAM -TCONFIG_BOOL_FN_TABLE -TCONFIG_BOOL_TABLE -TCONFIG_INT_FN_TABLE @@ -25,6 +26,8 @@ -TDICT_NISPLUS -TDICT_NODE -TDICT_OPEN_INFO +-TDICT_PCRE +-TDICT_UNIX -TDNS_FIXED -TDNS_REPLY -TDNS_RR @@ -68,8 +71,14 @@ -TRECIPIENT_LIST -TREC_TYPE_NAME -TRESOLVE_REPLY +-TRESPONSE -TSCAN_DIR +-TSCAN_INFO +-TSCAN_OBJ +-TSESSION -TSINGLE_SERVER +-TSINK_COMMAND +-TSINK_STATE -TSMTPD_STATE -TSMTPD_TOKEN -TSMTP_ADDR diff --git a/postfix/showq/Makefile.in b/postfix/showq/Makefile.in index efe8d71bd..c020235ba 100644 --- a/postfix/showq/Makefile.in +++ b/postfix/showq/Makefile.in @@ -69,7 +69,9 @@ showq.o: ../include/mail_proto.h showq.o: ../include/iostuff.h showq.o: ../include/mail_date.h showq.o: ../include/mail_params.h +showq.o: ../include/mail_scan_dir.h showq.o: ../include/config.h showq.o: ../include/record.h showq.o: ../include/rec_type.h +showq.o: ../include/htable.h showq.o: ../include/mail_server.h diff --git a/postfix/showq/showq.c b/postfix/showq/showq.c index f6443c226..8b23e77f2 100644 --- a/postfix/showq/showq.c +++ b/postfix/showq/showq.c @@ -75,9 +75,11 @@ #include #include #include +#include #include #include #include +#include /* Single-threaded server skeleton. */ @@ -85,25 +87,27 @@ /* Application-specific. */ +int var_dup_filter_limit; + #define STRING_FORMAT "%-10s %8s %-20s %s\n" -#define DATA_FORMAT "%-10s %8ld %20.20s %s\n" +#define DATA_FORMAT "%-10s%c%8ld %20.20s %s\n" + +static void showq_reasons(VSTREAM *, VSTREAM *, HTABLE *); -static void showq_report(VSTREAM *client, char *id, VSTREAM *qfile, long size, int stop) +static void showq_report(VSTREAM *client, char *queue, char *id, + VSTREAM *qfile, long size) { VSTRING *buf = vstring_alloc(100); int rec_type; time_t arrival_time = 0; char *start; long msg_size = 0; + VSTREAM *logfile; + HTABLE *dup_filter = 0; + char status = (strcmp(queue, MAIL_QUEUE_ACTIVE) == 0 ? '*' : ' '); - /* - * XXX Stop at the designated record type. This is a hack to avoid - * listing recipients, so that the defer log can be listed instead. - */ while (!vstream_ferror(client) && (rec_type = rec_get(qfile, buf, 0)) > 0) { start = vstring_str(buf); - if (rec_type == stop) - break; switch (rec_type) { case REC_TYPE_TIME: arrival_time = atol(start); @@ -114,16 +118,17 @@ static void showq_report(VSTREAM *client, char *id, VSTREAM *qfile, long size, i case REC_TYPE_FROM: if (*start == 0) start = "(MAILER-DAEMON)"; - vstream_fprintf(client, DATA_FORMAT, - id, msg_size > 0 ? msg_size : size, arrival_time > 0 ? + vstream_fprintf(client, DATA_FORMAT, id, status, + msg_size > 0 ? msg_size : size, arrival_time > 0 ? asctime(localtime(&arrival_time)) : "??", printable(start, '?')); break; case REC_TYPE_RCPT: - if (*start == 0) + if (*start == 0) /* can't happen? */ start = "(MAILER-DAEMON)"; - vstream_fprintf(client, STRING_FORMAT, - "", "", "", printable(start, '?')); + if (dup_filter == 0 || htable_locate(dup_filter, start) == 0) + vstream_fprintf(client, STRING_FORMAT, + "", "", "", printable(start, '?')); break; case REC_TYPE_MESG: if (vstream_fseek(qfile, atol(start), SEEK_SET) < 0) @@ -132,13 +137,35 @@ static void showq_report(VSTREAM *client, char *id, VSTREAM *qfile, long size, i case REC_TYPE_END: break; } + + /* + * With the heading printed, see if there is a defer logfile. The + * defer logfile is not necessarily complete: delivery may be + * interrupted (postfix stop or reload) before all recipients have + * been tried. + * + * Therefore we keep a record of recipients found in the defer logfile, + * and try to avoid listing those recipients again when processing + * the remainder of the queue file. + */ + if (rec_type == REC_TYPE_FROM + && dup_filter == 0 + && (logfile = mail_queue_open(MAIL_QUEUE_DEFER, id, + O_RDONLY, 0)) != 0) { + dup_filter = htable_create(var_dup_filter_limit); + showq_reasons(client, logfile, dup_filter); + if (vstream_fclose(logfile)) + msg_warn("close %s %s: %m", MAIL_QUEUE_DEFER, id); + } } vstring_free(buf); + if (dup_filter) + htable_free(dup_filter, (void (*) (char *)) 0); } /* showq_reasons - show deferral reasons */ -static void showq_reasons(VSTREAM *client, VSTREAM *logfile) +static void showq_reasons(VSTREAM *client, VSTREAM *logfile, HTABLE *dup_filter) { VSTRING *buf = vstring_alloc(100); char *recipient; @@ -185,6 +212,16 @@ static void showq_reasons(VSTREAM *client, VSTREAM *logfile) } *cp = 0; + /* + * Update the duplicate filter. + */ + if (*recipient == 0) /* can't happen? */ + recipient = "(MAILER-DAEMON)"; + if (var_dup_filter_limit == 0 + || dup_filter->used < var_dup_filter_limit) + if (htable_locate(dup_filter, recipient) == 0) + htable_enter(dup_filter, recipient, (char *) 0); + /* * Find the reason for deferral. Put parentheses around it. */ @@ -204,7 +241,8 @@ static void showq_reasons(VSTREAM *client, VSTREAM *logfile) saved_reason = mystrdup(reason); vstream_fprintf(client, "%78s\n", reason); } - vstream_fprintf(client, STRING_FORMAT, "", "", "", recipient); + vstream_fprintf(client, STRING_FORMAT, "", "", "", + printable(recipient, '?')); } if (saved_reason) myfree(saved_reason); @@ -218,7 +256,6 @@ static void showq_service(VSTREAM *client, char *unused_service, char **argv) { char **queue; VSTREAM *qfile; - VSTREAM *logfile; const char *path; int status; char *id; @@ -229,6 +266,7 @@ static void showq_service(VSTREAM *client, char *unused_service, char **argv) MAIL_QUEUE_INCOMING, MAIL_QUEUE_ACTIVE, MAIL_QUEUE_DEFERRED, + /* No maildrop until we can disable recursive scans. */ 0, }; @@ -248,7 +286,7 @@ static void showq_service(VSTREAM *client, char *unused_service, char **argv) SCAN_DIR *scan = scan_dir_open(*queue); char *saved_id = 0; - while ((id = scan_dir_next(scan)) != 0) { + while ((id = mail_scan_dir_next(scan)) != 0) { /* * XXX I have seen showq loop on the same queue id. That would be @@ -274,18 +312,7 @@ static void showq_service(VSTREAM *client, char *unused_service, char **argv) vstream_fprintf(client, "\n"); if ((qfile = mail_queue_open(*queue, id, O_RDONLY, 0)) != 0) { queue_size += st.st_size; - if (strcmp(*queue, MAIL_QUEUE_DEFERRED) == 0 - && (logfile = mail_queue_open(MAIL_QUEUE_DEFER, id, - O_RDONLY, 0)) != 0) { - - showq_report(client, id, qfile, (long) st.st_size, - REC_TYPE_RCPT); - showq_reasons(client, logfile); - if (vstream_fclose(logfile)) - msg_warn("close %s %s: %m", MAIL_QUEUE_DEFER, id); - } else { - showq_report(client, id, qfile, (long) st.st_size, 0); - } + showq_report(client, *queue, id, qfile, (long) st.st_size); if (vstream_fclose(qfile)) msg_warn("close file %s %s: %m", *queue, id); } else if (strcmp(*queue, MAIL_QUEUE_MAILDROP) == 0) { @@ -316,5 +343,12 @@ static void showq_service(VSTREAM *client, char *unused_service, char **argv) int main(int argc, char **argv) { - single_server_main(argc, argv, showq_service, 0); + static CONFIG_INT_TABLE int_table[] = { + VAR_DUP_FILTER_LIMIT, DEF_DUP_FILTER_LIMIT, &var_dup_filter_limit, 0, 0, + 0, + }; + + single_server_main(argc, argv, showq_service, + MAIL_SERVER_INT_TABLE, int_table, + 0); } diff --git a/postfix/smtp/.indent.pro b/postfix/smtp/.indent.pro index e59d396ad..b38eded21 100644 --- a/postfix/smtp/.indent.pro +++ b/postfix/smtp/.indent.pro @@ -6,6 +6,7 @@ -TBOUNCE_STAT -TCLEANUP_STATE -TCLIENT_LIST +-TCLNT_STREAM -TCONFIG_BOOL_FN_TABLE -TCONFIG_BOOL_TABLE -TCONFIG_INT_FN_TABLE @@ -25,6 +26,8 @@ -TDICT_NISPLUS -TDICT_NODE -TDICT_OPEN_INFO +-TDICT_PCRE +-TDICT_UNIX -TDNS_FIXED -TDNS_REPLY -TDNS_RR @@ -68,8 +71,14 @@ -TRECIPIENT_LIST -TREC_TYPE_NAME -TRESOLVE_REPLY +-TRESPONSE -TSCAN_DIR +-TSCAN_INFO +-TSCAN_OBJ +-TSESSION -TSINGLE_SERVER +-TSINK_COMMAND +-TSINK_STATE -TSMTPD_STATE -TSMTPD_TOKEN -TSMTP_ADDR diff --git a/postfix/smtp/Makefile.in b/postfix/smtp/Makefile.in index 1b63ff9ad..91e217546 100644 --- a/postfix/smtp/Makefile.in +++ b/postfix/smtp/Makefile.in @@ -92,6 +92,7 @@ smtp_addr.o: ../include/vstring.h smtp_addr.o: ../include/vbuf.h smtp_addr.o: ../include/mymalloc.h smtp_addr.o: ../include/inet_addr_list.h +smtp_addr.o: ../include/stringops.h smtp_addr.o: ../include/mail_params.h smtp_addr.o: ../include/own_inet_addr.h smtp_addr.o: ../include/dns.h diff --git a/postfix/smtp/smtp.c b/postfix/smtp/smtp.c index bededd5f5..48274ac30 100644 --- a/postfix/smtp/smtp.c +++ b/postfix/smtp/smtp.c @@ -62,6 +62,12 @@ /* List of domain or network patterns. When a remote host matches /* a pattern, increase the verbose logging level by the amount /* specified in the \fBdebug_peer_level\fR parameter. +/* .IP \fBfallback_relay\fR +/* Hosts to hand off mail to if a message destination is not found +/* or if a destination is unreachable. +/* .IP \fBignore_mx_lookup_error\fR +/* When a name server fails to respond to an MX query, search for an +/* A record instead of assuming that the name server will recover. /* .IP \fBinet_interfaces\fR /* The network interface addresses that this mail system receives /* mail on. When any of those addresses appears in the list of mail @@ -70,6 +76,10 @@ /* .IP \fBnotify_classes\fR /* 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_quit_response\fR +/* Do not wait for the server response after sending QUIT. /* .SH "Resource controls" /* .ad /* .fi @@ -178,6 +188,10 @@ char *var_inet_interfaces; char *var_debug_peer_list; int var_debug_peer_level; char *var_notify_classes; +int var_smtp_skip_4xx_greeting; +int var_ign_mx_lookup_err; +int var_skip_quit_resp; +char *var_fallback_relay; /* * Global variables. smtp_errno is set by the address lookup routines and by @@ -299,6 +313,7 @@ int main(int argc, char **argv) static CONFIG_STR_TABLE str_table[] = { VAR_DEBUG_PEER_LIST, DEF_DEBUG_PEER_LIST, &var_debug_peer_list, 0, 0, VAR_NOTIFY_CLASSES, DEF_NOTIFY_CLASSES, &var_notify_classes, 0, 0, + VAR_FALLBACK_RELAY, DEF_FALLBACK_RELAY, &var_fallback_relay, 0, 0, 0, }; static CONFIG_INT_TABLE int_table[] = { @@ -313,10 +328,17 @@ int main(int argc, char **argv) VAR_DEBUG_PEER_LEVEL, DEF_DEBUG_PEER_LEVEL, &var_debug_peer_level, 1, 0, 0, }; + static CONFIG_BOOL_TABLE bool_table[] = { + VAR_SMTP_SKIP_4XX, DEF_SMTP_SKIP_4XX, &var_smtp_skip_4xx_greeting, + VAR_IGN_MX_LOOKUP_ERR, DEF_IGN_MX_LOOKUP_ERR, &var_ign_mx_lookup_err, + VAR_SKIP_QUIT_RESP, DEF_SKIP_QUIT_RESP, &var_skip_quit_resp, + 0, + }; single_server_main(argc, argv, smtp_service, MAIL_SERVER_INT_TABLE, int_table, MAIL_SERVER_STR_TABLE, str_table, + MAIL_SERVER_BOOL_TABLE, bool_table, MAIL_SERVER_PRE_INIT, debug_peer_init, 0); } diff --git a/postfix/smtp/smtp_addr.c b/postfix/smtp/smtp_addr.c index 73ad0972e..f1d2bea13 100644 --- a/postfix/smtp/smtp_addr.c +++ b/postfix/smtp/smtp_addr.c @@ -18,13 +18,13 @@ /* lookups are done via the Internet domain name service (DNS). /* A reasonable number of CNAME indirections is permitted. /* -/* smtp_domain_addr() looks up the network addresses for mail -/* exchanger hosts listed for the named domain. Addresses are +/* smtp_domain_addr() looks up the network addresses for mail +/* exchanger hosts listed for the named domain. Addresses are /* returned in most-preferred first order. The result is truncated -/* so that it contains only hosts that are more preferred than the +/* so that it contains only hosts that are more preferred than the /* local mail server itself. /* -/* When no mail exchanger is listed in the DNS for \fIname\fR, the +/* When no mail exchanger is listed in the DNS for \fIname\fR, the /* request is passed to smtp_host_addr(). /* /* smtp_host_addr() looks up all addresses listed for the named @@ -75,6 +75,7 @@ #include #include #include +#include /* Global library. */ @@ -161,6 +162,83 @@ static DNS_RR *smtp_addr_list(DNS_RR *mx_names, VSTRING *why) return (addr_list); } +/* smtp_addr_fallback - add list of fallback addresses */ + +static DNS_RR *smtp_addr_fallback(DNS_RR *addr_list) +{ + static DNS_RR *fallback_list = 0; + DNS_RR *mx_names; + DNS_RR *mx_addr_list; + DNS_RR *addr; + char *saved_fallback_relay; + char *cp; + char *relay; + int saved_smtp_errno = smtp_errno; + VSTRING *why; + DNS_RR *rr; + unsigned int pref; + + /* + * Build a cached list of fall-back host addresses. Issue a warning when + * a fall-back host or domain is not found. This is most likely a local + * configuration problem. + * + * XXX For the sake of admin-friendliness we want to support MX lookups for + * fall-back relays. This comes at a price: the fallback relay lookup + * routine almost entirely duplicates the smtp_domain_addr() routine. + * + * Fall-back hosts are given a preference that is outside the range of valid + * DNS preferences (unsigned 16-bit integer). + */ +#define FB_PREF (0xffff + 1) + + if (fallback_list == 0) { + why = vstring_alloc(1); + cp = saved_fallback_relay = mystrdup(var_fallback_relay); + for (pref = FB_PREF; (relay = mystrtok(&cp, " \t\r\n,")) != 0; pref++) { + smtp_errno = 0; + switch (dns_lookup(relay, T_MX, 0, &mx_names, (VSTRING *) 0, why)) { + default: + smtp_errno = SMTP_RETRY; + break; + case DNS_FAIL: + smtp_errno = SMTP_FAIL; + break; + case DNS_OK: + mx_addr_list = smtp_addr_list(mx_names, why); + dns_rr_free(mx_names); + for (addr = mx_addr_list; addr; addr = addr->next) + addr->pref = pref; + fallback_list = dns_rr_append(fallback_list, mx_addr_list); + break; + case DNS_NOTFOUND: + fallback_list = smtp_addr_one(fallback_list, relay, pref, why); + break; + } + if (smtp_errno != SMTP_OK) + msg_warn("look up fall-back relay %s: %s", + relay, vstring_str(why)); + } + vstring_free(why); + myfree(saved_fallback_relay); + } + + /* + * Append a copy of the fall-back address list to the mail exchanger + * address list - which may be an empty list if no mail exchanger was + * found. + */ + for (rr = fallback_list; rr; rr = rr->next) + addr_list = dns_rr_append(addr_list, dns_rr_copy(rr)); + + /* + * Clean up. + */ + smtp_errno = saved_smtp_errno; + + return (addr_list); +} + /* smtp_find_self - spot myself in a crowd of mail exchangers */ static DNS_RR *smtp_find_self(DNS_RR *addr_list) @@ -241,18 +319,27 @@ DNS_RR *smtp_domain_addr(char *name, VSTRING *why) * truncate the list so that it contains only hosts that are more * preferred than myself. When no MX resource records exist, look up the * addresses listed for this name. + * + * XXX Optionally do A lookups even when the MX lookup didn't complete. + * Unfortunately with some DNS servers this is not a transient problem. */ switch (dns_lookup(name, T_MX, 0, &mx_names, (VSTRING *) 0, why)) { default: smtp_errno = SMTP_RETRY; + if (var_ign_mx_lookup_err) + addr_list = smtp_host_addr(name, why); break; case DNS_FAIL: smtp_errno = SMTP_FAIL; + if (var_ign_mx_lookup_err) + addr_list = smtp_host_addr(name, why); break; case DNS_OK: mx_names = dns_rr_sort(mx_names, smtp_compare_mx); addr_list = smtp_addr_list(mx_names, why); dns_rr_free(mx_names); + if (*var_fallback_relay) + addr_list = smtp_addr_fallback(addr_list); if (msg_verbose) smtp_print_addr(name, addr_list); if ((self = smtp_find_self(addr_list)) != 0) @@ -288,6 +375,8 @@ DNS_RR *smtp_host_addr(char *host, VSTRING *why) (char *) &addr, sizeof(addr)); } else { addr_list = smtp_addr_one((DNS_RR *) 0, host, PREF0, why); + if (*var_fallback_relay) + addr_list = smtp_addr_fallback(addr_list); } if (msg_verbose) smtp_print_addr(host, addr_list); diff --git a/postfix/smtp/smtp_connect.c b/postfix/smtp/smtp_connect.c index 132d878da..6cd3b2083 100644 --- a/postfix/smtp/smtp_connect.c +++ b/postfix/smtp/smtp_connect.c @@ -128,6 +128,7 @@ static SMTP_SESSION *smtp_connect_addr(DNS_RR *addr, unsigned port, int saved_errno; VSTREAM *stream; int ch; + unsigned long inaddr; /* * Sanity checks. @@ -152,14 +153,17 @@ static SMTP_SESSION *smtp_connect_addr(DNS_RR *addr, unsigned port, * the mail appears to come from the "right" machine address. */ addr_list = own_inet_addr_list(); - if (addr_list->used == 1 - && strcasecmp(var_inet_interfaces, DEF_INET_INTERFACES) != 0) { + if (addr_list->used == 1) { sin.sin_port = 0; memcpy((char *) &sin.sin_addr, addr_list->addrs, sizeof(sin.sin_addr)); - if (bind(sock, (struct sockaddr *) & sin, sizeof(sin)) < 0) - msg_warn("%s: bind %s: %m", myname, inet_ntoa(addr_list->addrs[0])); - else if (msg_verbose) - msg_info("%s: bind %s", myname, inet_ntoa(addr_list->addrs[0])); + inaddr = ntohl(sin.sin_addr.s_addr); + if (!IN_CLASSA(inaddr) + || !((inaddr & IN_CLASSA_NET) >> IN_CLASSA_NSHIFT) == IN_LOOPBACKNET) { + if (bind(sock, (struct sockaddr *) & sin, sizeof(sin)) < 0) + msg_warn("%s: bind %s: %m", myname, inet_ntoa(sin.sin_addr)); + if (msg_verbose) + msg_info("%s: bind %s", myname, inet_ntoa(sin.sin_addr)); + } } /* @@ -211,19 +215,15 @@ static SMTP_SESSION *smtp_connect_addr(DNS_RR *addr, unsigned port, } /* - * Skip this host if it does not send a numeric response. XXX The - * smtp_chat module does allow non-numeric responses, so disallowing them - * here seems inconsistent. + * Skip this host if it sends a 4xx greeting. */ -#if 0 - if (!ISDIGIT(ch)) { - vstring_sprintf(why, "connect to %s: non-numeric server response", + if (ch == '4' && var_smtp_skip_4xx_greeting) { + vstring_sprintf(why, "connect to %s: server refused mail service", addr->name); smtp_errno = SMTP_RETRY; vstream_fclose(stream); return (0); } -#endif vstream_ungetc(stream, ch); return (smtp_session_alloc(stream, addr->name, inet_ntoa(sin.sin_addr))); } diff --git a/postfix/smtp/smtp_proto.c b/postfix/smtp/smtp_proto.c index 87433cf1e..b29b5c418 100644 --- a/postfix/smtp/smtp_proto.c +++ b/postfix/smtp/smtp_proto.c @@ -106,9 +106,13 @@ /* * Sender and receiver state. A session does not necessarily go through a - * linear progression, so states should be compared for equality only. + * linear progression, but states are guaranteed to not jump backwards. * Normal sessions go from MAIL->RCPT->DATA->DOT->QUIT->LAST. The states * MAIL, RCPT, and DATA may also be followed by ABORT->QUIT->LAST. + * + * By default, the receiver skips the QUIT response. Some SMTP servers + * disconnect after responding to ".", and some SMTP servers wait before + * responding to QUIT. */ #define SMTP_STATE_MAIL 0 #define SMTP_STATE_RCPT 1 @@ -265,7 +269,7 @@ int smtp_xfer(SMTP_STATE *state) #define RETURN(x) do { vstring_free(next_command); return (x); } while (0) #define SENDER_IS_AHEAD \ - (recv_state != send_state || recv_rcpt != send_rcpt) + (recv_state < send_state || recv_rcpt != send_rcpt) #define SENDER_IN_WAIT_STATE \ (send_state == SMTP_STATE_DOT || send_state == SMTP_STATE_LAST) @@ -410,7 +414,8 @@ int smtp_xfer(SMTP_STATE *state) */ if (recv_state < SMTP_STATE_MAIL || recv_state > SMTP_STATE_QUIT) - msg_panic("%s: bad receiver state %d", myname, recv_state); + msg_panic("%s: bad receiver state %d (sender state %d)", + myname, recv_state, send_state); /* * Receive the next server response. Use the proper timeout, @@ -510,14 +515,16 @@ int smtp_xfer(SMTP_STATE *state) } } } - recv_state = SMTP_STATE_QUIT; + recv_state = (var_skip_quit_resp ? + SMTP_STATE_LAST : SMTP_STATE_QUIT); break; /* * Ignore the RSET response. */ case SMTP_STATE_ABORT: - recv_state = SMTP_STATE_QUIT; + recv_state = (var_skip_quit_resp ? + SMTP_STATE_LAST : SMTP_STATE_QUIT); break; /* diff --git a/postfix/smtpd/.indent.pro b/postfix/smtpd/.indent.pro index e59d396ad..b38eded21 100644 --- a/postfix/smtpd/.indent.pro +++ b/postfix/smtpd/.indent.pro @@ -6,6 +6,7 @@ -TBOUNCE_STAT -TCLEANUP_STATE -TCLIENT_LIST +-TCLNT_STREAM -TCONFIG_BOOL_FN_TABLE -TCONFIG_BOOL_TABLE -TCONFIG_INT_FN_TABLE @@ -25,6 +26,8 @@ -TDICT_NISPLUS -TDICT_NODE -TDICT_OPEN_INFO +-TDICT_PCRE +-TDICT_UNIX -TDNS_FIXED -TDNS_REPLY -TDNS_RR @@ -68,8 +71,14 @@ -TRECIPIENT_LIST -TREC_TYPE_NAME -TRESOLVE_REPLY +-TRESPONSE -TSCAN_DIR +-TSCAN_INFO +-TSCAN_OBJ +-TSESSION -TSINGLE_SERVER +-TSINK_COMMAND +-TSINK_STATE -TSMTPD_STATE -TSMTPD_TOKEN -TSMTP_ADDR diff --git a/postfix/smtpd/Makefile.in b/postfix/smtpd/Makefile.in index ad0665084..436dbd059 100644 --- a/postfix/smtpd/Makefile.in +++ b/postfix/smtpd/Makefile.in @@ -34,8 +34,10 @@ smtpd_token: smtpd_token.c $(LIBS) $(CC) $(CFLAGS) -DTEST -o $@ $@.c $(LIBS) $(SYSLIBS) smtpd_check: smtpd_check.c $(SMTPD_CHECK_OBJ) $(LIBS) + mv $@.o junk $(CC) $(CFLAGS) -DTEST -o $@ smtpd_check.c $(SMTPD_CHECK_OBJ) \ $(LIBS) $(SYSLIBS) + mv junk $@.o printfck: $(OBJS) $(PROG) rm -rf printfck @@ -49,7 +51,7 @@ lint: lint $(DEFS) $(SRCS) $(LINTFIX) clean: - rm -f *.o *core $(PROG) $(TESTPROG) junk *.db *.out + rm -f *.o *core $(PROG) $(TESTPROG) junk *.db *.out *.tmp rm -rf printfck tidy: clean @@ -65,6 +67,20 @@ depend: $(MAKES) done) | grep -v '[.][o][:][ ][/]' >$$$$ && mv $$$$ Makefile.in @make -f Makefile.in Makefile +tests: smtpd_check_test smtpd_check_test2 + +smtpd_check_test: smtpd_check smtpd_check.in smtpd_check.ref + ../postmap/postmap smtpd_check_access + ./smtpd_check smtpd_check.tmp 2>&1 + diff smtpd_check.ref smtpd_check.tmp + rm -f smtpd_check.tmp smtpd_check_access.* + +smtpd_check_test2: smtpd_check smtpd_check.in2 smtpd_check.ref2 + ../postmap/postmap smtpd_check_access + ./smtpd_check smtpd_check.tmp 2>&1 + diff smtpd_check.ref2 smtpd_check.tmp + rm -f smtpd_check.tmp smtpd_check_access.* + # do not edit below this line - it is generated by 'make depend' smtpd.o: smtpd.c smtpd.o: ../include/sys_defs.h @@ -77,8 +93,8 @@ smtpd.o: ../include/vstring_vstream.h smtpd.o: ../include/stringops.h smtpd.o: ../include/events.h smtpd.o: ../include/smtp_stream.h -smtpd.o: ../include/peer_name.h smtpd.o: ../include/valid_hostname.h +smtpd.o: ../include/peer_name.h smtpd.o: ../include/mail_params.h smtpd.o: ../include/record.h smtpd.o: ../include/rec_type.h diff --git a/postfix/smtpd/smtpd.c b/postfix/smtpd/smtpd.c index a19fe013a..5f57eced0 100644 --- a/postfix/smtpd/smtpd.c +++ b/postfix/smtpd/smtpd.c @@ -55,6 +55,8 @@ /* .SH Miscellaneous /* .ad /* .fi +/* .IP \fBalways_bcc\fR +/* Address to send a copy of each message that enters the system. /* .IP \fBcommand_directory\fR /* Location of Postfix support commands (default: /* \fB$program_directory\fR). @@ -205,11 +207,11 @@ #include #include #include -#include #include /* Global library. */ +#include #include #include #include @@ -269,6 +271,8 @@ char *var_maps_rbl_domains; int var_helo_required; int var_reject_code; int var_smtpd_err_sleep; +int var_non_fqdn_code; +char *var_always_bcc; /* * Global state, for stand-alone mode queue file cleanup. When this is @@ -580,6 +584,8 @@ static int data_cmd(SMTPD_STATE *state, int argc, SMTPD_TOKEN *unused_argv) * segment, and prepend our own Received: header. If there is only one * recipient, list the recipient address. */ + if (*var_always_bcc) + rec_fputs(state->cleanup, REC_TYPE_RCPT, var_always_bcc); rec_fputs(state->cleanup, REC_TYPE_MESG, ""); rec_fprintf(state->cleanup, REC_TYPE_NORM, "Received: from %s (%s [%s])", @@ -1123,6 +1129,7 @@ int main(int argc, char **argv) VAR_ACCESS_MAP_CODE, DEF_ACCESS_MAP_CODE, &var_access_map_code, 0, 0, VAR_REJECT_CODE, DEF_REJECT_CODE, &var_reject_code, 0, 0, VAR_SMTPD_ERR_SLEEP, DEF_SMTPD_ERR_SLEEP, &var_smtpd_err_sleep, 0, 0, + VAR_NON_FQDN_CODE, DEF_NON_FQDN_CODE, &var_non_fqdn_code, 0, 0, 0, }; static CONFIG_BOOL_TABLE bool_table[] = { @@ -1140,6 +1147,7 @@ int main(int argc, char **argv) VAR_RCPT_CHECKS, DEF_RCPT_CHECKS, &var_rcpt_checks, 0, 0, VAR_ETRN_CHECKS, DEF_ETRN_CHECKS, &var_etrn_checks, 0, 0, VAR_MAPS_RBL_DOMAINS, DEF_MAPS_RBL_DOMAINS, &var_maps_rbl_domains, 0, 0, + VAR_ALWAYS_BCC, DEF_ALWAYS_BCC, &var_always_bcc, 0, 0, 0, }; diff --git a/postfix/smtpd/smtpd_check.c b/postfix/smtpd/smtpd_check.c index b4c407798..c59fe1e92 100644 --- a/postfix/smtpd/smtpd_check.c +++ b/postfix/smtpd/smtpd_check.c @@ -92,6 +92,12 @@ /* in HELO/EHLO commands. /* This violates the RFC. You must enable this for some popular /* PC mail clients. +/* .IP reject_non_fqdn_hostname +/* .IP reject_non_fqdn_sender +/* .IP reject_non_fqdn_recipient +/* Require that the HELO, MAIL FROM or RCPT TO commands specify +/* a fully-qualified domain name. The non_fqdn_reject_code parameter +/* specifies the error code (default: 504). /* .IP reject_invalid_hostname /* Reject the request when the HELO/EHLO hostname does not satisfy RFC /* requirements. The underscore is considered a legal hostname character, @@ -264,6 +270,7 @@ static jmp_buf smtpd_check_buf; * memory manager routines. */ static RESOLVE_REPLY reply; +static VSTRING *query; static VSTRING *error_text; /* @@ -327,6 +334,7 @@ void smtpd_check_init(void) * used for returning error responses. */ resolve_clnt_init(&reply); + query = vstring_alloc(10); error_text = vstring_alloc(10); /* @@ -410,12 +418,66 @@ static int permit_mynetworks(SMTPD_STATE *state) return (SMTPD_CHECK_DUNNO); } +/* dup_if_truncate - save hostname and truncate if it ends in dot */ + +static char *dup_if_truncate(char *name) +{ + int len; + char *result; + + /* + * Truncate hostnames ending in dot but not dot-dot. + */ + if ((len = strlen(name)) > 1 + && name[len - 1] == '.' + && name[len - 2] != '.') { + result = mystrndup(name, len - 1); + } else + result = name; + return (result); +} + +/* reject_invalid_hostaddr - fail if host address is incorrect */ + +static int reject_invalid_hostaddr(SMTPD_STATE *state, char *addr) +{ + char *myname = "reject_invalid_hostaddr"; + int len; + char *test_addr; + int stat; + + if (msg_verbose) + msg_info("%s: %s", myname, addr); + + if (addr[0] == '[' && (len = strlen(addr)) > 2 && addr[len - 1] == ']') { + test_addr = mystrndup(&addr[1], len - 2); + } else + test_addr = addr; + + /* + * Validate the address. + */ + if (!valid_hostaddr(test_addr)) + stat = smtpd_check_reject(state, MAIL_ERROR_POLICY, + "%d <%s>: Invalid ip address", + var_bad_name_code, addr); + else + stat = SMTPD_CHECK_DUNNO; + + /* + * Cleanup. + */ + if (test_addr != addr) + myfree(test_addr); + + return (stat); +} + /* reject_invalid_hostname - fail if host/domain syntax is incorrect */ static int reject_invalid_hostname(SMTPD_STATE *state, char *name) { char *myname = "reject_invalid_hostname"; - int len; char *test_name; int stat; @@ -425,12 +487,7 @@ static int reject_invalid_hostname(SMTPD_STATE *state, char *name) /* * Truncate hostnames ending in dot but not dot-dot. */ - if ((len = strlen(name)) > 1 - && name[len - 1] == '.' - && name[len - 2] != '.') { - test_name = mystrndup(name, len - 1); - } else - test_name = name; + test_name = dup_if_truncate(name); /* * Validate the hostname. @@ -451,6 +508,41 @@ static int reject_invalid_hostname(SMTPD_STATE *state, char *name) return (stat); } +/* reject_non_fqdn_hostname - fail if host name is not in fqdn form */ + +static int reject_non_fqdn_hostname(SMTPD_STATE *state, char *name) +{ + char *myname = "reject_non_fqdn_hostname"; + char *test_name; + int stat; + + if (msg_verbose) + msg_info("%s: %s", myname, name); + + /* + * Truncate hostnames ending in dot but not dot-dot. + */ + test_name = dup_if_truncate(name); + + /* + * Validate the hostname. + */ + if (!valid_hostname(test_name) || !strchr(test_name, '.')) + stat = smtpd_check_reject(state, MAIL_ERROR_POLICY, + "%d <%s>: need fully-qualified hostname", + var_non_fqdn_code, name); + else + stat = SMTPD_CHECK_DUNNO; + + /* + * Cleanup. + */ + if (test_name != name) + myfree(test_name); + + return (stat); +} + /* reject_unknown_hostname - fail if name has no A or MX record */ static int reject_unknown_hostname(SMTPD_STATE *state, char *name) @@ -508,8 +600,8 @@ static int check_relay_domains(SMTPD_STATE *state, char *recipient) /* * Resolve the address. */ - canon_addr_internal(reply.recipient, recipient); - resolve_clnt_query(STR(reply.recipient), &reply); + canon_addr_internal(query, recipient); + resolve_clnt_query(STR(query), &reply); /* * Permit if destination is local. XXX This must be generalized for @@ -591,8 +683,8 @@ static int permit_mx_backup(SMTPD_STATE *unused_state, const char *recipient) /* * Resolve the address. */ - canon_addr_internal(reply.recipient, recipient); - resolve_clnt_query(STR(reply.recipient), &reply); + canon_addr_internal(query, recipient); + resolve_clnt_query(STR(query), &reply); /* * If the destination is local, it is acceptable, because we are @@ -659,6 +751,58 @@ static int permit_mx_backup(SMTPD_STATE *unused_state, const char *recipient) return (SMTPD_CHECK_DUNNO); } +/* reject_non_fqdn_address - fail if address is not in fqdn form */ + +static int reject_non_fqdn_address(SMTPD_STATE *state, char *addr) +{ + char *myname = "reject_non_fqdn_address"; + char *domain; + char *test_dom; + int stat; + + if (msg_verbose) + msg_info("%s: %s", myname, addr); + + /* + * Locate the domain information. + */ + if ((domain = strrchr(addr, '@')) != 0) + domain++; + else + domain = ""; + + /* + * Skip forms that we can't handle yet. Permit user@[ip_address]. + */ + if (domain[0] == '#') + return (SMTPD_CHECK_DUNNO); + if (domain[0] == '[' && domain[strlen(domain) - 1] == ']') + return (SMTPD_CHECK_OK); + + /* + * Truncate names ending in dot but not dot-dot. + */ + test_dom = dup_if_truncate(domain); + + /* + * Validate the domain. + */ + if (!*test_dom || !valid_hostname(test_dom) || !strchr(test_dom, '.')) + stat = smtpd_check_reject(state, MAIL_ERROR_POLICY, + "%d <%s>: need fully-qualified address", + var_non_fqdn_code, addr); + else + stat = SMTPD_CHECK_DUNNO; + + /* + * Cleanup. + */ + if (test_dom != domain) + myfree(test_dom); + + return (stat); +} + /* reject_unknown_address - fail if address does not resolve */ static int reject_unknown_address(SMTPD_STATE *state, char *addr) @@ -672,8 +816,8 @@ static int reject_unknown_address(SMTPD_STATE *state, char *addr) /* * Resolve the address. */ - canon_addr_internal(reply.recipient, addr); - resolve_clnt_query(STR(reply.recipient), &reply); + canon_addr_internal(query, addr); + resolve_clnt_query(STR(query), &reply); /* * Skip local destinations and non-DNS forms. @@ -842,8 +986,8 @@ static int check_mail_access(SMTPD_STATE *state, char *table, char *addr) /* * Resolve the address. */ - canon_addr_internal(reply.recipient, addr); - resolve_clnt_query(STR(reply.recipient), &reply); + canon_addr_internal(query, addr); + resolve_clnt_query(STR(query), &reply); /* * Garbage in, garbage out. Every address from canon_addr_internal() and @@ -927,8 +1071,8 @@ static int reject_maps_rbl(SMTPD_STATE *state) */ if (dns_status == DNS_OK) result = smtpd_check_reject(state, MAIL_ERROR_POLICY, - "%d Service unavailable; blocked using %s", - var_maps_rbl_code, rbl_domain); + "%d Service unavailable; [%s] blocked using %s", + var_maps_rbl_code, state->addr, rbl_domain); else result = SMTPD_CHECK_DUNNO; @@ -1014,19 +1158,31 @@ static int generic_checks(SMTPD_STATE *state, char *name, } if (strcasecmp(name, REJECT_INVALID_HOSTNAME) == 0) { if (*state->helo_name != '[') - *status = reject_invalid_hostname(state, what); + *status = reject_invalid_hostname(state, state->helo_name); + else + *status = reject_invalid_hostaddr(state, state->helo_name); return (1); } if (strcasecmp(name, REJECT_UNKNOWN_HOSTNAME) == 0) { if (*state->helo_name != '[') *status = reject_unknown_hostname(state, state->helo_name); + else + *status = reject_invalid_hostaddr(state, state->helo_name); return (1); } if (strcasecmp(name, PERMIT_NAKED_IP_ADDR) == 0) { - if (state->helo_name && inet_addr(state->helo_name) != INADDR_NONE) + if (state->helo_name[strspn(state->helo_name, "0123456789.")] == 0 + && (*status = reject_invalid_hostaddr(state, state->helo_name)) == 0) *status = SMTPD_CHECK_OK; return (1); } + if (strcasecmp(name, REJECT_NON_FQDN_HOSTNAME) == 0) { + if (*state->helo_name != '[') + *status = reject_non_fqdn_hostname(state, state->helo_name); + else + *status = reject_invalid_hostaddr(state, state->helo_name); + return (1); + } } /* @@ -1041,6 +1197,11 @@ static int generic_checks(SMTPD_STATE *state, char *name, *status = reject_unknown_address(state, state->sender); return (1); } + if (strcasecmp(name, REJECT_NON_FQDN_SENDER) == 0) { + if (*state->sender) + *status = reject_non_fqdn_address(state, state->sender); + return (1); + } } return (0); } @@ -1083,6 +1244,7 @@ char *smtpd_check_helo(SMTPD_STATE *state, char *helohost) char **cpp; char *name; int status; + char *saved_helo = state->helo_name; /* * Initialize. @@ -1107,7 +1269,7 @@ char *smtpd_check_helo(SMTPD_STATE *state, char *helohost) break; } myfree(state->helo_name); - state->helo_name = 0; + state->helo_name = saved_helo; return (status == SMTPD_CHECK_REJECT ? STR(error_text) : 0); } @@ -1118,6 +1280,7 @@ char *smtpd_check_mail(SMTPD_STATE *state, char *sender) char **cpp; char *name; int status; + char *saved_sender = state->sender; /* * Initialize. @@ -1142,7 +1305,7 @@ char *smtpd_check_mail(SMTPD_STATE *state, char *sender) break; } myfree(state->sender); - state->sender = 0; + state->sender = saved_sender; return (status == SMTPD_CHECK_REJECT ? STR(error_text) : 0); } @@ -1173,6 +1336,8 @@ char *smtpd_check_rcpt(SMTPD_STATE *state, char *recipient) status = permit_mx_backup(state, recipient); } else if (strcasecmp(name, CHECK_RELAY_DOMAINS) == 0) { status = check_relay_domains(state, recipient); + } else if (strcasecmp(name, REJECT_NON_FQDN_RCPT) == 0) { + status = reject_non_fqdn_address(state, recipient); } else if (generic_checks(state, name, &cpp, &status, recipient) == 0) { msg_warn("unknown %s check: \"%s\"", VAR_RCPT_CHECKS, name); break; @@ -1277,6 +1442,7 @@ char *var_client_checks = ""; char *var_helo_checks = ""; char *var_mail_checks = ""; char *var_rcpt_checks = ""; +char *var_etrn_checks = ""; char *var_relay_domains = ""; char *var_mynetworks = ""; char *var_notify_classes = ""; @@ -1345,6 +1511,7 @@ int var_relay_code; int var_maps_rbl_code; int var_access_map_code; int var_reject_code; +int var_non_fqdn_code; static INT_TABLE int_table[] = { "msg_verbose", 0, &msg_verbose, @@ -1356,6 +1523,7 @@ static INT_TABLE int_table[] = { VAR_MAPS_RBL_CODE, DEF_MAPS_RBL_CODE, &var_maps_rbl_code, VAR_ACCESS_MAP_CODE, DEF_ACCESS_MAP_CODE, &var_access_map_code, VAR_REJECT_CODE, DEF_REJECT_CODE, &var_reject_code, + VAR_NON_FQDN_CODE, DEF_NON_FQDN_CODE, &var_non_fqdn_code, 0, }; @@ -1392,7 +1560,7 @@ static int int_update(char **argv) typedef struct { char *name; ARGV **target; -} REST_TABLE; +} REST_TABLE; static REST_TABLE rest_table[] = { "client_restrictions", &client_restrctions, @@ -1432,6 +1600,8 @@ void resolve_clnt_init(RESOLVE_REPLY *reply) VSTRING *canon_addr_internal(VSTRING *result, const char *addr) { + if (addr == STR(result)) + msg_panic("canon_addr_internal: result clobbers input"); if (strchr(addr, '@') == 0) msg_fatal("%s: address rewriting is disabled", addr); vstring_strcpy(result, addr); @@ -1441,6 +1611,8 @@ VSTRING *canon_addr_internal(VSTRING *result, const char *addr) void resolve_clnt_query(const char *addr, RESOLVE_REPLY *reply) { + if (addr == STR(reply->recipient)) + msg_panic("resolve_clnt_query: result clobbers input"); vstring_strcpy(reply->transport, "foo"); vstring_strcpy(reply->nexthop, "foo"); if (strchr(addr, '%')) @@ -1587,6 +1759,9 @@ main(int argc, char **argv) } vstring_free(buf); smtpd_state_reset(&state); +#define FREE_STRING(s) { if (s) myfree(s); } + FREE_STRING(state.helo_name); + FREE_STRING(state.sender); exit(0); } diff --git a/postfix/smtpd/smtpd_check.in b/postfix/smtpd/smtpd_check.in index 542f9d9c1..a2777351d 100644 --- a/postfix/smtpd/smtpd_check.in +++ b/postfix/smtpd/smtpd_check.in @@ -1,7 +1,7 @@ # # Initialize. # -! ../bin/postmap smtpd_check_access +#! ../bin/postmap smtpd_check_access #msg_verbose 1 mynetworks 127.0.0.0/8,168.100.189.0/28 relay_domains porcupine.org @@ -129,3 +129,43 @@ rcpt foo@porcupine.org helo good.domain mail foo@bad.domain rcpt foo@porcupine.org +# +# FQDN restrictions +# +helo_restrictions reject_non_fqdn_hostname +sender_restrictions reject_non_fqdn_sender +recipient_restrictions reject_non_fqdn_recipient +helo foo.bar. +helo foo.bar +helo foo +mail foo@foo.bar. +mail foo@foo.bar +mail foo@foo +mail foo +rcpt foo@foo.bar. +rcpt foo@foo.bar +rcpt foo@foo +rcpt foo +# +# Numerical HELO checks +# +msg_verbose 1 +helo_restrictions permit_naked_ip_address,reject_non_fqdn_hostname +helo [1.2.3.4] +helo [321.255.255.255] +helo [0.255.255.255] +helo [1.2.3.321] +helo [1.2.3] +helo [1.2.3.4.5] +helo [1..2.3.4] +helo [.1.2.3.4] +helo [1.2.3.4.5.] +helo 1.2.3.4 +helo 321.255.255.255 +helo 0.255.255.255 +helo 1.2.3.321 +helo 1.2.3 +helo 1.2.3.4.5 +helo 1..2.3.4 +helo .1.2.3.4 +helo 1.2.3.4.5. diff --git a/postfix/smtpd/smtpd_check.in2 b/postfix/smtpd/smtpd_check.in2 index a6463f986..467251307 100644 --- a/postfix/smtpd/smtpd_check.in2 +++ b/postfix/smtpd/smtpd_check.in2 @@ -1,7 +1,7 @@ # # Initialize. # -! ../bin/postmap smtpd_check_access +#! ../bin/postmap smtpd_check_access #msg_verbose 1 mynetworks 127.0.0.0/8,168.100.189.0/28 relay_domains porcupine.org diff --git a/postfix/smtpd/smtpd_check.ref b/postfix/smtpd/smtpd_check.ref index 73c72c638..42144270b 100644 --- a/postfix/smtpd/smtpd_check.ref +++ b/postfix/smtpd/smtpd_check.ref @@ -1,8 +1,7 @@ >>> # >>> # Initialize. >>> # ->>> ! ../bin/postmap smtpd_check_access -exit 0 +>>> #! ../bin/postmap smtpd_check_access >>> #msg_verbose 1 >>> mynetworks 127.0.0.0/8,168.100.189.0/28 OK @@ -63,6 +62,7 @@ OK >>> helo_restrictions reject_invalid_hostname,reject_unknown_hostname OK >>> helo 123.123.123.123 +./smtpd_check: warning: valid_hostname: numeric hostname: 123.123.123.123 ./smtpd_check: reject: HELO from foo[123.123.123.123]: 450 <123.123.123.123>: Host not found 450 <123.123.123.123>: Host not found >>> helo_restrictions permit_naked_ip_address,reject_invalid_hostname,reject_unknown_hostname @@ -180,8 +180,8 @@ OK >>> client spike.porcupine.org 168.100.189.2 OK >>> client foo 127.0.0.2 -./smtpd_check: reject: CONNECT from foo[127.0.0.2]: 550 Service unavailable; blocked using rbl.maps.vix.com -550 Service unavailable; blocked using rbl.maps.vix.com +./smtpd_check: reject: CONNECT from foo[127.0.0.2]: 550 Service unavailable; [127.0.0.2] blocked using rbl.maps.vix.com +550 Service unavailable; [127.0.0.2] blocked using rbl.maps.vix.com >>> # >>> # Hybrids >>> # @@ -209,6 +209,7 @@ OK ./smtpd_check: reject: RCPT from foo[131.155.210.17]: 550 match bad.domain 550 match bad.domain >>> helo 131.155.210.17 +./smtpd_check: warning: valid_hostname: numeric hostname: 131.155.210.17 OK >>> rcpt foo@porcupine.org OK @@ -240,3 +241,157 @@ OK 550 Access denied >>> rcpt wietse@porcupine.org OK +>>> # +>>> # Deferred restrictions +>>> # +>>> client_restrictions permit +OK +>>> helo_restrictions permit +OK +>>> sender_restrictions permit +OK +>>> recipient_restrictions check_helo_access,hash:./smtpd_check_access,check_sender_access,hash:./smtpd_check_access +OK +>>> helo bad.domain +OK +>>> mail foo@good.domain +OK +>>> rcpt foo@porcupine.org +./smtpd_check: reject: RCPT from foo[131.155.210.17]: 550 match bad.domain +550 match bad.domain +>>> helo good.domain +OK +>>> mail foo@bad.domain +OK +>>> rcpt foo@porcupine.org +./smtpd_check: reject: RCPT from foo[131.155.210.17]: 550 match bad.domain +550 match bad.domain +>>> # +>>> # FQDN restrictions +>>> # +>>> helo_restrictions reject_non_fqdn_hostname +OK +>>> sender_restrictions reject_non_fqdn_sender +OK +>>> recipient_restrictions reject_non_fqdn_recipient +OK +>>> helo foo.bar. +OK +>>> helo foo.bar +OK +>>> helo foo +./smtpd_check: reject: HELO from foo[131.155.210.17]: 504 : need fully-qualified hostname +504 : need fully-qualified hostname +>>> mail foo@foo.bar. +OK +>>> mail foo@foo.bar +OK +>>> mail foo@foo +./smtpd_check: reject: MAIL from foo[131.155.210.17]: 504 : need fully-qualified address +504 : need fully-qualified address +>>> mail foo +./smtpd_check: reject: MAIL from foo[131.155.210.17]: 504 : need fully-qualified address +504 : need fully-qualified address +>>> rcpt foo@foo.bar. +OK +>>> rcpt foo@foo.bar +OK +>>> rcpt foo@foo +./smtpd_check: reject: RCPT from foo[131.155.210.17]: 504 : need fully-qualified address +504 : need fully-qualified address +>>> rcpt foo +./smtpd_check: reject: RCPT from foo[131.155.210.17]: 504 : need fully-qualified address +504 : need fully-qualified address +>>> # +>>> # Numerical HELO checks +>>> # +>>> msg_verbose 1 +OK +>>> helo_restrictions permit_naked_ip_address,reject_non_fqdn_hostname +OK +>>> helo [1.2.3.4] +./smtpd_check: reject_invalid_hostaddr: [1.2.3.4] +OK +>>> helo [321.255.255.255] +./smtpd_check: reject_invalid_hostaddr: [321.255.255.255] +./smtpd_check: warning: valid_hostaddr: invalid octet value: 321.255.255.255 +./smtpd_check: reject: HELO from foo[131.155.210.17]: 501 <[321.255.255.255]>: Invalid ip address +501 <[321.255.255.255]>: Invalid ip address +>>> helo [0.255.255.255] +./smtpd_check: reject_invalid_hostaddr: [0.255.255.255] +./smtpd_check: warning: valid_hostaddr: bad initial octet value: 0.255.255.255 +./smtpd_check: reject: HELO from foo[131.155.210.17]: 501 <[0.255.255.255]>: Invalid ip address +501 <[0.255.255.255]>: Invalid ip address +>>> helo [1.2.3.321] +./smtpd_check: reject_invalid_hostaddr: [1.2.3.321] +./smtpd_check: warning: valid_hostaddr: invalid octet value: 1.2.3.321 +./smtpd_check: reject: HELO from foo[131.155.210.17]: 501 <[1.2.3.321]>: Invalid ip address +501 <[1.2.3.321]>: Invalid ip address +>>> helo [1.2.3] +./smtpd_check: reject_invalid_hostaddr: [1.2.3] +./smtpd_check: warning: valid_hostaddr: invalid octet count: 1.2.3 +./smtpd_check: reject: HELO from foo[131.155.210.17]: 501 <[1.2.3]>: Invalid ip address +501 <[1.2.3]>: Invalid ip address +>>> helo [1.2.3.4.5] +./smtpd_check: reject_invalid_hostaddr: [1.2.3.4.5] +./smtpd_check: warning: valid_hostaddr: invalid octet count: 1.2.3.4.5 +./smtpd_check: reject: HELO from foo[131.155.210.17]: 501 <[1.2.3.4.5]>: Invalid ip address +501 <[1.2.3.4.5]>: Invalid ip address +>>> helo [1..2.3.4] +./smtpd_check: reject_invalid_hostaddr: [1..2.3.4] +./smtpd_check: warning: valid_hostaddr: misplaced dot: 1..2.3.4 +./smtpd_check: reject: HELO from foo[131.155.210.17]: 501 <[1..2.3.4]>: Invalid ip address +501 <[1..2.3.4]>: Invalid ip address +>>> helo [.1.2.3.4] +./smtpd_check: reject_invalid_hostaddr: [.1.2.3.4] +./smtpd_check: warning: valid_hostaddr: misplaced dot: .1.2.3.4 +./smtpd_check: reject: HELO from foo[131.155.210.17]: 501 <[.1.2.3.4]>: Invalid ip address +501 <[.1.2.3.4]>: Invalid ip address +>>> helo [1.2.3.4.5.] +./smtpd_check: reject_invalid_hostaddr: [1.2.3.4.5.] +./smtpd_check: warning: valid_hostaddr: misplaced dot: 1.2.3.4.5. +./smtpd_check: reject: HELO from foo[131.155.210.17]: 501 <[1.2.3.4.5.]>: Invalid ip address +501 <[1.2.3.4.5.]>: Invalid ip address +>>> helo 1.2.3.4 +./smtpd_check: reject_invalid_hostaddr: 1.2.3.4 +OK +>>> helo 321.255.255.255 +./smtpd_check: reject_invalid_hostaddr: 321.255.255.255 +./smtpd_check: warning: valid_hostaddr: invalid octet value: 321.255.255.255 +./smtpd_check: reject: HELO from foo[131.155.210.17]: 501 <321.255.255.255>: Invalid ip address +501 <321.255.255.255>: Invalid ip address +>>> helo 0.255.255.255 +./smtpd_check: reject_invalid_hostaddr: 0.255.255.255 +./smtpd_check: warning: valid_hostaddr: bad initial octet value: 0.255.255.255 +./smtpd_check: reject: HELO from foo[131.155.210.17]: 501 <0.255.255.255>: Invalid ip address +501 <0.255.255.255>: Invalid ip address +>>> helo 1.2.3.321 +./smtpd_check: reject_invalid_hostaddr: 1.2.3.321 +./smtpd_check: warning: valid_hostaddr: invalid octet value: 1.2.3.321 +./smtpd_check: reject: HELO from foo[131.155.210.17]: 501 <1.2.3.321>: Invalid ip address +501 <1.2.3.321>: Invalid ip address +>>> helo 1.2.3 +./smtpd_check: reject_invalid_hostaddr: 1.2.3 +./smtpd_check: warning: valid_hostaddr: invalid octet count: 1.2.3 +./smtpd_check: reject: HELO from foo[131.155.210.17]: 501 <1.2.3>: Invalid ip address +501 <1.2.3>: Invalid ip address +>>> helo 1.2.3.4.5 +./smtpd_check: reject_invalid_hostaddr: 1.2.3.4.5 +./smtpd_check: warning: valid_hostaddr: invalid octet count: 1.2.3.4.5 +./smtpd_check: reject: HELO from foo[131.155.210.17]: 501 <1.2.3.4.5>: Invalid ip address +501 <1.2.3.4.5>: Invalid ip address +>>> helo 1..2.3.4 +./smtpd_check: reject_invalid_hostaddr: 1..2.3.4 +./smtpd_check: warning: valid_hostaddr: misplaced dot: 1..2.3.4 +./smtpd_check: reject: HELO from foo[131.155.210.17]: 501 <1..2.3.4>: Invalid ip address +501 <1..2.3.4>: Invalid ip address +>>> helo .1.2.3.4 +./smtpd_check: reject_invalid_hostaddr: .1.2.3.4 +./smtpd_check: warning: valid_hostaddr: misplaced dot: .1.2.3.4 +./smtpd_check: reject: HELO from foo[131.155.210.17]: 501 <.1.2.3.4>: Invalid ip address +501 <.1.2.3.4>: Invalid ip address +>>> helo 1.2.3.4.5. +./smtpd_check: reject_invalid_hostaddr: 1.2.3.4.5. +./smtpd_check: warning: valid_hostaddr: misplaced dot: 1.2.3.4.5. +./smtpd_check: reject: HELO from foo[131.155.210.17]: 501 <1.2.3.4.5.>: Invalid ip address +501 <1.2.3.4.5.>: Invalid ip address diff --git a/postfix/smtpd/smtpd_check.ref2 b/postfix/smtpd/smtpd_check.ref2 index 82f89c184..78b1f2bda 100644 --- a/postfix/smtpd/smtpd_check.ref2 +++ b/postfix/smtpd/smtpd_check.ref2 @@ -1,8 +1,7 @@ >>> # >>> # Initialize. >>> # ->>> ! ../bin/postmap smtpd_check_access -exit 0 +>>> #! ../bin/postmap smtpd_check_access >>> #msg_verbose 1 >>> mynetworks 127.0.0.0/8,168.100.189.0/28 OK @@ -171,5 +170,5 @@ OK >>> client spike.porcupine.org 168.100.189.2 OK >>> client foo 127.0.0.2 -./smtpd_check: reject: CONNECT from foo[127.0.0.2]: 550 Service unavailable; blocked using rbl.maps.vix.com -550 Service unavailable; blocked using rbl.maps.vix.com +./smtpd_check: reject: CONNECT from foo[127.0.0.2]: 550 Service unavailable; [127.0.0.2] blocked using rbl.maps.vix.com +550 Service unavailable; [127.0.0.2] blocked using rbl.maps.vix.com diff --git a/postfix/smtpstone/.indent.pro b/postfix/smtpstone/.indent.pro index e59d396ad..b38eded21 100644 --- a/postfix/smtpstone/.indent.pro +++ b/postfix/smtpstone/.indent.pro @@ -6,6 +6,7 @@ -TBOUNCE_STAT -TCLEANUP_STATE -TCLIENT_LIST +-TCLNT_STREAM -TCONFIG_BOOL_FN_TABLE -TCONFIG_BOOL_TABLE -TCONFIG_INT_FN_TABLE @@ -25,6 +26,8 @@ -TDICT_NISPLUS -TDICT_NODE -TDICT_OPEN_INFO +-TDICT_PCRE +-TDICT_UNIX -TDNS_FIXED -TDNS_REPLY -TDNS_RR @@ -68,8 +71,14 @@ -TRECIPIENT_LIST -TREC_TYPE_NAME -TRESOLVE_REPLY +-TRESPONSE -TSCAN_DIR +-TSCAN_INFO +-TSCAN_OBJ +-TSESSION -TSINGLE_SERVER +-TSINK_COMMAND +-TSINK_STATE -TSMTPD_STATE -TSMTPD_TOKEN -TSMTP_ADDR diff --git a/postfix/smtpstone/hashed-deferred b/postfix/smtpstone/hashed-deferred new file mode 100644 index 000000000..93f0e122c --- /dev/null +++ b/postfix/smtpstone/hashed-deferred @@ -0,0 +1,37 @@ +Delivering 1000 deferred messages over the loopback transport, +outbound concurrency 10. smtp-sink pipelining disabled. Machine is +P230, BSD/OS 3.1, 64MB memory. + +hashing is 16 directories per level + +flat deferred queue + + start: Sun Feb 21 16:42:37 EST 1999 + done: Feb 21 16:44:35 + time: 1:58 = 118 seconds + + start: Sun Feb 21 16:48:01 EST 1999 + done: Feb 21 16:49:51 + time: 1:50 = 110 seconds + +hashed deferred queue, depth=1 (16 directories) + + start: Sun Feb 21 17:29:36 EST 1999 + done: Feb 21 17:31:32 + time: 1:56 = 116 seconds + + start: Sun Feb 21 17:33:36 EST 1999 + done: Feb 21 17:35:24 + time: 1:48 = 108 seconds + + start: Sun Feb 21 17:37:08 EST 1999 + done: Feb 21 17:39:02 + time: 1:52 = 112 seconds + +Hashing does not slow down deliveries. + +However the problem is scanning an empty deferred queue. On an idle +machine, it takes some 5 seconds to scan an empty depth=2 deferred +queue unless the blocks happen to be cached. During those 5 seconds +the queue manager will not pay attention to I/O from delivery +agents, which is bad. diff --git a/postfix/smtpstone/hashed-incoming b/postfix/smtpstone/hashed-incoming new file mode 100644 index 000000000..7bb199321 --- /dev/null +++ b/postfix/smtpstone/hashed-incoming @@ -0,0 +1,48 @@ +Sending 1000 messages through postfix over the loopback transport, +inbound concurrency 10. smtp-sink pipelining disabled. Machine is +P230, BSD/OS 3.1 + +accepted = time for postfix to accept all messages +moved = number of messages delivered by postfix when all mail accepted +done = time for postfix to deliver last message + +hashing is 16 directories per level + +flat incoming queue + + start: Sun Feb 21 15:12:44 EST 1999 + accepted: 66.10 real 0.50 user 1.56 sys + moved: 122 + done: Feb 21 15:15:19 + + total time: 2:35 = 155 seconds + + start: Sun Feb 21 15:30:44 EST 1999 + accepted: 67.47 real 0.48 user 1.60 sys + moved: + done: Feb 21 15:33:10 + + total time: 2:26 = 146 seconds + +hashed incoming queue, depth=1 (16 subdirs) + + start: Sun Feb 21 15:39:43 EST 1999 + accepted: 84.42 real 0.49 user 1.66 sys + moved: 144 + done: Feb 21 15:42:27 + + total time: 2:44 = 164 seconds + + start: Sun Feb 21 15:42:43 EST 1999 + accepted: 84.57 real 0.46 user 1.67 sys + moved: + done: Feb 21 15:45:34 + + total time: 2:51 = 171 seconds + + start: Sun Feb 21 15:47:11 EST 1999 + accepted: 84.11 real 0.34 user 1.72 sys + moved: + done: Feb 21 15:49:54 + + total time: 2:43 = 163 seconds diff --git a/postfix/smtpstone/smtp-sink.c b/postfix/smtpstone/smtp-sink.c index 025b1321d..3a2960271 100644 --- a/postfix/smtpstone/smtp-sink.c +++ b/postfix/smtpstone/smtp-sink.c @@ -2,18 +2,24 @@ /* NAME /* smtp-sink 8 /* SUMMARY -/* smtp test server +/* multi-threaded smtp test server /* SYNOPSIS -/* smtp-sink [-c] [-v] [host]:port backlog +/* smtp-sink [-c] [-p] [-v] [-w delay] [host]:port backlog /* DESCRIPTION /* \fIsmtp-sink\fR listens on the named host (or address) and port. /* It takes SMTP messages from the network and throws them away. +/* The purpose is to measure SMTP client performance, not protocol +/* compliance. /* This program is the complement of the \fIsmtp-source\fR program. /* .IP -c /* Display a running counter that is updated whenever an SMTP /* QUIT command is executed. +/* .IP -p +/* Disable ESMTP command pipelining. /* .IP -v /* Show the SMTP conversations. +/* .IP "-w delay" +/* Wait \fIdelay\fR seconds before responding to a DATA command. /* SEE ALSO /* smtp-source, SMTP test message generator /* LICENSE @@ -61,10 +67,11 @@ /* Application-specific. */ -struct data_state { +typedef struct SINK_STATE { VSTREAM *stream; - int state; -}; + int data_state; + int (*read) (struct SINK_STATE *); +} SINK_STATE; #define ST_ANY 0 #define ST_CR 1 @@ -77,34 +84,65 @@ static int var_tmout; static int var_max_line_length; static char *var_myhostname; static VSTRING *buffer; -static void command_read(int, char *); -static void disconnected(VSTREAM *); +static int command_read(SINK_STATE *); +static int data_read(SINK_STATE *); +static void disconnect(SINK_STATE *); static int count; static int counter; +static int disable_pipelining; +static int fixed_delay; + +/* ehlo_response - respond to EHLO command */ + +static void ehlo_response(SINK_STATE *state) +{ + smtp_printf(state->stream, "250-%s", var_myhostname); + if (!disable_pipelining) + smtp_printf(state->stream, "250-PIPELINING"); + smtp_printf(state->stream, "250 8BITMIME"); +} + +/* ok_response - send 250 OK */ + +static void ok_response(SINK_STATE *state) +{ + smtp_printf(state->stream, "250 Ok"); +} + +/* data_response - respond to DATA command */ + +static void data_response(SINK_STATE *state) +{ + state->data_state = ST_CR_LF; + smtp_printf(state->stream, "354 End data with ."); + state->read = data_read; +} -/* helo - process HELO/EHLO command */ +/* data_event - delayed response to DATA command */ -static void helo(VSTREAM *stream) +static void data_event(int unused_event, char *context) { - smtp_printf(stream, "250-%s", var_myhostname); - smtp_printf(stream, "250-8BITMIME"); - smtp_printf(stream, "250 PIPELINING"); + SINK_STATE *state = (SINK_STATE *) context; + + data_response(state); } -/* ok - send 250 OK */ +/* quit_response - respond to QUIT command */ -static void ok(VSTREAM *stream) +static void quit_response(SINK_STATE *state) { - smtp_printf(stream, "250 Ok"); + smtp_printf(state->stream, "221 Bye"); + if (count) { + counter++; + vstream_printf("%d\r", counter); + vstream_fflush(VSTREAM_OUT); + } } /* data_read - read data from socket */ -static void data_read(int unused_event, char *context) +static int data_read(SINK_STATE *state) { - struct data_state *dstate = (struct data_state *) context; - VSTREAM *stream = dstate->stream; - int avail; int ch; struct data_trans { int state; @@ -120,10 +158,15 @@ static void data_read(int unused_event, char *context) }; struct data_trans *dp; - avail = peekfd(vstream_fileno(stream)); - while (avail-- > 0) { - ch = VSTREAM_GETC(stream); - for (dp = data_trans; dp->state != dstate->state; dp++) + /* + * We must avoid blocking I/O, so get out of here as soon as both the + * VSTREAM and kernel read buffers dry up. + */ + while (vstream_peek(state->stream) > 0 + || peekfd(vstream_fileno(state->stream)) > 0) { + if ((ch = VSTREAM_GETC(state->stream)) == VSTREAM_EOF) + return (-1); + for (dp = data_trans; dp->state != state->data_state; dp++) /* void */ ; /* @@ -133,141 +176,134 @@ static void data_read(int unused_event, char *context) * (empty line) right before the end of the message data. */ if (ch == dp->want) - dstate->state = dp->next_state; + state->data_state = dp->next_state; else if (ch == data_trans[0].want) - dstate->state = data_trans[0].next_state; + state->data_state = data_trans[0].next_state; else - dstate->state = ST_ANY; - if (dstate->state == ST_CR_LF_DOT_CR_LF) { + state->data_state = ST_ANY; + if (state->data_state == ST_CR_LF_DOT_CR_LF) { if (msg_verbose) msg_info("."); - smtp_printf(stream, "250 Ok"); - event_disable_readwrite(vstream_fileno(stream)); - event_enable_read(vstream_fileno(stream), - command_read, (char *) stream); - myfree((char *) dstate); + ok_response(state); + state->read = command_read; + break; } } -} - -/* data - process DATA command */ - -static void data(VSTREAM *stream) -{ - struct data_state *dstate = (struct data_state *) mymalloc(sizeof(*dstate)); - - dstate->stream = stream; - dstate->state = ST_CR_LF; - smtp_printf(stream, "354 End data with ."); - event_disable_readwrite(vstream_fileno(stream)); - event_enable_read(vstream_fileno(stream), - data_read, (char *) dstate); -} - -/* quit - process QUIT command */ - -static void quit(VSTREAM *stream) -{ - smtp_printf(stream, "221 Bye"); - disconnected(stream); - if (count) { - counter++; - vstream_printf("%d\r", counter); - vstream_fflush(VSTREAM_OUT); - } + return (0); } /* * The table of all SMTP commands that we can handle. */ -typedef struct COMMAND { +typedef struct SINK_COMMAND { char *name; - void (*action) (VSTREAM *); -} COMMAND; - -static COMMAND command_table[] = { - "helo", helo, - "ehlo", helo, - "mail", ok, - "rcpt", ok, - "data", data, - "rset", ok, - "noop", ok, - "vrfy", ok, - "quit", quit, + void (*response) (SINK_STATE *); +} SINK_COMMAND; + +static SINK_COMMAND command_table[] = { + "helo", ok_response, + "ehlo", ehlo_response, + "mail", ok_response, + "rcpt", ok_response, + "data", data_response, + "rset", ok_response, + "noop", ok_response, + "vrfy", ok_response, + "quit", quit_response, 0, }; /* command_read - talk the SMTP protocol, server side */ -static void command_read(int unused_event, char *context) +static int command_read(SINK_STATE *state) { - VSTREAM *stream = (VSTREAM *) context; char *command; - COMMAND *cmdp; + SINK_COMMAND *cmdp; - switch (setjmp(smtp_timeout_buf)) { + smtp_get(buffer, state->stream, var_max_line_length); + if ((command = strtok(vstring_str(buffer), " \t")) == 0) { + smtp_printf(state->stream, "500 Error: unknown command"); + return (0); + } + if (msg_verbose) + msg_info("%s", command); + for (cmdp = command_table; cmdp->name != 0; cmdp++) + if (strcasecmp(command, cmdp->name) == 0) + break; + if (cmdp->name == 0) { + smtp_printf(state->stream, "500 Error: unknown command"); + return (0); + } + if (cmdp->response == data_response && fixed_delay > 0) { + event_request_timer(data_event, (char *) state, fixed_delay); + } else { + cmdp->response(state); + if (cmdp->response == quit_response) + return (-1); + } + return (0); +} - default: - msg_panic("unknown error reading input"); +/* read_event - handle command or data read events */ - case SMTP_ERR_TIME: - smtp_printf(stream, "421 Error: timeout exceeded"); - msg_warn("timeout reading input"); - disconnected(stream); - break; +static void read_event(int unused_event, char *context) +{ + SINK_STATE *state = (SINK_STATE *) context; - case SMTP_ERR_EOF: - msg_warn("lost connection"); - disconnected(stream); - break; + do { + switch (setjmp(smtp_timeout_buf)) { - case 0: - smtp_get(buffer, stream, var_max_line_length); - if ((command = strtok(vstring_str(buffer), " \t")) == 0) { - smtp_printf(stream, "500 Error: unknown command"); - break; - } - if (msg_verbose) - msg_info("%s", command); - for (cmdp = command_table; cmdp->name != 0; cmdp++) - if (strcasecmp(command, cmdp->name) == 0) - break; - if (cmdp->name == 0) { - smtp_printf(stream, "500 Error: unknown command"); - break; + default: + msg_panic("unknown error reading input"); + + case SMTP_ERR_TIME: + msg_panic("attempt to read non-readable socket"); + /* NOTREACHED */ + + case SMTP_ERR_EOF: + msg_warn("lost connection"); + disconnect(state); + return; + + case 0: + if (state->read(state) < 0) { + if (msg_verbose) + msg_info("disconnect"); + disconnect(state); + return; + } } - cmdp->action(stream); - break; - } + } while (vstream_peek(state->stream) > 0); } -/* disconnected - handle disconnection events */ +/* disconnect - handle disconnection events */ -static void disconnected(VSTREAM *stream) +static void disconnect(SINK_STATE *state) { - if (msg_verbose) - msg_info("disconnect"); - event_disable_readwrite(vstream_fileno(stream)); - vstream_fclose(stream); + event_disable_readwrite(vstream_fileno(state->stream)); + vstream_fclose(state->stream); + myfree((char *) state); } -/* connected - connection established */ +/* connect_event - handle connection events */ -static void connected(int unused_event, char *context) +static void connect_event(int unused_event, char *context) { int sock = (int) context; - VSTREAM *stream; + SINK_STATE *state; int fd; if ((fd = accept(sock, (struct sockaddr *) 0, (SOCKADDR_SIZE *) 0)) >= 0) { if (msg_verbose) msg_info("connect"); non_blocking(fd, NON_BLOCKING); - stream = vstream_fdopen(fd, O_RDWR); - smtp_timeout_setup(stream, var_tmout); - smtp_printf(stream, "220 %s ESMTP", var_myhostname); - event_enable_read(fd, command_read, (char *) stream); + state = (SINK_STATE *) mymalloc(sizeof(*state)); + state->stream = vstream_fdopen(fd, O_RDWR); + state->read = command_read; + state->data_state = 0; + smtp_timeout_setup(state->stream, var_tmout); + smtp_printf(state->stream, "220 %s ESMTP", var_myhostname); + event_enable_read(fd, read_event, (char *) state); } } @@ -275,7 +311,7 @@ static void connected(int unused_event, char *context) static void usage(char *myname) { - msg_fatal("usage: %s [-c] [-v] [host]:port backlog", myname); + msg_fatal("usage: %s [-c] [-p] [-v] [host]:port backlog", myname); } int main(int argc, char **argv) @@ -292,14 +328,21 @@ int main(int argc, char **argv) /* * Parse JCL. */ - while ((ch = GETOPT(argc, argv, "cv")) > 0) { + while ((ch = GETOPT(argc, argv, "cpvw:")) > 0) { switch (ch) { case 'c': count++; break; + case 'p': + disable_pipelining = 1; + break; case 'v': msg_verbose++; break; + case 'w': + if ((fixed_delay = atoi(optarg)) <= 0) + usage(argv[0]); + break; default: usage(argv[0]); } @@ -319,7 +362,7 @@ int main(int argc, char **argv) /* * Start the event handler. */ - event_enable_read(sock, connected, (char *) sock); + event_enable_read(sock, connect_event, (char *) sock); for (;;) event_loop(-1); } diff --git a/postfix/smtpstone/smtp-source.c b/postfix/smtpstone/smtp-source.c index 89780caac..6aae8f012 100644 --- a/postfix/smtpstone/smtp-source.c +++ b/postfix/smtpstone/smtp-source.c @@ -2,7 +2,7 @@ /* NAME /* smtp-source 8 /* SUMMARY -/* SMTP test generator +/* multi-threaded SMTP test generator /* SYNOPSIS /* smtp-source [options] host[:port] /* DESCRIPTION @@ -32,12 +32,19 @@ /* Send the specified number of messages (default: 1). /* .IP "-r recipient_count" /* Send the specified number of recipients per transaction (default: 1). -/* Recipient names are generated by appending a number to the +/* Recipient names are generated by prepending a number to the /* recipient address. The default is one recipient per transaction. /* .IP "-s session_count" /* Run the specified number of SMTP sessions in parallel (default: 1). /* .IP "-t to" /* Use the specified recipient address (default: ). +/* .IP "-R interval" +/* Wait for a random period of time 0 <= n <= interval between messages. +/* Suspending one thread does not affect other delivery threads. +/* threads keep running. +/* .IP "-w interval" +/* Wait a fixed time between messages. +/* Suspending one thread does not affect other delivery threads. /* LICENSE /* .ad /* .fi @@ -67,6 +74,7 @@ /* Utility library. */ #include +#include #include #include #include @@ -91,7 +99,6 @@ typedef struct { int xfer_count; /* # of xfers in session */ int rcpt_count; /* # of recipients to go */ VSTREAM *stream; /* open connection */ - int fd; /* ditto */ int connect_count; /* # of connect()s to retry */ } SESSION; @@ -123,6 +130,8 @@ static int counter = 0; static int send_helo_first = 1; static int send_headers = 1; static int connect_count = 1; +static int random_delay = 0; +static int fixed_delay = 0; static void connect_done(int, char *); static void send_helo(SESSION *); @@ -137,6 +146,13 @@ static void dot_done(int, char *); static void send_quit(SESSION *); static void quit_done(int, char *); +/* random_interval - generate a random value in 0 .. (small) interval */ + +static int random_interval(int interval) +{ + return (rand() % (interval + 1)); +} + /* command - send an SMTP command */ static void command(VSTREAM *stream, char *fmt,...) @@ -230,30 +246,55 @@ static char *exception_text(int except) static void startup(SESSION *session) { + int fd; + if (message_count-- <= 0) { myfree((char *) session); session_count--; return; } if (session->stream == 0) { - if ((session->fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) + if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) msg_fatal("socket: %m"); for (;;) { if (session->connect_count == 0) msg_fatal("connect: %m"); - if (!connect(session->fd, (struct sockaddr *) & sin, sizeof(sin))) + if (!connect(fd, (struct sockaddr *) & sin, sizeof(sin))) break; if (session->connect_count-- > 1) usleep(10); } - session->stream = vstream_fdopen(session->fd, O_RDWR); + session->stream = vstream_fdopen(fd, O_RDWR); smtp_timeout_setup(session->stream, var_timeout); - event_enable_read(session->fd, connect_done, (char *) session); + event_enable_read(vstream_fileno(session->stream), connect_done, (char *) session); } else { send_mail(session); } } +/* start_event - invoke startup from timer context */ + +static void start_event(int unused_event, char *context) +{ + SESSION *session = (SESSION *) context; + + startup(session); +} + +/* start_another - start another session */ + +static void start_another(SESSION *session) +{ + if (random_delay > 0) { + event_request_timer(start_event, (char *) session, + random_interval(random_delay)); + } else if (fixed_delay > 0) { + event_request_timer(start_event, (char *) session, fixed_delay); + } else { + startup(session); + } +} + /* connect_done - send message sender info */ static void connect_done(int unused_event, char *context) @@ -300,13 +341,13 @@ static void send_helo(SESSION *session) /* * Prepare for the next event. */ - event_disable_readwrite(session->fd); - event_enable_read(session->fd, helo_done, (char *) session); + event_disable_readwrite(vstream_fileno(session->stream)); + event_enable_read(vstream_fileno(session->stream), helo_done, (char *) session); } /* helo_done - handle HELO response */ -static void helo_done(int unused, char *context) +static void helo_done(int unused_event, char *context) { SESSION *session = (SESSION *) context; RESPONSE *resp; @@ -341,8 +382,8 @@ static void send_mail(SESSION *session) /* * Prepare for the next event. */ - event_disable_readwrite(session->fd); - event_enable_read(session->fd, mail_done, (char *) session); + event_disable_readwrite(vstream_fileno(session->stream)); + event_enable_read(vstream_fileno(session->stream), mail_done, (char *) session); } /* mail_done - handle MAIL response */ @@ -389,8 +430,8 @@ static void send_rcpt(int unused_event, char *context) /* * Prepare for the next event. */ - event_disable_readwrite(session->fd); - event_enable_read(session->fd, rcpt_done, (char *) session); + event_disable_readwrite(vstream_fileno(session->stream)); + event_enable_read(vstream_fileno(session->stream), rcpt_done, (char *) session); } /* rcpt_done - handle RCPT completion */ @@ -436,8 +477,8 @@ static void send_data(int unused_event, char *context) /* * Prepare for the next event. */ - event_disable_readwrite(session->fd); - event_enable_read(session->fd, data_done, (char *) session); + event_disable_readwrite(vstream_fileno(session->stream)); + event_enable_read(vstream_fileno(session->stream), data_done, (char *) session); } /* data_done - send message content */ @@ -470,7 +511,7 @@ static void data_done(int unused_event, char *context) smtp_printf(session->stream, "To: <%s>", recipient); smtp_printf(session->stream, "Date: %s", mydate); smtp_printf(session->stream, "Message-Id: <%04x.%04x.%04x@%s>", - mypid, session->fd, message_count, var_myhostname); + mypid, vstream_fileno(session->stream), message_count, var_myhostname); smtp_fputs("", 0, session->stream); } @@ -505,8 +546,8 @@ static void data_done(int unused_event, char *context) /* * Prepare for the next event. */ - event_disable_readwrite(session->fd); - event_enable_read(session->fd, dot_done, (char *) session); + event_disable_readwrite(vstream_fileno(session->stream)); + event_enable_read(vstream_fileno(session->stream), dot_done, (char *) session); } /* dot_done - send QUIT */ @@ -532,8 +573,8 @@ static void dot_done(int unused_event, char *context) if (disconnect || message_count < 1) { send_quit(session); } else { - event_disable_readwrite(session->fd); - startup(session); + event_disable_readwrite(vstream_fileno(session->stream)); + start_another(session); } } @@ -542,8 +583,8 @@ static void dot_done(int unused_event, char *context) static void send_quit(SESSION *session) { command(session->stream, "QUIT"); - event_disable_readwrite(session->fd); - event_enable_read(session->fd, quit_done, (char *) session); + event_disable_readwrite(vstream_fileno(session->stream)); + event_enable_read(vstream_fileno(session->stream), quit_done, (char *) session); } /* quit_done - disconnect */ @@ -553,17 +594,17 @@ static void quit_done(int unused_event, char *context) SESSION *session = (SESSION *) context; (void) response(session->stream, buffer); - event_disable_readwrite(session->fd); + event_disable_readwrite(vstream_fileno(session->stream)); vstream_fclose(session->stream); session->stream = 0; - startup(session); + start_another(session); } /* usage - explain */ static void usage(char *myname) { - msg_fatal("usage: %s -s sess -l msglen -m msgs -c -C count -d -f from -o -t to -v host[:port]", myname); + msg_fatal("usage: %s -s sess -l msglen -m msgs -c -C count -d -f from -o -t to -R delay -v -w delay host[:port]", myname); } /* main - parse JCL and start the machine */ @@ -578,11 +619,12 @@ int main(int argc, char **argv) int i; signal(SIGPIPE, SIG_IGN); + msg_vstream_init(argv[0], VSTREAM_ERR); /* * Parse JCL. */ - while ((ch = GETOPT(argc, argv, "cC:df:l:m:or:s:t:v")) > 0) { + while ((ch = GETOPT(argc, argv, "cC:df:l:m:or:R:s:t:vw:")) > 0) { switch (ch) { case 'c': count++; @@ -619,6 +661,10 @@ int main(int argc, char **argv) if ((recipients = atoi(optarg)) <= 0) usage(argv[0]); break; + case 'R': + if (fixed_delay > 0 || (random_delay = atoi(optarg)) <= 0) + usage(argv[0]); + break; case 's': if ((sessions = atoi(optarg)) <= 0) usage(argv[0]); @@ -629,6 +675,10 @@ int main(int argc, char **argv) case 'v': msg_verbose++; break; + case 'w': + if (random_delay > 0 || (fixed_delay = atoi(optarg)) <= 0) + usage(argv[0]); + break; default: usage(argv[0]); } @@ -638,6 +688,9 @@ int main(int argc, char **argv) if ((port = split_at(host = argv[optind], ':')) == 0) port = "smtp"; + if (random_delay > 0) + srand(getpid()); + /* * Translate endpoint address to internal form. */ diff --git a/postfix/trivial-rewrite/.indent.pro b/postfix/trivial-rewrite/.indent.pro index e59d396ad..b38eded21 100644 --- a/postfix/trivial-rewrite/.indent.pro +++ b/postfix/trivial-rewrite/.indent.pro @@ -6,6 +6,7 @@ -TBOUNCE_STAT -TCLEANUP_STATE -TCLIENT_LIST +-TCLNT_STREAM -TCONFIG_BOOL_FN_TABLE -TCONFIG_BOOL_TABLE -TCONFIG_INT_FN_TABLE @@ -25,6 +26,8 @@ -TDICT_NISPLUS -TDICT_NODE -TDICT_OPEN_INFO +-TDICT_PCRE +-TDICT_UNIX -TDNS_FIXED -TDNS_REPLY -TDNS_RR @@ -68,8 +71,14 @@ -TRECIPIENT_LIST -TREC_TYPE_NAME -TRESOLVE_REPLY +-TRESPONSE -TSCAN_DIR +-TSCAN_INFO +-TSCAN_OBJ +-TSESSION -TSINGLE_SERVER +-TSINK_COMMAND +-TSINK_STATE -TSMTPD_STATE -TSMTPD_TOKEN -TSMTP_ADDR diff --git a/postfix/trivial-rewrite/rewrite.c b/postfix/trivial-rewrite/rewrite.c index 6cae7d8a3..b2dc90587 100644 --- a/postfix/trivial-rewrite/rewrite.c +++ b/postfix/trivial-rewrite/rewrite.c @@ -90,6 +90,12 @@ void rewrite_tree(char *unused_ruleset, TOK822 *tree) TOK822 *bang; TOK822 *local; + /* + * Sanity check. + */ + if (tree->head == 0) + msg_panic("rewrite_tree: empty tree"); + /* * An empty address is a special case. */ @@ -152,6 +158,15 @@ void rewrite_addr(char *ruleset, char *addr, VSTRING *result) { TOK822 *tree; + /* + * Sanity check. An address is supposed to be in externalized form. + */ + if (*addr == 0) { + msg_warn("rewrite_addr: null address, ruleset \"%s\"", ruleset); + vstring_strcpy(result, addr); + return; + } + /* * Convert the address from externalized (quoted) form to token list, * rewrite it, and convert back. diff --git a/postfix/trivial-rewrite/transport.c b/postfix/trivial-rewrite/transport.c index 407250114..4b6eabcd1 100644 --- a/postfix/trivial-rewrite/transport.c +++ b/postfix/trivial-rewrite/transport.c @@ -86,7 +86,6 @@ int transport_lookup(const char *domain, VSTRING *channel, VSTRING *nexthop) const char *name; const char *value; const char *host; - const char *next; char *saved_value; char *transport; int found = 0; @@ -96,8 +95,7 @@ int transport_lookup(const char *domain, VSTRING *channel, VSTRING *nexthop) /* * Keep stripping domain components until nothing is left or until a - * matching entry is found. Don't do routing on top-level domains. - * Transport table lookups are expensive enough already. + * matching entry is found. * * After checking the full name, check for .upper.domain, to distinguish * between the upper domain and it's decendants, ala sendmail and tcp @@ -106,7 +104,7 @@ int transport_lookup(const char *domain, VSTRING *channel, VSTRING *nexthop) * Before changing the DB lookup result, make a copy first, in order to * avoid DB cache corruption. */ - for (name = low_domain; (next = strchr(name + 1, '.')) != 0; name = next) { + for (name = low_domain; name != 0; name = strchr(name + 1, '.')) { if ((value = maps_find(transport_path, name)) != 0) { saved_value = mystrdup(value); if ((host = split_at(saved_value, ':')) == 0 || *host == 0) diff --git a/postfix/util/.indent.pro b/postfix/util/.indent.pro index e59d396ad..b38eded21 100644 --- a/postfix/util/.indent.pro +++ b/postfix/util/.indent.pro @@ -6,6 +6,7 @@ -TBOUNCE_STAT -TCLEANUP_STATE -TCLIENT_LIST +-TCLNT_STREAM -TCONFIG_BOOL_FN_TABLE -TCONFIG_BOOL_TABLE -TCONFIG_INT_FN_TABLE @@ -25,6 +26,8 @@ -TDICT_NISPLUS -TDICT_NODE -TDICT_OPEN_INFO +-TDICT_PCRE +-TDICT_UNIX -TDNS_FIXED -TDNS_REPLY -TDNS_RR @@ -68,8 +71,14 @@ -TRECIPIENT_LIST -TREC_TYPE_NAME -TRESOLVE_REPLY +-TRESPONSE -TSCAN_DIR +-TSCAN_INFO +-TSCAN_OBJ +-TSESSION -TSINGLE_SERVER +-TSINK_COMMAND +-TSINK_STATE -TSMTPD_STATE -TSMTPD_TOKEN -TSMTP_ADDR diff --git a/postfix/util/Makefile.in b/postfix/util/Makefile.in index f75571ff5..04c65102e 100644 --- a/postfix/util/Makefile.in +++ b/postfix/util/Makefile.in @@ -2,8 +2,8 @@ SHELL = /bin/sh SRCS = argv.c argv_split.c attr.c basename.c binhash.c chroot_uid.c \ close_on_exec.c concatenate.c dict.c dict_db.c dict_dbm.c \ dict_env.c dict_ht.c dict_ldap.c dict_ni.c dict_nis.c \ - dict_nisplus.c dict_open.c dir_forest.c environ.c events.c \ - exec_command.c fifo_listen.c fifo_trigger.c file_limit.c \ + dict_nisplus.c dict_open.c dir_forest.c doze.c environ.c \ + events.c exec_command.c fifo_listen.c fifo_trigger.c file_limit.c \ find_inet.c fsspace.c fullname.c get_domainname.c get_hostname.c \ htable.c inet_addr_host.c inet_addr_list.c inet_addr_local.c \ inet_connect.c inet_listen.c inet_trigger.c inet_util.c \ @@ -11,19 +11,19 @@ SRCS = argv.c argv_split.c attr.c basename.c binhash.c chroot_uid.c \ match_list.c match_ops.c msg.c msg_output.c msg_syslog.c \ msg_vstream.c mvect.c myflock.c mymalloc.c mystrtok.c name_mask.c \ non_blocking.c open_as.c open_limit.c open_lock.c peekfd.c \ - peer_name.c percentm.c posix_signals.c printable.c read_wait.c \ - readable.c readline.c ring.c safe_getenv.c safe_open.c \ - sane_accept.c scan_dir.c set_eugid.c set_ugid.c sigdelay.c \ - skipblanks.c split_at.c stat_as.c sys_compat.c timed_connect.c \ - timed_wait.c translit.c trimblanks.c unix_connect.c unix_listen.c \ - unix_trigger.c unsafe.c username.c valid_hostname.c vbuf.c \ - vbuf_print.c vstream.c vstream_popen.c vstring.c vstring_vstream.c \ - writable.c write_buf.c write_wait.c doze.c + percentm.c posix_signals.c printable.c read_wait.c readable.c \ + readline.c ring.c safe_getenv.c safe_open.c sane_accept.c \ + scan_dir.c set_eugid.c set_ugid.c sigdelay.c skipblanks.c \ + split_at.c stat_as.c sys_compat.c timed_connect.c timed_wait.c \ + translit.c trimblanks.c unix_connect.c unix_listen.c unix_trigger.c \ + unsafe.c username.c valid_hostname.c vbuf.c vbuf_print.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 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_ni.o dict_nis.o \ - dict_nisplus.o dict_open.o dir_forest.o environ.o events.o \ - exec_command.o fifo_listen.o fifo_trigger.o file_limit.o \ + dict_nisplus.o dict_open.o dir_forest.o doze.o environ.o \ + events.o exec_command.o fifo_listen.o fifo_trigger.o file_limit.o \ find_inet.o fsspace.o fullname.o get_domainname.o get_hostname.o \ htable.o inet_addr_host.o inet_addr_list.o inet_addr_local.o \ inet_connect.o inet_listen.o inet_trigger.o inet_util.o \ @@ -31,14 +31,14 @@ OBJS = argv.o argv_split.o attr.o basename.o binhash.o chroot_uid.o \ match_list.o match_ops.o msg.o msg_output.o msg_syslog.o \ msg_vstream.o mvect.o myflock.o mymalloc.o mystrtok.o name_mask.o \ non_blocking.o open_as.o open_limit.o open_lock.o peekfd.o \ - peer_name.o percentm.o posix_signals.o printable.o read_wait.o \ - readable.o readline.o ring.o safe_getenv.o safe_open.o \ - sane_accept.o scan_dir.o set_eugid.o set_ugid.o sigdelay.o \ - skipblanks.o split_at.o stat_as.o sys_compat.o timed_connect.o \ - timed_wait.o translit.o trimblanks.o unix_connect.o unix_listen.o \ - unix_trigger.o unsafe.o username.o valid_hostname.o vbuf.o \ - vbuf_print.o vstream.o vstream_popen.o vstring.o vstring_vstream.o \ - writable.o write_buf.o write_wait.o doze.o + percentm.o posix_signals.o printable.o read_wait.o readable.o \ + readline.o ring.o safe_getenv.o safe_open.o sane_accept.o \ + scan_dir.o set_eugid.o set_ugid.o sigdelay.o skipblanks.o \ + split_at.o stat_as.o sys_compat.o timed_connect.o timed_wait.o \ + translit.o trimblanks.o unix_connect.o unix_listen.o unix_trigger.o \ + unsafe.o username.o valid_hostname.o vbuf.o vbuf_print.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 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_ni.h dict_nis.h \ dict_nisplus.h dir_forest.h events.h exec_command.h find_inet.h \ @@ -47,12 +47,13 @@ HDRS = argv.h attr.h binhash.h chroot_uid.h connect.h dict.h dict_db.h \ iostuff.h line_wrap.h listen.h lstat_as.h mac_parse.h make_dirs.h \ match_list.h match_ops.h msg.h msg_output.h msg_syslog.h \ msg_vstream.h mvect.h myflock.h mymalloc.h name_mask.h open_as.h \ - open_lock.h peer_name.h percentm.h posix_signals.h readline.h \ - ring.h safe.h safe_open.h sane_accept.h scan_dir.h set_eugid.h \ - set_ugid.h sigdelay.h split_at.h stat_as.h stringops.h sys_defs.h \ + open_lock.h percentm.h posix_signals.h readline.h ring.h safe.h \ + safe_open.h sane_accept.h scan_dir.h set_eugid.h set_ugid.h \ + sigdelay.h split_at.h stat_as.h stringops.h sys_defs.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 -TESTSRC = fifo_open.c fifo_rdwr_bug.c fifo_rdonly_bug.c + vbuf.h vbuf_print.h vstream.h vstring.h vstring_vstream.h \ + dict_unix.h dict_pcre.h +TESTSRC = fifo_open.c fifo_rdwr_bug.c fifo_rdonly_bug.c select_bug.c WARN = -W -Wformat -Wimplicit -Wmissing-prototypes \ -Wparentheses -Wstrict-prototypes -Wswitch -Wuninitialized \ -Wunused @@ -65,7 +66,7 @@ TESTPROG= dict_open events exec_command fifo_open fifo_rdonly_bug \ fifo_rdwr_bug fifo_trigger fsspace fullname inet_addr_host \ inet_addr_local mac_parse make_dirs msg_syslog \ mystrtok peer_name sigdelay translit valid_hostname vstream_popen \ - vstring vstring_vstream doze + vstring vstring_vstream doze select_bug LIB_DIR = ../lib INC_DIR = ../include @@ -109,7 +110,7 @@ lint: lint $(SRCS) clean: - rm -f *.o $(LIB) *core $(TESTPROG) junk $(MAKES) + rm -f *.o $(LIB) *core $(TESTPROG) junk $(MAKES) *.tmp rm -rf printfck tidy: clean @@ -178,6 +179,9 @@ fifo_rdwr_bug: fifo_rdwr_bug.c $(LIB) fifo_rdonly_bug: fifo_rdonly_bug.c $(LIB) $(CC) $(CFLAGS) -o $@ $@.c $(LIB) $(SYSLIBS) +select_bug: select_bug.c $(LIB) + $(CC) $(CFLAGS) -o $@ $@.c $(LIB) $(SYSLIBS) + translit: $(LIB) mv $@.o junk $(CC) $(CFLAGS) -DTEST -o $@ $@.c $(LIB) $(SYSLIBS) @@ -231,6 +235,13 @@ depend: $(MAKES) done) | grep -v '[.][o][:][ ][/]' >$$$$ && mv $$$$ Makefile.in @make Makefile +tests: valid_hostname_test + +valid_hostname_test: valid_hostname valid_hostname.in valid_hostname.ref + ./valid_hostname valid_hostname.tmp + diff valid_hostname.ref valid_hostname.tmp + rm -f valid_hostname.tmp + # do not edit below this line - it is generated by 'make depend' argv.o: argv.c argv.o: mymalloc.h @@ -310,10 +321,6 @@ dict_ht.o: vbuf.h dict_ht.o: dict_ht.h dict_ldap.o: dict_ldap.c dict_ldap.o: sys_defs.h -dict_ldap.o: dict.h -dict_ldap.o: vstream.h -dict_ldap.o: vbuf.h -dict_ldap.o: dict_ldap.h dict_ni.o: dict_ni.c dict_ni.o: sys_defs.h dict_nis.o: dict_nis.c @@ -343,14 +350,27 @@ dict_open.o: dict.h dict_open.o: vstream.h dict_open.o: vbuf.h dict_open.o: dict_env.h +dict_open.o: dict_unix.h dict_open.o: dict_dbm.h dict_open.o: dict_db.h dict_open.o: dict_nis.h dict_open.o: dict_nisplus.h dict_open.o: dict_ni.h dict_open.o: dict_ldap.h +dict_open.o: dict_pcre.h dict_open.o: stringops.h dict_open.o: split_at.h +dict_pcre.o: dict_pcre.c +dict_pcre.o: sys_defs.h +dict_unix.o: dict_unix.c +dict_unix.o: sys_defs.h +dict_unix.o: msg.h +dict_unix.o: mymalloc.h +dict_unix.o: vstring.h +dict_unix.o: vbuf.h +dict_unix.o: dict.h +dict_unix.o: vstream.h +dict_unix.o: dict_unix.h dir_forest.o: dir_forest.c dir_forest.o: sys_defs.h dir_forest.o: msg.h @@ -588,11 +608,6 @@ open_lock.o: open_lock.h peekfd.o: peekfd.c peekfd.o: sys_defs.h peekfd.o: iostuff.h -peer_name.o: peer_name.c -peer_name.o: sys_defs.h -peer_name.o: msg.h -peer_name.o: valid_hostname.h -peer_name.o: peer_name.h percentm.o: percentm.c percentm.o: sys_defs.h percentm.o: vstring.h @@ -636,7 +651,16 @@ scan_dir.o: scan_dir.c scan_dir.o: sys_defs.h scan_dir.o: msg.h scan_dir.o: mymalloc.h +scan_dir.o: stringops.h +scan_dir.o: vstring.h +scan_dir.o: vbuf.h scan_dir.o: scan_dir.h +select_bug.o: select_bug.c +select_bug.o: sys_defs.h +select_bug.o: msg.h +select_bug.o: vstream.h +select_bug.o: vbuf.h +select_bug.o: msg_vstream.h set_eugid.o: set_eugid.c set_eugid.o: sys_defs.h set_eugid.o: msg.h diff --git a/postfix/util/binhash.c b/postfix/util/binhash.c index 0ab34989d..8c9ca7dab 100644 --- a/postfix/util/binhash.c +++ b/postfix/util/binhash.c @@ -44,9 +44,10 @@ /* BINHASH *table; /* void (*free_fn)(char *); /* -/* void binhash_walk(table, action) +/* void binhash_walk(table, action, ptr) /* BINHASH *table; -/* void (*action)(BINHASH_INFO *); +/* void (*action)(BINHASH_INFO *info, char *ptr); +/* char *ptr; /* /* BINHASH_INFO **binhash_list(table) /* BINHASH *table; @@ -84,7 +85,8 @@ /* with the entry. /* /* binhash_walk() invokes the action function for each table entry, with -/* a pointer to the entry as its argument. +/* a pointer to the entry as its argument. The ptr argument is passed +/* on to the action function. /* /* binhash_list() returns a null-terminated list of pointers to /* all elements in the named table. The list should be passed to @@ -303,8 +305,8 @@ void binhash_free(BINHASH *table, void (*free_fn) (char *)) /* binhash_walk - iterate over hash table */ -void binhash_walk(BINHASH *table, void (*action) (BINHASH_INFO *)) -{ +void binhash_walk(BINHASH *table, void (*action) (BINHASH_INFO *, char *), + char *ptr) { if (table != 0) { unsigned i = table->size; BINHASH_INFO **h = table->data; @@ -312,7 +314,7 @@ void binhash_walk(BINHASH *table, void (*action) (BINHASH_INFO *)) while (i-- > 0) for (ht = *h++; ht; ht = ht->next) - (*action) (ht); + (*action) (ht, ptr); } } diff --git a/postfix/util/binhash.h b/postfix/util/binhash.h index 814ec8ad3..f94e35bee 100644 --- a/postfix/util/binhash.h +++ b/postfix/util/binhash.h @@ -35,7 +35,7 @@ extern BINHASH_INFO *binhash_locate(BINHASH *, const char *, int); extern char *binhash_find(BINHASH *, const char *, int); extern void binhash_delete(BINHASH *, const char *, int, void (*) (char *)); extern void binhash_free(BINHASH *, void (*) (char *)); -extern void binhash_walk(BINHASH *, void (*) (BINHASH_INFO *)); +extern void binhash_walk(BINHASH *, void (*) (BINHASH_INFO *, char *), char *); extern BINHASH_INFO **binhash_list(BINHASH *); /* LICENSE diff --git a/postfix/util/dict_db.c b/postfix/util/dict_db.c index 1d38b09fa..0e34c9375 100644 --- a/postfix/util/dict_db.c +++ b/postfix/util/dict_db.c @@ -74,6 +74,9 @@ typedef struct { #define DICT_DB_TRY0NULL (1<<0) #define DICT_DB_TRY1NULL (1<<1) +#define DICT_DB_CACHE_SIZE (1024 * 1024) +#define DICT_DB_NELM 4096 + /* dict_db_lookup - find database entry */ static const char *dict_db_lookup(DICT *dict, const char *name) @@ -185,19 +188,14 @@ static void dict_db_close(DICT *dict) /* dict_db_open - open data base */ -static DICT *dict_db_open(const char *path, int flags, int type) +static DICT *dict_db_open(const char *path, int flags, int type, void *tweak) { DICT_DB *dict_db; DB *db; char *db_path; - HASHINFO tweak; - - memset((char *) &tweak, 0, sizeof(tweak)); - tweak.nelem = 4096; - tweak.cachesize = 1024 * 1024; db_path = concatenate(path, ".db", (char *) 0); - if ((db = dbopen(db_path, flags, 0644, type, (void *) &tweak)) == 0) + if ((db = dbopen(db_path, flags, 0644, type, tweak)) == 0) msg_fatal("open database %s: %m", db_path); dict_db = (DICT_DB *) mymalloc(sizeof(*dict_db)); @@ -216,14 +214,24 @@ static DICT *dict_db_open(const char *path, int flags, int type) DICT *dict_hash_open(const char *path, int flags) { - return (dict_db_open(path, flags, DB_HASH)); + HASHINFO tweak; + + memset((char *) &tweak, 0, sizeof(tweak)); + tweak.nelem = DICT_DB_NELM; + tweak.cachesize = DICT_DB_CACHE_SIZE; + return (dict_db_open(path, flags, DB_HASH, (void *) &tweak)); } /* dict_btree_open - create association with data base */ DICT *dict_btree_open(const char *path, int flags) { - return (dict_db_open(path, flags, DB_BTREE)); + BTREEINFO tweak; + + memset((char *) &tweak, 0, sizeof(tweak)); + tweak.cachesize = DICT_DB_CACHE_SIZE; + + return (dict_db_open(path, flags, DB_BTREE, (void *) &tweak)); } #endif diff --git a/postfix/util/dict_ldap.c b/postfix/util/dict_ldap.c index 5d674f3c1..d63150cbc 100644 --- a/postfix/util/dict_ldap.c +++ b/postfix/util/dict_ldap.c @@ -1,11 +1,453 @@ - /* - * The LDAP software is not bundled with IBM's public release. It will be - * made available as contributed software from http://www.postfix.org/ - */ +/*++ +/* NAME +/* dict_ldap 3 +/* SUMMARY +/* dictionary manager interface to LDAP maps +/* SYNOPSIS +/* #include +/* +/* DICT *dict_ldap_open(attribute, dummy) +/* const char *attribute; +/* int dummy; +/* DESCRIPTION +/* dict_ldap_open() makes LDAP user information accessible via +/* the generic dictionary operations described in dict_open(3). +/* +/* Arguments: +/* .IP ldapsource +/* The prefix which will be used to obtain configuration parameters +/* for this search. If it's 'ldapone', the configuration variables below +/* would look like 'ldapone_server_host', 'ldapone_search_base', and so +/* on in main.cf. +/* .IP dummy +/* Not used; this argument exists only for compatibility with +/* the dict_open(3) interface. +/* .PP +/* Configuration parameters: +/* .IP \fIldapsource_\fRserver_host +/* The host at which all LDAP queries are directed. +/* .IP \fIldapsource_\fRserver_port +/* The port the LDAP server listens on. +/* .IP \fIldapsource_\fRsearch_base +/* The LDAP search base, for example: \fIO=organization name, C=country\fR. +/* .IP \fIldapsource_\fRtimeout +/* Deadline for LDAP open() and LDAP search() . +/* .IP \fIldapsource_\fRquery_filter +/* The filter used to search for directory entries, for example +/* \fI(mailacceptinggeneralid=%s)\fR. +/* .IP \fIldapsource_\fRresult_attribute +/* The attribute returned by the search, in which we expect to find +/* RFC822 addresses, for example \fImaildrop\fR. +/* .IP \fIldapsource_\fRbind +/* Whether or not to bind to the server -- LDAP v3 implementations don't +/* require it, which saves some overhead. +/* .IP \fIldapsource_\fRbind_dn +/* If you must bind to the server, do it with this distinguished name ... +/* .IP \fIldapsource_\fRbind_pw +/* \&... and this password. +/* BUGS +/* Of course not! :) +/* SEE ALSO +/* dict(3) generic dictionary manager +/* DIAGNOSTICS +/* Warnings: unable to connect to server, unable to bind to server. +/* AUTHOR(S) +/* Prabhat K Singh +/* VSNL, Bombay, India. +/* prabhat@giasbm01.vsnl.net.in +/* +/* Wietse Venema +/* IBM T.J. Watson Research +/* P.O. Box 704 +/* Yorktown Heights, NY 10532, USA +/* +/* John Hensley +/* Merit Network, Inc. +/* hensley@merit.edu +/* +/*--*/ + +/* System library. */ + #include "sys_defs.h" + +#ifdef HAS_LDAP + +#include +#include +#include +#include +#include +#include +#include + +/* Utility library. */ + +#include "msg.h" +#include "mymalloc.h" +#include "vstring.h" #include "dict.h" #include "dict_ldap.h" -#ifdef HAS_LDAP -#error "This requires contributed software from http://www.postfix.org/" + /* + * Grr.. this module should sit in the global library, because it interacts + * with application-specific configuration parameters. I will have to + * generalize the manner in which new dictionary types can register + * themselves, including their configuration file parameters. + */ + +/* + * structure containing all the configuration parameters for a given + * LDAP source, plus its connection handle + */ +typedef struct { + DICT dict; /* generic member */ + char *ldapsource; + char *server_host; + int server_port; + char *search_base; + char *query_filter; + char *result_attribute; + int bind; + char *bind_dn; + char *bind_pw; + int timeout; + LDAP *ld; +} DICT_LDAP; + + /* + * LDAP connection timeout support. + */ +static jmp_buf env; + +static void dict_ldap_timeout(int unused_sig) +{ + longjmp(env, 1); +} + +/* dict_ldap_lookup - find database entry */ + +static const char *dict_ldap_lookup(DICT *dict, const char *name) +{ + char *myname = "dict_ldap_lookup"; + DICT_LDAP *dict_ldap = (DICT_LDAP *) dict; + static VSTRING *result; + int LDAP_UNBIND = 0; + LDAPMessage *res = 0; + LDAPMessage *entry = 0; + struct timeval tv; + VSTRING *filter_buf = 0; + char **attr_values; + long i = 0, j = 0; + int rc = 0; + void (*saved_alarm) (int); + + /* + * Initialize. + */ + if (result == 0) + result = vstring_alloc(2); + + vstring_strcpy(result,""); + + if (msg_verbose) + msg_info("%s: In dict_ldap_lookup", myname); + + if (dict_ldap->ld == 0) { + msg_warn("%s: no existing connection for ldapsource %s, reopening", + myname, dict_ldap->ldapsource); + if (msg_verbose) + msg_info("%s: connecting to server %s", myname, + dict_ldap->server_host); + + if ((saved_alarm = signal(SIGALRM, dict_ldap_timeout)) == SIG_ERR) + msg_fatal("%s: signal: %m", myname); + + alarm(dict_ldap->timeout); + if (setjmp(env) == 0) + dict_ldap->ld = ldap_open(dict_ldap->server_host, + (int) dict_ldap->server_port); + alarm(0); + + if (signal(SIGALRM, saved_alarm) == SIG_ERR) + msg_fatal("%s: signal: %m", myname); + + if (msg_verbose) + msg_info("%s: after ldap_open", myname); + + if (dict_ldap->ld == 0) { + msg_fatal("%s: Unable to contact LDAP server %s", + myname, dict_ldap->server_host); + } else { + /* + * If this server requires us to bind, do so. + */ + if (dict_ldap->bind) { + if (msg_verbose) + msg_info("%s: about to bind: server %s, base %s", myname, + dict_ldap->server_host, dict_ldap->search_base); + + rc = ldap_bind_s(dict_ldap->ld, dict_ldap->search_base, NULL, + LDAP_AUTH_SIMPLE); + if (rc != LDAP_SUCCESS) { + msg_fatal("%s: Unable to bind with search base %s at server %s (%d -- %s): ", myname, dict_ldap->search_base, dict_ldap->server_host, rc, ldap_err2string(rc)); + } else { + if (msg_verbose) + msg_info("%s: Successful bind to server %s with search base %s(%d -- %s): ", myname, dict_ldap->search_base, dict_ldap->server_host, rc, ldap_err2string(rc)); + } + } + if (msg_verbose) + msg_info("%s: cached connection handle for LDAP source %s", + myname, dict_ldap->ldapsource); + } + } + + /* + * Look for entries matching query_filter. + */ + tv.tv_sec = dict_ldap->timeout; + tv.tv_usec = 0; + filter_buf = vstring_alloc(30); + vstring_sprintf(filter_buf, dict_ldap->query_filter, name); + + if (msg_verbose) + msg_info("%s: searching with filter %s", myname, + vstring_str(filter_buf)); + + if ((rc=ldap_search_st(dict_ldap->ld, dict_ldap->search_base, + LDAP_SCOPE_SUBTREE, + vstring_str(filter_buf), + 0, 0, &tv, &res)) != LDAP_SUCCESS) { + + msg_info("%s: right after search", myname); + + msg_warn("%s: Unable to search base %s at server %s (%d -- %s): ", + myname, dict_ldap->search_base, dict_ldap->server_host, rc, + ldap_err2string(rc)); + LDAP_UNBIND = 1; + + } else { + /* + * Extract the requested result_attribute. + */ + if (msg_verbose) + msg_info("%s: search completed", myname); + + if ((entry = ldap_first_entry(dict_ldap->ld, res)) != 0) { + attr_values = ldap_get_values(dict_ldap->ld, entry, + dict_ldap->result_attribute); + /* + * Append each returned address to the result list. + */ + while (attr_values[i]) { + if (VSTRING_LEN(result) > 0) + vstring_strcat(result, ","); + vstring_strcat(result, attr_values[i]); + i++; + } + ldap_value_free(attr_values); + if (msg_verbose) + msg_info("%s: search returned: %s", myname, vstring_str(result)); + } else { + if (msg_verbose) + msg_info("%s: search returned nothing", myname); + } + } + + /* + * Cleanup. Always return with dict_errno set when we were unable to + * perform the query. + */ + if (res != 0) + ldap_msgfree(res); + else + dict_errno = 1; + if (LDAP_UNBIND == 1) { + /* + * there was an LDAP problem; free the handle + */ + ldap_unbind(dict_ldap->ld); + dict_ldap->ld = 0; + if (msg_verbose) + msg_info("%s: freed connection handle for LDAP source %s", + myname, dict_ldap->ldapsource); + } + if (filter_buf != 0) + vstring_free(filter_buf); + return (entry != 0 ? vstring_str(result) : 0); +} + +/* dict_ldap_update - add or update database entry */ + +static void dict_ldap_update(DICT *dict, const char *unused_name, + const char *unused_value) +{ + msg_fatal("dict_ldap_update: operation not implemented"); +} + +/* dict_ldap_close - disassociate from data base */ + +static void dict_ldap_close(DICT *dict) +{ + char *myname = "dict_ldap_close"; + DICT_LDAP *dict_ldap = (DICT_LDAP *) dict; + + if (dict_ldap->ld) + ldap_unbind(dict_ldap->ld); + + myfree(dict_ldap->ldapsource); + myfree(dict_ldap->server_host); + myfree(dict_ldap->search_base); + myfree(dict_ldap->query_filter); + myfree(dict_ldap->result_attribute); + myfree(dict_ldap->bind_dn); + myfree(dict_ldap->bind_pw); + myfree((char *)dict_ldap); +} + +/* dict_ldap_open - create association with data base */ + +DICT *dict_ldap_open(const char *ldapsource, int flags) +{ + char *myname = "dict_ldap_open"; + DICT_LDAP *dict_ldap; + VSTRING *config_param; + int rc = 0; + void (*saved_alarm) (int); + + dict_ldap = (DICT_LDAP *) mymalloc(sizeof(*dict_ldap)); + dict_ldap->dict.lookup = dict_ldap_lookup; + dict_ldap->dict.update = dict_ldap_update; + dict_ldap->dict.close = dict_ldap_close; + dict_ldap->dict.fd = -1; + + if (msg_verbose) + msg_info("%s: using LDAP source %s", myname, ldapsource); + + dict_ldap->ldapsource = mystrdup(ldapsource); + + config_param = vstring_alloc(15); + vstring_sprintf(config_param, "%s_server_host", ldapsource); + + dict_ldap->server_host = + mystrdup((char *)get_config_str(vstring_str(config_param), + "localhost",0,0)); + if (msg_verbose) + msg_info("%s: %s is %s", myname, vstring_str(config_param), + dict_ldap->server_host); + + /* get configured value of "ldapsource_server_port"; default to + /* LDAP_PORT (389) */ + vstring_sprintf(config_param, "%s_server_port", ldapsource); + dict_ldap->server_port = + get_config_int(vstring_str(config_param),LDAP_PORT,0,0); + if (msg_verbose) + msg_info("%s: %s is %d", myname, vstring_str(config_param), + dict_ldap->server_port); + + vstring_sprintf(config_param, "%s_search_base", ldapsource); + dict_ldap->search_base = + mystrdup((char *)get_config_str(vstring_str(config_param),"",0,0)); + if (msg_verbose) + msg_info("%s: %s is %s", myname, vstring_str(config_param), + dict_ldap->search_base); + + /* get configured value of "ldapsource_timeout"; default to 10 */ + vstring_sprintf(config_param, "%s_timeout", ldapsource); + dict_ldap->timeout = get_config_int(config_param,10,0,0); + if (msg_verbose) + msg_info("%s: %s is %d", myname, vstring_str(config_param), + dict_ldap->timeout); + + vstring_sprintf(config_param, "%s_query_filter", ldapsource); + dict_ldap->query_filter = + mystrdup((char *)get_config_str(vstring_str(config_param), + "(mailacceptinggeneralid=%s)",0,0)); + if (msg_verbose) + msg_info("%s: %s is %s", myname, vstring_str(config_param), + dict_ldap->query_filter); + + vstring_sprintf(config_param, "%s_result_attribute", ldapsource); + dict_ldap->result_attribute = + mystrdup((char *)get_config_str(vstring_str(config_param), + "maildrop",0,0)); + if (msg_verbose) + msg_info("%s: %s is %s", myname, vstring_str(config_param), + dict_ldap->result_attribute); + + /* get configured value of "ldapsource_bind"; default to true */ + vstring_sprintf(config_param, "%s_bind", ldapsource); + dict_ldap->bind = get_config_bool(vstring_str(config_param), 1); + if (msg_verbose) + msg_info("%s: %s is %d", myname, vstring_str(config_param), + dict_ldap->bind); + + /* get configured value of "ldapsource_bind_dn"; default to "" */ + vstring_sprintf(config_param, "%s_bind_dn", ldapsource); + dict_ldap->bind_dn = + mystrdup((char *)get_config_str(vstring_str(config_param),"",0,0)); + if (msg_verbose) + msg_info("%s: %s is %s", myname, vstring_str(config_param), + dict_ldap->bind_dn); + + /* get configured value of "ldapsource_bind_pw"; default to "" */ + vstring_sprintf(config_param, "%s_bind_pw", ldapsource); + dict_ldap->bind_pw = + mystrdup((char *)get_config_str(vstring_str(config_param),"",0,0)); + if (msg_verbose) + msg_info("%s: %s is %s", myname, vstring_str(config_param), + dict_ldap->bind_pw); + + /* + * establish the connection to the LDAP server + */ + + if (msg_verbose) + msg_info("%s: connecting to server %s", myname, + dict_ldap->server_host); + + if ((saved_alarm = signal(SIGALRM, dict_ldap_timeout)) == SIG_ERR) + msg_fatal("%s: signal: %m", myname); + + alarm(dict_ldap->timeout); + if (setjmp(env) == 0) + dict_ldap->ld = ldap_open(dict_ldap->server_host, + (int) dict_ldap->server_port); + alarm(0); + + if (signal(SIGALRM, saved_alarm) == SIG_ERR) + msg_fatal("%s: signal: %m", myname); + + if (msg_verbose) + msg_info("%s: after ldap_open", myname); + + if (dict_ldap->ld == 0) { + msg_fatal("%s: Unable to contact LDAP server %s", + myname, dict_ldap->server_host); + } else { + /* + * If this server requires us to bind, do so. + */ + if (dict_ldap->bind) { + if (msg_verbose) + msg_info("%s: about to bind: server %s, base %s", myname, + dict_ldap->server_host, dict_ldap->search_base); + + rc = ldap_bind_s(dict_ldap->ld, dict_ldap->search_base, NULL, + LDAP_AUTH_SIMPLE); + if (rc != LDAP_SUCCESS) { + msg_fatal("%s: Unable to bind with search base %s at server %s (%d -- %s): ", myname, dict_ldap->search_base, dict_ldap->server_host, rc, ldap_err2string(rc)); + } else { + if (msg_verbose) + msg_info("%s: Successful bind to server %s with search base %s(%d -- %s): ", myname, dict_ldap->search_base, dict_ldap->server_host, rc, ldap_err2string(rc)); + } + } + if (msg_verbose) + msg_info("%s: cached connection handle for LDAP source %s", + myname, dict_ldap->ldapsource); + } + + return (&dict_ldap->dict); +} + #endif diff --git a/postfix/util/dict_ldap.h b/postfix/util/dict_ldap.h index aaced3e7e..8a88cdcef 100644 --- a/postfix/util/dict_ldap.h +++ b/postfix/util/dict_ldap.h @@ -1,4 +1,31 @@ +#ifndef _DICT_LDAP_H_INCLUDED_ +#define _DICT_LDAP_H_INCLUDED_ + +/*++ +/* NAME +/* dict_ldap 3h +/* SUMMARY +/* dictionary manager interface to LDAP maps +/* SYNOPSIS +/* #include +/* DESCRIPTION +/* .nf + /* - * The LDAP software is not bundled with IBM's public release. It will be - * made available as contributed software from http://www.postfix.org/ + * Utility library. */ +#include + + /* + * External interface. + */ +extern DICT *dict_ldap_open(const char *, int); + +/* AUTHOR(S) +/* Wietse Venema +/* IBM T.J. Watson Research +/* P.O. Box 704 +/* Yorktown Heights, NY 10532, USA +/*--*/ + +#endif diff --git a/postfix/util/dict_open.c b/postfix/util/dict_open.c index f1dbbbb48..b30defc86 100644 --- a/postfix/util/dict_open.c +++ b/postfix/util/dict_open.c @@ -50,7 +50,8 @@ /* NetInfo table. Only read access is supported. /* .IP ldap /* LDAP ("light-weight" directory access protocol) database access. -/* The support is still incomplete. +/* .IP pcre +/* PERL-compatible regular expressions. /* .PP /* dict_open3() takes separate arguments for dictionary type and /* name, but otherwise performs the same functions as dict_open(). @@ -94,12 +95,14 @@ #include #include #include +#include #include #include #include #include #include #include +#include #include #include @@ -113,6 +116,7 @@ typedef struct { static DICT_OPEN_INFO dict_open_info[] = { "environ", dict_env_open, + "unix", dict_unix_open, #ifdef HAS_DBM "dbm", dict_dbm_open, #endif @@ -131,6 +135,9 @@ static DICT_OPEN_INFO dict_open_info[] = { #endif #ifdef HAS_LDAP "ldap", dict_ldap_open, +#endif +#ifdef HAS_PCRE + "pcre", dict_pcre_open, #endif 0, }; diff --git a/postfix/util/dict_pcre.c b/postfix/util/dict_pcre.c new file mode 100644 index 000000000..359a8380b --- /dev/null +++ b/postfix/util/dict_pcre.c @@ -0,0 +1,382 @@ +/*++ +/* NAME +/* dict_pcre 3 +/* SUMMARY +/* dictionary manager interface to PCRE regular expression library +/* SYNOPSIS +/* #include +/* +/* DICT *dict_pcre_open(name, flags) +/* const char *name; +/* int flags; +/* DESCRIPTION +/* dict_pcre_open() opens the named file and compiles the contained +/* regular expressions. +/* +/* The lookup interface will match only user@domain form addresses. +/* SEE ALSO +/* dict(3) generic dictionary manager +/* LICENSE +/* .ad +/* .fi +/* The Secure Mailer license must be distributed with this software. +/* AUTHOR(S) +/* Andrew McNamara +/* andrewm@connect.com.au +/* connect.com.au Pty. Ltd. +/* Level 3, 213 Miller St +/* North Sydney, NSW, Australia +/* +/* Wietse Venema +/* IBM T.J. Watson Research +/* P.O. Box 704 +/* Yorktown Heights, NY 10598, USA +/*--*/ + +#include "sys_defs.h" + +#ifdef HAS_PCRE + +/* System library. */ + +#include /* sprintf() prototype */ +#include +#include +#include +#include + +/* Utility library. */ + +#include "mymalloc.h" +#include "msg.h" +#include "safe.h" +#include "vstream.h" +#include "vstring.h" +#include "stringops.h" +#include "readline.h" +#include "dict.h" +#include "dict_pcre.h" +#include "mac_parse.h" + +/* PCRE library */ + +#include "pcre.h" + +#define PCRE_MAX_CAPTURE 99 /* Max strings captured by regexp - */ + /* essentially the max number of (..) */ + +struct dict_pcre_list { + pcre *pattern; /* The compiled pattern */ + pcre_extra *hints; /* Hints to speed pattern execution */ + char *replace; /* Replacement string */ + int lineno; /* Source file line number */ + struct dict_pcre_list *next; /* Next regexp in dict */ +}; + +typedef struct { + DICT dict; /* generic members */ + char *map; /* map name */ + int flags; /* unused at the moment */ + struct dict_pcre_list *head; +} DICT_PCRE; + +static dict_pcre_init = 0; /* flag need to init pcre library */ + +/* + * dict_pcre_update - not supported + */ +static void dict_pcre_update(DICT *dict, const char *unused_name, + const char *unused_value) +{ + DICT_PCRE *dict_pcre = (DICT_PCRE *) dict; + + msg_fatal("dict_pcre_update: attempt to update regexp map %s", + dict_pcre->map); +} + +/* + * Context for macro expansion callback. + */ +struct dict_pcre_context { + const char *dict_name; /* source dict name */ + int lineno; /* source file line number */ + VSTRING *buf; /* target string buffer */ + const char *subject; /* str against which we match */ + int offsets[PCRE_MAX_CAPTURE * 3]; /* Cut substrings */ + int matches; /* Count of cuts */ +}; + +/* + * Macro expansion callback - replace $0-${99} with strings cut from + * matched string. + */ +static void dict_pcre_action(int type, VSTRING *buf, char *ptr) +{ + struct dict_pcre_context *ctxt = (struct dict_pcre_context *) ptr; + const char *pp; + int n, + ret; + + if (type == MAC_PARSE_VARNAME) { + n = atoi(vstring_str(buf)); + ret = pcre_get_substring(ctxt->subject, ctxt->offsets, ctxt->matches, + n, &pp); + if (ret < 0) { + if (ret == PCRE_ERROR_NOSUBSTRING) + msg_warn("regexp %s, line %d: replace index out of range", + ctxt->dict_name, ctxt->lineno); + else + msg_warn("regexp %s, line %d: pcre_get_substring error: %d", + ctxt->dict_name, ctxt->lineno, ret); + return; + } + vstring_strcat(ctxt->buf, pp); + } else + /* Straight text - duplicate with no substitution */ + vstring_strcat(ctxt->buf, vstring_str(buf)); +} + +/* + * Look up regexp dict and perform string substitution on matched + * strings. + */ +static const char *dict_pcre_lookup(DICT *dict, const char *name) +{ + DICT_PCRE *dict_pcre = (DICT_PCRE *) dict; + struct dict_pcre_list *pcre_list; + int name_len = strlen(name); + struct dict_pcre_context ctxt; + static VSTRING *buf; + char *at; + +/* msg_info("dict_pcre_lookup: %s: %s", dict_pcre->map, name );*/ + + /* + * XXX Require user@domain, to defeat partial address matching for smtp + * access control, canonical and virtual mappings, and to prevent regexps + * from being used as alias databases because one might inadvertantly + * copy "|command" or /file/name or :include: to the result. + */ + if (name[0] == '@' || (at = strrchr(name, '@')) == 0 || at[1] == 0) + return (0); + + /* Search for a matching expression */ + for (pcre_list = dict_pcre->head; pcre_list; pcre_list = pcre_list->next) { + if (pcre_list->pattern) { + ctxt.matches = pcre_exec(pcre_list->pattern, pcre_list->hints, + name, name_len, 0, ctxt.offsets, PCRE_MAX_CAPTURE * 3); + if (ctxt.matches != PCRE_ERROR_NOMATCH) { + if (ctxt.matches > 0) + break; /* Got a match! */ + else { + /* An error */ + switch (ctxt.matches) { + case 0: + msg_warn("regexp map %s, line %d: too many (...)", + dict_pcre->map, pcre_list->lineno); + break; + case PCRE_ERROR_NULL: + case PCRE_ERROR_BADOPTION: + msg_fatal("regexp map %s, line %d: bad args to re_exec", + dict_pcre->map, pcre_list->lineno); + break; + case PCRE_ERROR_BADMAGIC: + case PCRE_ERROR_UNKNOWN_NODE: + msg_fatal("regexp map %s, line %d: corrupt compiled regexp", + dict_pcre->map, pcre_list->lineno); + break; + default: + msg_fatal("regexp map %s, line %d: unknown re_exec error: %d", + dict_pcre->map, pcre_list->lineno, ctxt.matches); + break; + } + return ((char *) 0); + } + } + } + } + + /* If we've got a match, */ + if (ctxt.matches > 0) { + /* Then perform substitution on replacement string */ + if (buf == 0) + buf = vstring_alloc(10); + VSTRING_RESET(buf); + ctxt.buf = buf; + ctxt.subject = name; + ctxt.dict_name = dict_pcre->map; + ctxt.lineno = pcre_list->lineno; + + mac_parse(pcre_list->replace, dict_pcre_action, (char *) &ctxt); + + VSTRING_TERMINATE(buf); + return (vstring_str(buf)); + } + return ((char *) 0); +} + +/* dict_pcre_close - close pcre dictionary */ + +static void dict_pcre_close(DICT *dict) +{ + DICT_PCRE *dict_pcre = (DICT_PCRE *) dict; + struct dict_pcre_list *pcre_list; + + for (pcre_list = dict_pcre->head; pcre_list; pcre_list = pcre_list->next) { + if (pcre_list->pattern) + myfree((char *) pcre_list->pattern); + if (pcre_list->hints) + myfree((char *) pcre_list->hints); + if (pcre_list->replace) + myfree((char *) pcre_list->replace); + } + myfree((char *) dict_pcre); +} + +/* + * dict_pcre_open - load and compile a file containing regular expressions + */ +DICT *dict_pcre_open(const char *map, int unused_flags) +{ + DICT_PCRE *dict_pcre; + VSTREAM *map_fp; + VSTRING *line_buffer; + struct dict_pcre_list *pcre_list = NULL, + *pl; + int lineno = 0; + char *regexp, + *p, + re_delimiter; + int re_options; + pcre *pattern; + pcre_extra *hints; + const char *error; + int errptr; + + line_buffer = vstring_alloc(100); + + dict_pcre = (DICT_PCRE *) mymalloc(sizeof(*dict_pcre)); + dict_pcre->dict.lookup = dict_pcre_lookup; + dict_pcre->dict.update = dict_pcre_update; + dict_pcre->dict.close = dict_pcre_close; + dict_pcre->dict.fd = -1; + dict_pcre->map = mystrdup(map); + dict_pcre->flags = 0; + dict_pcre->head = NULL; + + if (dict_pcre_init == 0) { + pcre_malloc = (void *(*)(size_t)) mymalloc; + pcre_free = (void (*)(void *)) myfree; + dict_pcre_init = 1; + } + if ((map_fp = vstream_fopen(map, O_RDONLY, 0)) == 0) { + msg_fatal("open %s: %m", map); + } + while (readline(line_buffer, map_fp, &lineno)) { + + if (*vstring_str(line_buffer) == '#') /* Skip comments */ + continue; + + if (*vstring_str(line_buffer) == 0) /* Skip blank lines */ + continue; + + p = vstring_str(line_buffer); + re_delimiter = *p++; + regexp = p; + + /* Search for second delimiter, handling backslash escape */ + while (*p) { + if (*p == re_delimiter && + (p > vstring_str(line_buffer) && *(p - 1) != '\\')) + break; + ++p; + } + + if (!*p) { + msg_warn("%s, line %d: no closing regexp delimiter: %c", + VSTREAM_PATH(map_fp), lineno, re_delimiter); + continue; + } + *p++ = '\0'; /* Null term the regexp */ + + /* Now parse any regexp options */ + re_options = PCRE_CASELESS; + while (*p && !ISSPACE(*p)) { + switch (*p) { + case 'i': + re_options ^= PCRE_CASELESS; + break; + case 'm': + re_options ^= PCRE_MULTILINE; + break; + case 's': + re_options ^= PCRE_DOTALL; + break; + case 'x': + re_options ^= PCRE_EXTENDED; + break; + case 'A': + re_options ^= PCRE_ANCHORED; + break; + case 'E': + re_options ^= PCRE_DOLLAR_ENDONLY; + break; + case 'U': + re_options ^= PCRE_UNGREEDY; + break; + case 'X': + re_options ^= PCRE_EXTRA; + break; + default: + msg_warn("%s, line %d: unknown regexp option '%c'", + VSTREAM_PATH(map_fp), lineno, *p); + } + ++p; + } + + while (*p && ISSPACE(*p)) + ++p; + + if (!*p) { + msg_warn("%s, line %d: no replacement text", + VSTREAM_PATH(map_fp), lineno); + p = ""; + } + /* Compile the patern */ + pattern = pcre_compile(regexp, re_options, &error, &errptr, NULL); + if (pattern == NULL) { + msg_warn("%s, line %d: error in regex at offset %d: %s", + VSTREAM_PATH(map_fp), lineno, errptr, error); + continue; + } + hints = pcre_study(pattern, 0, &error); + if (error != NULL) { + msg_warn("%s, line %d: error while studying regex: %s", + VSTREAM_PATH(map_fp), lineno, error); + myfree((char *) pattern); + continue; + } + /* Add it to the list */ + pl = (struct dict_pcre_list *) mymalloc(sizeof(struct dict_pcre_list)); + + /* Save the replacement string (if any) */ + pl->replace = mystrdup(p); + pl->pattern = pattern; + pl->hints = hints; + pl->next = NULL; + pl->lineno = lineno; + + if (pcre_list == NULL) + dict_pcre->head = pl; + else + pcre_list->next = pl; + pcre_list = pl; + } + + vstring_free(line_buffer); + vstream_fclose(map_fp); + + return (&dict_pcre->dict); +} + +#endif /* HAS_PCRE */ diff --git a/postfix/util/dict_pcre.h b/postfix/util/dict_pcre.h new file mode 100644 index 000000000..2c6c118a6 --- /dev/null +++ b/postfix/util/dict_pcre.h @@ -0,0 +1,41 @@ +#ifndef _DICT_PCRE_H_INCLUDED_ +#define _DICT_PCRE_H_INCLUDED_ + +/*++ +/* NAME +/* dict_pcre 3h +/* SUMMARY +/* dictionary manager interface to PCRE regular expression library +/* SYNOPSIS +/* #include +/* DESCRIPTION +/* .nf + + /* + * Utility library. + */ +#include + + /* + * External interface. + */ +extern DICT *dict_pcre_open(const char *, int); + +/* 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 +/* +/* Andrew McNamara +/* andrewm@connect.com.au +/* connect.com.au Pty. Ltd. +/* Level 3, 213 Miller St +/* North Sydney, NSW, Australia +/*--*/ + +#endif diff --git a/postfix/util/dict_unix.c b/postfix/util/dict_unix.c new file mode 100644 index 000000000..82f36b805 --- /dev/null +++ b/postfix/util/dict_unix.c @@ -0,0 +1,125 @@ +/*++ +/* NAME +/* dict_unix 3 +/* SUMMARY +/* dictionary manager interface to UNIX tables +/* SYNOPSIS +/* #include +/* +/* DICT *dict_unix_open(map, dummy) +/* const char *map; +/* int dummy; +/* DESCRIPTION +/* dict_unix_open() makes the specified UNIX table accessible via +/* the generic dictionary operations described in dict_open(3). +/* The \fIdummy\fR argument is not used. +/* +/* Known map names: +/* .IP passwd.byname +/* The table is the UNIX password database. The key is a login name. +/* The result is a password file entry in passwd(5) format. +/* SEE ALSO +/* dict(3) generic dictionary manager +/* DIAGNOSTICS +/* Fatal errors: out of memory, unknown map name, attempt to update map. +/* 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 + +/* Utility library. */ + +#include "msg.h" +#include "mymalloc.h" +#include "vstring.h" +#include "dict.h" +#include "dict_unix.h" + +/* Application-specific. */ + +typedef struct { + DICT dict; /* generic members */ + char *map; /* UNIX map name */ +} DICT_UNIX; + +/* dict_unix_getpwnam - find password table entry */ + +static const char *dict_unix_getpwnam(DICT *unused_dict, const char *key) +{ + struct passwd *pwd; + static VSTRING *buf; + + dict_errno = 0; + + if ((pwd = getpwnam(key)) == 0) { + return (0); + } else { + if (buf == 0) + buf = vstring_alloc(10); + vstring_sprintf(buf, "%s:%s:%d:%d:%s:%s:%s", + pwd->pw_name, pwd->pw_passwd, pwd->pw_uid, pwd->pw_gid, + pwd->pw_gecos, pwd->pw_dir, pwd->pw_shell); + return (vstring_str(buf)); + } +} + +/* dict_unix_update - add or update table entry */ + +static void dict_unix_update(DICT *dict, const char *unused_name, const char *unused_value) +{ + DICT_UNIX *dict_unix = (DICT_UNIX *) dict; + + msg_fatal("dict_unix_update: attempt to update map %s", dict_unix->map); +} + +/* dict_unix_close - close UNIX map */ + +static void dict_unix_close(DICT *dict) +{ + DICT_UNIX *dict_unix = (DICT_UNIX *) dict; + + myfree(dict_unix->map); + myfree((char *) dict_unix); +} + +/* dict_unix_open - open UNIX map */ + +DICT *dict_unix_open(const char *map, int unused_flags) +{ + DICT_UNIX *dict_unix; + struct dict_unix_lookup { + char *name; + const char *(*lookup) (DICT *, const char *); + }; + static struct dict_unix_lookup dict_unix_lookup[] = { + "passwd.byname", dict_unix_getpwnam, + 0, + }; + struct dict_unix_lookup *lp; + + dict_unix = (DICT_UNIX *) mymalloc(sizeof(*dict_unix)); + for (lp = dict_unix_lookup; /* void */ ; lp++) { + if (lp->name == 0) + msg_fatal("dict_unix_open: unknown map name: %s", map); + if (strcmp(map, lp->name) == 0) + break; + } + dict_unix->dict.lookup = lp->lookup; + dict_unix->dict.update = dict_unix_update; + dict_unix->dict.close = dict_unix_close; + dict_unix->dict.fd = -1; + dict_unix->map = mystrdup(map); + return (&dict_unix->dict); +} diff --git a/postfix/util/dict_unix.h b/postfix/util/dict_unix.h new file mode 100644 index 000000000..df8dc6470 --- /dev/null +++ b/postfix/util/dict_unix.h @@ -0,0 +1,35 @@ +#ifndef _DIST_UNIX_H_INCLUDED_ +#define _DIST_UNIX_H_INCLUDED_ + +/*++ +/* NAME +/* dict_unix 3h +/* SUMMARY +/* dictionary manager interface to UNIX maps +/* SYNOPSIS +/* #include +/* DESCRIPTION +/* .nf + + /* + * Utility library. + */ +#include + + /* + * External interface. + */ +extern DICT *dict_unix_open(const char *, int); + +/* 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/events.c b/postfix/util/events.c index aaf423ba1..dc61e97c0 100644 --- a/postfix/util/events.c +++ b/postfix/util/events.c @@ -12,7 +12,7 @@ /* int delay; /* /* time_t event_request_timer(callback, context, delay) -/* void (*callback)(char *context); +/* void (*callback)(int event, char *context); /* char *context; /* int delay; /* @@ -57,6 +57,7 @@ /* be called with the specified context argument after \fIdelay\fR /* seconds, or as soon as possible thereafter. The delay should /* not be negative. +/* The event argument is equal to EVENT_TIME. /* Only one timer request can be active per (callback, context) pair. /* Calling event_request_timer() with an existing (callback, context) /* pair does not schedule a new event, but updates the moment of @@ -534,7 +535,7 @@ void event_loop(int delay) if (msg_verbose > 2) msg_info("%s: timer 0x%lx 0x%lx", myname, (long) timer->callback, (long) timer->context); - timer->callback(timer->context); /* then this */ + timer->callback(EVENT_TIME, timer->context); /* then this */ myfree((char *) timer); } diff --git a/postfix/util/events.h b/postfix/util/events.h index f3f6208d7..bc39b4787 100644 --- a/postfix/util/events.h +++ b/postfix/util/events.h @@ -20,7 +20,7 @@ * External interface. */ typedef void (*EVENT_NOTIFY_RDWR) (int, char *); -typedef void (*EVENT_NOTIFY_TIME) (char *); +typedef void (*EVENT_NOTIFY_TIME) (int, char *); extern time_t event_time(void); extern void event_enable_read(int, EVENT_NOTIFY_RDWR, char *); @@ -36,6 +36,7 @@ extern void event_loop(int); #define EVENT_READ (1<<0) /* read event */ #define EVENT_WRITE (1<<1) /* write event */ #define EVENT_XCPT (1<<2) /* exception */ +#define EVENT_TIME (1<<3) /* timer event */ #define EVENT_ERROR EVENT_XCPT diff --git a/postfix/util/htable.c b/postfix/util/htable.c index 2e67d9187..299d0c614 100644 --- a/postfix/util/htable.c +++ b/postfix/util/htable.c @@ -39,9 +39,10 @@ /* HTABLE *table; /* void (*free_fn)(char *); /* -/* void htable_walk(table, action) +/* void htable_walk(table, action, ptr) /* HTABLE *table; -/* void (*action)(HTABLE_INFO *); +/* void (*action)(HTABLE_INFO *, char *ptr); +/* char *ptr; /* /* HTABLE_INFO *htable_list(table) /* HTABLE *table; @@ -78,7 +79,8 @@ /* with the entry. /* /* htable_walk() invokes the action function for each table entry, with -/* a pointer to the entry as its argument. +/* a pointer to the entry as its argument. The ptr argument is passed +/* on to the action function. /* /* htable_list() returns a null-terminated list of pointers to /* all elements in the named table. The list should be passed to @@ -296,8 +298,8 @@ void htable_free(HTABLE *table, void (*free_fn) (char *)) /* htable_walk - iterate over hash table */ -void htable_walk(HTABLE *table, void (*action) (HTABLE_INFO *)) -{ +void htable_walk(HTABLE *table, void (*action) (HTABLE_INFO *, char *), + char *ptr) { if (table) { unsigned i = table->size; HTABLE_INFO **h = table->data; @@ -305,7 +307,7 @@ void htable_walk(HTABLE *table, void (*action) (HTABLE_INFO *)) while (i-- > 0) for (ht = *h++; ht; ht = ht->next) - (*action) (ht); + (*action) (ht, ptr); } } diff --git a/postfix/util/htable.h b/postfix/util/htable.h index 8a9bbfd3b..72eaa98f1 100644 --- a/postfix/util/htable.h +++ b/postfix/util/htable.h @@ -34,7 +34,7 @@ extern HTABLE_INFO *htable_locate(HTABLE *, const char *); extern char *htable_find(HTABLE *, const char *); extern void htable_delete(HTABLE *, const char *, void (*) (char *)); extern void htable_free(HTABLE *, void (*) (char *)); -extern void htable_walk(HTABLE *, void (*) (HTABLE_INFO *)); +extern void htable_walk(HTABLE *, void (*) (HTABLE_INFO *, char *), char *); extern HTABLE_INFO **htable_list(HTABLE *); /* LICENSE diff --git a/postfix/util/scan_dir.c b/postfix/util/scan_dir.c index b13b5eba6..1b5680bde 100644 --- a/postfix/util/scan_dir.c +++ b/postfix/util/scan_dir.c @@ -15,6 +15,13 @@ /* char *scan_dir_path(scan) /* SCAN_DIR *scan; /* +/* void scan_push(scan, entry) +/* SCAN_DIR *scan; +/* const char *entry; +/* +/* SCAN_DIR *scan_pop(scan) +/* SCAN_DIR *scan; +/* /* SCAN_DIR *scan_dir_close(scan) /* SCAN_DIR *scan; /* DESCRIPTION @@ -26,13 +33,20 @@ /* scan_dir_open() opens the named directory and /* returns a handle for subsequent use. /* -/* scan_dir_close() closes the directory and cleans up +/* scan_dir_close() terminates the directory scan, cleans up /* and returns a null pointer. /* -/* scan_dir_next() returns the next filename in the specified +/* scan_dir_next() returns the next requested object in the specified /* directory. It skips the "." and ".." entries. /* /* scan_dir_path() returns the name of the directory being scanned. +/* +/* scan_dir_push() causes the specified directory scan to enter the +/* named subdirectory. +/* +/* scan_dir_pop() leaves the directory being scanned and returns +/* to the previous one. The result is the argument, null if no +/* previous directory information is available. /* DIAGNOSTICS /* All errors are fatal. /* LICENSE @@ -52,43 +66,79 @@ #include #include -#ifdef HAVE_DIRENT_H -#include -#define NAMLEN(dirent) strlen((dirent)->d_name) -#else -#define dirent direct -#define NAMLEN(dirent) (dirent)->d_namlen -#ifdef HAVE_SYS_NDIR_H -#include -#endif -#ifdef HAVE_SYS_DIR_H -#include -#endif -#ifdef HAVE_NDIR_H -#include -#endif -#endif - /* Utility library. */ #include "msg.h" #include "mymalloc.h" +#include "stringops.h" +#include "vstring.h" #include "scan_dir.h" /* - * Opaque structure, so we don't have to expose the user to the above #ifdef - * spaghetti. + * The interface is based on an opaque structure, so we don't have to expose + * the user to the guts. Subdirectory info sits in front of parent directory + * info: a simple last-in, first-out list. */ +typedef struct SCAN_INFO SCAN_INFO; + +struct SCAN_INFO { + char *path; /* directory name */ + DIR *dir; /* directory structure */ + SCAN_INFO *parent; /* linkage */ +}; struct SCAN_DIR { - char *path; - DIR *dir; + SCAN_INFO *current; /* current scan */ }; +#define SCAN_DIR_PATH(scan) (scan->current->path) +#define STR(x) vstring_str(x) + /* scan_dir_path - return the path of the directory being read. */ char *scan_dir_path(SCAN_DIR *scan) { - return scan->path; + return (SCAN_DIR_PATH(scan)); +} + +/* scan_dir_push - enter directory */ + +void scan_dir_push(SCAN_DIR *scan, const char *path) +{ + char *myname = "scan_dir_push"; + SCAN_INFO *info; + + info = (SCAN_INFO *) mymalloc(sizeof(*info)); + if (scan->current) + info->path = concatenate(SCAN_DIR_PATH(scan), "/", path, (char *) 0); + else + info->path = mystrdup(path); + if ((info->dir = opendir(info->path)) == 0) + msg_fatal("%s: open directory %s: %m", myname, info->path); + if (msg_verbose > 1) + msg_info("%s: open %s", myname, info->path); + info->parent = scan->current; + scan->current = info; +} + +/* scan_dir_pop - leave directory */ + +SCAN_DIR *scan_dir_pop(SCAN_DIR *scan) +{ + char *myname = "scan_dir_pop"; + SCAN_INFO *info = scan->current; + SCAN_INFO *parent; + + if (info == 0) + return (0); + parent = info->parent; + if (closedir(info->dir)) + msg_fatal("%s: close directory %s: %m", myname, info->path); + if (msg_verbose > 1) + msg_info("%s: close %s", myname, info->path); + myfree(info->path); + myfree((char *) info); + scan->current = parent; + return (parent ? scan : 0); } /* scan_dir_open - start directory scan */ @@ -98,25 +148,32 @@ SCAN_DIR *scan_dir_open(const char *path) SCAN_DIR *scan; scan = (SCAN_DIR *) mymalloc(sizeof(*scan)); - if ((scan->dir = opendir(path)) == 0) - msg_fatal("open directory %s: %m", path); - if (msg_verbose > 1) - msg_info("scan_dir_open: %s", path); - scan->path = mystrdup(path); + scan->current = 0; + scan_dir_push(scan, path); return (scan); } +/* scan_dir_next - find next entry */ + char *scan_dir_next(SCAN_DIR *scan) { + char *myname = "scan_dir_next"; + SCAN_INFO *info = scan->current; struct dirent *dp; -#define STRNE(x,y) (strcmp((x),(y)) != 0) - - while ((dp = readdir(scan->dir)) != 0) { - if (STRNE(dp->d_name, ".") && STRNE(dp->d_name, "..")) { - if (msg_verbose > 1) - msg_info("scan_dir_next: %s", dp->d_name); - return (dp->d_name); +#define STREQ(x,y) (strcmp((x),(y)) == 0) + + if (info) { + while ((dp = readdir(info->dir)) != 0) { + if (STREQ(dp->d_name, ".") || STREQ(dp->d_name, "..")) { + if (msg_verbose > 1) + msg_info("%s: skip %s", myname, dp->d_name); + continue; + } else { + if (msg_verbose > 1) + msg_info("%s: found %s", myname, dp->d_name); + return (dp->d_name); + } } } return (0); @@ -126,11 +183,8 @@ char *scan_dir_next(SCAN_DIR *scan) SCAN_DIR *scan_dir_close(SCAN_DIR *scan) { - if (closedir(scan->dir)) - msg_fatal("close directory %s: %m", scan->path); - if (msg_verbose > 1) - msg_info("scan_dir_close: %s", scan->path); - myfree(scan->path); + while (scan->current) + scan_dir_pop(scan); myfree((char *) scan); return (0); } diff --git a/postfix/util/scan_dir.h b/postfix/util/scan_dir.h index cc7e5538c..8f3bf8b98 100644 --- a/postfix/util/scan_dir.h +++ b/postfix/util/scan_dir.h @@ -11,11 +11,6 @@ /* DESCRIPTION /* .nf - /* - * System library. - */ -#include - /* * The directory scanner interface. */ @@ -24,6 +19,8 @@ typedef struct SCAN_DIR SCAN_DIR; extern SCAN_DIR *scan_dir_open(const char *); extern char *scan_dir_next(SCAN_DIR *); extern char *scan_dir_path(SCAN_DIR *); +extern void scan_dir_push(SCAN_DIR *, const char *); +extern SCAN_DIR *scan_dir_pop(SCAN_DIR *); extern SCAN_DIR *scan_dir_close(SCAN_DIR *); /* LICENSE diff --git a/postfix/util/select_bug.c b/postfix/util/select_bug.c new file mode 100644 index 000000000..ff5f36293 --- /dev/null +++ b/postfix/util/select_bug.c @@ -0,0 +1,93 @@ +/*++ +/* NAME +/* select_bug 1 +/* SUMMARY +/* select test program +/* SYNOPSIS +/* select_bug +/* DESCRIPTION +/* select_bug forks child processes that perform select() +/* on a shared socket, and sees if a wakeup affects other +/* processes selecting on a different socket or stdin. +/* DIAGNOSTICS +/* Problems are reported to the standard error stream. +/* 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 +#include /* bzero() prototype for 44BSD */ + +/* Utility library. */ + +#include +#include +#include + +static pid_t fork_and_read_select(const char *what, int delay, int fd) +{ + struct timeval tv; + pid_t pid; + fd_set readfds; + + switch (pid = fork()) { + case -1: + msg_fatal("fork: %m"); + case 0: + tv.tv_sec = delay; + tv.tv_usec = 0; + FD_ZERO(&readfds); + FD_SET(fd, &readfds); + switch (select(fd + 1, &readfds, (fd_set *) 0, &readfds, &tv)) { + case -1: + msg_fatal("select: %m"); + case 0: + msg_info("%s select timed out", what); + exit(0); + default: + msg_info("%s select wakeup", what); + exit(0); + } + default: + return (pid); + } +} + +main(int argc, char **argv) +{ + int pair1[2]; + int pair2[2]; + + msg_vstream_init(argv[0], VSTREAM_ERR); + +#define DELAY 1 + + if (socketpair(AF_UNIX, SOCK_STREAM, 0, pair1) < 0) + msg_fatal("socketpair: %m"); + if (socketpair(AF_UNIX, SOCK_STREAM, 0, pair2) < 0) + msg_fatal("socketpair: %m"); + + vstream_printf("Doing multiple select on socket1, then write to it...\n"); + vstream_fflush(VSTREAM_OUT); + fork_and_read_select("socket1", DELAY, pair1[0]); /* one */ + fork_and_read_select("socket1", DELAY, pair1[0]); /* two */ + fork_and_read_select("socket2", DELAY, pair2[0]); + fork_and_read_select("stdin", DELAY, 0); + if (write(pair1[1], "", 1) != 1) + msg_fatal("write: %m"); + while (wait((int *) 0) >= 0) + /* void */ ; +} diff --git a/postfix/util/sys_defs.h b/postfix/util/sys_defs.h index e91483080..21b0e0151 100644 --- a/postfix/util/sys_defs.h +++ b/postfix/util/sys_defs.h @@ -19,7 +19,7 @@ * directory. Adding support for a new system type means updating the * makedefs script, and adding a section below for the new system. */ -#if defined(FREEBSD2) || defined(FREEBSD3) \ +#if defined(FREEBSD2) || defined(FREEBSD3) || defined(FREEBSD4) \ || defined(BSDI2) || defined(BSDI3) || defined(BSDI4) \ || defined(OPENBSD2) || defined(NETBSD1) #define SUPPORTED @@ -179,6 +179,32 @@ extern int opterr; #define UNIX_DOMAIN_CONNECT_BLOCKS_FOR_ACCEPT #endif +#ifdef UW21 /* UnixWare 2.1.x */ +#define SUPPORTED +#include +#define _PATH_MAILDIR "/var/mail" +#define _PATH_BSHELL "/bin/sh" +#define _PATH_DEFPATH "/usr/bin:/usr/ucb" +#define _PATH_STDPATH "/usr/bin:/usr/sbin:/usr/ucb" +#define MISSING_SETENV +#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" +/* Uncomment the following line if you have NIS package installed +#define HAS_NIS */ +#define USE_SYS_SOCKIO_H +#define GETTIMEOFDAY(t) gettimeofday(t,NULL) +#define ROOT_PATH "/bin:/usr/bin:/sbin:/usr/sbin:/usr/ucb" +#define FIONREAD_IN_SYS_FILIO_H +#define DBM_NO_TRAILING_NULL +#define USE_STATVFS +#define STATVFS_IN_SYS_STATVFS_H +#define UNIX_DOMAIN_CONNECT_BLOCKS_FOR_ACCEPT +#endif + #ifdef AIX4 #define SUPPORTED #include diff --git a/postfix/util/valid_hostname.c b/postfix/util/valid_hostname.c index 25d1f6c56..7fff779c3 100644 --- a/postfix/util/valid_hostname.c +++ b/postfix/util/valid_hostname.c @@ -8,13 +8,22 @@ /* /* int valid_hostname(name) /* const char *name; +/* +/* int valid_hostaddr(addr) +/* const char *addr; /* DESCRIPTION /* valid_hostname() scrutinizes a hostname: the name should be no /* longer than VALID_HOSTNAME_LEN characters, should contain only /* letters, digits, dots and hyphens, no adjacent dots and hyphens, -/* no leading or trailing dots or hyphens. +/* no leading or trailing dots or hyphens, no labels longer than +/* VALID_LABEL_LEN characters, and no numeric top-level domain. +/* +/* valid_hostaddr() requirs that the input is a valid string +/* representation of an internet network address. +/* DIAGNOSTICS +/* Both functions return zero if they disagree with the input. /* SEE ALSO -/* RFC 952, 1123 +/* RFC 952, 1123, RFC 1035 /* LICENSE /* .ad /* .fi @@ -43,62 +52,115 @@ int valid_hostname(const char *name) { + char *myname = "valid_hostname"; const char *cp; - char *str; + int label_length = 0; + int label_count = 0; + int non_numeric = 0; int ch; - int len; - int bad_val = 0; - int adjacent = 0; - -#define DELIMITER(c) (c == '.' || c == '-') /* * Trivial cases first. */ if (*name == 0) { - msg_warn("valid_hostname: empty hostname"); + msg_warn("%s: empty hostname", myname); return (0); } /* - * Find bad characters. Find adjacent delimiters. + * Find bad characters or label lengths. Find adjacent delimiters. */ - for (cp = name; (ch = *cp) != 0; cp++) { - if (DELIMITER(ch)) { - if (DELIMITER(cp[1])) - adjacent = 1; - } else if (!ISALNUM(ch) && ch != '_') { /* grr.. */ - if (bad_val == 0) - bad_val = ch; + for (cp = name; (ch = *(unsigned char *) cp) != 0; cp++) { + if (ISALNUM(ch) || ch == '_') { /* grr.. */ + if (label_length == 0) + label_count++; + label_length++; + if (label_length > VALID_LABEL_LEN) { + msg_warn("%s: hostname label too long: %.100s", myname, name); + return (0); + } + if (!ISDIGIT(ch)) + non_numeric = 1; + } else if (ch == '.' || ch == '-') { + if (label_length == 0 || cp[1] == 0) { + msg_warn("%s: misplaced delimiter: %.100s", myname, name); + return (0); + } + label_length = 0; + } else { + msg_warn("%s: invalid character %d(decimal): %.100s", + myname, ch, name); + return (0); } } - /* - * Before printing the name, validate its length. - */ - if ((len = strlen(name)) > VALID_HOSTNAME_LEN) { - str = printable(mystrdup(name), '?'); - msg_warn("valid_hostname: bad length %d for %.100s...", len, str); - myfree(str); + if (non_numeric == 0) { + msg_warn("%s: numeric hostname: %.100s", myname, name); + /* NOT: return (0); this confuses users of the DNS client */ + } + if (cp - name > VALID_HOSTNAME_LEN) { + msg_warn("%s: bad length %d for %.100s...", myname, cp - name, name); return (0); } + return (1); +} + +/* valid_hostaddr - test dotted quad string for correctness */ + +int valid_hostaddr(const char *addr) +{ + const char *cp; + char *myname = "valid_hostaddr"; + int in_byte = 0; + int byte_count = 0; + int byte_val = 0; + int ch; + +#define BYTES_NEEDED 4 /* - * Report bad characters. + * Trivial cases first. */ - if (bad_val) { - str = printable(mystrdup(name), '?'); - msg_warn("valid_hostname: invalid character %d(decimal) in %s", - bad_val, str); - myfree(str); + if (*addr == 0) { + msg_warn("%s: empty address", myname); return (0); } /* - * Misplaced delimiters. + * Scary code to avoid sscanf() overflow nasties. */ - if (DELIMITER(name[0]) || adjacent || DELIMITER(name[len - 1])) { - msg_warn("valid_hostname: misplaced delimiter in %s", name); + for (cp = addr; (ch = *(unsigned const char *) cp) != 0; cp++) { + if (ISDIGIT(ch)) { + if (in_byte == 0) { + in_byte = 1; + byte_val = 0; + byte_count++; + } + byte_val *= 10; + byte_val += ch - '0'; + if (byte_val > 255) { + msg_warn("%s: invalid octet value: %.100s", myname, addr); + return (0); + } + } else if (ch == '.') { + if (in_byte == 0 || cp[1] == 0) { + msg_warn("%s: misplaced dot: %.100s", myname, addr); + return (0); + } + if ((byte_count == 1 && byte_val == 0)) { + msg_warn("%s: bad initial octet value: %.100s", myname, addr); + return (0); + } + in_byte = 0; + } else { + msg_warn("%s: invalid character %d(decimal): %.100s", + myname, ch, addr); + return (0); + } + } + + if (byte_count != BYTES_NEEDED) { + msg_warn("%s: invalid octet count: %.100s", myname, addr); return (0); } return (1); @@ -124,8 +186,11 @@ int main(int unused_argc, char **argv) msg_vstream_init(argv[0], VSTREAM_ERR); msg_verbose = 1; - while (vstring_fgets_nonl(buffer, VSTREAM_IN)) + while (vstring_fgets_nonl(buffer, VSTREAM_IN)) { + msg_info("testing: \"%s\"", vstring_str(buffer)); valid_hostname(vstring_str(buffer)); + valid_hostaddr(vstring_str(buffer)); + } exit(0); } diff --git a/postfix/util/valid_hostname.h b/postfix/util/valid_hostname.h index 53f5eb99f..69db14056 100644 --- a/postfix/util/valid_hostname.h +++ b/postfix/util/valid_hostname.h @@ -13,9 +13,11 @@ /* External interface */ -#define VALID_HOSTNAME_LEN 256 +#define VALID_HOSTNAME_LEN 255 /* RFC 1035 */ +#define VALID_LABEL_LEN 63 /* RFC 1035 */ extern int valid_hostname(const char *); +extern int valid_hostaddr(const char *); /* LICENSE /* .ad diff --git a/postfix/util/valid_hostname.in b/postfix/util/valid_hostname.in new file mode 100644 index 000000000..78a6f2419 --- /dev/null +++ b/postfix/util/valid_hostname.in @@ -0,0 +1,28 @@ +123456789012345678901234567890123456789012345678901234567890123 +1234567890123456789012345678901234567890123456789012345678901234 +a.123456789012345678901234567890123456789012345678901234567890123.b +a.1234567890123456789012345678901234567890123456789012345678901234.b +1.2.3.4 +321.255.255.255 +0.0.0.0 +255.255.255.255 +0.255.255.255 +1.2.3.321 +1.2.3 +1.2.3.4.5 +1..2.3.4 +.1.2.3.4 +1.2.3.4.5. +1 +. + +321 +f +f.2.3.4 +1f.2.3.4 +f1.2.3.4 +1.2f.3.4 +1.f2.3.4 +1.2.3.4f +1.2.3.f4 +1.2.3.f diff --git a/postfix/util/valid_hostname.ref b/postfix/util/valid_hostname.ref new file mode 100644 index 000000000..44be99fe4 --- /dev/null +++ b/postfix/util/valid_hostname.ref @@ -0,0 +1,72 @@ +./valid_hostname: testing: "123456789012345678901234567890123456789012345678901234567890123" +./valid_hostname: warning: valid_hostname: numeric hostname: 123456789012345678901234567890123456789012345678901234567890123 +./valid_hostname: warning: valid_hostaddr: invalid octet value: 123456789012345678901234567890123456789012345678901234567890123 +./valid_hostname: testing: "1234567890123456789012345678901234567890123456789012345678901234" +./valid_hostname: warning: valid_hostname: hostname label too long: 1234567890123456789012345678901234567890123456789012345678901234 +./valid_hostname: warning: valid_hostaddr: invalid octet value: 1234567890123456789012345678901234567890123456789012345678901234 +./valid_hostname: testing: "a.123456789012345678901234567890123456789012345678901234567890123.b" +./valid_hostname: warning: valid_hostaddr: invalid character 97(decimal): a.123456789012345678901234567890123456789012345678901234567890123.b +./valid_hostname: testing: "a.1234567890123456789012345678901234567890123456789012345678901234.b" +./valid_hostname: warning: valid_hostname: hostname label too long: a.1234567890123456789012345678901234567890123456789012345678901234.b +./valid_hostname: warning: valid_hostaddr: invalid character 97(decimal): a.1234567890123456789012345678901234567890123456789012345678901234.b +./valid_hostname: testing: "1.2.3.4" +./valid_hostname: warning: valid_hostname: numeric hostname: 1.2.3.4 +./valid_hostname: testing: "321.255.255.255" +./valid_hostname: warning: valid_hostname: numeric hostname: 321.255.255.255 +./valid_hostname: warning: valid_hostaddr: invalid octet value: 321.255.255.255 +./valid_hostname: testing: "0.0.0.0" +./valid_hostname: warning: valid_hostname: numeric hostname: 0.0.0.0 +./valid_hostname: warning: valid_hostaddr: bad initial octet value: 0.0.0.0 +./valid_hostname: testing: "255.255.255.255" +./valid_hostname: warning: valid_hostname: numeric hostname: 255.255.255.255 +./valid_hostname: testing: "0.255.255.255" +./valid_hostname: warning: valid_hostname: numeric hostname: 0.255.255.255 +./valid_hostname: warning: valid_hostaddr: bad initial octet value: 0.255.255.255 +./valid_hostname: testing: "1.2.3.321" +./valid_hostname: warning: valid_hostname: numeric hostname: 1.2.3.321 +./valid_hostname: warning: valid_hostaddr: invalid octet value: 1.2.3.321 +./valid_hostname: testing: "1.2.3" +./valid_hostname: warning: valid_hostname: numeric hostname: 1.2.3 +./valid_hostname: warning: valid_hostaddr: invalid octet count: 1.2.3 +./valid_hostname: testing: "1.2.3.4.5" +./valid_hostname: warning: valid_hostname: numeric hostname: 1.2.3.4.5 +./valid_hostname: warning: valid_hostaddr: invalid octet count: 1.2.3.4.5 +./valid_hostname: testing: "1..2.3.4" +./valid_hostname: warning: valid_hostname: misplaced delimiter: 1..2.3.4 +./valid_hostname: warning: valid_hostaddr: misplaced dot: 1..2.3.4 +./valid_hostname: testing: ".1.2.3.4" +./valid_hostname: warning: valid_hostname: misplaced delimiter: .1.2.3.4 +./valid_hostname: warning: valid_hostaddr: misplaced dot: .1.2.3.4 +./valid_hostname: testing: "1.2.3.4.5." +./valid_hostname: warning: valid_hostname: misplaced delimiter: 1.2.3.4.5. +./valid_hostname: warning: valid_hostaddr: misplaced dot: 1.2.3.4.5. +./valid_hostname: testing: "1" +./valid_hostname: warning: valid_hostname: numeric hostname: 1 +./valid_hostname: warning: valid_hostaddr: invalid octet count: 1 +./valid_hostname: testing: "." +./valid_hostname: warning: valid_hostname: misplaced delimiter: . +./valid_hostname: warning: valid_hostaddr: misplaced dot: . +./valid_hostname: testing: "" +./valid_hostname: warning: valid_hostname: empty hostname +./valid_hostname: warning: valid_hostaddr: empty address +./valid_hostname: testing: "321" +./valid_hostname: warning: valid_hostname: numeric hostname: 321 +./valid_hostname: warning: valid_hostaddr: invalid octet value: 321 +./valid_hostname: testing: "f" +./valid_hostname: warning: valid_hostaddr: invalid character 102(decimal): f +./valid_hostname: testing: "f.2.3.4" +./valid_hostname: warning: valid_hostaddr: invalid character 102(decimal): f.2.3.4 +./valid_hostname: testing: "1f.2.3.4" +./valid_hostname: warning: valid_hostaddr: invalid character 102(decimal): 1f.2.3.4 +./valid_hostname: testing: "f1.2.3.4" +./valid_hostname: warning: valid_hostaddr: invalid character 102(decimal): f1.2.3.4 +./valid_hostname: testing: "1.2f.3.4" +./valid_hostname: warning: valid_hostaddr: invalid character 102(decimal): 1.2f.3.4 +./valid_hostname: testing: "1.f2.3.4" +./valid_hostname: warning: valid_hostaddr: invalid character 102(decimal): 1.f2.3.4 +./valid_hostname: testing: "1.2.3.4f" +./valid_hostname: warning: valid_hostaddr: invalid character 102(decimal): 1.2.3.4f +./valid_hostname: testing: "1.2.3.f4" +./valid_hostname: warning: valid_hostaddr: invalid character 102(decimal): 1.2.3.f4 +./valid_hostname: testing: "1.2.3.f" +./valid_hostname: warning: valid_hostaddr: invalid character 102(decimal): 1.2.3.f diff --git a/postfix/util/vstream.c b/postfix/util/vstream.c index 4014e1875..f6da5dbe7 100644 --- a/postfix/util/vstream.c +++ b/postfix/util/vstream.c @@ -87,6 +87,9 @@ /* char *vstream_vfprintf(vp, format, ap) /* char *format; /* va_list *ap; +/* +/* int vstream_peek(stream) +/* VSTREAM *stream; /* DESCRIPTION /* The \fIvstream\fR module implements light-weight buffered I/O /* similar to the standard I/O routines. @@ -215,6 +218,9 @@ /* The argument specifies the file descriptor to be used for writing. /* This feature is limited to double-buffered streams, and makes the /* stream non-seekable. +/* .IP "VSTREAM_CTL_WAITPID_FN (int (*)(pid_t, WAIT_STATUS_T *, int))" +/* A pointer to function that behaves like waitpid(). This information +/* is used by the vstream_pclose() routine. /* .PP /* vstream_fileno() gives access to the file handle associated with /* a buffered stream. With streams that have separate read/write @@ -233,6 +239,9 @@ /* /* vstream_vfprintf() provides an alternate interface /* for formatting an argument list according to a format string. +/* +/* vstream_peek() returns the number of characters that can be +/* read from the named stream without refilling the read buffer. /* DIAGNOSTICS /* Panics: interface violations. Fatal errors: out of memory. /* SEE ALSO @@ -833,6 +842,8 @@ VSTREAM *vstream_fdopen(int fd, int flags) vstream_buf_init(&stream->buf, flags); stream->offset = 0; stream->path = 0; + stream->pid = 0; + stream->waitpid_fn = 0; return (stream); } @@ -869,6 +880,8 @@ int vstream_fclose(VSTREAM *stream) { int err; + if (stream->pid != 0) + msg_panic("vstream_fclose: stream has process"); if ((stream->buf.flags & VSTREAM_FLAG_WRITE_DOUBLE) != 0) vstream_fflush(stream); err = vstream_ferror(stream); @@ -971,6 +984,9 @@ void vstream_control(VSTREAM *stream, int name,...) stream->write_fd = va_arg(ap, int); stream->buf.flags |= VSTREAM_FLAG_NSEEK; break; + case VSTREAM_CTL_WAITPID_FN: + stream->waitpid_fn = va_arg(ap, VSTREAM_WAITPID_FN); + break; default: msg_panic("%s: bad name %d", myname, name); } @@ -984,3 +1000,16 @@ VSTREAM *vstream_vfprintf(VSTREAM *vp, const char *format, va_list ap) vbuf_print(&vp->buf, format, ap); return (vp); } + +/* vstream_peek - peek at a stream */ + +int vstream_peek(VSTREAM *vp) +{ + if (vp->buf.flags & VSTREAM_FLAG_READ) { + return (-vp->buf.cnt); + } else if (vp->buf.flags & VSTREAM_FLAG_DOUBLE) { + return (-vp->read_buf.cnt); + } else { + return (0); + } +} diff --git a/postfix/util/vstream.h b/postfix/util/vstream.h index b7540bdba..68c4fbe20 100644 --- a/postfix/util/vstream.h +++ b/postfix/util/vstream.h @@ -27,6 +27,7 @@ * official interface and can change without prior notice. */ typedef int (*VSTREAM_FN) (int, void *, unsigned); +typedef int (*VSTREAM_WAITPID_FN) (pid_t, WAIT_STATUS_T *, int); typedef struct VSTREAM { VBUF buf; /* generic intelligent buffer */ @@ -39,6 +40,8 @@ typedef struct VSTREAM { int write_fd; /* write channel (double-buffered) */ VBUF read_buf; /* read buffer (double-buffered) */ VBUF write_buf; /* write buffer (double-buffered) */ + pid_t pid; /* vstream_popen/close() */ + VSTREAM_WAITPID_FN waitpid_fn; /* vstream_popen/close() */ } VSTREAM; extern VSTREAM vstream_fstd[]; /* pre-defined streams */ @@ -94,6 +97,7 @@ extern void vstream_control(VSTREAM *, int,...); #define VSTREAM_CTL_DOUBLE 4 #define VSTREAM_CTL_READ_FD 5 #define VSTREAM_CTL_WRITE_FD 6 +#define VSTREAM_CTL_WAITPID_FN 7 extern VSTREAM *vstream_printf(const char *,...); extern VSTREAM *vstream_fprintf(VSTREAM *, const char *,...); @@ -103,6 +107,8 @@ extern int vstream_pclose(VSTREAM *); extern VSTREAM *vstream_vfprintf(VSTREAM *, const char *, va_list); +extern int vstream_peek(VSTREAM *); + /* LICENSE /* .ad /* .fi diff --git a/postfix/util/vstream_popen.c b/postfix/util/vstream_popen.c index 739af60b5..d6cf60f37 100644 --- a/postfix/util/vstream_popen.c +++ b/postfix/util/vstream_popen.c @@ -74,7 +74,7 @@ VSTREAM *vstream_popen(const char *command, int flags) { VSTREAM *stream; int sockfd[2]; - int pid; + pid_t pid; int fd; if (socketpair(AF_UNIX, SOCK_STREAM, 0, sockfd) < 0)